forked from fsharp/fsharp-compiler-docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathservice.fs
More file actions
executable file
·3135 lines (2647 loc) · 173 KB
/
service.fs
File metadata and controls
executable file
·3135 lines (2647 loc) · 173 KB
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. Licensed under the Apache License, Version 2.0. 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 Microsoft.FSharp.Compiler.SourceCodeServices
open System
open System.IO
open System.Text
open System.Threading
open System.Reflection.Emit
open System.Runtime
open System.Collections.Generic
open Microsoft.FSharp.Core.Printf
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.AbstractIL
open Microsoft.FSharp.Compiler.AbstractIL.IL
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
open Microsoft.FSharp.Compiler.AbstractIL.Internal
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
open Microsoft.FSharp.Compiler.AccessibilityLogic
open Microsoft.FSharp.Compiler.Ast
open Microsoft.FSharp.Compiler.CompileOps
open Microsoft.FSharp.Compiler.Driver
open Microsoft.FSharp.Compiler.ErrorLogger
open Microsoft.FSharp.Compiler.Lib
open Microsoft.FSharp.Compiler.ReferenceResolver
open Microsoft.FSharp.Compiler.PrettyNaming
open Microsoft.FSharp.Compiler.Parser
open Microsoft.FSharp.Compiler.Range
open Microsoft.FSharp.Compiler.Lexhelp
open Microsoft.FSharp.Compiler.Layout
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.Tastops
open Microsoft.FSharp.Compiler.Tastops.DebugPrint
open Microsoft.FSharp.Compiler.TcGlobals
open Microsoft.FSharp.Compiler.Infos
open Microsoft.FSharp.Compiler.InfoReader
open Microsoft.FSharp.Compiler.NameResolution
open Microsoft.FSharp.Compiler.TypeChecker
open Microsoft.FSharp.Compiler.SourceCodeServices.ItemDescriptionsImpl
open Internal.Utilities
open Internal.Utilities.Collections
[<AutoOpen>]
module EnvMisc =
let getToolTipTextSize = GetEnvInteger "FCS_RecentForegroundTypeCheckCacheSize" 5
let maxTypeCheckErrorsOutOfProjectContext = GetEnvInteger "FCS_MaxErrorsOutOfProjectContext" 3
let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5
let parseFileInProjectCacheSize = GetEnvInteger "FCS_ParseFileInProjectCacheSize" 2
let incrementalTypeCheckCacheSize = GetEnvInteger "FCS_IncrementalTypeCheckCacheSize" 5
let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3
let frameworkTcImportsCacheStrongSize = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8
let maxMBDefault = GetEnvInteger "FCS_MaxMB" 1000000 // a million MB = 1TB = disabled
//let maxMBDefault = GetEnvInteger "FCS_maxMB" (if sizeof<int> = 4 then 1700 else 3400)
//----------------------------------------------------------------------------
// Methods
//--------------------------------------------------------------------------
[<Sealed>]
type FSharpMethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: string) =
member __.ParameterName = name
[<Obsolete("This member has been renamed to 'ParameterName'")>]
member __.Name = name
member __.CanonicalTypeTextForSorting = canonicalTypeTextForSorting
member __.Display = display
member __.Description = ""
/// Format parameters for Intellisense completion
module internal Params =
let printCanonicalizedTypeName g (denv:DisplayEnv) tau =
// get rid of F# abbreviations and such
let strippedType = stripTyEqnsWrtErasure EraseAll g tau
// pretend no namespaces are open
let denv = denv.SetOpenPaths([])
// now printing will see a .NET-like canonical representation, that is good for sorting overloads into a reasonable order (see bug 94520)
NicePrint.stringOfTy denv strippedType
let ParamOfRecdField g denv f =
FSharpMethodGroupItemParameter(
name = f.rfield_id.idText,
canonicalTypeTextForSorting = printCanonicalizedTypeName g denv f.rfield_type,
display = NicePrint.prettyStringOfTy denv f.rfield_type
)
let ParamOfUnionCaseField g denv isGenerated (i : int) f =
let initial = ParamOfRecdField g denv f
let display = if isGenerated i f then initial.Display else NicePrint.stringOfParamData denv (ParamData(false, false, NotOptional, NoCallerInfo, Some f.rfield_id, ReflectedArgInfo.None, f.rfield_type))
FSharpMethodGroupItemParameter(
name=initial.ParameterName,
canonicalTypeTextForSorting=initial.CanonicalTypeTextForSorting,
display=display)
let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isOutArg, _optArgInfo, _callerInfoInfo, nmOpt, _reflArgInfo, pty) as paramData) =
FSharpMethodGroupItemParameter(
name = (match nmOpt with None -> "" | Some pn -> pn.idText),
canonicalTypeTextForSorting = printCanonicalizedTypeName g denv pty,
display = NicePrint.stringOfParamData denv paramData)
// TODO this code is similar to NicePrint.fs:formatParamDataToBuffer, refactor or figure out why different?
let ParamsOfParamDatas g denv (paramDatas:ParamData list) rty =
let paramNames,paramPrefixes,paramTypes =
paramDatas
|> List.map (fun (ParamData(isParamArrayArg, _isOutArg, optArgInfo, _callerInfoInfo, nmOpt, _reflArgInfo, pty)) ->
let isOptArg = optArgInfo.IsOptional
match nmOpt, isOptArg, tryDestOptionTy denv.g pty with
// Layout an optional argument
| Some id, true, ptyOpt ->
let nm = id.idText
// detect parameter type, if ptyOpt is None - this is .NET style optional argument
let pty = defaultArg ptyOpt pty
nm, (sprintf "?%s:" nm), pty
// Layout an unnamed argument
| None, _,_ ->
"", "", pty
// Layout a named argument
| Some id,_,_ ->
let nm = id.idText
let prefix =
if isParamArrayArg then
sprintf "%s %s: " (NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute |> showL) nm
else
sprintf "%s: " nm
nm, prefix,pty)
|> List.unzip3
let paramTypeAndRetLs,_ = NicePrint.layoutPrettifiedTypes denv (paramTypes@[rty])
let paramTypeLs,_ = List.frontAndBack paramTypeAndRetLs
(paramNames,paramPrefixes,(paramTypes,paramTypeLs)||>List.zip) |||> List.map3 (fun nm paramPrefix (tau,tyL) ->
FSharpMethodGroupItemParameter(
name = nm,
canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tau,
display = paramPrefix+(showL tyL)
))
let ParamsOfTypes g denv args rtau =
let ptausL, _ = NicePrint.layoutPrettifiedTypes denv (args@[rtau])
let argsL,_ = List.frontAndBack ptausL
let mkParam (tau,tyL) =
FSharpMethodGroupItemParameter(
name = "",
canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tau,
display = Layout.showL tyL
)
(args,argsL) ||> List.zip |> List.map mkParam
#if EXTENSIONTYPING
let (|ItemIsProvidedType|_|) g item =
match item with
| Item.Types(_name,tys) ->
match tys with
| [AppTy g (tyconRef,_typeInst)] ->
if tyconRef.IsProvidedErasedTycon || tyconRef.IsProvidedGeneratedTycon then
Some tyconRef
else
None
| _ -> None
| _ -> None
let (|ItemIsProvidedTypeWithStaticArguments|_|) m g item =
match item with
| Item.Types(_name,tys) ->
match tys with
| [AppTy g (tyconRef,_typeInst)] ->
if tyconRef.IsProvidedErasedTycon || tyconRef.IsProvidedGeneratedTycon then
let typeBeforeArguments =
match tyconRef.TypeReprInfo with
| TProvidedTypeExtensionPoint info -> info.ProvidedType
| _ -> failwith "unreachable"
let staticParameters = typeBeforeArguments.PApplyWithProvider((fun (typeBeforeArguments,provider) -> typeBeforeArguments.GetStaticParameters(provider)), range=m)
let staticParameters = staticParameters.PApplyArray(id, "GetStaticParameters",m)
Some staticParameters
else
None
| _ -> None
| _ -> None
let (|ItemIsProvidedMethodWithStaticArguments|_|) item =
match item with
// Prefer the static parameters from the uninstantiated method info
| Item.MethodGroup(_,_,Some minfo) ->
match minfo.ProvidedStaticParameterInfo with
| Some (_,staticParameters) -> Some staticParameters
| _ -> None
| Item.MethodGroup(_,[minfo],_) ->
match minfo.ProvidedStaticParameterInfo with
| Some (_,staticParameters) -> Some staticParameters
| _ -> None
| _ -> None
let (|ItemIsWithStaticArguments|_|) m g item =
match item with
| ItemIsProvidedTypeWithStaticArguments m g staticParameters -> Some staticParameters
| ItemIsProvidedMethodWithStaticArguments staticParameters -> Some staticParameters
| _ -> None
#endif
let StaticParamsOfItem (infoReader:InfoReader) m denv d =
let amap = infoReader.amap
let g = infoReader.g
match d with
#if EXTENSIONTYPING
| ItemIsWithStaticArguments m g staticParameters ->
staticParameters
|> Array.map (fun sp ->
let typ = Import.ImportProvidedType amap m (sp.PApply((fun x -> x.ParameterType),m))
let spKind = NicePrint.stringOfTy denv typ
let spName = sp.PUntaint((fun sp -> sp.Name), m)
let spOpt = sp.PUntaint((fun sp -> sp.IsOptional), m)
FSharpMethodGroupItemParameter(
name = spName,
canonicalTypeTextForSorting = spKind,
display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind))
#endif
| _ -> [| |]
let rec ParamsOfItem (infoReader:InfoReader) m denv d =
let amap = infoReader.amap
let g = infoReader.g
match d with
| Item.Value vref ->
let getParamsOfTypes() =
let _, tau = vref.TypeScheme
if isFunTy denv.g tau then
let arg,rtau = destFunTy denv.g tau
let args = tryDestRefTupleTy denv.g arg
ParamsOfTypes g denv args rtau
else []
match vref.ValReprInfo with
| None ->
// ValReprInfo = None i.e. in let bindings defined in types or in local functions
// in this case use old approach and return only information about types
getParamsOfTypes ()
| Some valRefInfo ->
// ValReprInfo will exist for top-level syntactic functions
// per spec: binding is considered to define a syntactic function if it is either a function or its immediate right-hand-side is a anonymous function
let (_, argInfos, returnTy, _) = GetTopValTypeInFSharpForm g valRefInfo vref.Type m
match argInfos with
| [] ->
// handles cases like 'let foo = List.map'
getParamsOfTypes()
| argInfo::_ ->
// result 'paramDatas' collection corresponds to the first argument of curried function
// i.e. let func (a : int) (b : int) = a + b
// paramDatas will contain information about a and returnTy will be: int -> int
// This is good enough as we don't provide ways to display info for the second curried argument
let paramDatas =
argInfo
|> List.map ParamNameAndType.FromArgInfo
|> List.map (fun (ParamNameAndType(nmOpt, pty)) -> ParamData(false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, pty))
ParamsOfParamDatas g denv paramDatas returnTy
| Item.UnionCase(ucr,_) ->
match ucr.UnionCase.RecdFields with
| [f] -> [ParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField -1 f]
| fs -> fs |> List.mapi (ParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField)
| Item.ActivePatternCase(apref) ->
let v = apref.ActivePatternVal
let _,tau = v.TypeScheme
let args, _ = stripFunTy denv.g tau
ParamsOfTypes g denv args tau
| Item.ExnCase(ecref) ->
ecref |> recdFieldsOfExnDefRef |> List.mapi (ParamOfUnionCaseField g denv NicePrint.isGeneratedExceptionField)
| Item.Property(_,pinfo :: _) ->
let paramDatas = pinfo.GetParamDatas(amap,m)
let rty = pinfo.GetPropertyType(amap,m)
ParamsOfParamDatas g denv paramDatas rty
| Item.CtorGroup(_,(minfo :: _))
| Item.MethodGroup(_,(minfo :: _),_) ->
let paramDatas = minfo.GetParamDatas(amap, m, minfo.FormalMethodInst) |> List.head
let rty = minfo.GetFSharpReturnTy(amap, m, minfo.FormalMethodInst)
ParamsOfParamDatas g denv paramDatas rty
| Item.CustomBuilder (_,vref) -> ParamsOfItem infoReader m denv (Item.Value vref)
| Item.TypeVar _ -> []
| Item.CustomOperation (_,usageText, Some minfo) ->
match usageText() with
| None ->
let argNamesAndTys = ItemDescriptionsImpl.ParamNameAndTypesOfUnaryCustomOperation g minfo
let _, argTys, _ = PrettyTypes.PrettifyTypesN g (argNamesAndTys |> List.map (fun (ParamNameAndType(_,ty)) -> ty))
let paramDatas = (argNamesAndTys, argTys) ||> List.map2 (fun (ParamNameAndType(nmOpt, _)) argTy -> ParamData(false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None,argTy))
let rty = minfo.GetFSharpReturnTy(amap, m, minfo.FormalMethodInst)
ParamsOfParamDatas g denv paramDatas rty
| Some _ ->
[] // no parameter data available for binary operators like 'zip', 'join' and 'groupJoin' since they use bespoke syntax
| Item.FakeInterfaceCtor _ -> []
| Item.DelegateCtor delty ->
let (SigOfFunctionForDelegate(_, _, _, fty)) = GetSigOfFunctionForDelegate infoReader delty m AccessibleFromSomeFSharpCode
ParamsOfParamDatas g denv [ParamData(false, false, NotOptional, NoCallerInfo, None, ReflectedArgInfo.None, fty)] delty
| _ -> []
/// A single method for Intellisense completion
[<Sealed; NoEquality; NoComparison>]
// Note: instances of this type do not hold any references to any compiler resources.
type FSharpMethodGroupItem(description: FSharpToolTipText, typeText: string, parameters: FSharpMethodGroupItemParameter[], hasParameters: bool, staticParameters: FSharpMethodGroupItemParameter[]) =
member __.Description = description
member __.TypeText = typeText
[<Obsolete("This member has been renamed to 'TypeText'")>]
member __.Type = typeText
member __.Parameters = parameters
member __.HasParameters = hasParameters
// Does the type name or method support a static arguments list, like TP<42,"foo"> or conn.CreateCommand<42, "foo">(arg1, arg2)?
member __.StaticParameters = staticParameters
/// A table of methods for Intellisense completion
//
// Note: this type does not hold any strong references to any compiler resources, nor does evaluating any of the properties execute any
// code on the compiler thread.
[<Sealed>]
type FSharpMethodGroup( name: string, unsortedMethods: FSharpMethodGroupItem[] ) =
// BUG 413009 : [ParameterInfo] takes about 3 seconds to move from one overload parameter to another
// cache allows to avoid recomputing parameterinfo for the same item
#if FX_ATLEAST_40
static let methodOverloadsCache = System.Runtime.CompilerServices.ConditionalWeakTable()
#endif
let methods =
unsortedMethods
// Methods with zero arguments show up here as taking a single argument of type 'unit'. Patch them now to appear as having zero arguments.
|> Array.map (fun meth ->
let parms = meth.Parameters
if parms.Length = 1 && parms.[0].CanonicalTypeTextForSorting="Microsoft.FSharp.Core.Unit" then
FSharpMethodGroupItem(meth.Description,meth.TypeText,[||],true,meth.StaticParameters)
else
meth)
// Fix the order of methods, to be stable for unit testing.
|> Array.sortBy (fun meth ->
let parms = meth.Parameters
parms.Length, (parms |> Array.map (fun p -> p.CanonicalTypeTextForSorting)))
[<Obsolete("This member has been renamed to 'MethodName'")>]
member x.Name = name
member x.MethodName = name
member x.Methods = methods
static member Create(infoReader:InfoReader,m,denv,items:Item list) =
let g = infoReader.g
if List.isEmpty items then new FSharpMethodGroup("", [| |]) else
let name = items.Head.DisplayName
let getOverloadsForItem item =
#if FX_ATLEAST_40
match methodOverloadsCache.TryGetValue item with
| true, overloads -> overloads
| false, _ ->
#endif
let items =
match item with
| Item.CtorGroup(nm,cinfos) -> List.map (fun minfo -> Item.CtorGroup(nm,[minfo])) cinfos
| Item.FakeInterfaceCtor _
| Item.DelegateCtor _ -> [item]
| Item.NewDef _
| Item.ILField _ -> []
| Item.Event _ -> []
| Item.RecdField(rfinfo) ->
if isFunction g rfinfo.FieldType then [item] else []
| Item.Value v ->
if isFunction g v.Type then [item] else []
| Item.UnionCase(ucr,_) ->
if not ucr.UnionCase.IsNullary then [item] else []
| Item.ExnCase(ecr) ->
if List.isEmpty (recdFieldsOfExnDefRef ecr) then [] else [item]
| Item.Property(_,pinfos) ->
let pinfo = List.head pinfos
if pinfo.IsIndexer then [item] else []
#if EXTENSIONTYPING
| Params.ItemIsWithStaticArguments m g _ -> [item] // we pretend that provided-types-with-static-args are method-like in order to get ParamInfo for them
#endif
| Item.MethodGroup(nm,minfos,orig) -> minfos |> List.map (fun minfo -> Item.MethodGroup(nm,[minfo],orig))
| Item.CustomOperation(_name, _helpText, _minfo) -> [item]
| Item.TypeVar _ -> []
| Item.CustomBuilder _ -> []
| _ -> []
let methods =
items |> Array.ofList |> Array.map (fun item ->
FSharpMethodGroupItem(
description = FSharpToolTipText [FormatDescriptionOfItem true infoReader m denv item],
typeText = FormatReturnTypeOfItem infoReader m denv item,
parameters = (Params.ParamsOfItem infoReader m denv item |> Array.ofList),
hasParameters = (match item with Params.ItemIsProvidedTypeWithStaticArguments m g _ -> false | _ -> true),
staticParameters = Params.StaticParamsOfItem infoReader m denv item
))
#if FX_ATLEAST_40
methodOverloadsCache.Add(item, methods)
#endif
methods
let methods = [| for item in items do yield! getOverloadsForItem item |]
new FSharpMethodGroup(name, methods)
//----------------------------------------------------------------------------
// Scopes.
//--------------------------------------------------------------------------
[<RequireQualifiedAccess>]
type (*internal*) FSharpFindDeclFailureReason =
// generic reason: no particular information about error
| Unknown
// source code file is not available
| NoSourceCode
// trying to find declaration of ProvidedType without TypeProviderDefinitionLocationAttribute
| ProvidedType of string
// trying to find declaration of ProvidedMember without TypeProviderDefinitionLocationAttribute
| ProvidedMember of string
type FSharpFindDeclResult =
/// declaration not found + reason
| DeclNotFound of FSharpFindDeclFailureReason
/// found declaration
| DeclFound of 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 internal NameResResult =
| Members of (Item list * DisplayEnv * range)
| Cancel of DisplayEnv * range
| Empty
| TypecheckStaleAndTextChanged
[<RequireQualifiedAccess>]
type ResolveOverloads =
| Yes
| No
[<RequireQualifiedAccess>]
type GetPreciseCompletionListFromExprTypingsResult =
| NoneBecauseTypecheckIsStaleAndTextChanged
| NoneBecauseThereWereTypeErrors
| None
| Some of (Item list * DisplayEnv * range)
type Names = string list
[<Sealed>]
type FSharpSymbolUse(g:TcGlobals, denv: DisplayEnv, symbol:FSharpSymbol, itemOcc, range: range) =
member __.Symbol = symbol
member __.DisplayContext = FSharpDisplayContext(fun _ -> denv)
member x.IsDefinition = x.IsFromDefinition
member __.IsFromDefinition = (match itemOcc with ItemOccurence.Binding -> true | _ -> false)
member __.IsFromPattern = (match itemOcc with ItemOccurence.Pattern -> true | _ -> false)
member __.IsFromType = (match itemOcc with ItemOccurence.UseInType -> true | _ -> false)
member __.IsFromAttribute = (match itemOcc with ItemOccurence.UseInAttribute -> true | _ -> false)
member __.IsFromDispatchSlotImplementation = (match itemOcc with ItemOccurence.Implemented -> true | _ -> false)
member __.IsFromComputationExpression =
match symbol.Item, itemOcc with
// 'seq' in 'seq { ... }' gets colored as keywords
| (Item.Value vref), ItemOccurence.Use when valRefEq g g.seq_vref vref -> true
// custom builders, custom operations get colored as keywords
| (Item.CustomBuilder _ | Item.CustomOperation _), ItemOccurence.Use -> true
| _ -> false
member __.FileName = range.FileName
member __.Range = Range.toZ range
member __.RangeAlternate = range
// A scope 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 TypeCheckInfo
(// Information corresponding to miscellaneous command-line options (--define, etc).
_sTcConfig: TcConfig,
g: TcGlobals,
// The signature of the assembly being checked, up to and including the current file
ccuSig: ModuleOrNamespaceType,
thisCcu: CcuThunk,
tcImports: TcImports,
tcAccessRights: AccessorDomain,
projectFileName: string ,
mainInputFileName: string ,
sResolutions: TcResolutions,
sSymbolUses: TcSymbolUses,
// This is a name resolution environment to use if no better match can be found.
sFallback: NameResolutionEnv,
loadClosure : LoadClosure option,
reactorOps : IReactorOperations,
checkAlive : (unit -> bool),
textSnapshotInfo:obj option) =
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.
// 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<int*int*string,FSharpToolTipText>(getToolTipTextSize,areSame=(fun (x,y) -> x = y))
let amap = tcImports.GetImportMap()
let infoReader = new InfoReader(g,amap)
let ncenv = new NameResolver(g,amap,infoReader,NameResolution.FakeInstantiationGenerator)
/// Find the most precise naming environment for the given line and column
let GetBestEnvForPos cursorPos =
let bestSoFar = ref None
// Find the most deeply nested enclosing scope that contains given position
sResolutions.CapturedEnvs |> ResizeArray.iter (fun (possm,env,ad) ->
if rangeContainsPos possm cursorPos then
match !bestSoFar with
| Some (bestm,_,_) ->
if rangeContainsRange bestm possm then
bestSoFar := Some (possm,env,ad)
| None ->
bestSoFar := Some (possm,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 alwauys be at least as rich
let bestAlmostIncludedSoFar = ref None
sResolutions.CapturedEnvs |> ResizeArray.iter (fun (possm,env,ad) ->
// take only ranges that strictly do not include cursorPos (all ranges that touch cursorPos were processed during 'Strict Inclusion' part)
if rangeBeforePos possm cursorPos && not (posEq possm.End cursorPos) then
let contained =
match mostDeeplyNestedEnclosingScope with
| Some (bestm,_,_) -> rangeContainsRange bestm possm
| None -> true
if contained then
match !bestAlmostIncludedSoFar with
| Some (rightm:range,_,_) ->
if posGt possm.End rightm.End ||
(posEq possm.End rightm.End && posGt possm.Start rightm.Start) then
bestAlmostIncludedSoFar := Some (possm,env,ad)
| _ -> bestAlmostIncludedSoFar := Some (possm,env,ad))
let resEnv =
match !bestAlmostIncludedSoFar with
| Some (_m,env,ad) ->
env,ad
| None ->
match mostDeeplyNestedEnclosingScope with
| Some (_m,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 FSharpToolTipText
let FilterItemsForCtors filterCtors items =
let items = items |> List.filter (function (Item.CtorGroup _) when filterCtors = ResolveTypeNamesToTypeRefs -> false | _ -> true)
items
// Filter items to show only valid & return Some if there are any
let ReturnItemsOfType items g denv (m:range) filterCtors hasTextChangedSinceLastTypecheck f =
let items =
items
|> RemoveDuplicateItems g
|> RemoveExplicitlySuppressed g
|> FilterItemsForCtors filterCtors
if not (List.isEmpty items) then
if hasTextChangedSinceLastTypecheck(textSnapshotInfo, m) then
NameResResult.TypecheckStaleAndTextChanged // typecheck is stale, wait for second-chance IntelliSense to bring up right result
else
f(items, denv, m)
else NameResResult.Empty
let GetCapturedNameResolutions endOfNamesPos resolveOverloads =
let quals =
match resolveOverloads with
| ResolveOverloads.Yes -> sResolutions.CapturedNameResolutions
| ResolveOverloads.No -> sResolutions.CapturedMethodGroupResolutions
let quals = quals |> ResizeArray.filter (fun cnr -> posEq cnr.Pos endOfNamesPos)
quals
/// 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, hasTextChangedSinceLastTypecheck) =
let endOfNamesPos = mkPos line colAtEndOfNames
// Logic below expects the list to be in reverse order of resolution
let items = GetCapturedNameResolutions endOfNamesPos resolveOverloads |> ResizeArray.toList |> List.rev
match items, membersByResidue with
// 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(_,Item.Types(_,(typ::_)),_,denv,nenv,ad,m)::_, Some _ ->
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad true typ
ReturnItemsOfType items g denv m filterCtors hasTextChangedSinceLastTypecheck NameResResult.Members
// 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 tcref = generalizedTyconRef 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 Microsoft.FSharp.Compiler.TypeRelations.TypeFeasiblySubsumesType 0 g amap m tcref Microsoft.FSharp.Compiler.TypeRelations.CanCoerce ty then
ad
else
AccessibleFrom(paths, None)
| _ -> ad
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad false ty
ReturnItemsOfType items g denv m filterCtors hasTextChangedSinceLastTypecheck NameResResult.Members
// No residue, so the items are the full resolution of the name
| CNR(_,_,_,denv,_,_,m) :: _, None ->
let items = items |> List.map (fun (CNR(_,item,_,_,_,_,_)) -> item)
// "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 (function Item.CustomOperation(CustomOperations.Into,_,_) -> false | _ -> true)
ReturnItemsOfType items g denv m filterCtors hasTextChangedSinceLastTypecheck NameResResult.Members
| _ , _ -> NameResResult.Empty
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, _isOut, _optArgInfo, _callerInfoInfo, name, _, ty)) ->
match name with
| Some n -> Some (Item.ArgName(n, ty, Some (ArgumentContainer.Method meth)))
| None -> None
)
| _ -> []
)
let GetNamedParametersAndSettableFields endOfExprPos hasTextChangedSinceLastTypecheck =
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.EnclosingType
let parameters = CollectParameters ctors amap m
Some (denv, m, props @ parameters)
| CNR(_, Item.MethodGroup(_, methods, _), _, denv, nenv, ad, m)::_ ->
let props =
methods
|> List.collect (fun meth ->
let retTy = meth.GetFSharpReturnTy(amap, m, meth.FormalMethodInst)
ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false retTy
)
let parameters = CollectParameters methods amap m
Some (denv, m, props @ parameters)
| _ ->
None
match result with
| None ->
NameResResult.Empty
| Some (denv, m, result) ->
ReturnItemsOfType result g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs hasTextChangedSinceLastTypecheck NameResResult.Members
/// finds captured typing for the given position
let GetExprTypingForPosition(endOfExprPos) =
let quals =
sResolutions.CapturedExpressionTypings
|> Seq.filter (fun (pos,typ,denv,_,_,_) ->
// We only want expression types that end at the particular position in the file we are looking at.
let isLocationWeCareAbout = posEq pos 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.
let isFunction = isFunTy denv.g typ
isLocationWeCareAbout && not isFunction)
|> Seq.toArray
let thereWereSomeQuals = not (Array.isEmpty quals)
// filter out errors
let quals = quals
|> Array.filter (fun (_,typ,denv,_,_,_) -> not (isTyparTy denv.g typ && (destTyparTy denv.g typ).IsFromError))
thereWereSomeQuals, quals
/// obtains captured typing for the given position
/// if type of captured typing is record - returns list of record fields
let GetRecdFieldsForExpr(r : range) =
let _, quals = GetExprTypingForPosition(r.End)
let bestQual =
match quals with
| [||] -> None
| quals ->
quals |> Array.tryFind (fun (_,_,_,_,_,rq) ->
ignore(r) // for breakpoint
posEq r.Start rq.Start)
match bestQual with
| Some (_,typ,denv,_nenv,ad,m) when isRecdTy denv.g typ ->
let items = NameResolution.ResolveRecordOrClassFieldsOfType ncenv m ad typ false
Some (items, denv, m)
| _ -> 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, hasTextChangedSinceLastTypecheck: (obj * range -> bool)) =
let thereWereSomeQuals, quals = GetExprTypingForPosition(endOfExprPos)
match quals with
| [| |] ->
if thereWereSomeQuals then
GetPreciseCompletionListFromExprTypingsResult.NoneBecauseThereWereTypeErrors
else
GetPreciseCompletionListFromExprTypingsResult.None
| _ ->
let bestQual, textChanged =
match parseResults.ParseTree with
| Some(input) ->
match UntypedParseImpl.GetRangeOfExprLeftOfDot(endOfExprPos,Some(input)) with // TODO we say "colAtEndOfNames" everywhere, but that's not really a good name ("foo . $" hit Ctrl-Space at $)
| Some( exprRange) ->
if hasTextChangedSinceLastTypecheck(textSnapshotInfo, exprRange) then
None, true // typecheck is stale, wait for second-chance IntelliSense to bring up right result
else
// See bug 130733. 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
| _ -> None, false
match bestQual with
| Some bestQual ->
let (_,typ,denv,nenv,ad,m) = bestQual
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad false typ
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
let items = items |> FilterItemsForCtors filterCtors
GetPreciseCompletionListFromExprTypingsResult.Some(items,denv,m)
| None ->
if textChanged then GetPreciseCompletionListFromExprTypingsResult.NoneBecauseTypecheckIsStaleAndTextChanged
else GetPreciseCompletionListFromExprTypingsResult.None
/// Find items in the best naming environment.
let GetEnvironmentLookupResolutions(cursorPos, plid, filterCtors, showObsolete) =
let (nenv,ad),m = GetBestEnvForPos cursorPos
let items = NameResolution.ResolvePartialLongIdent ncenv nenv (ConstraintSolver.IsApplicableMethApprox g amap m) m ad plid showObsolete
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
let items = items |> FilterItemsForCtors filterCtors
items, nenv.DisplayEnv, m
/// Find record fields in the best naming environment.
let GetClassOrRecordFieldsEnvironmentLookupResolutions(cursorPos, plid, (_residue : string option)) =
let (nenv, ad),m = GetBestEnvForPos cursorPos
let items = NameResolution.ResolvePartialLongIdentToClassOrRecdFields ncenv nenv m ad plid false
let items = items |> RemoveDuplicateItems g
let items = items |> RemoveExplicitlySuppressed g
items, nenv.DisplayEnv,m
/// Resolve a location and/or text to items.
// Three techniques are used
// - look for an exact known name resolution from type checking
// - use the known type of an expression, e.g. (expr).Name, to generate an item list
// - lookup an entire name in the name resolution environment, e.g. A.B.Name, to generate an item list
//
// The overall aim is to resolve as accurately as possible based on what we know from type inference
let GetBaseClassCandidates = function
| Item.ModuleOrNamespaces _ -> true
| Item.Types(_, ty::_) when (isClassTy g ty) && not (isSealedTy g ty) -> true
| _ -> false
let GetInterfaceCandidates = function
| Item.ModuleOrNamespaces _ -> true
| Item.Types(_, ty::_) when (isInterfaceTy g ty) -> true
| _ -> false
// Return only items with the specified name
let FilterDeclItemsByResidue residue (items: Item list) =
items |> List.filter (fun item ->
let n1 = item.DisplayName
match item with
| Item.Types _ | Item.CtorGroup _ -> residue + "Attribute" = n1 || 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 (exactMatchResidueOpt : _ option) check (items, denv, m) =
// can throw if type is in located in non-resolved CCU: i.e. bigint if reference to System.Numerics is absent
let safeCheck item = try check item with _ -> false
// Are we looking for items with precisely the given name?
if not (List.isEmpty items) && exactMatchResidueOpt.IsSome then
let items = items |> FilterDeclItemsByResidue exactMatchResidueOpt.Value |> List.filter safeCheck
if not (List.isEmpty items) then Some(items, denv, m) else None
else
// 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.$ = ...)
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|_|) exactMatchResidueOpt orig =
FilterRelevantItemsBy exactMatchResidueOpt (fun _ -> true) orig
/// Find the first non-whitespace postion 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 && System.Char.IsWhiteSpace(lineStr.[p]) do
p <- p - 1
if p >= 0 then Some p else None
let GetDeclaredItems (parseResultsOpt: FSharpParseFileResults option, lineStr: string, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator) =
// Are the last two chars (except whitespaces) = ".."
let isLikeRangeOp =
match FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1) with
| Some x when x >= 1 && lineStr.[x] = '.' && lineStr.[x - 1] = '.' -> true
| _ -> false
// if last two chars are .. and we are not in range operator context - no completion
if isLikeRangeOp && not isInRangeOperator then None else
// Try to use the exact results of name resolution during type checking to generate the results
// This is based on position (i.e. colAtEndOfNamesAndResidue). This is not used if a residueOpt is given.
let nameResItems =
match residueOpt with
| None -> GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck)
| Some residue ->
// deals with cases when we have spaces between dot and\or identifier, like A . $
// if this is our case - then wen need to locate end position of the name skipping whitespaces
// this allows us to handle cases like: let x . $ = 1
// colAtEndOfNamesAndResidue is 1-based so at first we need to convert it to 0-based
//
// TODO: this code would be a lot simpler if we just passed in colAtEndOfNames in
// the first place. colAtEndOfNamesAndResidue serves no purpose. The cracking below is
// inaccurate and incomplete in any case since it only works on a single line.
match FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1) with
| Some p when lineStr.[p] = '.' ->
match FindFirstNonWhitespacePosition lineStr (p - 1) with
| Some colAtEndOfNames ->
let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based
GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck)
| None -> NameResResult.Empty
| _ -> NameResResult.Empty
// Normalize to form A.B.C.D where D is the residue. It may be empty for "A.B.C."
// residueOpt = Some when we are looking for the exact match
let plid, exactMatchResidueOpt =
match origLongIdentOpt, residueOpt with
| None, _ -> [], None
| Some(origLongIdent), Some _ -> origLongIdent, None
| Some(origLongIdent), None ->
assert (not (List.isEmpty origLongIdent))
// note: as above, this happens when we are called for "precise" resolution - (F1 keyword, data tip etc..)
let plid, residue = List.frontAndBack origLongIdent
plid, Some residue
match nameResItems with
| NameResResult.TypecheckStaleAndTextChanged -> None // second-chance intellisense will try again
| NameResResult.Cancel(denv,m) -> Some([], denv, m)
| NameResResult.Members(FilterRelevantItems exactMatchResidueOpt items) ->
// lookup based on name resolution results successful
Some items
| _ ->
match origLongIdentOpt with
| None -> None
| Some _ ->
// Try to use the type of the expression on the left to help generate a completion list
let qualItems, thereIsADotInvolved =
match parseResultsOpt with
| None ->
// Note, you will get here if the 'reason' is not CompleteWord/MemberSelect/DisplayMemberList, as those are currently the
// only reasons we do a sync parse to have the most precise and likely-to-be-correct-and-up-to-date info. So for example,
// if you do QuickInfo hovering over A in "f(x).A()", you will only get a tip if typechecking has a name-resolution recorded
// for A, not if merely we know the capturedExpressionTyping of f(x) and you very recently typed ".A()" - in that case,
// you won't won't get a tip until the typechecking catches back up.
GetPreciseCompletionListFromExprTypingsResult.None, false
| Some parseResults ->
match UntypedParseImpl.TryFindExpressionASTLeftOfDotLeftOfCursor(mkPos line colAtEndOfNamesAndResidue,parseResults.ParseTree) with
| Some(pos,_) ->
GetPreciseCompletionListFromExprTypings(parseResults, pos, filterCtors, hasTextChangedSinceLastTypecheck), true
| None ->
// Can get here in a case like: if "f xxx yyy" is legal, and we do "f xxx y"
// We have no interest in expression typings, those are only useful for dot-completion. We want to fallback
// to "Use an environment lookup as the last resort" below
GetPreciseCompletionListFromExprTypingsResult.None, false
match qualItems,thereIsADotInvolved with
| GetPreciseCompletionListFromExprTypingsResult.Some(FilterRelevantItems exactMatchResidueOpt items), _
// Initially we only use the expression typings when looking up, e.g. (expr).Nam or (expr).Name1.Nam
// These come through as an empty plid and residue "". Otherwise we try an environment lookup
// and then return to the qualItems. This is because the expression typings are a little inaccurate, primarily because
// it appears we're getting some typings recorded for non-atomic expressions like "f x"
when (match plid with [] -> true | _ -> false) ->
// lookup based on expression typings successful
Some items
| GetPreciseCompletionListFromExprTypingsResult.NoneBecauseThereWereTypeErrors, _ ->
// There was an error, e.g. we have "<expr>." and there is an error determining the type of <expr>
// In this case, we don't want any of the fallback logic, rather, we want to produce zero results.
None
| GetPreciseCompletionListFromExprTypingsResult.NoneBecauseTypecheckIsStaleAndTextChanged, _ ->
// we want to report no result and let second-chance intellisense kick in
None
| _, true when (match plid with [] -> true | _ -> false) ->
// If the user just pressed '.' after an _expression_ (not a plid), it is never right to show environment-lookup top-level completions.
// The user might by typing quickly, and the LS didn't have an expression type right before the dot yet.
// Second-chance intellisense will bring up the correct list in a moment.
None
| _ ->
// Use an environment lookup as the last resort
let envItems = GetEnvironmentLookupResolutions(mkPos line loc, plid, filterCtors, residueOpt.IsSome)
match nameResItems, envItems, qualItems with
// First, use unfiltered name resolution items, if they're not empty
| NameResResult.Members(items, denv, m), _, _ when not (List.isEmpty items) ->
// lookup based on name resolution results successful
Some(items, denv, m)
// If we have nonempty items from environment that were resolved from a type, then use them...
// (that's better than the next case - here we'd return 'int' as a type)
| _, FilterRelevantItems exactMatchResidueOpt (items, denv, m), _ when not (List.isEmpty items) ->
// lookup based on name and environment successful
Some(items, denv, m)
// Try again with the qualItems
| _, _, GetPreciseCompletionListFromExprTypingsResult.Some(FilterRelevantItems exactMatchResidueOpt items) ->
Some(items)
| _ -> None
/// Get the auto-complete items at a particular location.
let GetDeclItemsForNamesAtPosition(parseResultsOpt: FSharpParseFileResults option, origLongIdentOpt: string list option, residueOpt:string option, line:int, lineStr:string, colAtEndOfNamesAndResidue, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck: (obj * range -> bool)) =
let loc =
match colAtEndOfNamesAndResidue with
| pastEndOfLine when pastEndOfLine >= lineStr.Length -> lineStr.Length
| atDot when lineStr.[atDot] = '.' -> atDot + 1
| atStart when atStart = 0 -> 0
| otherwise -> otherwise - 1
// Look for a "special" completion context
match UntypedParseImpl.TryGetCompletionContext(mkPos line colAtEndOfNamesAndResidue, parseResultsOpt) with
// Invalid completion locations
| Some CompletionContext.Invalid -> None
// Completion at 'inherit C(...)"