Skip to content

Commit

Permalink
Merge pull request #194 from dawedawe/support_include_analyzers
Browse files Browse the repository at this point in the history
Support include analyzers and make life simpler for editor clients
  • Loading branch information
dawedawe authored Jan 4, 2024
2 parents d739236 + 3dfddfe commit c49d6e9
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
<PackageVersion Include="Microsoft.Build.Locator" Version="1.4.1" />
<!-- Need to update Directory.Build.props DotNet.ReproducibleBuilds.Isolated version when updating this-->
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="1.1.1" PrivateAssets="All" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="MSBuild.StructuredLogger" Version="2.1.815" />
<PackageVersion Include="NUnit" Version="3.13.3" />
Expand Down
22 changes: 18 additions & 4 deletions src/FSharp.Analyzers.Cli/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Arguments =
| [<Unique>] Treat_As_Error of string list
| [<Unique>] Ignore_Files of string list
| [<Unique>] Exclude_Analyzer of string list
| [<Unique>] Include_Analyzer of string list
| [<Unique>] Report of string
| [<Unique>] FSC_Args of string
| [<Unique>] Code_Root of string
Expand All @@ -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]."
Expand Down Expand Up @@ -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
Expand All @@ -548,13 +563,12 @@ let main argv =
exit 1
)

let client =
Client<CliAnalyzerAttribute, CliContext>(logger, Set.ofList excludeAnalyzers)
let client = Client<CliAnalyzerAttribute, CliContext>(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)
)

Expand Down
48 changes: 32 additions & 16 deletions src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Client.RegisteredAnalyzer<'TContext> 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$")
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
11 changes: 9 additions & 2 deletions src/FSharp.Analyzers.SDK/FSharp.Analyzers.SDK.Client.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@ type AnalysisResult =
Output: Result<Message list, exn>
}

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>
/// <summary>
/// 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.
/// </summary>
/// <returns>number of found dlls matching `*Analyzer*.dll` and number of registered analyzers</returns>
member LoadAnalyzers: dir: string -> int * int
member LoadAnalyzers: dir: string * ?excludeInclude: ExcludeInclude -> int * int
/// <summary>Runs all registered analyzers for given context (file).</summary>
/// <returns>list of messages. Ignores errors from the analyzers</returns>
member RunAnalyzers: ctx: 'TContext -> Async<AnalyzerMessage list>
Expand Down

0 comments on commit c49d6e9

Please sign in to comment.