Skip to content

WIP: Add Sarif output support to FSharpLint.Console #702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import_targets: false is because I was initially getting this error from builds after adding the nuget package reference:

Exec: EXEC(0,0): error : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
Exec: C:\Users\Richard Webb\.nuget\packages\microsoft.diagnostics.tracing.eventregister\1.1.28\build\Microsoft.Diagnostics.Tracing.EventRegister.targets(132,5): error MSB3073: The command ""C:\Users\Richard Webb\.nuget\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.

And I'm not sure why (I haven't used any of that eventregister stuff myself for years, and don't recall the defaults of it)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add this info in the commit msg