Skip to content

Commit 60b8a02

Browse files
committed
WIP: Add Sarif output support to FSharpLint.Console
This is using the Microsoft Sarif.Sdk to write Sarif files. The "import_targets: false" in paket.references is a workaround for this build error from a build task in the transitive microsoft.diagnostics.tracing.eventregister nuget package dependency.: Exec: %packages%\microsoft.diagnostics.tracing.eventregister\1.1.28\build\Microsoft.Diagnostics.Tracing.EventRegister.targets(132,5): error MSB3073: The command ""%packages%\microsoft.diagnostics.tracing.eventregister\1.1.28\build\eventRegister.exe" -DumpRegDlls @"S:\GitHubForks\FSharpLint\src\FSharpLint.Console\bin\Release\net6.0\dotnet-fsharplint.eventRegister.rsp" "S:\GitHubForks\FSharpLint\src\FSharpLint.Console\bin\Release\net6.0\dotnet-fsharplint.dll" " exited with code 1.
1 parent ee4ecfb commit 60b8a02

File tree

6 files changed

+131
-5
lines changed

6 files changed

+131
-5
lines changed

paket.dependencies

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ nuget Ionide.ProjInfo == 0.61.3
1414
nuget Ionide.ProjInfo.Sln == 0.61.3
1515
nuget FSharp.Core ~> 6.0
1616
nuget nunit ~> 3.0
17+
nuget Sarif.Sdk
1718
nuget System.Reactive ~> 5
1819
nuget NUnit3TestAdapter
1920
nuget Microsoft.NET.Test.Sdk 17.7.2
@@ -60,4 +61,4 @@ group Build
6061
nuget Fake.Core.UserInput
6162
nuget Fake.IO.FileSystem
6263
nuget Fake.DotNet.MsBuild
63-
nuget Fake.Api.GitHub
64+
nuget Fake.Api.GitHub

paket.lock

+12-3
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ NUGET
143143
Microsoft.Diagnostics.NETCore.Client (>= 0.2.410101)
144144
System.Collections.Immutable (>= 6.0)
145145
System.Runtime.CompilerServices.Unsafe (>= 6.0)
146+
Microsoft.Diagnostics.Tracing.EventRegister (1.1.28)
146147
Microsoft.Diagnostics.Tracing.TraceEvent (3.1.7)
147148
Microsoft.Win32.Registry (>= 4.4)
148149
System.Runtime.CompilerServices.Unsafe (>= 5.0)
@@ -250,6 +251,15 @@ NUGET
250251
runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
251252
runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
252253
runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
254+
Sarif.Sdk (4.5.4)
255+
Microsoft.Diagnostics.Tracing.EventRegister (>= 1.1.28)
256+
Microsoft.Diagnostics.Tracing.TraceEvent (>= 3.1.3)
257+
Newtonsoft.Json (>= 9.0.1)
258+
System.Collections.Immutable (>= 5.0)
259+
System.Diagnostics.Debug (>= 4.3)
260+
System.IO.FileSystem.Primitives (>= 4.3)
261+
System.Text.Encoding.CodePages (>= 4.3)
262+
System.Text.Encoding.Extensions (>= 4.3)
253263
SemanticVersioning (2.0.2) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0))
254264
System.Buffers (4.5.1)
255265
System.CodeDom (8.0) - copy_local: false
@@ -570,7 +580,6 @@ NUGET
570580
System.Security.Cryptography.Primitives (>= 4.3)
571581
System.Text.Encoding (>= 4.3)
572582
System.Security.Cryptography.Cng (5.0) - copy_local: false
573-
System.Formats.Asn1 (>= 5.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp3.0))
574583
System.Security.Cryptography.Csp (4.3)
575584
Microsoft.NETCore.Platforms (>= 1.1)
576585
System.IO (>= 4.3)
@@ -663,9 +672,9 @@ NUGET
663672
Microsoft.NETCore.Targets (>= 1.1)
664673
System.Runtime (>= 4.3)
665674
System.Text.Encoding (>= 4.3)
666-
System.Text.Encodings.Web (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net5.0))
675+
System.Text.Encodings.Web (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0))
667676
System.Runtime.CompilerServices.Unsafe (>= 6.0)
668-
System.Text.Json (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net5.0))
677+
System.Text.Json (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0))
669678
System.Runtime.CompilerServices.Unsafe (>= 6.0)
670679
System.Text.Encodings.Web (>= 8.0)
671680
System.Threading (4.3)

src/FSharpLint.Console/FSharpLint.Console.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
<ItemGroup>
1919
<Compile Include="Output.fs" />
20+
<Compile Include="Sarif.fs" />
2021
<Compile Include="Program.fs" />
2122
</ItemGroup>
2223

src/FSharpLint.Console/Program.fs

+11
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ type private FileType =
2424
type private ToolArgs =
2525
| [<AltCommandLine("-f")>] Format of OutputFormat
2626
| [<CliPrefix(CliPrefix.None)>] Lint of ParseResults<LintArgs>
27+
| [<Unique>] Report of string
28+
| [<Unique>] Code_Root of string
2729
| Version
2830
with
2931
interface IArgParserTemplate with
3032
member this.Usage =
3133
match this with
3234
| Format _ -> "Output format of the linter."
3335
| Lint _ -> "Runs FSharpLint against a file or a collection of files."
36+
| Report _ -> "Write the result messages to a (sarif) report file."
37+
| Code_Root _ -> "Root of the current code repository, used in the sarif report to construct the relative file path. The current working directory is used by default."
3438
| Version -> "Prints current version."
3539

