Skip to content

Commit b737ebc

Browse files
committed
FCS: capture additional types during analysis
1 parent e82d6f6 commit b737ebc

6 files changed

Lines changed: 88 additions & 19 deletions

File tree

src/Compiler/Checking/CheckPatterns.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ and TcSimplePat optionalArgsOK checkConstraints (cenv: cenv) ty env patEnv p (at
103103

104104
| SynSimplePat.Typed (p, cty, m) ->
105105
let ctyR, tpenv = TcTypeAndRecover cenv NewTyparsOK checkConstraints ItemOccurrence.UseInType WarnOnIWSAM.Yes env tpenv cty
106+
CallExprHasTypeSinkSynthetic cenv.tcSink (p.Range, env.NameEnv, ctyR, env.AccessRights)
106107

107108
match p with
108109
// Optional arguments on members
@@ -293,6 +294,7 @@ and TcPat warnOnUpper (cenv: cenv) env valReprInfo vFlags (patEnv: TcPatLinearEn
293294
TcConstPat warnOnUpper cenv env vFlags patEnv ty synConst m
294295

295296
| SynPat.Wild m ->
297+
CallExprHasTypeSinkSynthetic cenv.tcSink (m, env.NameEnv, ty, env.AccessRights)
296298
(fun _ -> TPat_wild m), patEnv
297299

298300
| SynPat.IsInst (synTargetTy, m)
@@ -675,6 +677,7 @@ and TcPatLongIdentUnionCaseOrExnCase warnOnUpper cenv env ad vFlags patEnv ty (m
675677

676678
// Report information about the case occurrence to IDE
677679
CallNameResolutionSink cenv.tcSink (mLongId, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Pattern, env.eAccessRights)
680+
CallExprHasTypeSinkSynthetic cenv.tcSink (m, env.NameEnv, ty, env.AccessRights)
678681

679682
let mkf, argTys, argNames = ApplyUnionCaseOrExn m cenv env ty item
680683
let numArgTys = argTys.Length

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,6 +1822,7 @@ let MakeAndPublishSimpleValsForMergedScope (cenv: cenv) env m (names: NameMap<_>
18221822
notifyNameResolution (pos, item, itemGroup, itemTyparInst, occurrence, nenv, ad, m, replacing)
18231823

18241824
member _.NotifyExprHasType(_, _, _, _) = assert false // no expr typings in MakeAndPublishSimpleVals
1825+
member _.NotifyExprHasTypeSynthetic(_, _, _, _) = assert false // no expr typings in MakeAndPublishSimpleVals
18251826

18261827
member _.NotifyFormatSpecifierLocation(_, _) = ()
18271828

@@ -5844,13 +5845,13 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE
58445845
TcNonControlFlowExpr env <| fun env ->
58455846
CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy.Commit, env.AccessRights)
58465847
TcConstExpr cenv overallTy env m tpenv synConst
5848+
58475849
| SynExpr.DotLambda (synExpr, m, trivia) ->
58485850
match env.NameEnv.eUnqualifiedItems |> Map.tryFind "_arg1" with
58495851
// Compiler-generated _arg items can have more forms, the real underscore will be 1-character wide
58505852
| Some (Item.Value(valRef)) when valRef.Range.StartColumn+1 = valRef.Range.EndColumn ->
58515853
warning(Error(FSComp.SR.tcAmbiguousDiscardDotLambda(), trivia.UnderscoreRange))
5852-
| Some _ -> ()
5853-
| None -> ()
5854+
| _ -> ()
58545855

58555856
let unaryArg = mkSynId trivia.UnderscoreRange (cenv.synArgNameGenerator.New())
58565857
let svar = mkSynCompGenSimplePatVar unaryArg
@@ -6131,6 +6132,7 @@ and TcExprMatchLambda (cenv: cenv) overallTy env tpenv (isExnMatch, mFunction, c
61316132
let domainTy, resultTy = UnifyFunctionType None cenv env.DisplayEnv m overallTy.Commit
61326133
let idv1, idve1 = mkCompGenLocal mFunction (cenv.synArgNameGenerator.New()) domainTy
61336134
CallExprHasTypeSink cenv.tcSink (mFunction.StartRange, env.NameEnv, domainTy, env.AccessRights)
6135+
CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy.Commit, env.AccessRights)
61346136
let envinner = ExitFamilyRegion env
61356137
let envinner = { envinner with eIsControlFlow = true }
61366138
let idv2, matchExpr, tpenv = TcAndPatternCompileMatchClauses m mFunction (if isExnMatch then Throw else ThrowIncompleteMatchException) cenv None domainTy (MustConvertTo (false, resultTy)) envinner tpenv clauses
@@ -6534,6 +6536,7 @@ and TcIteratedLambdas (cenv: cenv) isFirst (env: TcEnv) overallTy takenNames tpe
65346536
| [] -> envinner
65356537

65366538
let bodyExpr, tpenv = TcIteratedLambdas cenv false envinner (MustConvertTo (false, resultTy)) takenNames tpenv bodyExpr
6539+
CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy.Commit, env.AccessRights)
65376540

65386541
// See bug 5758: Non-monotonicity in inference: need to ensure that parameters are never inferred to have byref type, instead it is always declared
65396542
byrefs |> Map.iter (fun _ (orig, v) ->
@@ -7779,6 +7782,7 @@ and TcAssertExpr cenv overallTy env (m: range) tpenv x =
77797782
TcExpr cenv overallTy env tpenv callDiagnosticsExpr
77807783

77817784
and TcRecdExpr cenv overallTy env tpenv (inherits, withExprOpt, synRecdFields, mWholeExpr) =
7785+
CallExprHasTypeSink cenv.tcSink (mWholeExpr, env.NameEnv, overallTy, env.eAccessRights)
77827786
let g = cenv.g
77837787

77847788
let requiresCtor = (GetCtorShapeCounter env = 1) // Get special expression forms for constructors
@@ -8404,6 +8408,8 @@ and TcDelayed cenv (overallTy: OverallTy) env tpenv mExpr expr exprTy (atomicFla
84048408
// We can now record for posterity the type of this expression and the location of the expression.
84058409
if (atomicFlag = ExprAtomicFlag.Atomic) then
84068410
CallExprHasTypeSink cenv.tcSink (mExpr, env.NameEnv, exprTy, env.eAccessRights)
8411+
else
8412+
CallExprHasTypeSinkSynthetic cenv.tcSink (mExpr, env.NameEnv, exprTy, env.eAccessRights)
84078413

84088414
match delayed with
84098415
| []

src/Compiler/Checking/NameResolution.fs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,8 @@ type ITypecheckResultsSink =
17871787

17881788
abstract NotifyExprHasType: TType * NameResolutionEnv * AccessorDomain * range -> unit
17891789

1790+
abstract NotifyExprHasTypeSynthetic: TType * NameResolutionEnv * AccessorDomain * range -> unit
1791+
17901792
abstract NotifyNameResolution: pos * item: Item * TyparInstantiation * ItemOccurrence * NameResolutionEnv * AccessorDomain * range * replace: bool -> unit
17911793

17921794
abstract NotifyMethodGroupNameResolution : pos * item: Item * itemMethodGroup: Item * TyparInstantiation * ItemOccurrence * NameResolutionEnv * AccessorDomain * range * replace: bool -> unit
@@ -2188,6 +2190,10 @@ type TcResultsSinkImpl(tcGlobals, ?sourceText: ISourceText) =
21882190
if allowedRange m then
21892191
capturedExprTypings.Add((ty, nenv, ad, m))
21902192

2193+
member sink.NotifyExprHasTypeSynthetic(ty, nenv, ad, m) =
2194+
if allowedRange m then
2195+
capturedExprTypings.Add((ty, nenv, ad, m.MakeSynthetic()))
2196+
21912197
member sink.NotifyNameResolution(endPos, item, tpinst, occurrenceType, nenv, ad, m, replace) =
21922198
if allowedRange m then
21932199
if replace then
@@ -2305,6 +2311,11 @@ let CallExprHasTypeSink (sink: TcResultsSink) (m: range, nenv, ty, ad) =
23052311
| None -> ()
23062312
| Some sink -> sink.NotifyExprHasType(ty, nenv, ad, m)
23072313

2314+
let CallExprHasTypeSinkSynthetic (sink: TcResultsSink) (m: range, nenv, ty, ad) =
2315+
match sink.CurrentSink with
2316+
| None -> ()
2317+
| Some sink -> sink.NotifyExprHasTypeSynthetic(ty, nenv, ad, m)
2318+
23082319
let CallOpenDeclarationSink (sink: TcResultsSink) (openDeclaration: OpenDeclaration) =
23092320
match sink.CurrentSink with
23102321
| None -> ()

src/Compiler/Checking/NameResolution.fsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ type ITypecheckResultsSink =
482482
/// Record that an expression has a specific type at the given range.
483483
abstract NotifyExprHasType: TType * NameResolutionEnv * AccessorDomain * range -> unit
484484

485+
abstract NotifyExprHasTypeSynthetic: TType * NameResolutionEnv * AccessorDomain * range -> unit
486+
485487
/// Record that a name resolution occurred at a specific location in the source
486488
abstract NotifyNameResolution:
487489
pos * Item * TyparInstantiation * ItemOccurrence * NameResolutionEnv * AccessorDomain * range * bool -> unit
@@ -635,6 +637,9 @@ val internal RegisterUnionCaseTesterForProperty:
635637
/// Report a specific name resolution at a source range
636638
val internal CallExprHasTypeSink: TcResultsSink -> range * NameResolutionEnv * TType * AccessorDomain -> unit
637639

640+
/// Report a captured type at a range, but don't use it in features like code completion, only in TryGetCapturedType
641+
val internal CallExprHasTypeSinkSynthetic: TcResultsSink -> range * NameResolutionEnv * TType * AccessorDomain -> unit
642+
638643
/// Report an open declaration
639644
val internal CallOpenDeclarationSink: TcResultsSink -> OpenDeclaration -> unit
640645

src/Compiler/Service/FSharpCheckerResults.fs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,8 @@ type internal TypeCheckInfo
729729
let quals =
730730
sResolutions.CapturedExpressionTypings
731731
|> Seq.filter (fun (ty, nenv, _, m) ->
732+
not m.IsSynthetic &&
733+
732734
// We only want expression types that end at the particular position in the file we are looking at.
733735
posEq m.End endOfExprPos
734736
&&
@@ -2098,9 +2100,9 @@ type internal TypeCheckInfo
20982100
member scope.IsRelativeNameResolvableFromSymbol(cursorPos: pos, plid: string list, symbol: FSharpSymbol) : bool =
20992101
scope.IsRelativeNameResolvable(cursorPos, plid, symbol.Item)
21002102

2101-
member scope.TryGetCapturedType(range) =
2103+
member scope.TryGetCapturedType(range: range) =
21022104
sResolutions.CapturedExpressionTypings
2103-
|> Seq.tryFindBack (fun (_, _, _, m) -> equals m range)
2105+
|> Seq.tryFindBack (fun (_, _, _, m) -> equals (m.MakeSynthetic()) (range.MakeSynthetic()))
21042106
|> Option.map (fun (ty, _, _, _) -> FSharpType(cenv, ty))
21052107

21062108
member scope.TryGetCapturedDisplayContext(range) =

tests/FSharp.Compiler.Service.Tests/CapturedTypes.fs

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,64 @@ let assertCapturedType expectedTypeString markedSource =
1515
let capturedType = tryGetCapturedType markedSource
1616
capturedType.Value.Format displayContext |> shouldEqual expectedTypeString
1717

18-
[<Fact>]
19-
let ``Expr - If 01`` () =
20-
assertCapturedType "int * int" "{selstart}if true then 1, 2 else 1, true{selend}"
18+
module Expr =
19+
[<Fact>]
20+
let ``Function 01`` () =
21+
assertCapturedType "string -> int" "[\"\"] |> List.map ({selstart}function s -> s.Length{selend})"
2122

22-
[<Fact>]
23-
let ``Expr - Literal 01`` () =
24-
assertCapturedType "int" "{selstart}1{selend}"
23+
[<Fact>]
24+
let ``Function 02`` () =
25+
assertCapturedType "string" "[\"\"] |> List.map ({selstart}{selend}function s -> s.Length)"
2526

26-
[<Fact>]
27-
let ``Expr - Literal 02`` () =
28-
assertCapturedType "string" "{selstart}\"\"{selend}"
27+
[<Fact>]
28+
let ``Function 03`` () =
29+
assertCapturedType "string" "[\"\"] |> List.map ({selstart}{selend}function)"
2930

30-
[<Fact>]
31-
let ``Expr - Tuple 01`` () =
32-
assertCapturedType "int * int" "{selstart}1, 2{selend}"
31+
[<Fact(Skip = "Implement parser recovery")>]
32+
let ``Function 04`` () =
33+
assertCapturedType "string" "[\"\"] |> List.map {selstart}{selend}function"
3334

34-
[<Fact>]
35-
let ``Expr - Tuple 02`` () =
36-
assertCapturedType "int * int" "if true then {selstart}1, 2{selend} else 1, true"
35+
[<Fact>]
36+
let ``If 01`` () =
37+
assertCapturedType "int * int" "{selstart}if true then 1, 2 else 1, true{selend}"
38+
39+
[<Fact>]
40+
let ``Lambda 01`` () =
41+
assertCapturedType "string -> int" "[\"\"] |> List.map ({selstart}fun s -> s.Length{selend})"
42+
43+
[<Fact>]
44+
let ``Literal 01`` () =
45+
assertCapturedType "int" "{selstart}1{selend}"
46+
47+
[<Fact>]
48+
let ``Literal 02`` () =
49+
assertCapturedType "string" "{selstart}\"\"{selend}"
50+
51+
[<Fact>]
52+
let ``Paren 01`` () =
53+
assertCapturedType "string -> int" "[\"\"] |> List.map {selstart}(fun s -> s.Length){selend}"
54+
55+
[<Fact>]
56+
let ``Short lambda 01`` () =
57+
assertCapturedType "string" "[\"\"] |> List.map {selstart}_{selend}.Length"
58+
59+
[<Fact>]
60+
let ``Short lambda 02`` () =
61+
assertCapturedType "string -> int" "[\"\"] |> List.map {selstart}_.Length{selend}"
62+
63+
[<Fact>]
64+
let ``Tuple 01`` () =
65+
assertCapturedType "int * int" "{selstart}1, 2{selend}"
66+
67+
[<Fact>]
68+
let ``Tuple 02`` () =
69+
assertCapturedType "int * int" "if true then {selstart}1, 2{selend} else 1, true"
70+
71+
module Pattern =
72+
[<Fact>]
73+
let ``Literal 01`` () =
74+
assertCapturedType "int" "let {selstart}i{selend} = 1"
75+
76+
[<Fact>]
77+
let ``Wild 01`` () =
78+
assertCapturedType "int" "let {selstart}_{selend} = 1"

0 commit comments

Comments
 (0)