Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4aba54b
POC
0101 Nov 10, 2022
e0b8fab
fantomas
0101 Nov 10, 2022
62124ef
benchmarks
0101 Nov 11, 2022
ec6f313
Parallel find references in VS
0101 Nov 11, 2022
420a367
Merge branch 'faster-find-reference' into benchmarks
0101 Nov 11, 2022
76ba005
Some benchmarks
0101 Nov 11, 2022
f0dd929
refactoring
0101 Nov 11, 2022
f182b3f
Enable fastFindReferences in VS
0101 Nov 14, 2022
8db1370
experimental task workflow
0101 Nov 15, 2022
7884892
Task.Run
0101 Nov 15, 2022
9463e0b
Thread-safe rename
0101 Nov 15, 2022
c60fe2d
Simplification
0101 Nov 15, 2022
e41c6b3
Merge branch 'main' into faster-find-reference
0101 Nov 21, 2022
befde7f
Fast Find References fix for backticks, operators & attributes
0101 Nov 22, 2022
72a3d45
fantomas
0101 Nov 22, 2022
d6f6126
Fast find references configurable from performance options
0101 Nov 22, 2022
abe4267
update surface area
0101 Nov 22, 2022
10caa2d
Marked ParsedInput.Identifiers as experimental
0101 Nov 22, 2022
5b6b54b
Merge remote-tracking branch 'upstream/main' into faster-find-reference
0101 Nov 22, 2022
cb57811
Refactored fast find references setting
0101 Nov 22, 2022
9b98c2e
benchmark update
0101 Nov 22, 2022
15283f6
cleanup
0101 Nov 22, 2022
6ac1e40
cleanup
0101 Nov 22, 2022
e9fbb9b
Don't look for references in documents above declaration
0101 Nov 23, 2022
b9286fa
Fix finding attributes from usage (constructor)
0101 Nov 24, 2022
e387e4f
Merge remote-tracking branch 'upstream/main' into faster-find-reference
0101 Nov 24, 2022
64cc041
Added search keywords for fast find references option
0101 Nov 24, 2022
ca366b9
Test for SemanticClassificationKeyStoreBuilder
0101 Nov 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/Compiler/Checking/CheckDeclarations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5309,17 +5309,17 @@ let CheckOneImplFile
synImplFile,
diagnosticOptions) =

let (ParsedImplFileInput (fileName, isScript, qualNameOfFile, scopedPragmas, _, implFileFrags, isLastCompiland, _)) = synImplFile
let (ParsedImplFileInput (fileName, isScript, qualNameOfFile, scopedPragmas, _, implFileFrags, isLastCompiland, _, _)) = synImplFile
let infoReader = InfoReader(g, amap)

