diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c6abec..793db27 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Changed
* [Add missing TAST walkers](https://github.com/ionide/FSharp.Analyzers.SDK/pull/185) (thanks @dawedawe!)
+* [Add support for --include-analyzers to ignore all others](https://github.com/ionide/FSharp.Analyzers.SDK/pull/194) (thanks @dawedawe!)
## [0.22.0] - 2023-12-19
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5aa4b90..2bf8274 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -19,9 +19,9 @@
-
-
-
+
+
+
diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs
index 3af739d..8b31207 100644
--- a/src/FSharp.Analyzers.Cli/Program.fs
+++ b/src/FSharp.Analyzers.Cli/Program.fs
@@ -28,6 +28,7 @@ type Arguments =
| [] Treat_As_Error of string list
| [] Ignore_Files of string list
| [] Exclude_Analyzer of string list
+ | [] Include_Analyzer of string list
| [] Report of string
| [] FSC_Args of string
| [] Code_Root of string
@@ -53,6 +54,8 @@ type Arguments =
"List of analyzer codes that should be treated as severity Error by the tool. Regardless of the original severity."
| Ignore_Files _ -> "Source files that shouldn't be processed."
| Exclude_Analyzer _ -> "The names of analyzers that should not be executed."
+ | Include_Analyzer _ ->
+ "The names of analyzers that should exclusively be executed while all others are ignored. Takes precedence over --exclude-analyzer."
| Report _ -> "Write the result messages to a (sarif) report file."
| Verbosity _ ->
"The verbosity level. The available verbosity levels are: n[ormal], d[etailed], diag[nostic]."
@@ -531,7 +534,19 @@ let main argv =
logger.LogInformation("Loading analyzers from {0}", (String.concat ", " analyzersPaths))
- let excludeAnalyzers = results.GetResult(<@ Exclude_Analyzer @>, [])
+ let includeExclude =
+ let excludeAnalyzers = results.GetResult(<@ Exclude_Analyzer @>, [])
+ let includeAnalyzers = results.GetResult(<@ Include_Analyzer @>, [])
+
+ match excludeAnalyzers, includeAnalyzers with
+ | e, [] -> Exclude(Set.ofList e)
+ | [], i -> Include(Set.ofList i)
+ | i, _e ->
+ logger.LogWarning(
+ "--exclude-analyzers and --include-analyzers are mutually exclusive, ignoring --exclude-analyzers"
+ )
+
+ Include(Set.ofList i)
AssemblyLoadContext.Default.add_Resolving (fun _ctx assemblyName ->
if assemblyName.Name <> "FSharp.Core" then
@@ -548,13 +563,12 @@ let main argv =
exit 1
)
- let client =
- Client(logger, Set.ofList excludeAnalyzers)
+ let client = Client(logger)
let dlls, analyzers =
((0, 0), analyzersPaths)
||> List.fold (fun (accDlls, accAnalyzers) analyzersPath ->
- let dlls, analyzers = client.LoadAnalyzers analyzersPath
+ let dlls, analyzers = client.LoadAnalyzers(analyzersPath, includeExclude)
(accDlls + dlls), (accAnalyzers + analyzers)
)
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
index 3bf32b9..fb04e73 100644
--- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
@@ -127,17 +127,19 @@ module Client =
|> Seq.choose (analyzerFromMember<'TAnalyzerAttribute, 'TContext> path)
|> Seq.toList
-type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TContext :> Context>
- (logger: ILogger, excludedAnalyzers: string Set)
- =
+type ExcludeInclude =
+ | Exclude of string Set
+ | Include of string Set
+
+type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TContext :> Context>(logger: ILogger) =
do TASTCollecting.logger <- logger
let registeredAnalyzers =
ConcurrentDictionary list>()
- new() = Client(Abstractions.NullLogger.Instance, Set.empty)
+ new() = Client(Abstractions.NullLogger.Instance)
- member x.LoadAnalyzers(dir: string) : int * int =
+ member x.LoadAnalyzers(dir: string, ?excludeInclude: ExcludeInclude) : int * int =
if Directory.Exists dir then
let analyzerAssemblies =
let regex = Regex(@".*test.*\.dll$")
@@ -184,7 +186,7 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC
true
else
logger.LogError(
- "Trying to load {0} which was built using SDK version {1}. Expect {2} instead. Assembly will be skipped.",
+ "Trying to load {Name} which was built using SDK version {Version}. Expect {SdkVersion} instead. Assembly will be skipped.",
name,
version,
Utils.currentFSharpAnalyzersSDKVersion
@@ -197,16 +199,30 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC
assembly.GetExportedTypes()
|> Seq.collect (Client.analyzersFromType<'TAttribute, 'TContext> path)
|> Seq.filter (fun registeredAnalyzer ->
- let shouldExclude = excludedAnalyzers.Contains(registeredAnalyzer.Name)
-
- if shouldExclude then
- logger.LogInformation(
- "Excluding {0} from {1}",
- registeredAnalyzer.Name,
- assembly.FullName
- )
-
- not shouldExclude
+ match excludeInclude with
+ | Some(Exclude excluded) ->
+ let shouldExclude = excluded.Contains(registeredAnalyzer.Name)
+
+ if shouldExclude then
+ logger.LogInformation(
+ "Excluding {Name} from {FullName}",
+ registeredAnalyzer.Name,
+ assembly.FullName
+ )
+
+ not shouldExclude
+ | Some(Include included) ->
+ let shouldInclude = included.Contains(registeredAnalyzer.Name)
+
+ if shouldInclude then
+ logger.LogInformation(
+ "Including {Name} from {FullName}",
+ registeredAnalyzer.Name,
+ assembly.FullName
+ )
+
+ shouldInclude
+ | None -> true
)
|> Seq.toList
diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi
index 9a52a04..b5e5ad7 100644
--- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi
+++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi
@@ -8,15 +8,22 @@ type AnalysisResult =
Output: Result
}
+type ExcludeInclude =
+ /// Analyzers in this set should be ignored.
+ | Exclude of string Set
+ /// Analyzers in this set should be used exclusively, while all others are ignored.
+ | Include of string Set
+
type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TContext :> Context> =
- new: logger: ILogger * excludedAnalyzers: string Set -> Client<'TAttribute, 'TContext>
+ new: logger: ILogger -> Client<'TAttribute, 'TContext>
new: unit -> Client<'TAttribute, 'TContext>
///
/// Loads into private state any analyzers defined in any assembly
/// matching `*Analyzer*.dll` in given directory (and any subdirectories)
+ /// Analyzers are filtered according to the ExcludeInclude set, if provided.
///
/// number of found dlls matching `*Analyzer*.dll` and number of registered analyzers
- member LoadAnalyzers: dir: string -> int * int
+ member LoadAnalyzers: dir: string * ?excludeInclude: ExcludeInclude -> int * int
/// Runs all registered analyzers for given context (file).
/// list of messages. Ignores errors from the analyzers
member RunAnalyzers: ctx: 'TContext -> Async