From 1c8f53d8948dde43597c167a329edcd269d128a3 Mon Sep 17 00:00:00 2001 From: dawe Date: Wed, 3 Jan 2024 15:57:14 +0100 Subject: [PATCH 1/4] add support for --include-analyzers --- CHANGELOG.md | 1 + src/FSharp.Analyzers.Cli/Program.fs | 23 +++++++-- .../FSharp.Analyzers.SDK.Client.fs | 48 +++++++++++++------ .../FSharp.Analyzers.SDK.Client.fsi | 15 +++++- 4 files changed, 67 insertions(+), 20 deletions(-) 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/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index 3af739d..e42885b 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,20 @@ 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 + | [], [] -> None + | e, [] -> Some(Exclude(Set.ofList e)) + | [], i -> Some(Include(Set.ofList i)) + | i, _e -> + logger.LogWarning( + "--exclude-analyzers and --include-analyzers are mutually exclusive, ignoring --exclude-analyzers" + ) + + Some(Include(Set.ofList i)) AssemblyLoadContext.Default.add_Resolving (fun _ctx assemblyName -> if assemblyName.Name <> "FSharp.Core" then @@ -548,13 +564,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..e79fdc8 100644 --- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs +++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs @@ -127,17 +127,21 @@ 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 = x.LoadAnalyzers(dir, None) - member x.LoadAnalyzers(dir: string) : int * int = + member x.LoadAnalyzers(dir: string, excludeInclude: ExcludeInclude option) : int * int = if Directory.Exists dir then let analyzerAssemblies = let regex = Regex(@".*test.*\.dll$") @@ -197,16 +201,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 {0} from {1}", + registeredAnalyzer.Name, + assembly.FullName + ) + + not shouldExclude + | Some(Include included) -> + let shouldInclude = included.Contains(registeredAnalyzer.Name) + + if shouldInclude then + logger.LogInformation( + "Including {0} from {1}", + 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..d64e278 100644 --- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi +++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi @@ -8,8 +8,14 @@ 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 @@ -17,6 +23,13 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC /// /// number of found dlls matching `*Analyzer*.dll` and number of registered analyzers member LoadAnalyzers: dir: string -> int * int + /// + /// 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 given ExcludeInclude set. + /// + /// number of found dlls matching `*Analyzer*.dll` and number of registered analyzers + member LoadAnalyzers: dir: string * excludeInclude: ExcludeInclude option -> int * int /// Runs all registered analyzers for given context (file). /// list of messages. Ignores errors from the analyzers member RunAnalyzers: ctx: 'TContext -> Async From 9461b968f623cc4f5bab4c606e58e6bb4df047fd Mon Sep 17 00:00:00 2001 From: dawe Date: Wed, 3 Jan 2024 15:57:32 +0100 Subject: [PATCH 2/4] ease version requirements for Microsoft.Extensions.Logging --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 @@ - - - + + + From 7acb6d22a4158dd7c4751636a49a770462a0c30d Mon Sep 17 00:00:00 2001 From: dawe Date: Wed, 3 Jan 2024 16:47:57 +0100 Subject: [PATCH 3/4] use named placeholders in log messages --- src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs index e79fdc8..f54b670 100644 --- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs +++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs @@ -188,7 +188,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 @@ -207,7 +207,7 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC if shouldExclude then logger.LogInformation( - "Excluding {0} from {1}", + "Excluding {Name} from {FullName}", registeredAnalyzer.Name, assembly.FullName ) @@ -218,7 +218,7 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC if shouldInclude then logger.LogInformation( - "Including {0} from {1}", + "Including {Name} from {FullName}", registeredAnalyzer.Name, assembly.FullName ) From e423b4beb4f900d3d2b6f5203386f2541174f7b4 Mon Sep 17 00:00:00 2001 From: dawe Date: Wed, 3 Jan 2024 17:45:07 +0100 Subject: [PATCH 4/4] use optional parameter instead of overloaded member --- src/FSharp.Analyzers.Cli/Program.fs | 7 +++---- .../FSharp.Analyzers.SDK.Client.fs | 4 +--- .../FSharp.Analyzers.SDK.Client.fsi | 10 ++-------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index e42885b..8b31207 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -539,15 +539,14 @@ let main argv = let includeAnalyzers = results.GetResult(<@ Include_Analyzer @>, []) match excludeAnalyzers, includeAnalyzers with - | [], [] -> None - | e, [] -> Some(Exclude(Set.ofList e)) - | [], i -> Some(Include(Set.ofList i)) + | 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" ) - Some(Include(Set.ofList i)) + Include(Set.ofList i) AssemblyLoadContext.Default.add_Resolving (fun _ctx assemblyName -> if assemblyName.Name <> "FSharp.Core" then diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs index f54b670..fb04e73 100644 --- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs +++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs @@ -139,9 +139,7 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC new() = Client(Abstractions.NullLogger.Instance) - member x.LoadAnalyzers(dir: string) : int * int = x.LoadAnalyzers(dir, None) - - member x.LoadAnalyzers(dir: string, excludeInclude: ExcludeInclude option) : int * int = + member x.LoadAnalyzers(dir: string, ?excludeInclude: ExcludeInclude) : int * int = if Directory.Exists dir then let analyzerAssemblies = let regex = Regex(@".*test.*\.dll$") diff --git a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi index d64e278..b5e5ad7 100644 --- a/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi +++ b/src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi @@ -20,16 +20,10 @@ type Client<'TAttribute, 'TContext when 'TAttribute :> AnalyzerAttribute and 'TC /// /// 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 - /// - /// 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 given ExcludeInclude set. - /// - /// number of found dlls matching `*Analyzer*.dll` and number of registered analyzers - member LoadAnalyzers: dir: string * excludeInclude: ExcludeInclude option -> 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