Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimized find all references and reduced memory usage in VS #8339

Merged
merged 25 commits into from Feb 14, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8975468
Added ItemKey.fsi/fsi. Added blank SemanticClassification.fs/fsi.
TIHan Jan 23, 2020
6fc6bda
Raise disposed exception
TIHan Jan 23, 2020
8212e37
Re-worked semantic classification. Renamed ItemKeyReader to ItemKeySt…
TIHan Jan 23, 2020
9e6d8f5
Fixing build
TIHan Jan 23, 2020
d619e66
Storing semantic classification
TIHan Jan 23, 2020
7c460fe
Caching semantic classification
TIHan Jan 25, 2020
3fb228e
Wiring it up
TIHan Jan 28, 2020
a488eaf
Need to fix lexing
TIHan Jan 29, 2020
b2c8764
Added experimental lexing API to handle find all refs syntactic class…
TIHan Feb 3, 2020
add45b5
Added System.Memory
TIHan Feb 3, 2020
123f652
Using Span to check equality without allocating
TIHan Feb 3, 2020
86fad2b
Allocate less
TIHan Feb 4, 2020
cc3d071
Fixing build. Reducing more allocations and not using lex filter on l…
TIHan Feb 4, 2020
98453e3
Remove langversion
TIHan Feb 4, 2020
da7550b
Fixed record find all refs
TIHan Feb 4, 2020
fb9d5e0
Fixing test
TIHan Feb 4, 2020
03f5304
Partial match for active pattern
TIHan Feb 5, 2020
3b6a47f
Feedback changes
TIHan Feb 6, 2020
8291454
Added comment on TcResolutionsExtensions
TIHan Feb 6, 2020
eefd1f0
Creating view accessor when needed in ItemKey. Fixed UnionCase find a…
TIHan Feb 12, 2020
851a061
Added comment on warning
TIHan Feb 12, 2020
c88398c
Added Range.comparer. Moving opens to top of file
TIHan Feb 12, 2020
277e3db
More feedback changes
TIHan Feb 12, 2020
101c4b0
Merged master
TIHan Feb 12, 2020
c0cf59a
Added comment on sliding expiration
TIHan Feb 13, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/Versions.props
Expand Up @@ -96,6 +96,7 @@
<SystemThreadingThreadPoolVersion>4.3.0</SystemThreadingThreadPoolVersion>
<SystemValueTupleVersion>4.5.0</SystemValueTupleVersion>
<SystemBuffersVersion>4.5.0</SystemBuffersVersion>
<SystemMemoryVersion>4.5.3</SystemMemoryVersion>
<!-- Roslyn packages -->
<MicrosoftCodeAnalysisEditorFeaturesVersion>$(RoslynVersion)</MicrosoftCodeAnalysisEditorFeaturesVersion>
<MicrosoftCodeAnalysisEditorFeaturesTextVersion>$(RoslynVersion)</MicrosoftCodeAnalysisEditorFeaturesTextVersion>
Expand Down
13 changes: 13 additions & 0 deletions fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
Expand Up @@ -573,6 +573,18 @@
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\Reactor.fs">
<Link>Service/Reactor.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\SemanticClassification.fsi">
<Link>Service/SemanticClassification.fsi</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\SemanticClassification.fs">
<Link>Service/SemanticClassification.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\ItemKey.fsi">
<Link>Service/ItemKey.fsi</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\ItemKey.fs">
<Link>Service/ItemKey.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\IncrementalBuild.fsi">
<Link>Service/IncrementalBuild.fsi</Link>
</Compile>
Expand Down Expand Up @@ -681,6 +693,7 @@
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Reflection.Metadata" Version="1.6.0" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Diagnostics.Process" Version="4.1.0" />
Expand Down
14 changes: 14 additions & 0 deletions src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
Expand Up @@ -28,6 +28,7 @@
<BuiltProjectOutputGroupKeyOutput Include="$(BuildOutputGroupLocation)\System.Reflection.Metadata.dll" />
<BuiltProjectOutputGroupKeyOutput Include="$(BuildOutputGroupLocation)\System.Collections.Immutable.dll" />
<BuiltProjectOutputGroupKeyOutput Include="$(BuildOutputGroupLocation)\System.Buffers.dll" />
<BuiltProjectOutputGroupKeyOutput Include="$(BuildOutputGroupLocation)\System.Memory.dll" />
</ItemGroup>
</Target>