cancellable {
use _ =
Activity.start "CheckDeclarations.CheckOneImplFile"
use _ =
Activity.start "CheckDeclarations.CheckOneImplFile"
[|
"fileName", fileName
"qualifiedNameOfFile", qualNameOfFile.Text
|]
let cenv =
let cenv =
cenv.Create (g, isScript, amap, thisCcu, false, Option.isSome rootSigOpt,
conditionalDefines, tcSink, (LightweightTcValForUsingInBuildMethodCall g), isInternalTestSpanStackReferring,
diagnosticOptions,
Expand Down Expand Up @@ -5445,15 +5445,15 @@ let CheckOneImplFile


/// Check an entire signature file
let CheckOneSigFile (g, amap, thisCcu, checkForErrors, conditionalDefines, tcSink, isInternalTestSpanStackReferring, diagnosticOptions) tcEnv (sigFile: ParsedSigFileInput) =
let CheckOneSigFile (g, amap, thisCcu, checkForErrors, conditionalDefines, tcSink, isInternalTestSpanStackReferring, diagnosticOptions) tcEnv (sigFile: ParsedSigFileInput) =
cancellable {
use _ =
Activity.start "CheckDeclarations.CheckOneSigFile"
[|
"fileName", sigFile.FileName
"qualifiedNameOfFile", sigFile.QualifiedName.Text
|]
let cenv =
let cenv =
cenv.Create
(g, false, amap, thisCcu, true, false, conditionalDefines, tcSink,
(LightweightTcValForUsingInBuildMethodCall g), isInternalTestSpanStackReferring,
Expand Down
64 changes: 46 additions & 18 deletions src/Compiler/Driver/ParseAndCheckInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ let PrependPathToSpec x (SynModuleOrNamespaceSig (longId, isRecursive, kind, dec

let PrependPathToInput x inp =
match inp with
| ParsedInput.ImplFile (ParsedImplFileInput (b, c, q, d, hd, impls, e, trivia)) ->
| ParsedInput.ImplFile (ParsedImplFileInput (b, c, q, d, hd, impls, e, trivia, i)) ->
ParsedInput.ImplFile(
ParsedImplFileInput(b, c, PrependPathToQualFileName x q, d, hd, List.map (PrependPathToImpl x) impls, e, trivia)
ParsedImplFileInput(b, c, PrependPathToQualFileName x q, d, hd, List.map (PrependPathToImpl x) impls, e, trivia, i)
)

| ParsedInput.SigFile (ParsedSigFileInput (b, q, d, hd, specs, trivia)) ->
ParsedInput.SigFile(ParsedSigFileInput(b, PrependPathToQualFileName x q, d, hd, List.map (PrependPathToSpec x) specs, trivia))
| ParsedInput.SigFile (ParsedSigFileInput (b, q, d, hd, specs, trivia, i)) ->
ParsedInput.SigFile(ParsedSigFileInput(b, PrependPathToQualFileName x q, d, hd, List.map (PrependPathToSpec x) specs, trivia, i))

let IsValidAnonModuleName (modname: string) =
modname |> String.forall (fun c -> Char.IsLetterOrDigit c || c = '_')
Expand Down Expand Up @@ -244,7 +244,8 @@ let PostParseModuleImpls
isLastCompiland,
ParsedImplFile (hashDirectives, impls),
lexbuf: UnicodeLexing.Lexbuf,
tripleSlashComments: range list
tripleSlashComments: range list,
identifiers: Set<string>
) =
let othersWithSameName =
impls
Expand Down Expand Up @@ -284,7 +285,9 @@ let PostParseModuleImpls
CodeComments = codeComments
}

ParsedInput.ImplFile(ParsedImplFileInput(fileName, isScript, qualName, scopedPragmas, hashDirectives, impls, isLastCompiland, trivia))
ParsedInput.ImplFile(
ParsedImplFileInput(fileName, isScript, qualName, scopedPragmas, hashDirectives, impls, isLastCompiland, trivia, identifiers)
)

let PostParseModuleSpecs
(
Expand All @@ -293,7 +296,8 @@ let PostParseModuleSpecs
isLastCompiland,
ParsedSigFile (hashDirectives, specs),
lexbuf: UnicodeLexing.Lexbuf,
tripleSlashComments: range list
tripleSlashComments: range list,
identifiers: Set<string>
) =
let othersWithSameName =
specs
Expand Down Expand Up @@ -332,7 +336,7 @@ let PostParseModuleSpecs
CodeComments = codeComments
}

ParsedInput.SigFile(ParsedSigFileInput(fileName, qualName, scopedPragmas, hashDirectives, specs, trivia))
ParsedInput.SigFile(ParsedSigFileInput(fileName, qualName, scopedPragmas, hashDirectives, specs, trivia, identifiers))

type ModuleNamesDict = Map<string, Map<string, QualifiedNameOfFile>>

Expand Down Expand Up @@ -377,26 +381,26 @@ let DeduplicateModuleName (moduleNamesDict: ModuleNamesDict) fileName (qualNameO
let DeduplicateParsedInputModuleName (moduleNamesDict: ModuleNamesDict) input =
match input with
| ParsedInput.ImplFile implFile ->
let (ParsedImplFileInput (fileName, isScript, qualNameOfFile, scopedPragmas, hashDirectives, modules, flags, trivia)) =
let (ParsedImplFileInput (fileName, isScript, qualNameOfFile, scopedPragmas, hashDirectives, modules, flags, trivia, identifiers)) =
implFile

let qualNameOfFileR, moduleNamesDictR =
DeduplicateModuleName moduleNamesDict fileName qualNameOfFile

let implFileR =
ParsedImplFileInput(fileName, isScript, qualNameOfFileR, scopedPragmas, hashDirectives, modules, flags, trivia)
ParsedImplFileInput(fileName, isScript, qualNameOfFileR, scopedPragmas, hashDirectives, modules, flags, trivia, identifiers)

let inputR = ParsedInput.ImplFile implFileR
inputR, moduleNamesDictR
| ParsedInput.SigFile sigFile ->
let (ParsedSigFileInput (fileName, qualNameOfFile, scopedPragmas, hashDirectives, modules, trivia)) =
let (ParsedSigFileInput (fileName, qualNameOfFile, scopedPragmas, hashDirectives, modules, trivia, identifiers)) =
sigFile

let qualNameOfFileR, moduleNamesDictR =
DeduplicateModuleName moduleNamesDict fileName qualNameOfFile

let sigFileR =
ParsedSigFileInput(fileName, qualNameOfFileR, scopedPragmas, hashDirectives, modules, trivia)
ParsedSigFileInput(fileName, qualNameOfFileR, scopedPragmas, hashDirectives, modules, trivia, identifiers)

let inputT = ParsedInput.SigFile sigFileR
inputT, moduleNamesDictR
Expand Down Expand Up @@ -427,6 +431,28 @@ let ParseInput

try
let input =
let identStore = HashSet<string>()

let identCaptureLexer x =
let token = lexer x

match token with
| Parser.token.PERCENT_OP ident
| Parser.token.FUNKY_OPERATOR_NAME ident
| Parser.token.ADJACENT_PREFIX_OP ident
| Parser.token.PLUS_MINUS_OP ident
| Parser.token.INFIX_AMP_OP ident
| Parser.token.INFIX_STAR_DIV_MOD_OP ident
| Parser.token.PREFIX_OP ident
| Parser.token.INFIX_BAR_OP ident
| Parser.token.INFIX_AT_HAT_OP ident
| Parser.token.INFIX_COMPARE_OP ident
| Parser.token.INFIX_STAR_STAR_OP ident
| Parser.token.IDENT ident -> identStore.Add ident |> ignore
| _ -> ()

token

if FSharpMLCompatFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) then
if lexbuf.SupportsFeature LanguageFeature.MLCompatRevisions then
errorR (Error(FSComp.SR.buildInvalidSourceFileExtensionML fileName, rangeStartup))
Expand All @@ -435,19 +461,19 @@ let ParseInput

// Call the appropriate parser - for signature files or implementation files
if FSharpImplFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) then
let impl = Parser.implementationFile lexer lexbuf
let impl = Parser.implementationFile identCaptureLexer lexbuf

let tripleSlashComments =
LexbufLocalXmlDocStore.ReportInvalidXmlDocPositions(lexbuf)

PostParseModuleImpls(defaultNamespace, fileName, isLastCompiland, impl, lexbuf, tripleSlashComments)
PostParseModuleImpls(defaultNamespace, fileName, isLastCompiland, impl, lexbuf, tripleSlashComments, Set identStore)
elif FSharpSigFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) then
let intfs = Parser.signatureFile lexer lexbuf
let intfs = Parser.signatureFile identCaptureLexer lexbuf

let tripleSlashComments =
LexbufLocalXmlDocStore.ReportInvalidXmlDocPositions(lexbuf)

PostParseModuleSpecs(defaultNamespace, fileName, isLastCompiland, intfs, lexbuf, tripleSlashComments)
PostParseModuleSpecs(defaultNamespace, fileName, isLastCompiland, intfs, lexbuf, tripleSlashComments, Set identStore)
else if lexbuf.SupportsFeature LanguageFeature.MLCompatRevisions then
error (Error(FSComp.SR.buildInvalidSourceFileExtensionUpdated fileName, rangeStartup))
else
Expand Down Expand Up @@ -519,7 +545,8 @@ let EmptyParsedInput (fileName, isLastCompiland) =
{
ConditionalDirectives = []
CodeComments = []
}
},
Set.empty
)
)
else
Expand All @@ -535,7 +562,8 @@ let EmptyParsedInput (fileName, isLastCompiland) =
{
ConditionalDirectives = []
CodeComments = []
}
},
Set.empty
)
)

