Skip to content

Commit

Permalink
WIP: Add Sarif output support to FSharpLint.Console
Browse files Browse the repository at this point in the history
  • Loading branch information
Numpsy committed Mar 10, 2024
1 parent ee4ecfb commit ba63f6c
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 5 deletions.
3 changes: 2 additions & 1 deletion paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ nuget Ionide.ProjInfo == 0.61.3
nuget Ionide.ProjInfo.Sln == 0.61.3
nuget FSharp.Core ~> 6.0
nuget nunit ~> 3.0
nuget Sarif.Sdk
nuget System.Reactive ~> 5
nuget NUnit3TestAdapter
nuget Microsoft.NET.Test.Sdk 17.7.2
Expand Down Expand Up @@ -60,4 +61,4 @@ group Build
nuget Fake.Core.UserInput
nuget Fake.IO.FileSystem
nuget Fake.DotNet.MsBuild
nuget Fake.Api.GitHub
nuget Fake.Api.GitHub
15 changes: 12 additions & 3 deletions paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ NUGET
Microsoft.Diagnostics.NETCore.Client (>= 0.2.410101)
System.Collections.Immutable (>= 6.0)
System.Runtime.CompilerServices.Unsafe (>= 6.0)
Microsoft.Diagnostics.Tracing.EventRegister (1.1.28)
Microsoft.Diagnostics.Tracing.TraceEvent (3.1.7)
Microsoft.Win32.Registry (>= 4.4)
System.Runtime.CompilerServices.Unsafe (>= 5.0)
Expand Down Expand Up @@ -250,6 +251,15 @@ NUGET
runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
Sarif.Sdk (4.5.4)
Microsoft.Diagnostics.Tracing.EventRegister (>= 1.1.28)
Microsoft.Diagnostics.Tracing.TraceEvent (>= 3.1.3)
Newtonsoft.Json (>= 9.0.1)
System.Collections.Immutable (>= 5.0)
System.Diagnostics.Debug (>= 4.3)
System.IO.FileSystem.Primitives (>= 4.3)
System.Text.Encoding.CodePages (>= 4.3)
System.Text.Encoding.Extensions (>= 4.3)
SemanticVersioning (2.0.2) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0))
System.Buffers (4.5.1)
System.CodeDom (8.0) - copy_local: false
Expand Down Expand Up @@ -570,7 +580,6 @@ NUGET
System.Security.Cryptography.Primitives (>= 4.3)
System.Text.Encoding (>= 4.3)
System.Security.Cryptography.Cng (5.0) - copy_local: false
System.Formats.Asn1 (>= 5.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp3.0))
System.Security.Cryptography.Csp (4.3)
Microsoft.NETCore.Platforms (>= 1.1)
System.IO (>= 4.3)
Expand Down Expand Up @@ -663,9 +672,9 @@ NUGET
Microsoft.NETCore.Targets (>= 1.1)
System.Runtime (>= 4.3)
System.Text.Encoding (>= 4.3)
System.Text.Encodings.Web (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net5.0))
System.Text.Encodings.Web (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0))
System.Runtime.CompilerServices.Unsafe (>= 6.0)
System.Text.Json (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net5.0))
System.Text.Json (8.0) - copy_local: false, restriction: || (== net6.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0))
System.Runtime.CompilerServices.Unsafe (>= 6.0)
System.Text.Encodings.Web (>= 8.0)
System.Threading (4.3)
Expand Down
1 change: 1 addition & 0 deletions src/FSharpLint.Console/FSharpLint.Console.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<ItemGroup>
<Compile Include="Output.fs" />
<Compile Include="Sarif.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

Expand Down
11 changes: 11 additions & 0 deletions src/FSharpLint.Console/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ type private FileType =
type private ToolArgs =
| [<AltCommandLine("-f")>] Format of OutputFormat
| [<CliPrefix(CliPrefix.None)>] Lint of ParseResults<LintArgs>
| [<Unique>] Report of string
| [<Unique>] Code_Root of string
| Version
with
interface IArgParserTemplate with
member this.Usage =
match this with
| Format _ -> "Output format of the linter."
| Lint _ -> "Runs FSharpLint against a file or a collection of files."
| Report _ -> "Write the result messages to a (sarif) report file."
| 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."
| Version -> "Prints current version."

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

let reportPath = arguments.TryGetResult Report
let codeRoot = arguments.TryGetResult Code_Root