Expand Down Expand Up @@ -579,6 +580,18 @@
</Compile>

<!-- the incremental builder and service . -->
<Compile Include="..\service\SemanticClassification.fsi">
<Link>Service/SemanticClassification.fsi</Link>
</Compile>
<Compile Include="..\service\SemanticClassification.fs">
<Link>Service/SemanticClassification.fs</Link>
</Compile>
<Compile Include="..\service\ItemKey.fsi">
<Link>Service/ItemKey.fsi</Link>
</Compile>
<Compile Include="..\service\ItemKey.fs">
<Link>Service/ItemKey.fs</Link>
</Compile>
<Compile Include="..\service\IncrementalBuild.fsi">
<Link>Service/IncrementalBuild.fsi</Link>
</Compile>
Expand Down Expand Up @@ -745,6 +758,7 @@
<PackageReference Include="System.Threading.Thread" Version="$(SystemThreadingThreadVersion)" />
<PackageReference Include="System.Threading.ThreadPool" Version="$(SystemThreadingThreadPoolVersion)" />
<PackageReference Include="System.Buffers" Version="$(SystemBuffersVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
</ItemGroup>

</Project>
Expand Up @@ -34,6 +34,7 @@
<dependency id="System.Threading.ThreadPool" version="4.0.10" />
<dependency id="System.ValueTuple" version="4.3.0" />
<dependency id="System.Buffers" version="4.5.0" />
<dependency id="System.Memory" version="4.5.3" />
</group>
</dependencies>
</metadata>
Expand Down
Expand Up @@ -23,6 +23,7 @@
<dependency id="System.Threading.ThreadPool" version="4.3.0" />
<dependency id="System.ValueTuple" version="4.4.0" />
<dependency id="System.Buffers" version="4.5.0" />
<dependency id="System.Memory" version="4.5.3" />
</group>
</dependencies>
<contentFiles>
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/NameResolution.fs
Expand Up @@ -1757,6 +1757,9 @@ type TcResultsSinkImpl(g, ?sourceText: ISourceText) =
member this.GetOpenDeclarations() =
capturedOpenDeclarations |> Seq.distinctBy (fun x -> x.Range, x.AppliedScope, x.IsOwnNamespace) |> Seq.toArray

member this.GetFormatSpecifierLocations() =
capturedFormatSpecifierLocations.ToArray()

interface ITypecheckResultsSink with
member sink.NotifyEnvWithScope(m, nenv, ad) =
if allowedRange m then
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/NameResolution.fsi
Expand Up @@ -438,6 +438,9 @@ type internal TcResultsSinkImpl =
/// Get all open declarations reported to the sink
member GetOpenDeclarations : unit -> OpenDeclaration[]

/// Get the format specifier locations
member GetFormatSpecifierLocations : unit -> (range * int)[]

interface ITypecheckResultsSink

/// An abstract type for reporting the results of name resolution and type checking, and which allows
Expand Down
4 changes: 2 additions & 2 deletions src/fsharp/lexhelp.fs
Expand Up @@ -36,8 +36,8 @@ type LightSyntaxStatus(initial:bool,warn:bool) =