Expand Down
14 changes: 12 additions & 2 deletions src/Compiler/Driver/ScriptClosure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -515,13 +515,23 @@ module ScriptPreprocessClosure =
match lastParsedInput with
| Some (ParsedInput.ImplFile lastParsedImplFile) ->

let (ParsedImplFileInput (name, isScript, qualNameOfFile, scopedPragmas, hashDirectives, implFileFlags, _, trivia)) =
let (ParsedImplFileInput (name, isScript, qualNameOfFile, scopedPragmas, hashDirectives, implFileFlags, _, trivia, identifiers)) =
lastParsedImplFile

let isLastCompiland = (true, tcConfig.target.IsExe)

let lastParsedImplFileR =
ParsedImplFileInput(name, isScript, qualNameOfFile, scopedPragmas, hashDirectives, implFileFlags, isLastCompiland, trivia)
ParsedImplFileInput(
name,
isScript,
qualNameOfFile,
scopedPragmas,
hashDirectives,
implFileFlags,
isLastCompiland,
trivia,
identifiers
)

let lastClosureFileR =
ClosureFile(fileName, m, Some(ParsedInput.ImplFile lastParsedImplFileR), parseDiagnostics, metaDiagnostics, nowarns)
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Interactive/fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,7 @@ type internal FsiDynamicCompiler(
let impl = SynModuleOrNamespace(prefix,false, SynModuleOrNamespaceKind.NamedModule,defs,PreXmlDoc.Empty,[],None,m, { LeadingKeyword = SynModuleOrNamespaceLeadingKeyword.None })
let isLastCompiland = true
let isExe = false
let input = ParsedInput.ImplFile (ParsedImplFileInput (fileName,true, ComputeQualifiedNameOfFileFromUniquePath (m,prefixPath),[],[],[impl],(isLastCompiland, isExe), { ConditionalDirectives = []; CodeComments = [] }))
let input = ParsedInput.ImplFile (ParsedImplFileInput (fileName,true, ComputeQualifiedNameOfFileFromUniquePath (m,prefixPath),[],[],[impl],(isLastCompiland, isExe), { ConditionalDirectives = []; CodeComments = [] }, Set.empty))
let isIncrementalFragment = true
let istate,tcEnvAtEndOfLastInput,declaredImpls = ProcessInputs (ctok, diagnosticsLogger, istate, [input], showTypes, isIncrementalFragment, isInteractiveItExpr, prefix, m)
let tcState = istate.tcState
Expand Down
6 changes: 4 additions & 2 deletions src/Compiler/Service/IncrementalBuild.fs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ module IncrementalBuildSyntaxTree =
[],
[],
isLastCompiland,
{ ConditionalDirectives = []; CodeComments = [] }
{ ConditionalDirectives = []; CodeComments = [] },
Set.empty
)
)
else
Expand Down Expand Up @@ -292,7 +293,8 @@ type BoundModel private (tcConfig: TcConfig,
GraphNode(node {
match! this.TypeCheck(false) with
| FullState(tcInfo, tcInfoExtras) -> return tcInfo, tcInfoExtras
| PartialState(tcInfo) -> return tcInfo, emptyTcInfoExtras
| PartialState(tcInfo) ->
return tcInfo, emptyTcInfoExtras
})

let partialGraphNode =
Expand Down
9 changes: 7 additions & 2 deletions src/Compiler/Service/SemanticClassificationKey.fs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@ type SemanticClassificationKeyStoreBuilder() =
let b = BlobBuilder()

member _.WriteAll(semanticClassification: SemanticClassificationItem[]) =
use ptr = fixed semanticClassification
b.WriteBytes(NativePtr.ofNativeInt (NativePtr.toNativeInt ptr), semanticClassification.Length * sizeof<SemanticClassificationItem>)
if semanticClassification.Length > 0 then
use ptr = fixed semanticClassification

b.WriteBytes(
NativePtr.ofNativeInt (NativePtr.toNativeInt ptr),
semanticClassification.Length * sizeof<SemanticClassificationItem>
)

member _.TryBuildAndReset() =
if b.Count > 0 then
Expand Down
28 changes: 27 additions & 1 deletion src/Compiler/Service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ module Helpers =
let AreSubsumable3 ((fileName1: string, _, o1: FSharpProjectOptions), (fileName2: string, _, o2: FSharpProjectOptions)) =
(fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2)

/// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix
let rec NamesContainAttribute (symbol: FSharpSymbol) names =
match symbol with
| :? FSharpMemberOrFunctionOrValue as mofov ->
mofov.DeclaringEntity
|> Option.map (fun entity -> NamesContainAttribute entity names)
|> Option.defaultValue false
| :? FSharpEntity as entity when entity.IsAttributeType && symbol.DisplayNameCore.EndsWithOrdinal "Attribute" ->
let nameWithoutAttribute = String.dropSuffix symbol.DisplayNameCore "Attribute"
names |> Set.contains nameWithoutAttribute
| _ -> false

module CompileHelpers =
let mkCompilationDiagnosticsHandlers () =
let diagnostics = ResizeArray<_>()
Expand Down Expand Up @@ -1445,12 +1457,26 @@ type FSharpChecker
options: FSharpProjectOptions,
symbol: FSharpSymbol,
?canInvalidateProject: bool,
?fastCheck: bool,
?userOpName: string
) =
let canInvalidateProject = defaultArg canInvalidateProject true
let userOpName = defaultArg userOpName "Unknown"

backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName)
node {
if fastCheck <> Some true then
return! backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName)
else
let! parseResults = backgroundCompiler.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName)

if
parseResults.ParseTree.Identifiers |> Set.contains symbol.DisplayNameCore
|| parseResults.ParseTree.Identifiers |> NamesContainAttribute symbol
then
return! backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName)
else
return Seq.empty
}
|> Async.AwaitNodeCode

member _.GetBackgroundSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, ?userOpName) =
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Service/service.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,14 @@ type public FSharpChecker =
/// <param name="options">The options for the project or script, used to determine active --define conditionals and other options relevant to parsing.</param>
/// <param name="symbol">The symbol to find all uses in the file.</param>
/// <param name="canInvalidateProject">Default: true. If true, this call can invalidate the current state of project if the options have changed. If false, the current state of the project will be used.</param>
/// <param name="fastCheck">Default: false. Experimental feature that makes the operation faster.</param>
/// <param name="userOpName">An optional string used for tracing compiler operations associated with this request.</param>
member FindBackgroundReferencesInFile:
fileName: string *
options: FSharpProjectOptions *
symbol: FSharpSymbol *
?canInvalidateProject: bool *
?fastCheck: bool *
?userOpName: string ->
Async<range seq>

Expand Down
12 changes: 10 additions & 2 deletions src/Compiler/SyntaxTree/SyntaxTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1680,7 +1680,8 @@ type ParsedImplFileInput =
hashDirectives: ParsedHashDirective list *
contents: SynModuleOrNamespace list *
flags: (bool * bool) *
trivia: ParsedImplFileInputTrivia
trivia: ParsedImplFileInputTrivia *
identifiers: Set<string>