match arguments.GetSubCommand() with
| Lint lintArgs ->

let handleLintResult = function
| LintResult.Success(warnings) ->
String.Format(Resources.GetString("ConsoleFinished"), List.length warnings)
|> output.WriteInfo

reportPath
|> Option.iter (fun report -> Sarif.writeReport warnings codeRoot report output)

if not (List.isEmpty warnings) then exitCode <- -1
| LintResult.Failure(failure) ->
handleError failure.Description
Expand Down
103 changes: 103 additions & 0 deletions src/FSharpLint.Console/Sarif.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
module internal Sarif

open FSharpLint.Framework
open System.IO
open System
open Microsoft.CodeAnalysis.Sarif
open Microsoft.CodeAnalysis.Sarif.Writers
open FSharpLint.Console.Output

let writeReport (results: Suggestion.LintWarning list) (codeRoot: string option) (report: string) (logger: IOutput) =
try
let codeRoot =
match codeRoot with
| None -> Directory.GetCurrentDirectory() |> Uri
| Some root -> Path.GetFullPath root |> Uri

// Construct full path to ensure path separators are normalized.
let report = Path.GetFullPath report
// Ensure the parent directory exists
let reportFile = FileInfo(report)
reportFile.Directory.Create()

let driver = ToolComponent()
driver.Name <- "FSharpLint.Console"
driver.InformationUri <- Uri("https://fsprojects.github.io/FSharpLint/")
driver.Version <- string<Version> (System.Reflection.Assembly.GetExecutingAssembly().GetName().Version)
let tool = Tool()
tool.Driver <- driver
let run = Run()
run.Tool <- tool

use sarifLogger =
new SarifLogger(
report,
logFilePersistenceOptions =
(FilePersistenceOptions.PrettyPrint ||| FilePersistenceOptions.ForceOverwrite),
run = run,
levels = BaseLogger.ErrorWarningNote,
kinds = BaseLogger.Fail,
closeWriterOnDispose = true
)

sarifLogger.AnalysisStarted()

for analyzerResult in results do
let reportDescriptor = ReportingDescriptor()
reportDescriptor.Id <- analyzerResult.RuleIdentifier
reportDescriptor.Name <- analyzerResult.RuleName

(*
analyzerResult.ShortDescription
|> Option.iter (fun shortDescription ->
reportDescriptor.ShortDescription <-
MultiformatMessageString(shortDescription, shortDescription, dict [])
)
*)

let helpUri = $"https://fsprojects.github.io/FSharpLint/how-tos/rules/%s{analyzerResult.RuleIdentifier}.html"
reportDescriptor.HelpUri <- Uri(helpUri)

let result = Result()
result.RuleId <- reportDescriptor.Id

(*
result.Level <-
match analyzerResult.Message.Severity with
| Severity.Info -> FailureLevel.Note
| Severity.Hint -> FailureLevel.Note
| Severity.Warning -> FailureLevel.Warning
| Severity.Error -> FailureLevel.Error
*)
result.Level <- FailureLevel.Warning

let msg = Message()
msg.Text <- analyzerResult.Details.Message
result.Message <- msg

let physicalLocation = PhysicalLocation()

physicalLocation.ArtifactLocation <-
let al = ArtifactLocation()
al.Uri <- codeRoot.MakeRelativeUri(Uri(analyzerResult.Details.Range.FileName))
al

physicalLocation.Region <-
let r = Region()
r.StartLine <- analyzerResult.Details.Range.StartLine
r.StartColumn <- analyzerResult.Details.Range.StartColumn + 1
r.EndLine <- analyzerResult.Details.Range.EndLine
r.EndColumn <- analyzerResult.Details.Range.EndColumn + 1
r

let location: Location = Location()
location.PhysicalLocation <- physicalLocation
result.Locations <- [| location |]

sarifLogger.Log(reportDescriptor, result, System.Nullable())

sarifLogger.AnalysisStopped(RuntimeConditions.None)

sarifLogger.Dispose()
with ex ->
logger.WriteError($"Could not write sarif to %s{report}: %s{ex.Message}")
3 changes: 2 additions & 1 deletion src/FSharpLint.Console/paket.references
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Argu
FSharp.Compiler.Service
FSharp.Core
Microsoft.SourceLink.GitHub
Microsoft.SourceLink.GitHub
Sarif.Sdk import_targets: false

0 comments on commit ba63f6c

Please sign in to comment.