-
Notifications
You must be signed in to change notification settings - Fork 793
/
Copy pathFSharpCheckerResults.fs
4022 lines (3411 loc) · 176 KB
/
FSharpCheckerResults.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
// Open up the compiler as an incremental service for parsing,
// type checking and intellisense-like environment-reporting.
namespace FSharp.Compiler.CodeAnalysis
open System
open System.Collections.Generic
open System.Diagnostics
open System.IO
open System.Reflection
open System.Threading
open FSharp.Compiler.IO
open FSharp.Compiler.NicePrint
open Internal.Utilities.Library
open Internal.Utilities.Library.Extras
open FSharp.Core.Printf
open FSharp.Compiler
open FSharp.Compiler.Syntax
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.AccessibilityLogic
open FSharp.Compiler.CheckExpressions
open FSharp.Compiler.CheckDeclarations
open FSharp.Compiler.CompilerConfig
open FSharp.Compiler.CompilerDiagnostics
open FSharp.Compiler.CompilerImports
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.EditorServices
open FSharp.Compiler.EditorServices.DeclarationListHelpers
open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.Features
open FSharp.Compiler.Infos
open FSharp.Compiler.InfoReader
open FSharp.Compiler.Lexhelp
open FSharp.Compiler.NameResolution
open FSharp.Compiler.OptimizeInputs
open FSharp.Compiler.Parser
open FSharp.Compiler.ParseAndCheckInputs
open FSharp.Compiler.ParseHelpers
open FSharp.Compiler.ScriptClosure
open FSharp.Compiler.Symbols
open FSharp.Compiler.Symbols.SymbolHelpers
open FSharp.Compiler.Syntax.PrettyNaming
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Text
open FSharp.Compiler.Text.Layout
open FSharp.Compiler.Text.Position
open FSharp.Compiler.Text.Range
open FSharp.Compiler.TypedTree
open FSharp.Compiler.TypedTreeBasics
open FSharp.Compiler.TypedTreeOps
open Internal.Utilities
open Internal.Utilities.Collections
open FSharp.Compiler.AbstractIL.ILBinaryReader
open System.Threading.Tasks
open System.Runtime.CompilerServices
open Internal.Utilities.Hashing
type FSharpUnresolvedReferencesSet = FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list
[<RequireQualifiedAccess>]
type DocumentSource =
| FileSystem
| Custom of (string -> Async<ISourceText option>)
[<Sealed>]
type DelayedILModuleReader =
val private name: string
val private gate: obj
val mutable private getStream: (CancellationToken -> Stream option)
val mutable private result: ILModuleReader
new(name, getStream) =
{
name = name
gate = obj ()
getStream = getStream
result = Unchecked.defaultof<_>
}
member this.OutputFile = this.name
member this.TryGetILModuleReader() =
// fast path
match box this.result with
| null ->
cancellable {
let! ct = Cancellable.token ()
return
lock this.gate (fun () ->
// see if we have a result or not after the lock so we do not evaluate the stream more than once
match box this.result with
| null ->
try
let streamOpt = this.getStream ct
match streamOpt with
| Some stream ->
let ilReaderOptions: ILReaderOptions =
{
pdbDirPath = None
reduceMemoryUsage = ReduceMemoryFlag.Yes
metadataOnly = MetadataOnlyFlag.Yes
tryGetMetadataSnapshot = fun _ -> None
}
let ilReader = OpenILModuleReaderFromStream this.name stream ilReaderOptions
this.result <- ilReader
this.getStream <- Unchecked.defaultof<_> // clear out the function so we do not hold onto anything
Some ilReader
| _ -> None
with ex ->
Trace.TraceInformation("FCS: Unable to get an ILModuleReader: {0}", ex)
None
| _ -> Some this.result)
}
| _ -> cancellable.Return(Some this.result)
[<RequireQualifiedAccess; NoComparison; CustomEquality>]
type FSharpReferencedProject =
| FSharpReference of projectOutputFile: string * options: FSharpProjectOptions
| PEReference of getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader
| ILModuleReference of projectOutputFile: string * getStamp: (unit -> DateTime) * getReader: (unit -> ILModuleReader)
member this.OutputFile =
match this with
| FSharpReference(projectOutputFile = projectOutputFile)
| ILModuleReference(projectOutputFile = projectOutputFile) -> projectOutputFile
| PEReference(delayedReader = reader) -> reader.OutputFile
override this.Equals(o) =
match o with
| :? FSharpReferencedProject as o ->
match this, o with
| FSharpReference(projectOutputFile1, options1), FSharpReference(projectOutputFile2, options2) ->
projectOutputFile1 = projectOutputFile2
&& FSharpProjectOptions.AreSameForChecking(options1, options2)
| PEReference(getStamp1, reader1), PEReference(getStamp2, reader2) ->
reader1.OutputFile = reader2.OutputFile && (getStamp1 ()) = (getStamp2 ())
| ILModuleReference(projectOutputFile1, getStamp1, _), ILModuleReference(projectOutputFile2, getStamp2, _) ->
projectOutputFile1 = projectOutputFile2 && (getStamp1 ()) = (getStamp2 ())
| _ -> false
| _ -> false
override this.GetHashCode() = this.OutputFile.GetHashCode()
// NOTE: may be better just to move to optional arguments here
and FSharpProjectOptions =
{
ProjectFileName: string
ProjectId: string option
SourceFiles: string[]
OtherOptions: string[]
ReferencedProjects: FSharpReferencedProject[]
IsIncompleteTypeCheckEnvironment: bool
UseScriptResolutionRules: bool
LoadTime: DateTime
UnresolvedReferences: FSharpUnresolvedReferencesSet option
OriginalLoadReferences: (range * string * string) list
Stamp: int64 option
}
static member UseSameProject(options1, options2) =
match options1.ProjectId, options2.ProjectId with
| Some(projectId1), Some(projectId2) when
not (String.IsNullOrWhiteSpace(projectId1))
&& not (String.IsNullOrWhiteSpace(projectId2))
->
projectId1 = projectId2
| Some _, Some _
| None, None -> options1.ProjectFileName = options2.ProjectFileName
| _ -> false
static member AreSameForChecking(options1, options2) =
match options1.Stamp, options2.Stamp with
| Some x, Some y -> (x = y)
| _ ->
FSharpProjectOptions.UseSameProject(options1, options2)
&& options1.SourceFiles = options2.SourceFiles
&& options1.OtherOptions = options2.OtherOptions
&& options1.UnresolvedReferences = options2.UnresolvedReferences
&& options1.OriginalLoadReferences = options2.OriginalLoadReferences
&& options1.ReferencedProjects.Length = options2.ReferencedProjects.Length
&& options1.ReferencedProjects = options2.ReferencedProjects
&& options1.LoadTime = options2.LoadTime
member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName)
override this.ToString() =
"FSharpProjectOptions(" + this.ProjectFileName + ")"
[<AutoOpen>]
module internal FSharpCheckerResultsSettings =
let getToolTipTextSize = GetEnvInteger "FCS_GetToolTipTextCacheSize" 5
let maxTypeCheckErrorsOutOfProjectContext =
GetEnvInteger "FCS_MaxErrorsOutOfProjectContext" 3
// Look for DLLs in the location of the service DLL first.
let defaultFSharpBinariesDir =
FSharpEnvironment
.BinFolderOfDefaultFSharpCompiler(Some(Path.GetDirectoryName(typeof<IncrementalBuilder>.Assembly.Location)))
.Value
[<Sealed>]
type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstantiation, itemOcc, range: range) =
member _.Symbol = symbol
member _.GenericArguments =
let cenv = symbol.SymbolEnv
inst
|> List.map (fun (v, ty) -> FSharpGenericParameter(cenv, v), FSharpType(cenv, ty))
member _.DisplayContext = FSharpDisplayContext(fun _ -> denv)
member x.IsDefinition = x.IsFromDefinition
member _.IsFromDefinition = itemOcc = ItemOccurence.Binding
member _.IsFromPattern = itemOcc = ItemOccurence.Pattern
member _.IsFromType = itemOcc = ItemOccurence.UseInType
member _.IsFromAttribute = itemOcc = ItemOccurence.UseInAttribute
member _.IsFromDispatchSlotImplementation = itemOcc = ItemOccurence.Implemented
member _.IsFromUse = itemOcc = ItemOccurence.Use
member _.IsFromComputationExpression =
match symbol.Item, itemOcc with
// 'seq' in 'seq { ... }' gets colored as keywords
| Item.Value vref, ItemOccurence.Use when valRefEq denv.g denv.g.seq_vref vref -> true
// custom builders, custom operations get colored as keywords
| (Item.CustomBuilder _ | Item.CustomOperation _), ItemOccurence.Use -> true
| _ -> false
member _.IsFromOpenStatement = itemOcc = ItemOccurence.Open
member _.FileName = range.FileName
member _.Range = range
member this.IsPrivateToFileAndSignatureFile =
let couldBeParameter, declarationLocation =
match this.Symbol with
| :? FSharpParameter as p -> true, Some p.DeclarationLocation
| :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation
| _ -> false, None
let thisIsSignature = SourceFileImpl.IsSignatureFile this.Range.FileName
let signatureLocation = this.Symbol.SignatureLocation
couldBeParameter
&& (thisIsSignature
|| (signatureLocation.IsSome && signatureLocation <> declarationLocation))
member this.IsPrivateToFile =
let isPrivate =
match this.Symbol with
| _ when this.IsPrivateToFileAndSignatureFile -> false
| :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember ->
// local binding or parameter
true
| :? FSharpMemberOrFunctionOrValue as m ->
let fileSignatureLocation =
m.DeclaringEntity |> Option.bind (fun e -> e.SignatureLocation)
let fileDeclarationLocation =
m.DeclaringEntity |> Option.map (fun e -> e.DeclarationLocation)
let fileHasSignatureFile = fileSignatureLocation <> fileDeclarationLocation
fileHasSignatureFile && not m.HasSignatureFile || m.Accessibility.IsPrivate
| :? FSharpEntity as m -> m.Accessibility.IsPrivate
| :? FSharpParameter -> true
| :? FSharpGenericParameter -> true
| :? FSharpUnionCase as m -> m.Accessibility.IsPrivate
| :? FSharpField as m -> m.Accessibility.IsPrivate
| :? FSharpActivePatternCase as m -> m.Accessibility.IsPrivate
| _ -> false
let declarationLocation =
match this.Symbol.SignatureLocation with
| Some x -> Some x
| _ ->
match this.Symbol.DeclarationLocation with
| Some x -> Some x
| _ -> this.Symbol.ImplementationLocation
let declaredInTheFile =
match declarationLocation with
| Some declRange -> declRange.FileName = this.Range.FileName
| _ -> false
isPrivate && declaredInTheFile
override _.ToString() =
sprintf "%O, %O, %O" symbol itemOcc range
/// This type is used to describe what was found during the name resolution.
/// (Depending on the kind of the items, we may stop processing or continue to find better items)
[<RequireQualifiedAccess; NoEquality; NoComparison>]
type NameResResult =
| Members of (ItemWithInst list * DisplayEnv * range)
| Cancel of DisplayEnv * range
| Empty
[<RequireQualifiedAccess>]
type ResolveOverloads =
| Yes
| No
[<RequireQualifiedAccess>]
type ExprTypingsResult =
| NoneBecauseTypecheckIsStaleAndTextChanged
| NoneBecauseThereWereTypeErrors
| None
| Some of (ItemWithInst list * DisplayEnv * range) * TType
type Names = string list
/// A TypeCheckInfo represents everything we get back from the typecheck of a file.
/// It acts like an in-memory database about the file.
/// It is effectively immutable and not updated: when we re-typecheck we just drop the previous
/// scope object on the floor and make a new one.
[<Sealed>]
type internal TypeCheckInfo
(
_sTcConfig: TcConfig,
g: TcGlobals,
ccuSigForFile: ModuleOrNamespaceType,
thisCcu: CcuThunk,
tcImports: TcImports,
tcAccessRights: AccessorDomain,
projectFileName: string,
mainInputFileName: string,
projectOptions: FSharpProjectOptions,
sResolutions: TcResolutions,
sSymbolUses: TcSymbolUses,
sFallback: NameResolutionEnv,
loadClosure: LoadClosure option,
implFileOpt: CheckedImplFile option,
openDeclarations: OpenDeclaration[]
) =
// 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.
// Is not keyed on 'Names' collection because this is invariant for the current position in
// this unchanged file. Keyed on lineStr though to prevent a change to the currently line
// being available against a stale scope.
let getToolTipTextCache =
AgedLookup<AnyCallerThreadToken, int * int * string * int option, ToolTipText>(
getToolTipTextSize,
areSimilar = (fun (x, y) -> x = y)
)
let amap = tcImports.GetImportMap()
let infoReader = InfoReader(g, amap)
let ncenv = NameResolver(g, amap, infoReader, FakeInstantiationGenerator)
let cenv = SymbolEnv(g, thisCcu, Some ccuSigForFile, tcImports, amap, infoReader)
/// Find the most precise naming environment for the given line and column
let GetBestEnvForPos cursorPos =
let mutable bestSoFar = None
// Find the most deeply nested enclosing scope that contains given position
sResolutions.CapturedEnvs
|> ResizeArray.iter (fun (mPossible, env, ad) ->
if rangeContainsPos mPossible cursorPos then
match bestSoFar with
| Some(bestm, _, _) ->
if rangeContainsRange bestm mPossible then
bestSoFar <- Some(mPossible, env, ad)
| None -> bestSoFar <- Some(mPossible, env, ad))
let mostDeeplyNestedEnclosingScope = bestSoFar
// Look for better subtrees on the r.h.s. of the subtree to the left of where we are
// Should really go all the way down the r.h.s. of the subtree to the left of where we are
// This is all needed when the index is floating free in the area just after the environment we really want to capture
// We guarantee to only refine to a more nested environment. It may not be strictly
// the right environment, but will always be at least as rich
let mutable bestAlmostIncludedSoFar = None
sResolutions.CapturedEnvs
|> ResizeArray.iter (fun (mPossible, env, ad) ->
// take only ranges that strictly do not include cursorPos (all ranges that touch cursorPos were processed during 'Strict Inclusion' part)
if rangeBeforePos mPossible cursorPos && not (posEq mPossible.End cursorPos) then
let contained =
match mostDeeplyNestedEnclosingScope with
| Some(bestm, _, _) -> rangeContainsRange bestm mPossible
| None -> true
if contained then
match bestAlmostIncludedSoFar with
| Some(mRight: range, _, _) ->
if
posGt mPossible.End mRight.End
|| (posEq mPossible.End mRight.End && posGt mPossible.Start mRight.Start)
then
bestAlmostIncludedSoFar <- Some(mPossible, env, ad)
| _ -> bestAlmostIncludedSoFar <- Some(mPossible, env, ad))
let resEnv =
match bestAlmostIncludedSoFar, mostDeeplyNestedEnclosingScope with
| Some(_, env, ad), None -> env, ad
| Some(_, almostIncludedEnv, ad), Some(_, mostDeeplyNestedEnv, _) when
almostIncludedEnv.eFieldLabels.Count >= mostDeeplyNestedEnv.eFieldLabels.Count
->
almostIncludedEnv, ad
| _ ->
match mostDeeplyNestedEnclosingScope with
| Some(_, env, ad) -> env, ad
| None -> sFallback, AccessibleFromSomeFSharpCode
let pm = mkRange mainInputFileName cursorPos cursorPos
resEnv, pm
/// The items that come back from ResolveCompletionsInType are a bit
/// noisy. Filter a few things out.
///
/// e.g. prefer types to constructors for ToolTipText
let FilterItemsForCtors filterCtors (items: ItemWithInst list) =
let items =
items
|> List.filter (fun item ->
match item.Item with
| Item.CtorGroup _ when filterCtors = ResolveTypeNamesToTypeRefs -> false
| _ -> true)
items
// Filter items to show only valid & return Some if there are any
let ReturnItemsOfType (items: ItemWithInst list) g denv (m: range) filterCtors =
let items =
items
|> RemoveDuplicateItems g
|> RemoveExplicitlySuppressed g
|> FilterItemsForCtors filterCtors
if not (isNil items) then
NameResResult.Members(items, denv, m)
else
NameResResult.Empty
let GetCapturedNameResolutions (endOfNamesPos: pos) resolveOverloads =
let filter (endPos: pos) items =
items
|> ResizeArray.filter (fun (cnr: CapturedNameResolution) ->
let range = cnr.Range
range.EndLine = endPos.Line && range.EndColumn = endPos.Column)
match resolveOverloads with
| ResolveOverloads.Yes -> filter endOfNamesPos sResolutions.CapturedNameResolutions
| ResolveOverloads.No ->
let items = filter endOfNamesPos sResolutions.CapturedMethodGroupResolutions
if items.Count <> 0 then
items
else
filter endOfNamesPos sResolutions.CapturedNameResolutions
/// Looks at the exact name resolutions that occurred during type checking
/// If 'membersByResidue' is specified, we look for members of the item obtained
/// from the name resolution and filter them by the specified residue (?)
let GetPreciseItemsFromNameResolution (line, colAtEndOfNames, membersByResidue, filterCtors, resolveOverloads) =
let endOfNamesPos = mkPos line colAtEndOfNames
// Logic below expects the list to be in reverse order of resolution
let cnrs =
GetCapturedNameResolutions endOfNamesPos resolveOverloads
|> ResizeArray.toList
|> List.rev
match cnrs, membersByResidue with
// Exact resolution via SomeType.$ or SomeType<int>.$
//
// If we're looking for members using a residue, we'd expect only
// a single item (pick the first one) and we need the residue (which may be "")
| CNR(_, ItemOccurence.InvalidUse, _, _, _, _) :: _, _ -> NameResResult.Empty
| CNR(Item.Types(_, ty :: _), _, denv, nenv, ad, m) :: _, Some _ ->
let targets =
ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)
let items = ResolveCompletionsInType ncenv nenv targets m ad true ty
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m filterCtors
// Exact resolution via 'T.$
| CNR(Item.TypeVar(_, tp), _, denv, nenv, ad, m) :: _, Some _ ->
let targets =
ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)
let items = ResolveCompletionsInType ncenv nenv targets m ad true (mkTyparTy tp)
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m filterCtors
// Value reference from the name resolution. Primarily to disallow "let x.$ = 1"
// In most of the cases, value references can be obtained from expression typings or from environment,
// so we wouldn't have to handle values here. However, if we have something like:
// let varA = "string"
// let varA = if b then 0 else varA.
// then the expression typings get confused (thinking 'varA:int'), so we use name resolution even for usual values.
| CNR(Item.Value(vref), occurence, denv, nenv, ad, m) :: _, Some _ ->
if occurence = ItemOccurence.Binding || occurence = ItemOccurence.Pattern then
// Return empty list to stop further lookup - for value declarations
NameResResult.Cancel(denv, m)
else
// If we have any valid items for the value, then return completions for its type now.
// Adjust the type in case this is the 'this' pointer stored in a reference cell.
let ty = StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)
// patch accessibility domain to remove protected members if accessing NormalVal
let ad =
match vref.BaseOrThisInfo, ad with
| ValBaseOrThisInfo.NormalVal, AccessibleFrom(paths, Some tcref) ->
let thisTy = generalizedTyconRef g tcref
// check that type of value is the same or subtype of tcref
// yes - allow access to protected members
// no - strip ability to access protected members
if TypeRelations.TypeFeasiblySubsumesType 0 g amap m thisTy TypeRelations.CanCoerce ty then
ad
else
AccessibleFrom(paths, None)
| _ -> ad
let targets =
ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)
let items = ResolveCompletionsInType ncenv nenv targets m ad false ty
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m filterCtors
// No residue, so the items are the full resolution of the name
| CNR(_, _, denv, _, _, m) :: _, None ->
let items =
cnrs
|> List.map (fun cnr -> cnr.ItemWithInst)
// "into" is special magic syntax, not an identifier or a library call. It is part of capturedNameResolutions as an
// implementation detail of syntax coloring, but we should not report name resolution results for it, to prevent spurious QuickInfo.
|> List.filter (fun item ->
match item.Item with
| Item.CustomOperation(CustomOperations.Into, _, _) -> false
| _ -> true)
ReturnItemsOfType items g denv m filterCtors
| _, _ -> NameResResult.Empty
let TryGetTypeFromNameResolution (line, colAtEndOfNames, membersByResidue, resolveOverloads) =
let endOfNamesPos = mkPos line colAtEndOfNames
let items =
GetCapturedNameResolutions endOfNamesPos resolveOverloads
|> ResizeArray.toList
|> List.rev
match items, membersByResidue with
| CNR(Item.Types(_, ty :: _), _, _, _, _, _) :: _, Some _ -> Some ty
| CNR(Item.Value(vref), occurence, _, _, _, _) :: _, Some _ ->
if (occurence = ItemOccurence.Binding || occurence = ItemOccurence.Pattern) then
None
else
Some(StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType))
| _, _ -> None
/// Build a CompletionItem
let CompletionItemWithMoreSetting
(ty: TyconRef voption)
(assemblySymbol: AssemblySymbol voption)
minorPriority
insertText
displayText
(item: ItemWithInst)
=
let kind =
match item.Item with
| Item.DelegateCtor _
| Item.CtorGroup _ -> CompletionItemKind.Method false
| Item.MethodGroup(_, minfos, _) ->
match minfos with
| [] -> CompletionItemKind.Method false
| minfo :: _ -> CompletionItemKind.Method minfo.IsExtensionMember
| Item.AnonRecdField _
| Item.RecdField _
| Item.UnionCaseField _
| Item.Property _ -> CompletionItemKind.Property
| Item.Event _ -> CompletionItemKind.Event
| Item.ILField _
| Item.Value _ -> CompletionItemKind.Field
| Item.CustomOperation _ -> CompletionItemKind.CustomOperation
// These items are not given a completion kind. This could be reviewed
| Item.ActivePatternResult _
| Item.ExnCase _
| Item.ImplicitOp _
| Item.ModuleOrNamespaces _
| Item.Trait _
| Item.TypeVar _
| Item.Types _
| Item.UnionCase _
| Item.UnqualifiedType _
| Item.NewDef _
| Item.SetterArg _
| Item.CustomBuilder _
| Item.OtherName _
| Item.ActivePatternCase _ -> CompletionItemKind.Other
let isUnresolved =
match assemblySymbol with
| ValueSome x -> Some x.UnresolvedSymbol
| _ -> None
let ty =
match ty with
| ValueSome x -> Some x
| _ -> None
{
ItemWithInst = item
MinorPriority = minorPriority
Kind = kind
IsOwnMember = false
Type = ty
Unresolved = isUnresolved
CustomInsertText = insertText
CustomDisplayText = displayText
}
let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) (item: ItemWithInst) =
CompletionItemWithMoreSetting ty assemblySymbol 0 ValueNone ValueNone item
let CollectParameters (methods: MethInfo list) amap m : Item list =
methods
|> List.collect (fun meth ->
match meth.GetParamDatas(amap, m, meth.FormalMethodInst) with
| x :: _ ->
x
|> List.choose (fun (ParamData(_isParamArray, _isInArg, _isOutArg, _optArgInfo, _callerInfo, name, _, ty)) ->
match name with
| Some id -> Some(Item.OtherName(Some id, ty, None, Some(ArgumentContainer.Method meth), id.idRange))
| None -> None)
| _ -> [])
let GetNamedParametersAndSettableFields endOfExprPos =
let cnrs =
GetCapturedNameResolutions endOfExprPos ResolveOverloads.No
|> ResizeArray.toList
|> List.rev
let result =
match cnrs with
| CNR(Item.CtorGroup(_, (ctor :: _ as ctors)), _, denv, nenv, ad, m) :: _ ->
let props =
ResolveCompletionsInType
ncenv
nenv
ResolveCompletionTargets.SettablePropertiesAndFields
m
ad
false
ctor.ApparentEnclosingType
let parameters = CollectParameters ctors amap m
let items = props @ parameters
Some(denv, m, items)
| CNR(Item.MethodGroup(_, methods, _), _, denv, nenv, ad, m) :: _ ->
let props =
methods
|> List.collect (fun meth ->
let retTy = meth.GetFSharpReturnType(amap, m, meth.FormalMethodInst)
ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false retTy)
let parameters = CollectParameters methods amap m
let items = props @ parameters
Some(denv, m, items)
| _ -> None
match result with
| None -> NameResResult.Empty
| Some(denv, m, items) ->
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs
/// finds captured typing for the given position
let GetExprTypingForPosition endOfExprPos =
let quals =
sResolutions.CapturedExpressionTypings
|> Seq.filter (fun (ty, nenv, _, m) ->
// We only want expression types that end at the particular position in the file we are looking at.
posEq m.End endOfExprPos
&&
// Get rid of function types. True, given a 2-arg curried function "f x y", it is legal to do "(f x).GetType()",
// but you almost never want to do this in practice, and we choose not to offer up any intellisense for
// F# function types.
not (isFunTy nenv.DisplayEnv.g ty))
|> Seq.toArray
// filter out errors
let quals =
quals
|> Array.filter (fun (ty, nenv, _, _) ->
let denv = nenv.DisplayEnv
not (isTyparTy denv.g ty && (destTyparTy denv.g ty).IsFromError))
let thereWereSomeQuals = not (Array.isEmpty quals)
thereWereSomeQuals, quals
/// Returns the list of available record fields, taking into account potential nesting
let GetRecdFieldsForCopyAndUpdateExpr (identRange: range, plid: string list) =
let rec dive ty (denv: DisplayEnv) ad m plid isPastTypePrefix wasPathEmpty =
if isRecdTy denv.g ty then
let fields =
ncenv.InfoReader.GetRecordOrClassFieldsOfType(None, ad, m, ty)
|> List.filter (fun rfref -> not rfref.IsStatic && IsFieldInfoAccessible ad rfref)
match plid with
| [] ->
if wasPathEmpty || isPastTypePrefix then
Some(fields |> List.map Item.RecdField, denv, m)
else
None
| id :: rest ->
match fields |> List.tryFind (fun f -> f.LogicalName = id) with
| Some f -> dive f.FieldType denv ad m rest true wasPathEmpty
| _ ->
// Field name can be optionally qualified.
// If we haven't matched a field name yet, keep peeling off the prefix.
if isPastTypePrefix then
Some([], denv, m)
else
dive ty denv ad m rest false wasPathEmpty
else
match tryDestAnonRecdTy denv.g ty with
| ValueSome(anonInfo, tys) ->
match plid with
| [] ->
let items =
[
for i in 0 .. anonInfo.SortedIds.Length - 1 do
Item.AnonRecdField(anonInfo, tys, i, anonInfo.SortedIds[i].idRange)
]
Some(items, denv, m)
| id :: rest ->
match anonInfo.SortedNames |> Array.tryFindIndex (fun x -> x = id) with
| Some i -> dive tys[i] denv ad m rest true wasPathEmpty
| _ -> Some([], denv, m)
| ValueNone -> Some([], denv, m)
match
GetExprTypingForPosition identRange.End
|> snd
|> Array.tryFind (fun (_, _, _, rq) -> posEq identRange.Start rq.Start)
with
| Some(ty, nenv, ad, m) -> dive ty nenv.DisplayEnv ad m plid false plid.IsEmpty
| _ -> None
/// Looks at the exact expression types at the position to the left of the
/// residue then the source when it was typechecked.
let GetPreciseCompletionListFromExprTypings (parseResults: FSharpParseFileResults, endOfExprPos, filterCtors) =
let thereWereSomeQuals, quals = GetExprTypingForPosition(endOfExprPos)
match quals with
| [||] ->
if thereWereSomeQuals then
ExprTypingsResult.NoneBecauseThereWereTypeErrors
else
ExprTypingsResult.None
| _ ->
let bestQual, textChanged =
let input = parseResults.ParseTree
match ParsedInput.GetRangeOfExprLeftOfDot(endOfExprPos, input) with // TODO we say "colAtEndOfNames" everywhere, but that's not really a good name ("foo . $" hit Ctrl-Space at $)
| Some(exprRange) ->
// We have an up-to-date sync parse, and know the exact range of the prior expression.
// The quals all already have the same ending position, so find one with a matching starting position, if it exists.
// If not, then the stale typecheck info does not have a capturedExpressionTyping for this exact expression, and the
// user can wait for typechecking to catch up and second-chance intellisense to give the right result.
let qual =
quals
|> Array.tryFind (fun (_, _, _, r) ->
ignore (r) // for breakpoint
posEq exprRange.Start r.Start)
qual, false
| None ->
// TODO In theory I think we should never get to this code path; it would be nice to add an assert.
// In practice, we do get here in some weird cases like "2.0 .. 3.0" and hitting Ctrl-Space in between the two dots of the range operator.
// I wasn't able to track down what was happening in those weird cases, not worth worrying about, it doesn't manifest as a product bug or anything.
None, false
match bestQual with
| Some bestQual ->
let ty, nenv, ad, m = bestQual
let targets =
ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)
let items = ResolveCompletionsInType ncenv nenv targets m ad false ty
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
let items = items |> FilterItemsForCtors filterCtors
ExprTypingsResult.Some((items, nenv.DisplayEnv, m), ty)
| None ->
if textChanged then
ExprTypingsResult.NoneBecauseTypecheckIsStaleAndTextChanged
else
ExprTypingsResult.None
/// Find items in the best naming environment.
let GetEnvironmentLookupResolutions (nenv, ad, m, plid, filterCtors, showObsolete) =
let items =
ResolvePartialLongIdent ncenv nenv (ConstraintSolver.IsApplicableMethApprox g amap m) m ad plid showObsolete
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
let items = items |> FilterItemsForCtors filterCtors
(items, nenv.DisplayEnv, m)
/// Find items in the best naming environment.
let GetEnvironmentLookupResolutionsAtPosition (cursorPos, plid, filterCtors, showObsolete) =
let (nenv, ad), m = GetBestEnvForPos cursorPos
GetEnvironmentLookupResolutions(nenv, ad, m, plid, filterCtors, showObsolete)
/// Find record fields in the best naming environment.
let GetClassOrRecordFieldsEnvironmentLookupResolutions (cursorPos, plid, fieldsOnly) =
let (nenv, ad), m = GetBestEnvForPos cursorPos
let items =
ResolvePartialLongIdentToClassOrRecdFields ncenv nenv m ad plid false fieldsOnly
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
items, nenv.DisplayEnv, m
/// Is the item suitable for completion at "inherits $"
let IsInheritsCompletionCandidate item =
match item with
| Item.ModuleOrNamespaces _ -> true
| Item.Types(_, ty :: _) when isClassTy g ty && not (isSealedTy g ty) -> true
| _ -> false
/// Is the item suitable for completion at "interface $"
let IsInterfaceCompletionCandidate item =
match item with
| Item.ModuleOrNamespaces _ -> true
| Item.Types(_, ty :: _) when isInterfaceTy g ty -> true
| _ -> false
/// Is the item suitable for completion in a pattern
let IsPatternCandidate (item: CompletionItem) =
match item.Item with
| Item.Value v -> v.LiteralValue.IsSome
| Item.ILField field -> field.LiteralValue.IsSome
| Item.ActivePatternCase _
| Item.ExnCase _
| Item.ModuleOrNamespaces _
| Item.Types _
| Item.UnionCase _ -> true
| _ -> false
/// Is the item suitable for completion in a type application or type annotation
let IsTypeCandidate (item: CompletionItem) =
match item.Item with
| Item.ModuleOrNamespaces _
| Item.Types _
| Item.TypeVar _
| Item.UnqualifiedType _
| Item.ExnCase _ -> true
| _ -> false
/// Return only items with the specified name, modulo "Attribute" for type completions
let FilterDeclItemsByResidue (getItem: 'a -> Item) residue (items: 'a list) =
let attributedResidue = residue + "Attribute"
let nameMatchesResidue name =
(residue = name) || (attributedResidue = name)
items
|> List.filter (fun x ->
let item = getItem x
let n1 = item.DisplayName
match item with
| Item.Types _ -> nameMatchesResidue n1
| Item.CtorGroup(_, meths) ->
nameMatchesResidue n1
|| meths
|> List.exists (fun meth ->
let tcref = meth.ApparentEnclosingTyconRef
#if !NO_TYPEPROVIDERS
tcref.IsProvided
||
#endif
nameMatchesResidue tcref.DisplayName)
| Item.Value v when (v.IsPropertyGetterMethod || v.IsPropertySetterMethod) -> residue = v.Id.idText || residue = n1
| _ -> residue = n1)
/// Post-filter items to make sure they have precisely the right name
/// This also checks that there are some remaining results
/// exactMatchResidueOpt = Some _ -- means that we are looking for exact matches
let FilterRelevantItemsBy (getItem: 'a -> Item) (exactMatchResidueOpt: string option) check (items: 'a list, denv, m) =
// can throw if type is in located in non-resolved CCU: i.e. bigint if reference to System.Numerics is absent
let inline safeCheck item =
try
check item
with _ ->
false
// Are we looking for items with precisely the given name?
if isNil items then
// When (items = []) we must returns Some([],..) and not None
// because this value is used if we want to stop further processing (e.g. let x.$ = ...)
Some(items, denv, m)
else
match exactMatchResidueOpt with
| Some exactMatchResidue ->
let items =
items
|> FilterDeclItemsByResidue getItem exactMatchResidue
|> List.filter safeCheck
if not (isNil items) then Some(items, denv, m) else None
| _ ->
let items = items |> List.filter safeCheck
Some(items, denv, m)
/// Post-filter items to make sure they have precisely the right name
/// This also checks that there are some remaining results
let (|FilterRelevantItems|_|) getItem exactMatchResidueOpt orig =
FilterRelevantItemsBy getItem exactMatchResidueOpt (fun _ -> true) orig
/// Find the first non-whitespace position in a line prior to the given character
let FindFirstNonWhitespacePosition (lineStr: string) i =
if i >= lineStr.Length then
None
else
let mutable p = i
while p >= 0 && Char.IsWhiteSpace(lineStr[p]) do
p <- p - 1
if p >= 0 then Some p else None
let DefaultCompletionItem item = CompletionItem ValueNone ValueNone item
let CompletionItemSuggestedName displayName =
{
ItemWithInst = ItemWithNoInst(Item.NewDef(Ident(displayName, range0)))
MinorPriority = 0
Type = None
Kind = CompletionItemKind.SuggestedName
IsOwnMember = false
Unresolved = None
CustomInsertText = ValueNone
CustomDisplayText = ValueNone
}
let getItem (x: ItemWithInst) = x.Item
let getItem2 (x: CompletionItem) = x.Item
/// Checks whether the suggested name is unused.
/// In the future we could use an increasing numeric suffix for conflict resolution
let CreateCompletionItemForSuggestedPatternName (pos: pos) name =
if String.IsNullOrWhiteSpace name then
None
else
let name = String.lowerCaseFirstChar name
let unused =
sResolutions.CapturedNameResolutions
|> ResizeArray.forall (fun r ->
match r.Item with
| Item.Value vref when r.Pos.Line = pos.Line -> vref.DisplayName <> name
| _ -> true)
if unused then
Some(CompletionItemSuggestedName name)