member x.QualifiedName =
(let (ParsedImplFileInput (qualifiedNameOfFile = qualNameOfFile)) = x in qualNameOfFile)
Expand Down Expand Up @@ -1712,7 +1713,8 @@ type ParsedSigFileInput =
scopedPragmas: ScopedPragma list *
hashDirectives: ParsedHashDirective list *
contents: SynModuleOrNamespaceSig list *
trivia: ParsedSigFileInputTrivia
trivia: ParsedSigFileInputTrivia *
identifiers: Set<string>

member x.QualifiedName =
(let (ParsedSigFileInput (qualifiedNameOfFile = qualNameOfFile)) = x in qualNameOfFile)
Expand Down Expand Up @@ -1755,3 +1757,9 @@ type ParsedInput =
| ParsedInput.ImplFile (ParsedImplFileInput(contents = SynModuleOrNamespace (range = m) :: _))
| ParsedInput.SigFile (ParsedSigFileInput(contents = SynModuleOrNamespaceSig (range = m) :: _)) -> m
| _ -> rangeN inp.FileName 0

[<Experimental("This FCS API is experimental and subject to change.")>]
member inp.Identifiers =
match inp with
| ParsedInput.ImplFile (ParsedImplFileInput (identifiers = identifiers))
| ParsedInput.SigFile (ParsedSigFileInput (identifiers = identifiers)) -> identifiers
9 changes: 7 additions & 2 deletions src/Compiler/SyntaxTree/SyntaxTree.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1889,7 +1889,8 @@ type ParsedImplFileInput =
hashDirectives: ParsedHashDirective list *
contents: SynModuleOrNamespace list *
flags: (bool * bool) *
trivia: ParsedImplFileInputTrivia
trivia: ParsedImplFileInputTrivia *
identifiers: Set<string>

member FileName: string

Expand Down Expand Up @@ -1918,7 +1919,8 @@ type ParsedSigFileInput =
scopedPragmas: ScopedPragma list *
hashDirectives: ParsedHashDirective list *
contents: SynModuleOrNamespaceSig list *
trivia: ParsedSigFileInputTrivia
trivia: ParsedSigFileInputTrivia *
identifiers: Set<string>

member FileName: string

Expand Down Expand Up @@ -1952,3 +1954,6 @@ type ParsedInput =

/// Gets the #nowarn and other scoped pragmas
member ScopedPragmas: ScopedPragma list

/// Gets a set of all identifiers used in this parsed input
member Identifiers: Set<string>
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@
<Compile Include="Globalization\GlobalizationTestCases.fs" />
<Compile Include="OCamlCompat\OCamlCompat.fs" />
<Compile Include="Miscellaneous\ListLiterals.fs" />
<Compile Include="Miscellaneous\SemanticClassificationKeyBuilder.fs" />
<Compile Include="Miscellaneous\XmlDoc.fs" />
<Compile Include="Signatures\TestHelpers.fs" />
<Compile Include="Signatures\ModuleOrNamespaceTests.fs" />
Expand Down
Loading