/// Manage lexer resources (string interning)
[<Sealed>]
type LexResourceManager() =
let strings = new System.Collections.Generic.Dictionary<string, Parser.token>(1024)
type LexResourceManager(?capacity: int) =
let strings = new System.Collections.Generic.Dictionary<string, Parser.token>(defaultArg capacity 1024)
member x.InternIdentifierToken(s) =
match strings.TryGetValue s with
| true, res -> res
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/lexhelp.fsi
Expand Up @@ -25,7 +25,7 @@ type LightSyntaxStatus =

[<Sealed>]
type LexResourceManager =
new : unit -> LexResourceManager
new : ?capacity: int -> LexResourceManager

type lexargs =
{ defines: string list
Expand Down
149 changes: 2 additions & 147 deletions src/fsharp/service/FSharpCheckerResults.fs
Expand Up @@ -109,24 +109,6 @@ type GetPreciseCompletionListFromExprTypingsResult =
| Some of (ItemWithInst list * DisplayEnv * range) * TType

type Names = string list

[<RequireQualifiedAccess>]
type SemanticClassificationType =
| ReferenceType
| ValueType
| UnionCase
| Function
| Property
| MutableVar
| Module
| Printf
| ComputationExpression
| IntrinsicFunction
| Enumeration
| Interface
| TypeArgument
| Operator
| Disposable

/// A TypeCheckInfo represents everything we get back from the typecheck of a file.
/// It acts like an in-memory database about the file.
Expand Down Expand Up @@ -155,8 +137,6 @@ type internal TypeCheckInfo
openDeclarations: OpenDeclaration[]) =

let textSnapshotInfo = defaultArg textSnapshotInfo null
let (|CNR|) (cnr:CapturedNameResolution) =
(cnr.Pos, cnr.Item, cnr.ItemOccurence, cnr.DisplayEnv, cnr.NameResolutionEnv, cnr.AccessorDomain, cnr.Range)

// These strings are potentially large and the editor may choose to hold them for a while.
// Use this cache to fold together data tip text results that are the same.
Expand Down Expand Up @@ -1262,133 +1242,8 @@ type internal TypeCheckInfo
member __.GetFormatSpecifierLocationsAndArity() =
sSymbolUses.GetFormatSpecifierLocationsAndArity()

