Skip to content

Commit fbfd15d

Browse files
committed
Accept direct fsc arguments as input.
1 parent a804a3f commit fbfd15d

File tree

1 file changed

+67
-12
lines changed

1 file changed

+67
-12
lines changed

src/FSharp.Analyzers.Cli/Program.fs

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Arguments =
1717
| Ignore_Files of string list
1818
| Exclude_Analyzer of string list
1919
| Report of string
20+
| FSC_Args of string
2021
| Verbose
2122

2223
interface IArgParserTemplate with
@@ -30,6 +31,7 @@ type Arguments =
3031
| Exclude_Analyzer _ -> "The names of analyzers that should not be executed."
3132
| Report _ -> "Write the result messages to a (sarif) report file."
3233
| Verbose -> "Verbose logging."
34+
| FSC_Args _ -> "Pass in the raw fsc compiler arguments. Cannot be combined with the `--project` flag."
3335

3436
let mutable verbose = false
3537

@@ -71,16 +73,18 @@ let loadProject toolsPath projPath =
7173
return fcsPo
7274
}
7375

74-
let runProject (client: Client<CliAnalyzerAttribute, CliContext>) toolsPath proj (globs: Glob list) =
76+
let runProjectAux
77+
(client: Client<CliAnalyzerAttribute, CliContext>)
78+
(fsharpOptions: FSharpProjectOptions)
79+
(ignoreFiles: Glob list)
80+
=
7581
async {
76-
let path = Path.Combine(Environment.CurrentDirectory, proj) |> Path.GetFullPath
77-
let! option = loadProject toolsPath path
78-
let! checkProjectResults = fcs.ParseAndCheckProject(option)
82+
let! checkProjectResults = fcs.ParseAndCheckProject(fsharpOptions)
7983

8084
let! messagesPerAnalyzer =
81-
option.SourceFiles
85+
fsharpOptions.SourceFiles
8286
|> Array.filter (fun file ->
83-
match globs |> List.tryFind (fun g -> g.IsMatch file) with
87+
match ignoreFiles |> List.tryFind (fun g -> g.IsMatch file) with
8488
| Some g ->
8589
printInfo $"Ignoring file %s{file} for pattern %s{g.Pattern}"
8690
false
@@ -90,7 +94,7 @@ let runProject (client: Client<CliAnalyzerAttribute, CliContext>) toolsPath proj
9094
let fileContent = File.ReadAllText fileName
9195
let sourceText = SourceText.ofString fileContent
9296

93-
Utils.typeCheckFile fcs printError option fileName (Utils.SourceOfSource.SourceText sourceText)
97+
Utils.typeCheckFile fcs printError fsharpOptions fileName (Utils.SourceOfSource.SourceText sourceText)
9498
|> Option.map (Utils.createContext checkProjectResults fileName sourceText)
9599
)
96100
|> Array.map (fun ctx ->
@@ -107,6 +111,52 @@ let runProject (client: Client<CliAnalyzerAttribute, CliContext>) toolsPath proj
107111
]
108112
}
109113

114+
let runProject (client: Client<CliAnalyzerAttribute, CliContext>) toolsPath proj (globs: Glob list) =
115+
async {
116+
let path = Path.Combine(Environment.CurrentDirectory, proj) |> Path.GetFullPath
117+
let! option = loadProject toolsPath path
118+
return! runProjectAux client option globs
119+
}
120+
121+
let fsharpFiles = set [| ".fs"; ".fsi"; ".fsx" |]
122+
123+
let isFSharpFile (file: string) =
124+
Seq.exists (fun (ext: string) -> file.EndsWith ext) fsharpFiles
125+
126+
let runFscArgs (client: Client<CliAnalyzerAttribute, CliContext>) (fscArgs: string) (globs: Glob list) =
127+
let fscArgs = fscArgs.Split(';', StringSplitOptions.RemoveEmptyEntries)
128+
129+
let sourceFiles =
130+
fscArgs
131+
|> Array.choose (fun (argument: string) ->
132+
// We make an absolute path because the sarif report cannot deal properly with relative path.
133+
let path = Path.Combine(Directory.GetCurrentDirectory(), argument)
134+
135+
if not (isFSharpFile path) || not (File.Exists path) then
136+
None
137+
else
138+
Some path
139+
)
140+
141+
let otherOptions = fscArgs |> Array.filter (fun line -> not (isFSharpFile line))
142+
143+
let projectOptions =
144+
{
145+
ProjectFileName = "Project"
146+
ProjectId = None
147+
SourceFiles = sourceFiles
148+
OtherOptions = otherOptions
149+
ReferencedProjects = [||]
150+
IsIncompleteTypeCheckEnvironment = false
151+
UseScriptResolutionRules = false
152+
LoadTime = DateTime.Now
153+
UnresolvedReferences = None
154+
OriginalLoadReferences = []
155+
Stamp = None
156+
}
157+
158+
runProjectAux client projectOptions globs
159+
110160
let printMessages failOnWarnings (msgs: AnalyzerMessage list) =
111161
if verbose then
112162
printfn ""
@@ -219,7 +269,7 @@ let writeReport (results: AnalyzerMessage list option) (report: string) =
219269

220270
sarifLogger.Dispose()
221271
with ex ->
222-
let details = if not verbose then "" else $" %s{ex.Message}"
272+
let details = if not verbose then "" else $" %A{ex}"
223273
printfn $"Could not write sarif to %s{report}%s{details}"
224274

225275
let calculateExitCode failOnWarnings (msgs: AnalyzerMessage list option) : int =
@@ -302,20 +352,25 @@ let main argv =
302352
printInfo "Registered %d analyzers from %d dlls" analyzers dlls
303353

304354
let projOpts = results.TryGetResult <@ Project @>
355+
let fscArgs = results.TryGetResult <@ FSC_Args @>
305356
let report = results.TryGetResult <@ Report @>
306357

307358
let results =
308359
if analyzers = 0 then
309360
Some []
310361
else
311-
match projOpts with
312-
| None
313-
| Some [] ->
362+
match projOpts, fscArgs with
363+
| None, None
364+
| Some [], None ->
314365
printError
315366
"No project given. Use `--project PATH_TO_FSPROJ`. Pass path relative to current directory.%s"
316367

317368
None
318-
| Some projects ->
369+
| Some _, Some _ ->
370+
printError "`--project` and `--fsc-args` cannot be combined."
371+
exit 1
372+
| None, Some fscArgs -> runFscArgs client fscArgs ignoreFiles |> Async.RunSynchronously
373+
| Some projects, None ->
319374
let runProj (proj: string) =
320375
async {
321376
let project =

0 commit comments

Comments
 (0)