Skip to content

Commit d2882af

Browse files
TIHanbaronfel
authored andcommitted
Optimized find all references and reduced memory usage in VS (#8339)
* Added ItemKey.fsi/fsi. Added blank SemanticClassification.fs/fsi. * Raise disposed exception * Re-worked semantic classification. Renamed ItemKeyReader to ItemKeyStore. Exposing ItemKeyStore/Builder * Fixing build * Storing semantic classification * Caching semantic classification * Wiring it up * Need to fix lexing * Added experimental lexing API to handle find all refs syntactic classification from allocating a lot * Added System.Memory * Using Span to check equality without allocating * Allocate less * Fixing build. Reducing more allocations and not using lex filter on lexing tokens. * Remove langversion * Fixed record find all refs * Fixing test * Partial match for active pattern * Feedback changes * Added comment on TcResolutionsExtensions * Creating view accessor when needed in ItemKey. Fixed UnionCase find all refs. * Added comment on warning * Added Range.comparer. Moving opens to top of file * More feedback changes * Added comment on sliding expiration
1 parent c5e25c0 commit d2882af

21 files changed

+1706
-190
lines changed

eng/Versions.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
<SystemThreadingThreadPoolVersion>4.3.0</SystemThreadingThreadPoolVersion>
9797
<SystemValueTupleVersion>4.5.0</SystemValueTupleVersion>
9898
<SystemBuffersVersion>4.5.0</SystemBuffersVersion>
99+
<SystemMemoryVersion>4.5.3</SystemMemoryVersion>
99100
<!-- Roslyn packages -->
100101
<MicrosoftCodeAnalysisEditorFeaturesVersion>$(RoslynVersion)</MicrosoftCodeAnalysisEditorFeaturesVersion>
101102
<MicrosoftCodeAnalysisEditorFeaturesTextVersion>$(RoslynVersion)</MicrosoftCodeAnalysisEditorFeaturesTextVersion>

fcs/FSharp.Compiler.Service.Tests/SemanticColorizationServiceTests.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type SemanticClassificationServiceTests() =
3030
let checker = FSharpChecker.Create()
3131
let perfOptions = { LanguageServicePerformanceOptions.Default with AllowStaleCompletionResults = false }
3232

33-
let getRanges (source: string) : (Range.range * SemanticClassificationType) list =
33+
let getRanges (source: string) : struct (Range.range * SemanticClassificationType) list =
3434
asyncMaybe {
3535

3636
let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, 0, SourceText.From(source), projectOptions, perfOptions, "")
@@ -45,7 +45,7 @@ type SemanticClassificationServiceTests() =
4545
let ranges = getRanges fileContents
4646
let line = text.Lines.GetLinePosition (fileContents.IndexOf(marker) + marker.Length - 1)
4747
let markerPos = Range.mkPos (Range.Line.fromZ line.Line) (line.Character + marker.Length - 1)
48-
match ranges |> List.tryFind (fun (range, _) -> Range.rangeContainsPos range markerPos) with
48+
match ranges |> List.tryFind (fun struct (range, _) -> Range.rangeContainsPos range markerPos) with
4949
| None -> Assert.Fail("Cannot find colorization data for end of marker")
5050
| Some(_, ty) -> Assert.AreEqual(classificationType, FSharpClassificationTypes.getClassificationTypeName ty, "Classification data doesn't match for end of marker")
5151

@@ -54,7 +54,7 @@ type SemanticClassificationServiceTests() =
5454
let ranges = getRanges fileContents
5555
let line = text.Lines.GetLinePosition (fileContents.IndexOf(marker) + marker.Length - 1)
5656
let markerPos = Range.mkPos (Range.Line.fromZ line.Line) (line.Character + marker.Length - 1)
57-
let anyData = ranges |> List.exists (fun (range, sct) -> Range.rangeContainsPos range markerPos && ((FSharpClassificationTypes.getClassificationTypeName sct) = classificationType))
57+
let anyData = ranges |> List.exists (fun struct (range, sct) -> Range.rangeContainsPos range markerPos && ((FSharpClassificationTypes.getClassificationTypeName sct) = classificationType))
5858
Assert.False(anyData, "Classification data was found when it wasn't expected.")
5959

6060
[<TestCase("(*1*)", FSharpClassificationTypes.ValueType)>]

fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,18 @@
576576
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\Reactor.fs">
577577
<Link>Service/Reactor.fs</Link>
578578
</Compile>
579+
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\SemanticClassification.fsi">
580+
<Link>Service/SemanticClassification.fsi</Link>
581+
</Compile>
582+
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\SemanticClassification.fs">
583+
<Link>Service/SemanticClassification.fs</Link>
584+
</Compile>
585+
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\ItemKey.fsi">
586+
<Link>Service/ItemKey.fsi</Link>
587+
</Compile>
588+
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\ItemKey.fs">
589+
<Link>Service/ItemKey.fs</Link>
590+
</Compile>
579591
<Compile Include="$(FSharpSourcesRoot)\fsharp\service\IncrementalBuild.fsi">
580592
<Link>Service/IncrementalBuild.fsi</Link>
581593
</Compile>
@@ -684,6 +696,7 @@
684696
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
685697
<PackageReference Include="System.Reflection.Metadata" Version="1.6.0" />
686698
<PackageReference Include="System.Buffers" Version="4.5.0" />
699+
<PackageReference Include="System.Memory" Version="4.5.3" />
687700
</ItemGroup>
688701
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
689702
<PackageReference Include="System.Diagnostics.Process" Version="4.1.0" />

src/fsharp/NameResolution.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,9 @@ type TcResultsSinkImpl(g, ?sourceText: ISourceText) =
17571757
member this.GetOpenDeclarations() =
17581758
capturedOpenDeclarations |> Seq.distinctBy (fun x -> x.Range, x.AppliedScope, x.IsOwnNamespace) |> Seq.toArray
17591759

1760+
member this.GetFormatSpecifierLocations() =
1761+
capturedFormatSpecifierLocations.ToArray()
1762+
17601763
interface ITypecheckResultsSink with
17611764
member sink.NotifyEnvWithScope(m, nenv, ad) =
17621765
if allowedRange m then

src/fsharp/NameResolution.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ type internal TcResultsSinkImpl =
438438
/// Get all open declarations reported to the sink
439439
member GetOpenDeclarations : unit -> OpenDeclaration[]
440440

441+
/// Get the format specifier locations
442+
member GetFormatSpecifierLocations : unit -> (range * int)[]
443+
441444
interface ITypecheckResultsSink
442445

443446
/// An abstract type for reporting the results of name resolution and type checking, and which allows

src/fsharp/lexhelp.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ type LightSyntaxStatus(initial:bool,warn:bool) =
3636

3737
/// Manage lexer resources (string interning)
3838
[<Sealed>]
39-
type LexResourceManager() =
40-
let strings = new System.Collections.Generic.Dictionary<string, Parser.token>(1024)
39+
type LexResourceManager(?capacity: int) =
40+
let strings = new System.Collections.Generic.Dictionary<string, Parser.token>(defaultArg capacity 1024)
4141
member x.InternIdentifierToken(s) =
4242
match strings.TryGetValue s with
4343
| true, res -> res

src/fsharp/lexhelp.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type LightSyntaxStatus =
2525

2626
[<Sealed>]
2727
type LexResourceManager =
28-
new : unit -> LexResourceManager
28+
new : ?capacity: int -> LexResourceManager
2929

3030
type lexargs =
3131
{ defines: string list

src/fsharp/range.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module FSharp.Compiler.Range
55

66
open System
77
open System.IO
8+
open System.Collections.Generic
89
open System.Collections.Concurrent
910
open Microsoft.FSharp.Core.Printf
1011
open FSharp.Compiler.AbstractIL.Internal.Library
@@ -380,4 +381,9 @@ module Range =
380381

381382
let toFileZ (m:range) = m.FileName, toZ m
382383

384+
let comparer =
385+
{ new IEqualityComparer<range> with
386+
member _.Equals(x1, x2) = equals x1 x2
387+
member _.GetHashCode o = o.GetHashCode() }
388+
383389

src/fsharp/range.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,6 @@ module Range =
200200

201201
/// Convert a range from one-based line counting (used internally in the F# compiler and in F# error messages) to zero-based line counting (used by Visual Studio)
202202
val toFileZ : range -> string * Range01
203+
204+
/// Equality comparer for range.
205+
val comparer : IEqualityComparer<range>

src/fsharp/service/FSharpCheckerResults.fs

Lines changed: 2 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,6 @@ type GetPreciseCompletionListFromExprTypingsResult =
109109
| Some of (ItemWithInst list * DisplayEnv * range) * TType
110110

111111
type Names = string list
112-
113-
[<RequireQualifiedAccess>]
114-
type SemanticClassificationType =
115-
| ReferenceType
116-
| ValueType
117-
| UnionCase
118-
| Function
119-
| Property
120-
| MutableVar
121-
| Module
122-
| Printf
123-
| ComputationExpression
124-
| IntrinsicFunction
125-
| Enumeration
126-
| Interface
127-
| TypeArgument
128-
| Operator
129-
| Disposable
130112

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

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

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

1265-
member __.GetSemanticClassification(range: range option) : (range * SemanticClassificationType) [] =
1266-
ErrorScope.Protect Range.range0
1267-
(fun () ->
1268-
let (|LegitTypeOccurence|_|) = function
1269-
| ItemOccurence.UseInType
1270-
| ItemOccurence.UseInAttribute
1271-
| ItemOccurence.Use _
1272-
| ItemOccurence.Binding _
1273-
| ItemOccurence.Pattern _ -> Some()
1274-
| _ -> None
1275-
1276-
let (|OptionalArgumentAttribute|_|) ttype =
1277-
match ttype with
1278-
| TType.TType_app(tref, _) when tref.Stamp = g.attrib_OptionalArgumentAttribute.TyconRef.Stamp -> Some()
1279-
| _ -> None
1280-
1281-
let (|KeywordIntrinsicValue|_|) (vref: ValRef) =
1282-
if valRefEq g g.raise_vref vref ||
1283-
valRefEq g g.reraise_vref vref ||
1284-
valRefEq g g.typeof_vref vref ||
1285-
valRefEq g g.typedefof_vref vref ||
1286-
valRefEq g g.sizeof_vref vref ||
1287-
valRefEq g g.nameof_vref vref
1288-
then Some()
1289-
else None
1290-
1291-
let (|EnumCaseFieldInfo|_|) (rfinfo : RecdFieldInfo) =
1292-
match rfinfo.TyconRef.TypeReprInfo with
1293-
| TFSharpObjectRepr x ->
1294-
match x.fsobjmodel_kind with
1295-
| TTyconEnum -> Some ()
1296-
| _ -> None
1297-
| _ -> None
1298-
1299-
let resolutions =
1300-
match range with
1301-
| Some range ->
1302-
sResolutions.CapturedNameResolutions
1303-
|> Seq.filter (fun cnr -> rangeContainsPos range cnr.Range.Start || rangeContainsPos range cnr.Range.End)
1304-
| None ->
1305-
sResolutions.CapturedNameResolutions :> seq<_>
1306-
1307-
let isDisposableTy (ty: TType) =
1308-
protectAssemblyExplorationNoReraise false false (fun () -> Infos.ExistsHeadTypeInEntireHierarchy g amap range0 ty g.tcref_System_IDisposable)
1309-
1310-
let isStructTyconRef (tyconRef: TyconRef) =
1311-
let ty = generalizedTyconRef tyconRef
1312-
let underlyingTy = stripTyEqnsAndMeasureEqns g ty
1313-
isStructTy g underlyingTy
1314-
1315-
let isValRefMutable (vref: ValRef) =
1316-
// Mutable values, ref cells, and non-inref byrefs are mutable.
1317-
vref.IsMutable
1318-
|| Tastops.isRefCellTy g vref.Type
1319-
|| (Tastops.isByrefTy g vref.Type && not (Tastops.isInByrefTy g vref.Type))
1320-
1321-
let isRecdFieldMutable (rfinfo: RecdFieldInfo) =
1322-
(rfinfo.RecdField.IsMutable && rfinfo.LiteralValue.IsNone)
1323-
|| Tastops.isRefCellTy g rfinfo.RecdField.FormalType
1324-
1325-
resolutions
1326-
|> Seq.choose (fun cnr ->
1327-
match cnr with
1328-
// 'seq' in 'seq { ... }' gets colored as keywords
1329-
| CNR(_, (Item.Value vref), ItemOccurence.Use, _, _, _, m) when valRefEq g g.seq_vref vref ->
1330-
Some (m, SemanticClassificationType.ComputationExpression)
1331-
| CNR(_, (Item.Value vref), _, _, _, _, m) when isValRefMutable vref ->
1332-
Some (m, SemanticClassificationType.MutableVar)
1333-
| CNR(_, Item.Value KeywordIntrinsicValue, ItemOccurence.Use, _, _, _, m) ->
1334-
Some (m, SemanticClassificationType.IntrinsicFunction)
1335-
| CNR(_, (Item.Value vref), _, _, _, _, m) when isFunction g vref.Type ->
1336-
if valRefEq g g.range_op_vref vref || valRefEq g g.range_step_op_vref vref then
1337-
None
1338-
elif vref.IsPropertyGetterMethod || vref.IsPropertySetterMethod then
1339-
Some (m, SemanticClassificationType.Property)
1340-
elif IsOperatorName vref.DisplayName then
1341-
Some (m, SemanticClassificationType.Operator)
1342-
else
1343-
Some (m, SemanticClassificationType.Function)
1344-
| CNR(_, Item.RecdField rfinfo, _, _, _, _, m) when isRecdFieldMutable rfinfo ->
1345-
Some (m, SemanticClassificationType.MutableVar)
1346-
| CNR(_, Item.RecdField rfinfo, _, _, _, _, m) when isFunction g rfinfo.FieldType ->
1347-
Some (m, SemanticClassificationType.Function)
1348-
| CNR(_, Item.RecdField EnumCaseFieldInfo, _, _, _, _, m) ->
1349-
Some (m, SemanticClassificationType.Enumeration)
1350-
| CNR(_, Item.MethodGroup _, _, _, _, _, m) ->
1351-
Some (m, SemanticClassificationType.Function)
1352-
// custom builders, custom operations get colored as keywords
1353-
| CNR(_, (Item.CustomBuilder _ | Item.CustomOperation _), ItemOccurence.Use, _, _, _, m) ->
1354-
Some (m, SemanticClassificationType.ComputationExpression)
1355-
// types get colored as types when they occur in syntactic types or custom attributes
1356-
// type variables get colored as types when they occur in syntactic types custom builders, custom operations get colored as keywords
1357-
| CNR(_, Item.Types (_, [OptionalArgumentAttribute]), LegitTypeOccurence, _, _, _, _) -> None
1358-
| CNR(_, Item.CtorGroup(_, [MethInfo.FSMeth(_, OptionalArgumentAttribute, _, _)]), LegitTypeOccurence, _, _, _, _) -> None
1359-
| CNR(_, Item.Types(_, types), LegitTypeOccurence, _, _, _, m) when types |> List.exists (isInterfaceTy g) ->
1360-
Some (m, SemanticClassificationType.Interface)
1361-
| CNR(_, Item.Types(_, types), LegitTypeOccurence, _, _, _, m) when types |> List.exists (isStructTy g) ->
1362-
Some (m, SemanticClassificationType.ValueType)
1363-
| CNR(_, Item.Types(_, TType_app(tyconRef, TType_measure _ :: _) :: _), LegitTypeOccurence, _, _, _, m) when isStructTyconRef tyconRef ->
1364-
Some (m, SemanticClassificationType.ValueType)
1365-
| CNR(_, Item.Types(_, types), LegitTypeOccurence, _, _, _, m) when types |> List.exists isDisposableTy ->
1366-
Some (m, SemanticClassificationType.Disposable)
1367-
| CNR(_, Item.Types _, LegitTypeOccurence, _, _, _, m) ->
1368-
Some (m, SemanticClassificationType.ReferenceType)
1369-
| CNR(_, (Item.TypeVar _ ), LegitTypeOccurence, _, _, _, m) ->
1370-
Some (m, SemanticClassificationType.TypeArgument)
1371-
| CNR(_, Item.UnqualifiedType tyconRefs, LegitTypeOccurence, _, _, _, m) ->
1372-
if tyconRefs |> List.exists (fun tyconRef -> tyconRef.Deref.IsStructOrEnumTycon) then
1373-
Some (m, SemanticClassificationType.ValueType)
1374-
else Some (m, SemanticClassificationType.ReferenceType)
1375-
| CNR(_, Item.CtorGroup(_, minfos), LegitTypeOccurence, _, _, _, m) ->
1376-
if minfos |> List.exists (fun minfo -> isStructTy g minfo.ApparentEnclosingType) then
1377-
Some (m, SemanticClassificationType.ValueType)
1378-
else Some (m, SemanticClassificationType.ReferenceType)
1379-
| CNR(_, Item.ExnCase _, LegitTypeOccurence, _, _, _, m) ->
1380-
Some (m, SemanticClassificationType.ReferenceType)
1381-
| CNR(_, Item.ModuleOrNamespaces refs, LegitTypeOccurence, _, _, _, m) when refs |> List.exists (fun x -> x.IsModule) ->
1382-
Some (m, SemanticClassificationType.Module)
1383-
| CNR(_, (Item.ActivePatternCase _ | Item.UnionCase _ | Item.ActivePatternResult _), _, _, _, _, m) ->
1384-
Some (m, SemanticClassificationType.UnionCase)
1385-
| _ -> None)
1386-
|> Seq.toArray
1387-
|> Array.append (sSymbolUses.GetFormatSpecifierLocationsAndArity() |> Array.map (fun m -> fst m, SemanticClassificationType.Printf))
1388-
)
1389-
(fun msg ->
1390-
Trace.TraceInformation(sprintf "FCS: recovering from error in GetSemanticClassification: '%s'" msg)
1391-
Array.empty)
1245+
member __.GetSemanticClassification(range: range option) : struct (range * SemanticClassificationType) [] =
1246+
sResolutions.GetSemanticClassification(g, amap, sSymbolUses.GetFormatSpecifierLocationsAndArity(), range)
13921247

13931248
/// The resolutions in the file
13941249
member __.ScopeResolutions = sResolutions

0 commit comments

Comments
 (0)