member __.GetSemanticClassification(range: range option) : (range * SemanticClassificationType) [] =
ErrorScope.Protect Range.range0
(fun () ->
let (|LegitTypeOccurence|_|) = function
| ItemOccurence.UseInType
| ItemOccurence.UseInAttribute
| ItemOccurence.Use _
| ItemOccurence.Binding _
| ItemOccurence.Pattern _ -> Some()
| _ -> None

let (|OptionalArgumentAttribute|_|) ttype =
match ttype with
| TType.TType_app(tref, _) when tref.Stamp = g.attrib_OptionalArgumentAttribute.TyconRef.Stamp -> Some()
| _ -> None

let (|KeywordIntrinsicValue|_|) (vref: ValRef) =
if valRefEq g g.raise_vref vref ||
valRefEq g g.reraise_vref vref ||
valRefEq g g.typeof_vref vref ||
valRefEq g g.typedefof_vref vref ||
valRefEq g g.sizeof_vref vref ||
valRefEq g g.nameof_vref vref
then Some()
else None

let (|EnumCaseFieldInfo|_|) (rfinfo : RecdFieldInfo) =
match rfinfo.TyconRef.TypeReprInfo with
| TFSharpObjectRepr x ->
match x.fsobjmodel_kind with
| TTyconEnum -> Some ()
| _ -> None
| _ -> None

let resolutions =
match range with
| Some range ->
sResolutions.CapturedNameResolutions
|> Seq.filter (fun cnr -> rangeContainsPos range cnr.Range.Start || rangeContainsPos range cnr.Range.End)
| None ->
sResolutions.CapturedNameResolutions :> seq<_>

let isDisposableTy (ty: TType) =
protectAssemblyExplorationNoReraise false false (fun () -> Infos.ExistsHeadTypeInEntireHierarchy g amap range0 ty g.tcref_System_IDisposable)

let isStructTyconRef (tyconRef: TyconRef) =
let ty = generalizedTyconRef tyconRef
let underlyingTy = stripTyEqnsAndMeasureEqns g ty
isStructTy g underlyingTy

let isValRefMutable (vref: ValRef) =
// Mutable values, ref cells, and non-inref byrefs are mutable.
vref.IsMutable
|| Tastops.isRefCellTy g vref.Type
|| (Tastops.isByrefTy g vref.Type && not (Tastops.isInByrefTy g vref.Type))

let isRecdFieldMutable (rfinfo: RecdFieldInfo) =
(rfinfo.RecdField.IsMutable && rfinfo.LiteralValue.IsNone)
|| Tastops.isRefCellTy g rfinfo.RecdField.FormalType

resolutions
|> Seq.choose (fun cnr ->
match cnr with
// 'seq' in 'seq { ... }' gets colored as keywords
| CNR(_, (Item.Value vref), ItemOccurence.Use, _, _, _, m) when valRefEq g g.seq_vref vref ->
Some (m, SemanticClassificationType.ComputationExpression)
| CNR(_, (Item.Value vref), _, _, _, _, m) when isValRefMutable vref ->
Some (m, SemanticClassificationType.MutableVar)
| CNR(_, Item.Value KeywordIntrinsicValue, ItemOccurence.Use, _, _, _, m) ->
Some (m, SemanticClassificationType.IntrinsicFunction)
| CNR(_, (Item.Value vref), _, _, _, _, m) when isFunction g vref.Type ->
if valRefEq g g.range_op_vref vref || valRefEq g g.range_step_op_vref vref then
None
elif vref.IsPropertyGetterMethod || vref.IsPropertySetterMethod then
Some (m, SemanticClassificationType.Property)
elif IsOperatorName vref.DisplayName then
Some (m, SemanticClassificationType.Operator)
else
Some (m, SemanticClassificationType.Function)
| CNR(_, Item.RecdField rfinfo, _, _, _, _, m) when isRecdFieldMutable rfinfo ->
Some (m, SemanticClassificationType.MutableVar)
| CNR(_, Item.RecdField rfinfo, _, _, _, _, m) when isFunction g rfinfo.FieldType ->
Some (m, SemanticClassificationType.Function)
| CNR(_, Item.RecdField EnumCaseFieldInfo, _, _, _, _, m) ->
Some (m, SemanticClassificationType.Enumeration)
| CNR(_, Item.MethodGroup _, _, _, _, _, m) ->
Some (m, SemanticClassificationType.Function)
// custom builders, custom operations get colored as keywords
| CNR(_, (Item.CustomBuilder _ | Item.CustomOperation _), ItemOccurence.Use, _, _, _, m) ->
Some (m, SemanticClassificationType.ComputationExpression)
// types get colored as types when they occur in syntactic types or custom attributes
// type variables get colored as types when they occur in syntactic types custom builders, custom operations get colored as keywords
| CNR(_, Item.Types (_, [OptionalArgumentAttribute]), LegitTypeOccurence, _, _, _, _) -> None
| CNR(_, Item.CtorGroup(_, [MethInfo.FSMeth(_, OptionalArgumentAttribute, _, _)]), LegitTypeOccurence, _, _, _, _) -> None
| CNR(_, Item.Types(_, types), LegitTypeOccurence, _, _, _, m) when types |> List.exists (isInterfaceTy g) ->
Some (m, SemanticClassificationType.Interface)
| CNR(_, Item.Types(_, types), LegitTypeOccurence, _, _, _, m) when types |> List.exists (isStructTy g) ->
Some (m, SemanticClassificationType.ValueType)
| CNR(_, Item.Types(_, TType_app(tyconRef, TType_measure _ :: _) :: _), LegitTypeOccurence, _, _, _, m) when isStructTyconRef tyconRef ->
Some (m, SemanticClassificationType.ValueType)
| CNR(_, Item.Types(_, types), LegitTypeOccurence, _, _, _, m) when types |> List.exists isDisposableTy ->
Some (m, SemanticClassificationType.Disposable)
| CNR(_, Item.Types _, LegitTypeOccurence, _, _, _, m) ->
Some (m, SemanticClassificationType.ReferenceType)
| CNR(_, (Item.TypeVar _ ), LegitTypeOccurence, _, _, _, m) ->
Some (m, SemanticClassificationType.TypeArgument)
| CNR(_, Item.UnqualifiedType tyconRefs, LegitTypeOccurence, _, _, _, m) ->
if tyconRefs |> List.exists (fun tyconRef -> tyconRef.Deref.IsStructOrEnumTycon) then
Some (m, SemanticClassificationType.ValueType)
else Some (m, SemanticClassificationType.ReferenceType)
| CNR(_, Item.CtorGroup(_, minfos), LegitTypeOccurence, _, _, _, m) ->
if minfos |> List.exists (fun minfo -> isStructTy g minfo.ApparentEnclosingType) then
Some (m, SemanticClassificationType.ValueType)
else Some (m, SemanticClassificationType.ReferenceType)
| CNR(_, Item.ExnCase _, LegitTypeOccurence, _, _, _, m) ->
Some (m, SemanticClassificationType.ReferenceType)
| CNR(_, Item.ModuleOrNamespaces refs, LegitTypeOccurence, _, _, _, m) when refs |> List.exists (fun x -> x.IsModule) ->
Some (m, SemanticClassificationType.Module)
| CNR(_, (Item.ActivePatternCase _ | Item.UnionCase _ | Item.ActivePatternResult _), _, _, _, _, m) ->
Some (m, SemanticClassificationType.UnionCase)
| _ -> None)
|> Seq.toArray
|> Array.append (sSymbolUses.GetFormatSpecifierLocationsAndArity() |> Array.map (fun m -> fst m, SemanticClassificationType.Printf))
)
(fun msg ->
Trace.TraceInformation(sprintf "FCS: recovering from error in GetSemanticClassification: '%s'" msg)
Array.empty)
member __.GetSemanticClassification(range: range option) : struct (range * SemanticClassificationType) [] =
sResolutions.GetSemanticClassification(g, amap, sSymbolUses.GetFormatSpecifierLocationsAndArity(), range)

