From fbfd15d420753cbf2d55f525caa2bbf9032b2744 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 26 Oct 2023 13:36:01 +0200 Subject: [PATCH 1/2] Accept direct fsc arguments as input. --- src/FSharp.Analyzers.Cli/Program.fs | 79 ++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index d0af147..55ed91f 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -17,6 +17,7 @@ type Arguments = | Ignore_Files of string list | Exclude_Analyzer of string list | Report of string + | FSC_Args of string | Verbose interface IArgParserTemplate with @@ -30,6 +31,7 @@ type Arguments = | Exclude_Analyzer _ -> "The names of analyzers that should not be executed." | Report _ -> "Write the result messages to a (sarif) report file." | Verbose -> "Verbose logging." + | FSC_Args _ -> "Pass in the raw fsc compiler arguments. Cannot be combined with the `--project` flag." let mutable verbose = false @@ -71,16 +73,18 @@ let loadProject toolsPath projPath = return fcsPo } -let runProject (client: Client) toolsPath proj (globs: Glob list) = +let runProjectAux + (client: Client) + (fsharpOptions: FSharpProjectOptions) + (ignoreFiles: Glob list) + = async { - let path = Path.Combine(Environment.CurrentDirectory, proj) |> Path.GetFullPath - let! option = loadProject toolsPath path - let! checkProjectResults = fcs.ParseAndCheckProject(option) + let! checkProjectResults = fcs.ParseAndCheckProject(fsharpOptions) let! messagesPerAnalyzer = - option.SourceFiles + fsharpOptions.SourceFiles |> Array.filter (fun file -> - match globs |> List.tryFind (fun g -> g.IsMatch file) with + match ignoreFiles |> List.tryFind (fun g -> g.IsMatch file) with | Some g -> printInfo $"Ignoring file %s{file} for pattern %s{g.Pattern}" false @@ -90,7 +94,7 @@ let runProject (client: Client) toolsPath proj let fileContent = File.ReadAllText fileName let sourceText = SourceText.ofString fileContent - Utils.typeCheckFile fcs printError option fileName (Utils.SourceOfSource.SourceText sourceText) + Utils.typeCheckFile fcs printError fsharpOptions fileName (Utils.SourceOfSource.SourceText sourceText) |> Option.map (Utils.createContext checkProjectResults fileName sourceText) ) |> Array.map (fun ctx -> @@ -107,6 +111,52 @@ let runProject (client: Client) toolsPath proj ] } +let runProject (client: Client) toolsPath proj (globs: Glob list) = + async { + let path = Path.Combine(Environment.CurrentDirectory, proj) |> Path.GetFullPath + let! option = loadProject toolsPath path + return! runProjectAux client option globs + } + +let fsharpFiles = set [| ".fs"; ".fsi"; ".fsx" |] + +let isFSharpFile (file: string) = + Seq.exists (fun (ext: string) -> file.EndsWith ext) fsharpFiles + +let runFscArgs (client: Client) (fscArgs: string) (globs: Glob list) = + let fscArgs = fscArgs.Split(';', StringSplitOptions.RemoveEmptyEntries) + + let sourceFiles = + fscArgs + |> Array.choose (fun (argument: string) -> + // We make an absolute path because the sarif report cannot deal properly with relative path. + let path = Path.Combine(Directory.GetCurrentDirectory(), argument) + + if not (isFSharpFile path) || not (File.Exists path) then + None + else + Some path + ) + + let otherOptions = fscArgs |> Array.filter (fun line -> not (isFSharpFile line)) + + let projectOptions = + { + ProjectFileName = "Project" + ProjectId = None + SourceFiles = sourceFiles + OtherOptions = otherOptions + ReferencedProjects = [||] + IsIncompleteTypeCheckEnvironment = false + UseScriptResolutionRules = false + LoadTime = DateTime.Now + UnresolvedReferences = None + OriginalLoadReferences = [] + Stamp = None + } + + runProjectAux client projectOptions globs + let printMessages failOnWarnings (msgs: AnalyzerMessage list) = if verbose then printfn "" @@ -219,7 +269,7 @@ let writeReport (results: AnalyzerMessage list option) (report: string) = sarifLogger.Dispose() with ex -> - let details = if not verbose then "" else $" %s{ex.Message}" + let details = if not verbose then "" else $" %A{ex}" printfn $"Could not write sarif to %s{report}%s{details}" let calculateExitCode failOnWarnings (msgs: AnalyzerMessage list option) : int = @@ -302,20 +352,25 @@ let main argv = printInfo "Registered %d analyzers from %d dlls" analyzers dlls let projOpts = results.TryGetResult <@ Project @> + let fscArgs = results.TryGetResult <@ FSC_Args @> let report = results.TryGetResult <@ Report @> let results = if analyzers = 0 then Some [] else - match projOpts with - | None - | Some [] -> + match projOpts, fscArgs with + | None, None + | Some [], None -> printError "No project given. Use `--project PATH_TO_FSPROJ`. Pass path relative to current directory.%s" None - | Some projects -> + | Some _, Some _ -> + printError "`--project` and `--fsc-args` cannot be combined." + exit 1 + | None, Some fscArgs -> runFscArgs client fscArgs ignoreFiles |> Async.RunSynchronously + | Some projects, None -> let runProj (proj: string) = async { let project = From 23b683b360c1fcddfb018f5aa0d08849212fb26f Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 26 Oct 2023 13:40:00 +0200 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 421d174..bf18c43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.17.0] - 2023-10-26 ### Changed * [Use fixed version of FCS and FSharp.Core](https://github.com/ionide/FSharp.Analyzers.SDK/pull/127) (thanks @nojaf!) * [Allow to specify multiple analyzers-paths](https://github.com/ionide/FSharp.Analyzers.SDK/pull/128) (thanks @nojaf!) +### Added +* [Accept direct fsc arguments as input](https://github.com/ionide/FSharp.Analyzers.SDK/pull/129) (thanks @nojaf!) + ## [0.16.0] - 2023-10-16 ### Added