Skip to content

Commit

Permalink
Update analyzers support (#1217)
Browse files Browse the repository at this point in the history
* bump FSharp.Analyzers.SDK to 0.22.0

* adjust to new analyzers sdk

* add ExcludeAnalyzers to configuration

* fix test build

* support the WIP feature of IncludeAnalyzers filter in the SDK

* adjust

* adjust to analyzer filter predicate

* bump the analyzers sdk dependency

* put the CheckFileResults into the EditorContext for the analyzers

* settle on "'t array" in FSharpConfigDto and FSharpConfig
  • Loading branch information
dawedawe authored Jan 14, 2024
1 parent 49f8b96 commit 4ac4b87
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 78 deletions.
2 changes: 1 addition & 1 deletion paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ nuget Microsoft.Build.Utilities.Core >= 17.4 copy_local:false
nuget Microsoft.Build.Tasks.Core >= 17.4 copy_local: false
nuget Nuget.Frameworks >= 6.3 copy_local: false
nuget Microsoft.CodeAnalysis 4.5.0
nuget FSharp.Analyzers.SDK
nuget FSharp.Analyzers.SDK 0.23.0
nuget ICSharpCode.Decompiler
nuget Mono.Cecil >= 0.11.4
nuget FSharpLint.Core
Expand Down
15 changes: 7 additions & 8 deletions paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ NUGET
FSharp.Core (>= 4.3.4)
FSharp.Analyzers.Build (0.2)
NETStandard.Library (>= 1.6.1)
FSharp.Analyzers.SDK (0.11)
FSharp.Compiler.Service (>= 41.0.1) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0))
FSharp.Core (>= 6.0.1) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0))
McMaster.NETCore.Plugins (>= 1.4) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0))
FSharp.Analyzers.SDK (0.23)
FSharp.Compiler.Service (43.8.100) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
FSharp.Core (8.0.100) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
McMaster.NETCore.Plugins (>= 1.4) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
Microsoft.Extensions.Logging.Abstractions (>= 6.0) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
FSharp.Compiler.Service (43.8.100)
FSharp.Core (8.0.100)
System.Buffers (>= 4.5.1)
Expand Down Expand Up @@ -157,7 +158,7 @@ NUGET
Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
Ionide.ProjInfo.Sln (0.62)
LinkDotNet.StringBuilder (1.18)
McMaster.NETCore.Plugins (1.4) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0))
McMaster.NETCore.Plugins (1.4) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.1) (>= netcoreapp2.1))
Microsoft.Extensions.DependencyModel (>= 5.0) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.1) (>= netcoreapp2.1))
MessagePack (2.5.108)
Expand Down Expand Up @@ -277,7 +278,7 @@ NUGET
Microsoft.Extensions.DependencyInjection.Abstractions (6.0)
Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461))
System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461))
Microsoft.Extensions.DependencyModel (6.0) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0))
Microsoft.Extensions.DependencyModel (6.0) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
System.Buffers (>= 4.5.1)
System.Memory (>= 4.5.4)
System.Runtime.CompilerServices.Unsafe (>= 6.0)
Expand All @@ -291,8 +292,6 @@ NUGET
Microsoft.Extensions.Options (>= 6.0)
System.Diagnostics.DiagnosticSource (>= 6.0)
Microsoft.Extensions.Logging.Abstractions (6.0.2)
System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1)
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1)
Microsoft.Extensions.Logging.Configuration (6.0)
Microsoft.Extensions.Configuration (>= 6.0)
Microsoft.Extensions.Configuration.Abstractions (>= 6.0)
Expand Down
48 changes: 28 additions & 20 deletions src/FsAutoComplete.Core/Commands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1137,14 +1137,22 @@ module Commands =



let analyzerHandler (file: string<LocalPath>, content, pt, tast, symbols, getAllEntities) =
let ctx: SDK.Context =
let analyzerHandler
(
client: SDK.Client<SDK.EditorAnalyzerAttribute, SDK.EditorContext>,
file: string<LocalPath>,
content: ISourceText,
pt,
tast,
checkFileResults: FSharpCheckFileResults
) =
let ctx: SDK.EditorContext =
{ FileName = UMX.untag file
Content = content
ParseTree = pt
TypedTree = tast
Symbols = symbols
GetAllEntities = getAllEntities }
SourceText = content
ParseFileResults = pt
CheckFileResults = Some checkFileResults
TypedTree = Some tast
CheckProjectResults = None }

let extractResultsFromAnalyzer (r: SDK.AnalysisResult) =
match r.Output with
Expand All @@ -1168,20 +1176,20 @@ module Commands =

