Skip to content

Commit 02456e1

Browse files
Numpsyxperiandri
authored andcommitted
WIP: Add Sarif output support to FSharpLint.Console
This is using the Microsoft Sarif.Sdk to write Sarif files.
1 parent e078a76 commit 02456e1

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
2424
<PackageVersion Include="NUnit" Version="3.14.0" />
2525
<PackageVersion Include="NUnit3TestAdapter" Version="5.0.0" />
26+
<PackageVersion Include="Sarif.Sdk" Version="4.5.4" />
2627
<PackageVersion Include="System.Reactive" Version="6.0.1" />
2728
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
2829
</ItemGroup>

src/FSharpLint.Console/FSharpLint.Console.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323

2424
<ItemGroup>
2525
<Compile Include="Output.fs" />
26+
<Compile Include="Sarif.fs" />
2627
<Compile Include="Program.fs" />
2728
</ItemGroup>
2829

2930
<ItemGroup>
3031
<PackageReference Include="Argu" />
3132
<PackageReference Include="FSharp.Core" />
33+
<PackageReference Include="Sarif.Sdk" />
3234
</ItemGroup>
3335

3436
<ItemGroup>

src/FSharpLint.Console/Program.fs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ type internal 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
@@ -91,13 +95,20 @@ let private start (arguments:ParseResults<ToolArgs>) (toolsPath:Ionide.ProjInfo.
9195
output.WriteError str
9296
exitCode <- -1
9397

98+
let reportPath = arguments.TryGetResult Report
99+
let codeRoot = arguments.TryGetResult Code_Root
100+
94101
match arguments.GetSubCommand() with
95102
| Lint lintArgs ->
96103

97104
let handleLintResult = function
98105
| LintResult.Success(warnings) ->
99106
String.Format(Resources.GetString("ConsoleFinished"), List.length warnings)
100107
|> output.WriteInfo
108+
109+
reportPath
110+
|> Option.iter (fun report -> Sarif.writeReport warnings codeRoot report output)
111+
101112
if not (List.isEmpty warnings) then exitCode <- -1
102113
| LintResult.Failure(failure) ->
103114
handleError failure.Description

src/FSharpLint.Console/Sarif.fs

Lines changed: 103 additions & 0 deletions
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}")

0 commit comments

Comments
 (0)