@@ -17,6 +17,7 @@ type Arguments =
17
17
| Ignore_ Files of string list
18
18
| Exclude_ Analyzer of string list
19
19
| Report of string
20
+ | FSC_ Args of string
20
21
| Verbose
21
22
22
23
interface IArgParserTemplate with
@@ -30,6 +31,7 @@ type Arguments =
30
31
| Exclude_ Analyzer _ -> " The names of analyzers that should not be executed."
31
32
| Report _ -> " Write the result messages to a (sarif) report file."
32
33
| Verbose -> " Verbose logging."
34
+ | FSC_ Args _ -> " Pass in the raw fsc compiler arguments. Cannot be combined with the `--project` flag."
33
35
34
36
let mutable verbose = false
35
37
@@ -71,16 +73,18 @@ let loadProject toolsPath projPath =
71
73
return fcsPo
72
74
}
73
75
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
+ =
75
81
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)
79
83
80
84
let! messagesPerAnalyzer =
81
- option .SourceFiles
85
+ fsharpOptions .SourceFiles
82
86
|> 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
84
88
| Some g ->
85
89
printInfo $" Ignoring file %s {file} for pattern %s {g.Pattern}"
86
90
false
@@ -90,7 +94,7 @@ let runProject (client: Client<CliAnalyzerAttribute, CliContext>) toolsPath proj
90
94
let fileContent = File.ReadAllText fileName
91
95
let sourceText = SourceText.ofString fileContent
92
96
93
- Utils.typeCheckFile fcs printError option fileName ( Utils.SourceOfSource.SourceText sourceText)
97
+ Utils.typeCheckFile fcs printError fsharpOptions fileName ( Utils.SourceOfSource.SourceText sourceText)
94
98
|> Option.map ( Utils.createContext checkProjectResults fileName sourceText)
95
99
)
96
100
|> Array.map ( fun ctx ->
@@ -107,6 +111,52 @@ let runProject (client: Client<CliAnalyzerAttribute, CliContext>) toolsPath proj
107
111
]
108
112
}
109
113
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
+
110
160
let printMessages failOnWarnings ( msgs : AnalyzerMessage list ) =
111
161
if verbose then
112
162
printfn " "
@@ -219,7 +269,7 @@ let writeReport (results: AnalyzerMessage list option) (report: string) =
219
269
220
270
sarifLogger.Dispose()
221
271
with ex ->
222
- let details = if not verbose then " " else $" %s {ex.Message }"
272
+ let details = if not verbose then " " else $" %A {ex}"
223
273
printfn $" Could not write sarif to %s {report}%s {details}"
224
274
225
275
let calculateExitCode failOnWarnings ( msgs : AnalyzerMessage list option ) : int =
@@ -302,20 +352,25 @@ let main argv =
302
352
printInfo " Registered %d analyzers from %d dlls" analyzers dlls
303
353
304
354
let projOpts = results.TryGetResult <@ Project @>
355
+ let fscArgs = results.TryGetResult <@ FSC_ Args @>
305
356
let report = results.TryGetResult <@ Report @>
306
357
307
358
let results =
308
359
if analyzers = 0 then
309
360
Some []
310
361
else
311
- match projOpts with
312
- | None
313
- | Some [] ->
362
+ match projOpts, fscArgs with
363
+ | None, None
364
+ | Some [], None ->
314
365
printError
315
366
" No project given. Use `--project PATH_TO_FSPROJ`. Pass path relative to current directory.%s "
316
367
317
368
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 ->
319
374
let runProj ( proj : string ) =
320
375
async {
321
376
let project =
0 commit comments