3640
// TODO: investigate erroneous warning on this type definition
@@ -94,13 +98,20 @@ let private start (arguments:ParseResults<ToolArgs>) (toolsPath:Ionide.ProjInfo.
9498
output.WriteError str
9599
exitCode <- -1
96100

101+
let reportPath = arguments.TryGetResult Report
102+
let codeRoot = arguments.TryGetResult Code_Root
103+
97104
match arguments.GetSubCommand() with
98105
| Lint lintArgs ->
99106

100107
let handleLintResult = function
101108
| LintResult.Success(warnings) ->
102109
String.Format(Resources.GetString("ConsoleFinished"), List.length warnings)
103110
|> output.WriteInfo
111+
112+
reportPath
113+
|> Option.iter (fun report -> Sarif.writeReport warnings codeRoot report output)
114+
104115
if not (List.isEmpty warnings) then exitCode <- -1
105116
| LintResult.Failure(failure) ->
106117
handleError failure.Description

src/FSharpLint.Console/Sarif.fs

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
module internal Sarif
2+
3+
open FSharpLint.Framework
4+
open System.IO
5+
open System
6+
open Microsoft.CodeAnalysis.Sarif
7+
open Microsoft.CodeAnalysis.Sarif.Writers
8+
open FSharpLint.Console.Output
9+
10+
let writeReport (results: Suggestion.LintWarning list) (codeRoot: string option) (report: string) (logger: IOutput) =
11+
try
12+
let codeRoot =
13+
match codeRoot with
14+
| None -> Directory.GetCurrentDirectory() |> Uri
15+
| Some root -> Path.GetFullPath root |> Uri
16+
17+
// Construct full path to ensure path separators are normalized.
18+
let report = Path.GetFullPath report
19+
// Ensure the parent directory exists
20+
let reportFile = FileInfo(report)
21+
reportFile.Directory.Create()
22+
23+
let driver = ToolComponent()
24+
driver.Name <- "FSharpLint.Console"
25+
driver.InformationUri <- Uri("https://fsprojects.github.io/FSharpLint/")
26+
driver.Version <- string<Version> (System.Reflection.Assembly.GetExecutingAssembly().GetName().Version)
27+
let tool = Tool()
28+
tool.Driver <- driver
29+
let run = Run()
30+
run.Tool <- tool
31+
32+
use sarifLogger =
33+
new SarifLogger(
34+
report,
35+
logFilePersistenceOptions =
36+
(FilePersistenceOptions.PrettyPrint ||| FilePersistenceOptions.ForceOverwrite),
37+
run = run,
38+
levels = BaseLogger.ErrorWarningNote,
39+
kinds = BaseLogger.Fail,
40+
closeWriterOnDispose = true
41+
)
42+
43+
sarifLogger.AnalysisStarted()
44+
45+
for analyzerResult in results do
46+
let reportDescriptor = ReportingDescriptor()
47+
reportDescriptor.Id <- analyzerResult.RuleIdentifier
48+
reportDescriptor.Name <- analyzerResult.RuleName
49+
50+
(*
51+
analyzerResult.ShortDescription
52+
|> Option.iter (fun shortDescription ->
53+
reportDescriptor.ShortDescription <-
54+
MultiformatMessageString(shortDescription, shortDescription, dict [])
55+
)
56+
*)
57+
58+
let helpUri = $"https://fsprojects.github.io/FSharpLint/how-tos/rules/%s{analyzerResult.RuleIdentifier}.html"
59+
reportDescriptor.HelpUri <- Uri(helpUri)
60+
61+
let result = Result()
62+
result.RuleId <- reportDescriptor.Id
63+
64+
(*
65+
result.Level <-
66+
match analyzerResult.Message.Severity with
67+
| Severity.Info -> FailureLevel.Note
68+
| Severity.Hint -> FailureLevel.Note
69+
| Severity.Warning -> FailureLevel.Warning
70+
| Severity.Error -> FailureLevel.Error
71+
*)
72+
result.Level <- FailureLevel.Warning
73+
74+
let msg = Message()
75+
msg.Text <- analyzerResult.Details.Message
76+
result.Message <- msg
77+
78+
let physicalLocation = PhysicalLocation()
79+
80+
physicalLocation.ArtifactLocation <-
81+
let al = ArtifactLocation()
82+
al.Uri <- codeRoot.MakeRelativeUri(Uri(analyzerResult.Details.Range.FileName))
83+
al
84+
85+
physicalLocation.Region <-
86+
let r = Region()
87+
r.StartLine <- analyzerResult.Details.Range.StartLine
88+
r.StartColumn <- analyzerResult.Details.Range.StartColumn + 1
89+
r.EndLine <- analyzerResult.Details.Range.EndLine
90+
r.EndColumn <- analyzerResult.Details.Range.EndColumn + 1
91+
r
92+
93+
let location: Location = Location()
94+
location.PhysicalLocation <- physicalLocation
95+
result.Locations <- [| location |]
96+
97+
sarifLogger.Log(reportDescriptor, result, System.Nullable())
98+
99+
sarifLogger.AnalysisStopped(RuntimeConditions.None)
100+
101+
sarifLogger.Dispose()
102+
with ex ->
103+
logger.WriteError($"Could not write sarif to %s{report}: %s{ex.Message}")
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Argu
22
FSharp.Compiler.Service
33
FSharp.Core
4-
Microsoft.SourceLink.GitHub
4+
Microsoft.SourceLink.GitHub
5+
Sarif.Sdk import_targets: false

0 commit comments

Comments
 (0)