[]

try
SDK.Client.runAnalyzersSafely ctx
|> List.collect extractResultsFromAnalyzer
|> List.toArray
with ex ->
Loggers.analyzers.error (
Log.setMessage "Error while processing analyzers for {file}: {message}"
>> Log.addContextDestructured "message" ex.Message
>> Log.addExn ex
>> Log.addContextDestructured "file" file
)

[||]
async {
try
let! r = client.RunAnalyzersSafely ctx
return r |> List.collect extractResultsFromAnalyzer |> List.toArray
with ex ->
Loggers.analyzers.error (
Log.setMessage "Error while processing analyzers for {file}: {message}"
>> Log.addContextDestructured "message" ex.Message
>> Log.addExn ex
>> Log.addContextDestructured "file" file
)

return [||]
}

type Commands() =

Expand Down
26 changes: 18 additions & 8 deletions src/FsAutoComplete/LspHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ type FSACDto =
type FSharpConfigDto =
{ AutomaticWorkspaceInit: bool option
WorkspaceModePeekDeepLevel: int option
ExcludeProjectDirectories: string[] option
ExcludeProjectDirectories: string array option
KeywordsAutocomplete: bool option
ExternalAutocomplete: bool option
FullNameExternalAutocomplete: bool option
Expand All @@ -660,13 +660,15 @@ type FSharpConfigDto =
ResolveNamespaces: bool option
EnableReferenceCodeLens: bool option
EnableAnalyzers: bool option
AnalyzersPath: string[] option
AnalyzersPath: string array option
ExcludeAnalyzers: string array option
IncludeAnalyzers: string array option
DisableInMemoryProjectReferences: bool option
LineLens: LineLensConfig option
UseSdkScripts: bool option
DotNetRoot: string option
FSIExtraParameters: string[] option
FSICompilerToolLocations: string[] option
FSIExtraParameters: string array option
FSICompilerToolLocations: string array option
TooltipMode: string option
GenerateBinlog: bool option
AbstractClassStubGeneration: bool option
Expand Down Expand Up @@ -772,7 +774,7 @@ let tryCreateRegex (pattern: string) =
type FSharpConfig =
{ AutomaticWorkspaceInit: bool
WorkspaceModePeekDeepLevel: int
ExcludeProjectDirectories: string[]
ExcludeProjectDirectories: string array
KeywordsAutocomplete: bool
ExternalAutocomplete: bool
FullNameExternalAutocomplete: bool
Expand All @@ -799,13 +801,15 @@ type FSharpConfig =
ResolveNamespaces: bool
EnableReferenceCodeLens: bool
EnableAnalyzers: bool
AnalyzersPath: string[]
AnalyzersPath: string array
ExcludeAnalyzers: string array
IncludeAnalyzers: string array
DisableInMemoryProjectReferences: bool
LineLens: LineLensConfig
UseSdkScripts: bool
DotNetRoot: string
FSIExtraParameters: string[]
FSICompilerToolLocations: string[]
FSIExtraParameters: string array
FSICompilerToolLocations: string array
TooltipMode: string
GenerateBinlog: bool
CodeLenses: CodeLensConfig
Expand Down Expand Up @@ -846,6 +850,8 @@ type FSharpConfig =
EnableReferenceCodeLens = false
EnableAnalyzers = false
AnalyzersPath = [||]
ExcludeAnalyzers = [||]
IncludeAnalyzers = [||]
DisableInMemoryProjectReferences = false
LineLens = { Enabled = "never"; Prefix = "" }
UseSdkScripts = true
Expand Down Expand Up @@ -894,6 +900,8 @@ type FSharpConfig =
EnableReferenceCodeLens = defaultArg dto.EnableReferenceCodeLens false
EnableAnalyzers = defaultArg dto.EnableAnalyzers false
AnalyzersPath = defaultArg dto.AnalyzersPath [||]
ExcludeAnalyzers = defaultArg dto.ExcludeAnalyzers [||]
IncludeAnalyzers = defaultArg dto.IncludeAnalyzers [||]
DisableInMemoryProjectReferences = defaultArg dto.DisableInMemoryProjectReferences false
LineLens =
{ Enabled = defaultArg (dto.LineLens |> Option.map (fun n -> n.Enabled)) "never"
Expand Down Expand Up @@ -999,6 +1007,8 @@ type FSharpConfig =
EnableReferenceCodeLens = defaultArg dto.EnableReferenceCodeLens x.EnableReferenceCodeLens
EnableAnalyzers = defaultArg dto.EnableAnalyzers x.EnableAnalyzers
AnalyzersPath = defaultArg dto.AnalyzersPath x.AnalyzersPath
ExcludeAnalyzers = defaultArg dto.ExcludeAnalyzers x.ExcludeAnalyzers
IncludeAnalyzers = defaultArg dto.IncludeAnalyzers x.IncludeAnalyzers
DisableInMemoryProjectReferences =
defaultArg dto.DisableInMemoryProjectReferences x.DisableInMemoryProjectReferences
LineLens =
Expand Down
20 changes: 12 additions & 8 deletions src/FsAutoComplete/LspHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ type FSACDto =
type FSharpConfigDto =
{ AutomaticWorkspaceInit: bool option
WorkspaceModePeekDeepLevel: int option
ExcludeProjectDirectories: string[] option
ExcludeProjectDirectories: string array option
KeywordsAutocomplete: bool option
ExternalAutocomplete: bool option
FullNameExternalAutocomplete: bool option
Expand All @@ -290,13 +290,15 @@ type FSharpConfigDto =
ResolveNamespaces: bool option
EnableReferenceCodeLens: bool option
EnableAnalyzers: bool option
AnalyzersPath: string[] option
AnalyzersPath: string array option
ExcludeAnalyzers: string array option
IncludeAnalyzers: string array option
DisableInMemoryProjectReferences: bool option
LineLens: LineLensConfig option
UseSdkScripts: bool option
DotNetRoot: string option
FSIExtraParameters: string[] option
FSICompilerToolLocations: string[] option
FSIExtraParameters: string array option
FSICompilerToolLocations: string array option
TooltipMode: string option
GenerateBinlog: bool option
AbstractClassStubGeneration: bool option
Expand Down Expand Up @@ -361,7 +363,7 @@ type DebugConfig =
type FSharpConfig =
{ AutomaticWorkspaceInit: bool
WorkspaceModePeekDeepLevel: int
ExcludeProjectDirectories: string[]
ExcludeProjectDirectories: string array
KeywordsAutocomplete: bool
ExternalAutocomplete: bool
FullNameExternalAutocomplete: bool
Expand All @@ -388,13 +390,15 @@ type FSharpConfig =
ResolveNamespaces: bool
EnableReferenceCodeLens: bool
EnableAnalyzers: bool
AnalyzersPath: string[]
AnalyzersPath: string array
ExcludeAnalyzers: string array
IncludeAnalyzers: string array
DisableInMemoryProjectReferences: bool
LineLens: LineLensConfig
UseSdkScripts: bool
DotNetRoot: string
FSIExtraParameters: string[]
FSICompilerToolLocations: string[]
FSIExtraParameters: string array
FSICompilerToolLocations: string array
TooltipMode: string
GenerateBinlog: bool
CodeLenses: CodeLensConfig
Expand Down
30 changes: 24 additions & 6 deletions src/FsAutoComplete/LspServers/AdaptiveServerState.fs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
[| yield! fsiCompilerToolLocations |> Array.map toCompilerToolArgument
yield! fsiExtraParameters |]

let analyzersClient =
FSharp.Analyzers.SDK.Client<FSharp.Analyzers.SDK.EditorAnalyzerAttribute, FSharp.Analyzers.SDK.EditorContext>(
Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance
)

/// <summary>Loads F# Analyzers from the configured directories</summary>
/// <param name="config">The FSharpConfig</param>
/// <param name="rootPath">The RootPath</param>
Expand All @@ -135,6 +140,18 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
if config.EnableAnalyzers then
Loggers.analyzers.info (Log.setMessageI $"Using analyzer roots of {config.AnalyzersPath:roots}")

let excludeInclude =
match config.ExcludeAnalyzers, config.IncludeAnalyzers with
| e, [||] -> FSharp.Analyzers.SDK.ExcludeInclude.ExcludeFilter(fun (s: string) -> Array.contains s e)
| [||], i -> FSharp.Analyzers.SDK.ExcludeInclude.IncludeFilter(fun (s: string) -> Array.contains s i)
| _e, i ->
Loggers.analyzers.warn (
Log.setMessage
"--exclude-analyzers and --include-analyzers are mutually exclusive, ignoring --exclude-analyzers"
)

FSharp.Analyzers.SDK.ExcludeInclude.IncludeFilter(fun (s: string) -> Array.contains s i)

config.AnalyzersPath
|> Array.iter (fun analyzerPath ->
match rootPath with
Expand All @@ -152,7 +169,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac

Loggers.analyzers.info (Log.setMessageI $"Loading analyzers from {dir:dir}")

let (dllCount, analyzerCount) = dir |> FSharp.Analyzers.SDK.Client.loadAnalyzers
let (dllCount, analyzerCount) = analyzersClient.LoadAnalyzers(dir, excludeInclude)

Loggers.analyzers.info (
Log.setMessageI
Expand Down Expand Up @@ -369,14 +386,14 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
// Since analyzers are not async, we need to switch to a new thread to not block threadpool
do! Async.SwitchToNewThread()

let res =
let! res =
Commands.analyzerHandler (
analyzersClient,
file,
volatileFile.Source.ToString().Split("\n"),
parseAndCheck.GetParseResults.ParseTree,
volatileFile.Source,
parseAndCheck.GetParseResults,
tast,
parseAndCheck.GetCheckResults.PartialAssemblySignature.Entities |> Seq.toList,
parseAndCheck.GetAllEntities
parseAndCheck.GetCheckResults
)

let! ct = Async.CancellationToken
Expand Down Expand Up @@ -552,6 +569,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac

let severity =
match m.Severity with
| FSharp.Analyzers.SDK.Hint -> DiagnosticSeverity.Hint
| FSharp.Analyzers.SDK.Info -> DiagnosticSeverity.Information
| FSharp.Analyzers.SDK.Warning -> DiagnosticSeverity.Warning
| FSharp.Analyzers.SDK.Error -> DiagnosticSeverity.Error
Expand Down
2 changes: 2 additions & 0 deletions test/FsAutoComplete.Tests.Lsp/Helpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ let defaultConfigDto: FSharpConfigDto =
EnableReferenceCodeLens = None
EnableAnalyzers = None
AnalyzersPath = None
ExcludeAnalyzers = None
IncludeAnalyzers = None
DisableInMemoryProjectReferences = None
AutomaticWorkspaceInit = Some true
InterfaceStubGeneration = None
Expand Down
59 changes: 32 additions & 27 deletions test/OptionAnalyzer/Analyzer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -117,31 +117,36 @@ let info message items =

let inline (==>) x y = x, box y

[<Analyzer "OptionAnalyzer">]
let optionValueAnalyzer: Analyzer =
[<EditorAnalyzer "OptionAnalyzer">]
let optionValueAnalyzer: Analyzer<EditorContext> =
fun ctx ->
info "analyzing {file} for uses of Option.Value" [ "file" ==> ctx.FileName ]
let state = ResizeArray<Range>()

let handler (range: Range) (m: FSharpMemberOrFunctionOrValue) =
let rangeString =
sprintf "(%d,%d)-(%d,%d)" range.Start.Line range.Start.Column range.End.Line range.End.Column

let name = String.Join(".", m.DeclaringEntity.Value.FullName, m.DisplayName)
info "checking value at {range} with name {name}" [ "range" ==> rangeString; "name" ==> name ]

if name = "Microsoft.FSharp.Core.FSharpOption`1.Value" then
info "matched at range {range}" [ "range" ==> rangeString ]
state.Add range

ctx.TypedTree.Declarations |> List.iter (visitDeclaration handler)

state
|> Seq.map (fun r ->
{ Type = "Option.Value analyzer"
Message = "Option.Value shouldn't be used"
Code = "OV001"
Severity = Warning
Range = r
Fixes = [] })
|> Seq.toList
async {
info "analyzing {file} for uses of Option.Value" [ "file" ==> ctx.FileName ]
let state = ResizeArray<Range>()

let handler (range: Range) (m: FSharpMemberOrFunctionOrValue) =
let rangeString =
sprintf "(%d,%d)-(%d,%d)" range.Start.Line range.Start.Column range.End.Line range.End.Column

let name = String.Join(".", m.DeclaringEntity.Value.FullName, m.DisplayName)
info "checking value at {range} with name {name}" [ "range" ==> rangeString; "name" ==> name ]

if name = "Microsoft.FSharp.Core.FSharpOption`1.Value" then
info "matched at range {range}" [ "range" ==> rangeString ]
state.Add range

match ctx.TypedTree with
| Some tt -> tt.Declarations |> List.iter (visitDeclaration handler)
| None -> ()

return
state
|> Seq.map (fun r ->
{ Type = "Option.Value analyzer"
Message = "Option.Value shouldn't be used"
Code = "OV001"
Severity = Warning
Range = r
Fixes = [] })
|> Seq.toList
}

0 comments on commit 4ac4b87

Please sign in to comment.