/
ServiceUntypedParse.fs
executable file
·1434 lines (1278 loc) · 76.1 KB
/
ServiceUntypedParse.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.SourceCodeServices
open System
open System.IO
open System.Collections.Generic
open System.Diagnostics
open System.Text.RegularExpressions
open FSharp.Compiler.AbstractIL.Internal.Library
open FSharp.Compiler.CompilerConfig
open FSharp.Compiler.Lib
open FSharp.Compiler.PrettyNaming
open FSharp.Compiler.Range
open FSharp.Compiler.SyntaxTree
open FSharp.Compiler.SyntaxTreeOps
/// Methods for dealing with F# sources files.
module SourceFile =
/// Source file extensions
let private compilableExtensions = FSharpSigFileSuffixes @ FSharpImplFileSuffixes @ FSharpScriptFileSuffixes
/// Single file projects extensions
let private singleFileProjectExtensions = FSharpScriptFileSuffixes
/// Whether or not this file is compilable
let IsCompilable file =
let ext = Path.GetExtension file
compilableExtensions |> List.exists(fun e->0 = String.Compare(e, ext, StringComparison.OrdinalIgnoreCase))
/// Whether or not this file should be a single-file project
let MustBeSingleFileProject file =
let ext = Path.GetExtension file
singleFileProjectExtensions |> List.exists(fun e-> 0 = String.Compare(e, ext, StringComparison.OrdinalIgnoreCase))
module SourceFileImpl =
let IsInterfaceFile file =
let ext = Path.GetExtension file
0 = String.Compare(".fsi", ext, StringComparison.OrdinalIgnoreCase)
/// Additional #defines that should be in place when editing a file in a file editor such as VS.
let AdditionalDefinesForUseInEditor(isInteractive: bool) =
if isInteractive then ["INTERACTIVE";"EDITING"] // This is still used by the foreground parse
else ["COMPILED";"EDITING"]
type CompletionPath = string list * string option // plid * residue
[<RequireQualifiedAccess>]
type InheritanceOrigin =
| Class
| Interface
| Unknown
[<RequireQualifiedAccess>]
type InheritanceContext =
| Class
| Interface
| Unknown
[<RequireQualifiedAccess>]
type RecordContext =
| CopyOnUpdate of range * CompletionPath // range of copy-expr + current field
| Constructor of string // typename
| New of CompletionPath
[<RequireQualifiedAccess>]
type CompletionContext =
// completion context cannot be determined due to errors
| Invalid
// completing something after the inherit keyword
| Inherit of InheritanceContext * CompletionPath
// completing records field
| RecordField of RecordContext
| RangeOperator
// completing named parameters\setters in parameter list of constructor\method calls
// end of name ast node * list of properties\parameters that were already set
| ParameterList of pos * HashSet<string>
| AttributeApplication
| OpenDeclaration of isOpenType: bool
/// completing pattern type (e.g. foo (x: |))
| PatternType
//----------------------------------------------------------------------------
// FSharpParseFileResults
//----------------------------------------------------------------------------
[<Sealed>]
type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option, parseHadErrors: bool, dependencyFiles: string[]) =
member scope.Errors = errors
member scope.ParseHadErrors = parseHadErrors
member scope.ParseTree = input
member scope.TryRangeOfRefCellDereferenceContainingPos expressionPos =
match input with
| Some input ->
let res =
AstTraversal.Traverse(expressionPos, input, { new AstTraversal.AstVisitorBase<_>() with
member _.VisitExpr(_, _, defaultTraverse, expr) =
match expr with
| SynExpr.App(_, false, SynExpr.Ident funcIdent, expr, _) ->
if funcIdent.idText = "op_Dereference" && rangeContainsPos expr.Range expressionPos then
Some funcIdent.idRange
else
None
| _ -> defaultTraverse expr
})
res
| None ->
None
member scope.FindNoteworthyParamInfoLocations pos =
match input with
| Some input -> FSharpNoteworthyParamInfoLocations.Find(pos, input)
| _ -> None
/// Get declared items and the selected item at the specified location
member private scope.GetNavigationItemsImpl() =
ErrorScope.Protect range0
(fun () ->
match input with
| Some (ParsedInput.ImplFile _ as p) ->
FSharpNavigation.getNavigation p
| Some (ParsedInput.SigFile _) ->
FSharpNavigation.empty
| _ ->
FSharpNavigation.empty)
(fun err ->
Trace.TraceInformation(sprintf "FCS: recovering from error in GetNavigationItemsImpl: '%s'" err)
FSharpNavigation.empty)
member private scope.ValidateBreakpointLocationImpl pos =
let isMatchRange m = rangeContainsPos m pos || m.StartLine = pos.Line
// Process let-binding
let findBreakPoints () =
let checkRange m = [ if isMatchRange m then yield m ]
let walkBindSeqPt sp = [ match sp with DebugPointAtBinding m -> yield! checkRange m | _ -> () ]
let walkForSeqPt sp = [ match sp with DebugPointAtFor.Yes m -> yield! checkRange m | _ -> () ]
let walkWhileSeqPt sp = [ match sp with DebugPointAtWhile.Yes m -> yield! checkRange m | _ -> () ]
let walkTrySeqPt sp = [ match sp with DebugPointAtTry.Yes m -> yield! checkRange m | _ -> () ]
let walkWithSeqPt sp = [ match sp with DebugPointAtWith.Yes m -> yield! checkRange m | _ -> () ]
let walkFinallySeqPt sp = [ match sp with DebugPointAtFinally.Yes m -> yield! checkRange m | _ -> () ]
let rec walkBind (Binding(_, _, _, _, _, _, SynValData(memFlagsOpt, _, _), synPat, _, synExpr, _, spInfo)) =
[ // Don't yield the binding sequence point if there are any arguments, i.e. we're defining a function or a method
let isFunction =
Option.isSome memFlagsOpt ||
match synPat with
| SynPat.LongIdent (_, _, _, SynArgPats.Pats args, _, _) when not (List.isEmpty args) -> true
| _ -> false
if not isFunction then
yield! walkBindSeqPt spInfo
yield! walkExpr (isFunction || (match spInfo with DebugPointAtBinding _ -> false | _-> true)) synExpr ]
and walkExprs es = List.collect (walkExpr false) es
and walkBinds es = List.collect walkBind es
and walkMatchClauses cl =
[ for (Clause(_, whenExpr, e, _, _)) in cl do
match whenExpr with
| Some e -> yield! walkExpr false e
| _ -> ()
yield! walkExpr true e ]
and walkExprOpt (spAlways: bool) eOpt = [ match eOpt with Some e -> yield! walkExpr spAlways e | _ -> () ]
and IsBreakableExpression e =
match e with
| SynExpr.Match _
| SynExpr.IfThenElse _
| SynExpr.For _
| SynExpr.ForEach _
| SynExpr.While _ -> true
| _ -> not (IsControlFlowExpression e)
// Determine the breakpoint locations for an expression. spAlways indicates we always
// emit a breakpoint location for the expression unless it is a syntactic control flow construct
and walkExpr (spAlways: bool) e =
let m = e.Range
if not (isMatchRange m) then [] else
[ if spAlways && IsBreakableExpression e then
yield! checkRange m
match e with
| SynExpr.ArbitraryAfterError _
| SynExpr.LongIdent _
| SynExpr.LibraryOnlyILAssembly _
| SynExpr.LibraryOnlyStaticOptimization _
| SynExpr.Null _
| SynExpr.Ident _
| SynExpr.ImplicitZero _
| SynExpr.Const _ ->
()
| SynExpr.Quote (_, _, e, _, _)
| SynExpr.TypeTest (e, _, _)
| SynExpr.Upcast (e, _, _)
| SynExpr.AddressOf (_, e, _, _)
| SynExpr.CompExpr (_, _, e, _)
| SynExpr.ArrayOrListOfSeqExpr (_, e, _)
| SynExpr.Typed (e, _, _)
| SynExpr.FromParseError (e, _)
| SynExpr.DiscardAfterMissingQualificationAfterDot (e, _)
| SynExpr.Do (e, _)
| SynExpr.Assert (e, _)
| SynExpr.Fixed (e, _)
| SynExpr.DotGet (e, _, _, _)
| SynExpr.LongIdentSet (_, e, _)
| SynExpr.New (_, _, e, _)
| SynExpr.TypeApp (e, _, _, _, _, _, _)
| SynExpr.LibraryOnlyUnionCaseFieldGet (e, _, _, _)
| SynExpr.Downcast (e, _, _)
| SynExpr.InferredUpcast (e, _)
| SynExpr.InferredDowncast (e, _)
| SynExpr.Lazy (e, _)
| SynExpr.TraitCall (_, _, e, _)
| SynExpr.Paren (e, _, _, _) ->
yield! walkExpr false e
| SynExpr.InterpolatedString (parts, _) ->
yield! walkExprs [ for part in parts do
match part with
| SynInterpolatedStringPart.String _ -> ()
| SynInterpolatedStringPart.FillExpr (fillExpr, _) -> yield fillExpr ]
| SynExpr.YieldOrReturn (_, e, _)
| SynExpr.YieldOrReturnFrom (_, e, _)
| SynExpr.DoBang (e, _) ->
yield! checkRange e.Range
yield! walkExpr false e
| SynExpr.NamedIndexedPropertySet (_, e1, e2, _)
| SynExpr.DotSet (e1, _, e2, _)
| SynExpr.Set (e1, e2, _)
| SynExpr.LibraryOnlyUnionCaseFieldSet (e1, _, _, e2, _)
| SynExpr.App (_, _, e1, e2, _) ->
yield! walkExpr false e1
yield! walkExpr false e2
| SynExpr.ArrayOrList (_, es, _)
| SynExpr.Tuple (_, es, _, _) ->
yield! walkExprs es
| SynExpr.Record (_, copyExprOpt, fs, _) ->
match copyExprOpt with
| Some (e, _) -> yield! walkExpr true e
| None -> ()
yield! walkExprs (fs |> List.choose p23)
| SynExpr.AnonRecd (_isStruct, copyExprOpt, fs, _) ->
match copyExprOpt with
| Some (e, _) -> yield! walkExpr true e
| None -> ()
yield! walkExprs (fs |> List.map snd)
| SynExpr.ObjExpr (_, args, bs, is, _, _) ->
match args with
| None -> ()
| Some (arg, _) -> yield! walkExpr false arg
yield! walkBinds bs
for (InterfaceImpl(_, bs, _)) in is do yield! walkBinds bs
| SynExpr.While (spWhile, e1, e2, _) ->
yield! walkWhileSeqPt spWhile
yield! walkExpr false e1
yield! walkExpr true e2
| SynExpr.JoinIn (e1, _range, e2, _range2) ->
yield! walkExpr false e1
yield! walkExpr false e2
| SynExpr.For (spFor, _, e1, _, e2, e3, _) ->
yield! walkForSeqPt spFor
yield! walkExpr false e1
yield! walkExpr true e2
yield! walkExpr true e3
| SynExpr.ForEach (spFor, _, _, _, e1, e2, _) ->
yield! walkForSeqPt spFor
yield! walkExpr false e1
yield! walkExpr true e2
| SynExpr.MatchLambda (_isExnMatch, _argm, cl, spBind, _wholem) ->
yield! walkBindSeqPt spBind
for (Clause(_, whenExpr, e, _, _)) in cl do
yield! walkExprOpt false whenExpr
yield! walkExpr true e
| SynExpr.Lambda (_, _, _, e, _, _) ->
yield! walkExpr true e
| SynExpr.Match (spBind, e, cl, _) ->
yield! walkBindSeqPt spBind
yield! walkExpr false e
for (Clause(_, whenExpr, e, _, _)) in cl do
yield! walkExprOpt false whenExpr
yield! walkExpr true e
| SynExpr.LetOrUse (_, _, bs, e, _) ->
yield! walkBinds bs
yield! walkExpr true e
| SynExpr.TryWith (e, _, cl, _, _, spTry, spWith) ->
yield! walkTrySeqPt spTry
yield! walkWithSeqPt spWith
yield! walkExpr true e
yield! walkMatchClauses cl
| SynExpr.TryFinally (e1, e2, _, spTry, spFinally) ->
yield! walkExpr true e1
yield! walkExpr true e2
yield! walkTrySeqPt spTry
yield! walkFinallySeqPt spFinally
| SynExpr.SequentialOrImplicitYield (spSeq, e1, e2, _, _)
| SynExpr.Sequential (spSeq, _, e1, e2, _) ->
yield! walkExpr (match spSeq with DebugPointAtSequential.ExprOnly -> false | _ -> true) e1
yield! walkExpr (match spSeq with DebugPointAtSequential.StmtOnly -> false | _ -> true) e2
| SynExpr.IfThenElse (e1, e2, e3opt, spBind, _, _, _) ->
yield! walkBindSeqPt spBind
yield! walkExpr false e1
yield! walkExpr true e2
yield! walkExprOpt true e3opt
| SynExpr.DotIndexedGet (e1, es, _, _) ->
yield! walkExpr false e1
yield! walkExprs [ for e in es do yield! e.Exprs ]
| SynExpr.DotIndexedSet (e1, es, e2, _, _, _) ->
yield! walkExpr false e1
yield! walkExprs [ for e in es do yield! e.Exprs ]
yield! walkExpr false e2
| SynExpr.DotNamedIndexedPropertySet (e1, _, e2, e3, _) ->
yield! walkExpr false e1
yield! walkExpr false e2
yield! walkExpr false e3
| SynExpr.LetOrUseBang (spBind, _, _, _, e1, es, e2, _) ->
yield! walkBindSeqPt spBind
yield! walkExpr true e1
for (andBangSpBind,_,_,_,eAndBang,_) in es do
yield! walkBindSeqPt andBangSpBind
yield! walkExpr true eAndBang
yield! walkExpr true e2
| SynExpr.MatchBang (spBind, e, cl, _) ->
yield! walkBindSeqPt spBind
yield! walkExpr false e
for (Clause(_, whenExpr, e, _, _)) in cl do
yield! walkExprOpt false whenExpr
yield! walkExpr true e ]
// Process a class declaration or F# type declaration
let rec walkTycon (TypeDefn(ComponentInfo(_, _, _, _, _, _, _, _), repr, membDefns, m)) =
if not (isMatchRange m) then [] else
[ for memb in membDefns do yield! walkMember memb
match repr with
| SynTypeDefnRepr.ObjectModel(_, membDefns, _) ->
for memb in membDefns do yield! walkMember memb
| _ -> () ]
// Returns class-members for the right dropdown
and walkMember memb =
if not (rangeContainsPos memb.Range pos) then [] else
[ match memb with
| SynMemberDefn.LetBindings(binds, _, _, _) -> yield! walkBinds binds
| SynMemberDefn.AutoProperty(_attribs, _isStatic, _id, _tyOpt, _propKind, _, _xmlDoc, _access, synExpr, _, _) -> yield! walkExpr true synExpr
| SynMemberDefn.ImplicitCtor(_, _, _, _, _, m) -> yield! checkRange m
| SynMemberDefn.Member(bind, _) -> yield! walkBind bind
| SynMemberDefn.Interface(_, Some membs, _) -> for m in membs do yield! walkMember m
| SynMemberDefn.Inherit(_, _, m) ->
// can break on the "inherit" clause
yield! checkRange m
| SynMemberDefn.ImplicitInherit(_, arg, _, m) ->
// can break on the "inherit" clause
yield! checkRange m
yield! walkExpr true arg
| _ -> () ]
// Process declarations nested in a module that should be displayed in the left dropdown
// (such as type declarations, nested modules etc.)
let rec walkDecl decl =
[ match decl with
| SynModuleDecl.Let(_, binds, m) when isMatchRange m ->
yield! walkBinds binds
| SynModuleDecl.DoExpr(spExpr, expr, m) when isMatchRange m ->
yield! walkBindSeqPt spExpr
yield! walkExpr false expr
| SynModuleDecl.ModuleAbbrev _ -> ()
| SynModuleDecl.NestedModule(_, _isRec, decls, _, m) when isMatchRange m ->
for d in decls do yield! walkDecl d
| SynModuleDecl.Types(tydefs, m) when isMatchRange m ->
for d in tydefs do yield! walkTycon d
| SynModuleDecl.Exception(SynExceptionDefn(SynExceptionDefnRepr(_, _, _, _, _, _), membDefns, _), m)
when isMatchRange m ->
for m in membDefns do yield! walkMember m
| _ -> () ]
// Collect all the items in a module
let walkModule (SynModuleOrNamespace(_, _, _, decls, _, _, _, m)) =
if isMatchRange m then
List.collect walkDecl decls
else
[]
/// Get information for implementation file
let walkImplFile (modules: SynModuleOrNamespace list) = List.collect walkModule modules
match input with
| Some (ParsedInput.ImplFile (ParsedImplFileInput (modules = modules))) -> walkImplFile modules
| _ -> []
ErrorScope.Protect range0
(fun () ->
let locations = findBreakPoints()
if pos.Column = 0 then
// we have a breakpoint that was set with mouse at line start
match locations |> List.filter (fun m -> m.StartLine = m.EndLine && pos.Line = m.StartLine) with
| [] ->
match locations |> List.filter (fun m -> rangeContainsPos m pos) with
| [] ->
match locations |> List.filter (fun m -> rangeBeforePos m pos |> not) with
| [] -> Seq.tryHead locations
| locationsAfterPos -> Seq.tryHead locationsAfterPos
| coveringLocations -> Seq.tryLast coveringLocations
| locationsOnSameLine -> Seq.tryHead locationsOnSameLine
else
match locations |> List.filter (fun m -> rangeContainsPos m pos) with
| [] ->
match locations |> List.filter (fun m -> rangeBeforePos m pos |> not) with
| [] -> Seq.tryHead locations
| locationsAfterPos -> Seq.tryHead locationsAfterPos
| coveringLocations -> Seq.tryLast coveringLocations)
(fun msg ->
Trace.TraceInformation(sprintf "FCS: recovering from error in ValidateBreakpointLocationImpl: '%s'" msg)
None)
/// When these files appear or disappear the configuration for the current project is invalidated.
member scope.DependencyFiles = dependencyFiles
member scope.FileName =
match input with
| Some (ParsedInput.ImplFile (ParsedImplFileInput (fileName = modname)))
| Some (ParsedInput.SigFile (ParsedSigFileInput (fileName = modname))) -> modname
| _ -> ""
// Get items for the navigation drop down bar
member scope.GetNavigationItems() =
// This does not need to be run on the background thread
scope.GetNavigationItemsImpl()
member scope.ValidateBreakpointLocation pos =
// This does not need to be run on the background thread
scope.ValidateBreakpointLocationImpl pos
type ModuleKind = { IsAutoOpen: bool; HasModuleSuffix: bool }
[<RequireQualifiedAccess>]
type EntityKind =
| Attribute
| Type
| FunctionOrValue of isActivePattern: bool
| Module of ModuleKind
override x.ToString() = sprintf "%A" x
module UntypedParseImpl =
let emptyStringSet = HashSet<string>()
let GetRangeOfExprLeftOfDot(pos: pos, parseTreeOpt) =
match parseTreeOpt with
| None -> None
| Some parseTree ->
let CheckLongIdent(longIdent: LongIdent) =
// find the longest prefix before the "pos" dot
let mutable r = (List.head longIdent).idRange
let mutable couldBeBeforeFront = true
for i in longIdent do
if posGeq pos i.idRange.End then
r <- unionRanges r i.idRange
couldBeBeforeFront <- false
couldBeBeforeFront, r
AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with
member this.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) =
let expr = expr // fix debugger locals
match expr with
| SynExpr.LongIdent (_, LongIdentWithDots(longIdent, _), _altNameRefCell, _range) ->
let _, r = CheckLongIdent longIdent
Some r
| SynExpr.LongIdentSet (LongIdentWithDots(longIdent, _), synExpr, _range) ->
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
else
let _, r = CheckLongIdent longIdent
Some r
| SynExpr.DotGet (synExpr, _dotm, LongIdentWithDots(longIdent, _), _range) ->
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
else
let inFront, r = CheckLongIdent longIdent
if inFront then
Some (synExpr.Range)
else
// see comment below for SynExpr.DotSet
Some ((unionRanges synExpr.Range r))
| SynExpr.Set (synExpr, synExpr2, range) ->
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
elif AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr2.Range pos then
traverseSynExpr synExpr2
else
Some range
| SynExpr.DotSet (synExpr, LongIdentWithDots(longIdent, _), synExpr2, _range) ->
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
elif AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr2.Range pos then
traverseSynExpr synExpr2
else
let inFront, r = CheckLongIdent longIdent
if inFront then
Some (synExpr.Range)
else
// f(0).X.Y.Z
// ^
// - r has this value
// ---- synExpr.Range has this value
// ------ we want this value
Some ((unionRanges synExpr.Range r))
| SynExpr.DotNamedIndexedPropertySet (synExpr, LongIdentWithDots(longIdent, _), synExpr2, synExpr3, _range) ->
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
elif AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr2.Range pos then
traverseSynExpr synExpr2
elif AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr3.Range pos then
traverseSynExpr synExpr3
else
let inFront, r = CheckLongIdent longIdent
if inFront then
Some (synExpr.Range)
else
Some ((unionRanges synExpr.Range r))
| SynExpr.DiscardAfterMissingQualificationAfterDot (synExpr, _range) -> // get this for e.g. "bar()."
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
else
Some (synExpr.Range)
| SynExpr.FromParseError (synExpr, range) ->
if AstTraversal.rangeContainsPosLeftEdgeInclusive synExpr.Range pos then
traverseSynExpr synExpr
else
Some range
| SynExpr.App (ExprAtomicFlag.NonAtomic, true, (SynExpr.Ident ident), rhs, _)
when ident.idText = "op_ArrayLookup"
&& not(AstTraversal.rangeContainsPosLeftEdgeInclusive rhs.Range pos) ->
match defaultTraverse expr with
| None ->
// (expr).(expr) is an ML-deprecated array lookup, but we want intellisense on the dot
// also want it for e.g. [|arr|].(0)
Some (expr.Range)
| x -> x // we found the answer deeper somewhere in the lhs
| SynExpr.Const (SynConst.Double(_), range) -> Some range
| _ -> defaultTraverse expr
})
/// searches for the expression island suitable for the evaluation by the debugger
let TryFindExpressionIslandInPosition(pos: pos, parseTreeOpt) =
match parseTreeOpt with
| None -> None
| Some parseTree ->
let getLidParts (lid : LongIdent) =
lid
|> Seq.takeWhile (fun i -> posGeq pos i.idRange.Start)
|> Seq.map (fun i -> i.idText)
|> Seq.toList
// tries to locate simple expression island
// foundCandidate = false means that we are looking for the candidate expression
// foundCandidate = true - we found candidate (DotGet) and now drill down to the left part
let rec TryGetExpression foundCandidate expr =
match expr with
| SynExpr.Paren (e, _, _, _) when foundCandidate ->
TryGetExpression foundCandidate e
| SynExpr.LongIdent (_isOptional, LongIdentWithDots(lid, _), _altNameRefCell, _m) ->
getLidParts lid |> Some
| SynExpr.DotGet (leftPart, _, LongIdentWithDots(lid, _), _) when (rangeContainsPos (rangeOfLid lid) pos) || foundCandidate ->
// requested position is at the lid part of the DotGet
// process left part and append result to the result of processing lid
let leftPartResult = TryGetExpression true leftPart
match leftPartResult with
| Some leftPartResult ->
[
yield! leftPartResult
yield! getLidParts lid
] |> Some
| None -> None
| SynExpr.FromParseError (synExpr, _range) -> TryGetExpression foundCandidate synExpr
| _ -> None
let rec walker =
{ new AstTraversal.AstVisitorBase<_>() with
member this.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) =
if rangeContainsPos expr.Range pos then
match TryGetExpression false expr with
| (Some parts) -> parts |> String.concat "." |> Some
| _ -> defaultTraverse expr
else
None }
AstTraversal.Traverse(pos, parseTree, walker)
// Given a cursor position here:
// f(x) . ident
// ^
// walk the AST to find the position here:
// f(x) . ident
// ^
// On success, return Some (thatPos, boolTrueIfCursorIsAfterTheDotButBeforeTheIdentifier)
// If there's no dot, return None, so for example
// foo
// ^
// would return None
// TODO would be great to unify this with GetRangeOfExprLeftOfDot above, if possible, as they are similar
let TryFindExpressionASTLeftOfDotLeftOfCursor(pos, parseTreeOpt) =
match parseTreeOpt with
| None -> None
| Some parseTree ->
let dive x = AstTraversal.dive x
let pick x = AstTraversal.pick pos x
let walker =
{ new AstTraversal.AstVisitorBase<_>() with
member this.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) =
let pick = pick expr.Range
let traverseSynExpr, defaultTraverse, expr = traverseSynExpr, defaultTraverse, expr // for debugging: debugger does not get object expression params as local vars
if not(rangeContainsPos expr.Range pos) then
match expr with
| SynExpr.DiscardAfterMissingQualificationAfterDot (e, _m) ->
// This happens with e.g. "f(x) . $" when you bring up a completion list a few spaces after a dot. The cursor is not 'in the parse tree',
// but the dive algorithm will dive down into this node, and this is the one case where we do want to give a result despite the cursor
// not properly being in a node.
match traverseSynExpr e with
| None -> Some (e.Range.End, false)
| r -> r
| _ ->
// This happens for e.g. "System.Console.[]$", where the ".[]" token is thrown away by the parser and we dive into the System.Console longId
// even though the cursor/dot is not in there. In those cases we want to return None, because there is not really a dot completion before
// the cursor location.
None
else
let rec traverseLidOrElse (optExprIfLeftOfLongId : SynExpr option) (LongIdentWithDots(lid, dots) as lidwd) =
let resultIfLeftOfLongId =
match optExprIfLeftOfLongId with
| None -> None
| Some e -> Some (e.Range.End, posGeq lidwd.Range.Start pos)
match dots |> List.mapi (fun i x -> i, x) |> List.rev |> List.tryFind (fun (_, m) -> posGt pos m.Start) with
| None -> resultIfLeftOfLongId
| Some (n, _) -> Some ((List.item n lid).idRange.End, (List.length lid = n+1) // foo.$
|| (posGeq (List.item (n+1) lid).idRange.Start pos)) // foo.$bar
match expr with
| SynExpr.LongIdent (_isOptional, lidwd, _altNameRefCell, _m) ->
traverseLidOrElse None lidwd
| SynExpr.LongIdentSet (lidwd, exprRhs, _m) ->
[ dive lidwd lidwd.Range (traverseLidOrElse None)
dive exprRhs exprRhs.Range traverseSynExpr
] |> pick expr
| SynExpr.DotGet (exprLeft, dotm, lidwd, _m) ->
let afterDotBeforeLid = mkRange dotm.FileName dotm.End lidwd.Range.Start
[ dive exprLeft exprLeft.Range traverseSynExpr
dive exprLeft afterDotBeforeLid (fun e -> Some (e.Range.End, true))
dive lidwd lidwd.Range (traverseLidOrElse (Some exprLeft))
] |> pick expr
| SynExpr.DotSet (exprLeft, lidwd, exprRhs, _m) ->
[ dive exprLeft exprLeft.Range traverseSynExpr
dive lidwd lidwd.Range (traverseLidOrElse(Some exprLeft))
dive exprRhs exprRhs.Range traverseSynExpr
] |> pick expr
| SynExpr.Set (exprLeft, exprRhs, _m) ->
[ dive exprLeft exprLeft.Range traverseSynExpr
dive exprRhs exprRhs.Range traverseSynExpr
] |> pick expr
| SynExpr.NamedIndexedPropertySet (lidwd, exprIndexer, exprRhs, _m) ->
[ dive lidwd lidwd.Range (traverseLidOrElse None)
dive exprIndexer exprIndexer.Range traverseSynExpr
dive exprRhs exprRhs.Range traverseSynExpr
] |> pick expr
| SynExpr.DotNamedIndexedPropertySet (exprLeft, lidwd, exprIndexer, exprRhs, _m) ->
[ dive exprLeft exprLeft.Range traverseSynExpr
dive lidwd lidwd.Range (traverseLidOrElse(Some exprLeft))
dive exprIndexer exprIndexer.Range traverseSynExpr
dive exprRhs exprRhs.Range traverseSynExpr
] |> pick expr
| SynExpr.Const (SynConst.Double(_), m) ->
if posEq m.End pos then
// the cursor is at the dot
Some (m.End, false)
else
// the cursor is left of the dot
None
| SynExpr.DiscardAfterMissingQualificationAfterDot (e, m) ->
match traverseSynExpr e with
| None ->
if posEq m.End pos then
// the cursor is at the dot
Some (e.Range.End, false)
else
// the cursor is left of the dot
None
| r -> r
| SynExpr.App (ExprAtomicFlag.NonAtomic, true, (SynExpr.Ident ident), lhs, _m)
when ident.idText = "op_ArrayLookup"
&& not(AstTraversal.rangeContainsPosLeftEdgeInclusive lhs.Range pos) ->
match defaultTraverse expr with
| None ->
// (expr).(expr) is an ML-deprecated array lookup, but we want intellisense on the dot
// also want it for e.g. [|arr|].(0)
Some (lhs.Range.End, false)
| x -> x // we found the answer deeper somewhere in the lhs
| _ -> defaultTraverse expr }
AstTraversal.Traverse(pos, parseTree, walker)
let GetEntityKind (pos: pos, input: ParsedInput) : EntityKind option =
let (|ConstructorPats|) = function
| Pats ps -> ps
| NamePatPairs(xs, _) -> List.map snd xs
/// An recursive pattern that collect all sequential expressions to avoid StackOverflowException
let rec (|Sequentials|_|) = function
| SynExpr.Sequential (_, _, e, Sequentials es, _) -> Some (e :: es)
| SynExpr.Sequential (_, _, e1, e2, _) -> Some [e1; e2]
| _ -> None
let inline isPosInRange range = rangeContainsPos range pos
let inline ifPosInRange range f =
if isPosInRange range then f()
else None
let rec walkImplFileInput (ParsedImplFileInput (modules = moduleOrNamespaceList)) =
List.tryPick (walkSynModuleOrNamespace true) moduleOrNamespaceList
and walkSynModuleOrNamespace isTopLevel (SynModuleOrNamespace(_, _, _, decls, _, Attributes attrs, _, r)) =
List.tryPick walkAttribute attrs
|> Option.orElse (ifPosInRange r (fun _ -> List.tryPick (walkSynModuleDecl isTopLevel) decls))
and walkAttribute (attr: SynAttribute) =
if isPosInRange attr.Range then Some EntityKind.Attribute else None
|> Option.orElse (walkExprWithKind (Some EntityKind.Type) attr.ArgExpr)
and walkTypar (Typar (ident, _, _)) = ifPosInRange ident.idRange (fun _ -> Some EntityKind.Type)
and walkTyparDecl (SynTyparDecl.TyparDecl (Attributes attrs, typar)) =
List.tryPick walkAttribute attrs
|> Option.orElse (walkTypar typar)
and walkTypeConstraint = function
| SynTypeConstraint.WhereTyparDefaultsToType (t1, t2, _) -> walkTypar t1 |> Option.orElse (walkType t2)
| SynTypeConstraint.WhereTyparIsValueType(t, _) -> walkTypar t
| SynTypeConstraint.WhereTyparIsReferenceType(t, _) -> walkTypar t
| SynTypeConstraint.WhereTyparIsUnmanaged(t, _) -> walkTypar t
| SynTypeConstraint.WhereTyparSupportsNull (t, _) -> walkTypar t
| SynTypeConstraint.WhereTyparIsComparable(t, _) -> walkTypar t
| SynTypeConstraint.WhereTyparIsEquatable(t, _) -> walkTypar t
| SynTypeConstraint.WhereTyparSubtypeOfType(t, ty, _) -> walkTypar t |> Option.orElse (walkType ty)
| SynTypeConstraint.WhereTyparSupportsMember(ts, sign, _) ->
List.tryPick walkType ts |> Option.orElse (walkMemberSig sign)
| SynTypeConstraint.WhereTyparIsEnum(t, ts, _) -> walkTypar t |> Option.orElse (List.tryPick walkType ts)
| SynTypeConstraint.WhereTyparIsDelegate(t, ts, _) -> walkTypar t |> Option.orElse (List.tryPick walkType ts)
and walkPatWithKind (kind: EntityKind option) = function
| SynPat.Ands (pats, _) -> List.tryPick walkPat pats
| SynPat.Named(SynPat.Wild nameRange as pat, _, _, _, _) ->
if isPosInRange nameRange then None
else walkPat pat
| SynPat.Typed(pat, t, _) -> walkPat pat |> Option.orElse (walkType t)
| SynPat.Attrib(pat, Attributes attrs, _) -> walkPat pat |> Option.orElse (List.tryPick walkAttribute attrs)
| SynPat.Or(pat1, pat2, _) -> List.tryPick walkPat [pat1; pat2]
| SynPat.LongIdent(_, _, typars, ConstructorPats pats, _, r) ->
ifPosInRange r (fun _ -> kind)
|> Option.orElse (
typars
|> Option.bind (fun (SynValTyparDecls (typars, _, constraints)) ->
List.tryPick walkTyparDecl typars
|> Option.orElse (List.tryPick walkTypeConstraint constraints)))
|> Option.orElse (List.tryPick walkPat pats)
| SynPat.Tuple(_, pats, _) -> List.tryPick walkPat pats
| SynPat.Paren(pat, _) -> walkPat pat
| SynPat.ArrayOrList(_, pats, _) -> List.tryPick walkPat pats
| SynPat.IsInst(t, _) -> walkType t
| SynPat.QuoteExpr(e, _) -> walkExpr e
| _ -> None
and walkPat = walkPatWithKind None
and walkBinding (SynBinding.Binding(_, _, _, _, Attributes attrs, _, _, pat, returnInfo, e, _, _)) =
List.tryPick walkAttribute attrs
|> Option.orElse (walkPat pat)
|> Option.orElse (walkExpr e)
|> Option.orElse (
match returnInfo with
| Some (SynBindingReturnInfo (t, _, _)) -> walkType t
| None -> None)
and walkInterfaceImpl (InterfaceImpl(_, bindings, _)) =
List.tryPick walkBinding bindings
and walkIndexerArg = function
| SynIndexerArg.One (e, _, _) -> walkExpr e
| SynIndexerArg.Two(e1, _, e2, _, _, _) -> List.tryPick walkExpr [e1; e2]
and walkType = function
| SynType.LongIdent ident ->
// we protect it with try..with because System.Exception : rangeOfLidwd may raise
// at FSharp.Compiler.SyntaxTree.LongIdentWithDots.get_Range() in D:\j\workspace\release_ci_pa---3f142ccc\src\fsharp\ast.fs: line 156
try ifPosInRange ident.Range (fun _ -> Some EntityKind.Type) with _ -> None
| SynType.App(ty, _, types, _, _, _, _) ->
walkType ty |> Option.orElse (List.tryPick walkType types)
| SynType.LongIdentApp(_, _, _, types, _, _, _) -> List.tryPick walkType types
| SynType.Tuple(_, ts, _) -> ts |> List.tryPick (fun (_, t) -> walkType t)
| SynType.Array(_, t, _) -> walkType t
| SynType.Fun(t1, t2, _) -> walkType t1 |> Option.orElse (walkType t2)
| SynType.WithGlobalConstraints(t, _, _) -> walkType t
| SynType.HashConstraint(t, _) -> walkType t
| SynType.MeasureDivide(t1, t2, _) -> walkType t1 |> Option.orElse (walkType t2)
| SynType.MeasurePower(t, _, _) -> walkType t
| SynType.Paren(t, _) -> walkType t
| _ -> None
and walkClause (Clause(pat, e1, e2, _, _)) =
walkPatWithKind (Some EntityKind.Type) pat
|> Option.orElse (walkExpr e2)
|> Option.orElse (Option.bind walkExpr e1)
and walkExprWithKind (parentKind: EntityKind option) = function
| SynExpr.LongIdent (_, LongIdentWithDots(_, dotRanges), _, r) ->
match dotRanges with
| [] when isPosInRange r -> parentKind |> Option.orElse (Some (EntityKind.FunctionOrValue false))
| firstDotRange :: _ ->
let firstPartRange =
mkRange "" r.Start (mkPos firstDotRange.StartLine (firstDotRange.StartColumn - 1))
if isPosInRange firstPartRange then
parentKind |> Option.orElse (Some (EntityKind.FunctionOrValue false))
else None
| _ -> None
| SynExpr.Paren (e, _, _, _) -> walkExprWithKind parentKind e
| SynExpr.Quote (_, _, e, _, _) -> walkExprWithKind parentKind e
| SynExpr.Typed (e, _, _) -> walkExprWithKind parentKind e
| SynExpr.Tuple (_, es, _, _) -> List.tryPick (walkExprWithKind parentKind) es
| SynExpr.ArrayOrList (_, es, _) -> List.tryPick (walkExprWithKind parentKind) es
| SynExpr.Record (_, _, fields, r) ->
ifPosInRange r (fun _ ->
fields |> List.tryPick (fun (_, e, _) -> e |> Option.bind (walkExprWithKind parentKind)))
| SynExpr.New (_, t, e, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t)
| SynExpr.ObjExpr (ty, _, bindings, ifaces, _, _) ->
walkType ty
|> Option.orElse (List.tryPick walkBinding bindings)
|> Option.orElse (List.tryPick walkInterfaceImpl ifaces)
| SynExpr.While (_, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.For (_, _, e1, _, e2, e3, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2; e3]
| SynExpr.ForEach (_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.ArrayOrListOfSeqExpr (_, e, _) -> walkExprWithKind parentKind e
| SynExpr.CompExpr (_, _, e, _) -> walkExprWithKind parentKind e
| SynExpr.Lambda (_, _, _, e, _, _) -> walkExprWithKind parentKind e
| SynExpr.MatchLambda (_, _, synMatchClauseList, _, _) ->
List.tryPick walkClause synMatchClauseList
| SynExpr.Match (_, e, synMatchClauseList, _) ->
walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList)
| SynExpr.Do (e, _) -> walkExprWithKind parentKind e
| SynExpr.Assert (e, _) -> walkExprWithKind parentKind e
| SynExpr.App (_, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.TypeApp (e, _, tys, _, _, _, _) ->
walkExprWithKind (Some EntityKind.Type) e |> Option.orElse (List.tryPick walkType tys)
| SynExpr.LetOrUse (_, _, bindings, e, _) -> List.tryPick walkBinding bindings |> Option.orElse (walkExprWithKind parentKind e)
| SynExpr.TryWith (e, _, clauses, _, _, _, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause clauses)
| SynExpr.TryFinally (e1, e2, _, _, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.Lazy (e, _) -> walkExprWithKind parentKind e
| Sequentials es -> List.tryPick (walkExprWithKind parentKind) es
| SynExpr.IfThenElse (e1, e2, e3, _, _, _, _) ->
List.tryPick (walkExprWithKind parentKind) [e1; e2] |> Option.orElse (match e3 with None -> None | Some e -> walkExprWithKind parentKind e)
| SynExpr.Ident ident -> ifPosInRange ident.idRange (fun _ -> Some (EntityKind.FunctionOrValue false))
| SynExpr.LongIdentSet (_, e, _) -> walkExprWithKind parentKind e
| SynExpr.DotGet (e, _, _, _) -> walkExprWithKind parentKind e
| SynExpr.DotSet (e, _, _, _) -> walkExprWithKind parentKind e
| SynExpr.Set (e, _, _) -> walkExprWithKind parentKind e
| SynExpr.DotIndexedGet (e, args, _, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkIndexerArg args)
| SynExpr.DotIndexedSet (e, args, _, _, _, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkIndexerArg args)
| SynExpr.NamedIndexedPropertySet (_, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.DotNamedIndexedPropertySet (e1, _, e2, e3, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2; e3]
| SynExpr.TypeTest (e, t, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t)
| SynExpr.Upcast (e, t, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t)
| SynExpr.Downcast (e, t, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t)
| SynExpr.InferredUpcast (e, _) -> walkExprWithKind parentKind e
| SynExpr.InferredDowncast (e, _) -> walkExprWithKind parentKind e
| SynExpr.AddressOf (_, e, _, _) -> walkExprWithKind parentKind e
| SynExpr.JoinIn (e1, _, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.YieldOrReturn (_, e, _) -> walkExprWithKind parentKind e
| SynExpr.YieldOrReturnFrom (_, e, _) -> walkExprWithKind parentKind e
| SynExpr.Match (_, e, synMatchClauseList, _)
| SynExpr.MatchBang (_, e, synMatchClauseList, _) ->
walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList)
| SynExpr.LetOrUseBang(_, _, _, _, e1, es, e2, _) ->
[
yield e1
for (_,_,_,_,eAndBang,_) in es do
yield eAndBang
yield e2
]
|> List.tryPick (walkExprWithKind parentKind)
| SynExpr.DoBang (e, _) -> walkExprWithKind parentKind e
| SynExpr.TraitCall (ts, sign, e, _) ->
List.tryPick walkTypar ts
|> Option.orElse (walkMemberSig sign)
|> Option.orElse (walkExprWithKind parentKind e)
| _ -> None
and walkExpr = walkExprWithKind None
and walkSimplePat = function
| SynSimplePat.Attrib (pat, Attributes attrs, _) ->
walkSimplePat pat |> Option.orElse (List.tryPick walkAttribute attrs)
| SynSimplePat.Typed(pat, t, _) -> walkSimplePat pat |> Option.orElse (walkType t)
| _ -> None
and walkField (SynField.Field(Attributes attrs, _, _, t, _, _, _, _)) =
List.tryPick walkAttribute attrs |> Option.orElse (walkType t)
and walkValSig (SynValSig.ValSpfn(Attributes attrs, _, _, t, _, _, _, _, _, _, _)) =
List.tryPick walkAttribute attrs |> Option.orElse (walkType t)
and walkMemberSig = function
| SynMemberSig.Inherit (t, _) -> walkType t
| SynMemberSig.Member(vs, _, _) -> walkValSig vs
| SynMemberSig.Interface(t, _) -> walkType t
| SynMemberSig.ValField(f, _) -> walkField f
| SynMemberSig.NestedType(SynTypeDefnSig.TypeDefnSig (info, repr, memberSigs, _), _) ->
walkComponentInfo false info
|> Option.orElse (walkTypeDefnSigRepr repr)
|> Option.orElse (List.tryPick walkMemberSig memberSigs)
and walkMember = function
| SynMemberDefn.AbstractSlot (valSig, _, _) -> walkValSig valSig
| SynMemberDefn.Member(binding, _) -> walkBinding binding
| SynMemberDefn.ImplicitCtor(_, Attributes attrs, SynSimplePats.SimplePats(simplePats, _), _, _, _) ->
List.tryPick walkAttribute attrs |> Option.orElse (List.tryPick walkSimplePat simplePats)
| SynMemberDefn.ImplicitInherit(t, e, _, _) -> walkType t |> Option.orElse (walkExpr e)
| SynMemberDefn.LetBindings(bindings, _, _, _) -> List.tryPick walkBinding bindings
| SynMemberDefn.Interface(t, members, _) ->
walkType t |> Option.orElse (members |> Option.bind (List.tryPick walkMember))
| SynMemberDefn.Inherit(t, _, _) -> walkType t
| SynMemberDefn.ValField(field, _) -> walkField field
| SynMemberDefn.NestedType(tdef, _, _) -> walkTypeDefn tdef
| SynMemberDefn.AutoProperty(Attributes attrs, _, _, t, _, _, _, _, e, _, _) ->
List.tryPick walkAttribute attrs
|> Option.orElse (Option.bind walkType t)
|> Option.orElse (walkExpr e)
| _ -> None
and walkEnumCase (EnumCase(Attributes attrs, _, _, _, _)) = List.tryPick walkAttribute attrs
and walkUnionCaseType = function
| SynUnionCaseType.UnionCaseFields fields -> List.tryPick walkField fields
| SynUnionCaseType.UnionCaseFullType(t, _) -> walkType t
and walkUnionCase (UnionCase(Attributes attrs, _, t, _, _, _)) =
List.tryPick walkAttribute attrs |> Option.orElse (walkUnionCaseType t)
and walkTypeDefnSimple = function
| SynTypeDefnSimpleRepr.Enum (cases, _) -> List.tryPick walkEnumCase cases
| SynTypeDefnSimpleRepr.Union(_, cases, _) -> List.tryPick walkUnionCase cases
| SynTypeDefnSimpleRepr.Record(_, fields, _) -> List.tryPick walkField fields
| SynTypeDefnSimpleRepr.TypeAbbrev(_, t, _) -> walkType t
| _ -> None
and walkComponentInfo isModule (ComponentInfo(Attributes attrs, typars, constraints, _, _, _, _, r)) =
if isModule then None else ifPosInRange r (fun _ -> Some EntityKind.Type)
|> Option.orElse (
List.tryPick walkAttribute attrs
|> Option.orElse (List.tryPick walkTyparDecl typars)
|> Option.orElse (List.tryPick walkTypeConstraint constraints))
and walkTypeDefnRepr = function
| SynTypeDefnRepr.ObjectModel (_, defns, _) -> List.tryPick walkMember defns
| SynTypeDefnRepr.Simple(defn, _) -> walkTypeDefnSimple defn
| SynTypeDefnRepr.Exception(_) -> None
and walkTypeDefnSigRepr = function
| SynTypeDefnSigRepr.ObjectModel (_, defns, _) -> List.tryPick walkMemberSig defns
| SynTypeDefnSigRepr.Simple(defn, _) -> walkTypeDefnSimple defn
| SynTypeDefnSigRepr.Exception(_) -> None