/// The resolutions in the file
member __.ScopeResolutions = sResolutions
Expand Down
20 changes: 1 addition & 19 deletions src/fsharp/service/FSharpCheckerResults.fsi
Expand Up @@ -56,24 +56,6 @@ type public FSharpProjectContext =
/// Get the accessibility rights for this project context w.r.t. InternalsVisibleTo attributes granting access to other assemblies
member AccessibilityRights : FSharpAccessibilityRights

[<RequireQualifiedAccess>]
type public SemanticClassificationType =
| ReferenceType
| ValueType
| UnionCase
| Function
| Property
| MutableVar
| Module
| Printf
| ComputationExpression
| IntrinsicFunction
| Enumeration
| Interface
| TypeArgument
| Operator
| Disposable

/// Options used to determine active --define conditionals and other options relevant to parsing files in a project
type public FSharpParsingOptions =
{
Expand Down Expand Up @@ -234,7 +216,7 @@ type public FSharpCheckFileResults =
member GetSymbolUseAtLocation : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async<FSharpSymbolUse option>

/// <summary>Get any extra colorization info that is available after the typecheck</summary>
member GetSemanticClassification : range option -> (range * SemanticClassificationType)[]
member GetSemanticClassification : range option -> struct (range * SemanticClassificationType)[]
TIHan marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>Get the locations of format specifiers</summary>
[<System.Obsolete("This member has been replaced by GetFormatSpecifierLocationsAndArity, which returns both range and arity of specifiers")>]
Expand Down