Skip to content

Commit

Permalink
Use MEF instead of GetService calls in diagnostic analyzers (dotnet#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
cartermp committed Nov 24, 2020
1 parent 5428d13 commit 2cd971c
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 42 deletions.
22 changes: 8 additions & 14 deletions Diagnostics/DocumentDiagnosticAnalyzer.fs
Expand Up @@ -10,29 +10,25 @@ open System.Threading
open System.Threading.Tasks

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Diagnostics
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.Host.Mef
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics

open FSharp.Compiler
open FSharp.Compiler.SourceCodeServices


[<RequireQualifiedAccess>]
type internal DiagnosticsType =
| Syntax
| Semantic

[<Export(typeof<IFSharpDocumentDiagnosticAnalyzer>)>]
type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () =
type internal FSharpDocumentDiagnosticAnalyzer
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =

static let userOpName = "DocumentDiagnosticAnalyzer"
let getChecker(document: Document) =
document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().Checker

let getProjectInfoManager(document: Document) =
document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().FSharpProjectOptionsManager

static let errorInfoEqualityComparer =
{ new IEqualityComparer<FSharpErrorInfo> with
Expand Down Expand Up @@ -110,27 +106,25 @@ type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () =
interface IFSharpDocumentDiagnosticAnalyzer with

member this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task<ImmutableArray<Diagnostic>> =
let projectInfoManager = getProjectInfoManager document
asyncMaybe {
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! sourceText = document.GetTextAsync(cancellationToken)
let! textVersion = document.GetTextVersionAsync(cancellationToken)
return!
FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax)
FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax)
|> liftAsync
}
|> Async.map (Option.defaultValue ImmutableArray<Diagnostic>.Empty)
|> RoslynHelpers.StartAsyncAsTask cancellationToken

member this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task<ImmutableArray<Diagnostic>> =
let projectInfoManager = getProjectInfoManager document
asyncMaybe {
let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken, userOpName)
let! sourceText = document.GetTextAsync(cancellationToken)
let! textVersion = document.GetTextVersionAsync(cancellationToken)
if document.Project.Name <> FSharpConstants.FSharpMiscellaneousFilesName || isScriptFile document.FilePath then
return!
FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic)
FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic)
|> liftAsync
else
return ImmutableArray<Diagnostic>.Empty
Expand Down
14 changes: 8 additions & 6 deletions Diagnostics/SimplifyNameDiagnosticAnalyzer.fs
Expand Up @@ -17,12 +17,14 @@ open FSharp.Compiler.SourceCodeServices
type private PerDocumentSavedData = { Hash: int; Diagnostics: ImmutableArray<Diagnostic> }

[<Export(typeof<IFSharpSimplifyNameDiagnosticAnalyzer>)>]
type internal SimplifyNameDiagnosticAnalyzer [<ImportingConstructor>] () =
type internal SimplifyNameDiagnosticAnalyzer
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =

static let userOpName = "SimplifyNameDiagnosticAnalyzer"
let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().FSharpProjectOptionsManager
let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().Checker

static let cache = new MemoryCache("FSharp.Editor." + userOpName)
// Make sure only one document is being analyzed at a time, to be nice
static let guard = new SemaphoreSlim(1)
Expand All @@ -36,7 +38,7 @@ type internal SimplifyNameDiagnosticAnalyzer [<ImportingConstructor>] () =
do! Option.guard document.FSharpOptions.CodeFixes.SimplifyName
do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds)
do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync
let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! textVersion = document.GetTextVersionAsync(cancellationToken)
let textVersionHash = textVersion.GetHashCode()
let! _ = guard.WaitAsync(cancellationToken) |> Async.AwaitTask |> liftAsync
Expand All @@ -46,7 +48,7 @@ type internal SimplifyNameDiagnosticAnalyzer [<ImportingConstructor>] () =
| :? PerDocumentSavedData as data when data.Hash = textVersionHash -> return data.Diagnostics
| _ ->
let! sourceText = document.GetTextAsync()
let checker = getChecker document
let checker = checkerProvider.Checker
let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName=userOpName)
let! result = SimplifyNames.getSimplifiableNames(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync
let mutable diag = ResizeArray()
Expand Down
18 changes: 8 additions & 10 deletions Diagnostics/UnusedDeclarationsAnalyzer.fs
Expand Up @@ -4,24 +4,22 @@ namespace rec Microsoft.VisualStudio.FSharp.Editor

open System
open System.Composition
open System.Collections.Generic
open System.Collections.Immutable
open System.Diagnostics
open System.Threading.Tasks

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Diagnostics
open Microsoft.CodeAnalysis.Host.Mef
open FSharp.Compiler.SourceCodeServices
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics

[<Export(typeof<IFSharpUnusedDeclarationsDiagnosticAnalyzer>)>]
type internal UnusedDeclarationsAnalyzer [<ImportingConstructor>] () =
type internal UnusedDeclarationsAnalyzer
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =

static let userOpName = "UnusedDeclarationsAnalyzer"
let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().FSharpProjectOptionsManager
let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().Checker


interface IFSharpUnusedDeclarationsDiagnosticAnalyzer with

Expand All @@ -31,10 +29,10 @@ type internal UnusedDeclarationsAnalyzer [<ImportingConstructor>] () =

do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds)
do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time
match! getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) with
match! projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) with
| (_parsingOptions, projectOptions) ->
let! sourceText = document.GetTextAsync()
let checker = getChecker document
let checker = checkerProvider.Checker
let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName)
let! unusedRanges = UnusedDeclarations.getUnusedDeclarations( checkResults, (isScriptFile document.FilePath)) |> liftAsync
return
Expand Down
20 changes: 8 additions & 12 deletions Diagnostics/UnusedOpensDiagnosticAnalyzer.fs
Expand Up @@ -16,10 +16,12 @@ open FSharp.Compiler.SourceCodeServices
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics

[<Export(typeof<IFSharpUnusedOpensDiagnosticAnalyzer>)>]
type internal UnusedOpensDiagnosticAnalyzer [<ImportingConstructor>] () =

let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().FSharpProjectOptionsManager
let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService<FSharpCheckerWorkspaceService>().Checker
type internal UnusedOpensDiagnosticAnalyzer
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =

static let userOpName = "UnusedOpensAnalyzer"

Expand All @@ -28,13 +30,7 @@ type internal UnusedOpensDiagnosticAnalyzer [<ImportingConstructor>] () =
do! Option.guard document.FSharpOptions.CodeFixes.UnusedOpens
let! sourceText = document.GetTextAsync()
let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, userOpName = userOpName)
#if DEBUG
let sw = Stopwatch.StartNew()
#endif
let! unusedOpens = UnusedOpens.getUnusedOpens(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync
#if DEBUG
Logging.Logging.logInfof "*** Got %d unused opens in %O" unusedOpens.Length sw.Elapsed
#endif
return unusedOpens
}

Expand All @@ -44,9 +40,9 @@ type internal UnusedOpensDiagnosticAnalyzer [<ImportingConstructor>] () =
asyncMaybe {
do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds)
do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time
let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! sourceText = document.GetTextAsync()
let checker = getChecker document
let checker = checkerProvider.Checker
let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker)

return
Expand Down

0 comments on commit 2cd971c

Please sign in to comment.