From 6e512df6258d788cc67438632fe3c1ae4f835748 Mon Sep 17 00:00:00 2001 From: Petr Date: Wed, 9 Nov 2022 17:33:29 +0100 Subject: [PATCH 1/6] Fix #14269 --- src/Compiler/Service/ServiceParseTreeWalk.fs | 86 +++++++++---------- src/Compiler/Service/ServiceParseTreeWalk.fsi | 2 +- src/Compiler/Service/ServiceParsedInputOps.fs | 12 +-- .../tests/UnitTests/InlineTypeHintTests.fs | 32 +++++++ 4 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 2c61c20036c..e40911baca0 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -199,7 +199,7 @@ module SyntaxTraversal = let dive node range project = range, (fun () -> project node) - let pick pos (outerRange: range) (debugObj: obj) (diveResults: (range * _) list) = + let pick pos (outerRange: range) (diveResults: (range * _) list) = match diveResults with | [] -> None | _ -> @@ -259,13 +259,7 @@ module SyntaxTraversal = snd (e) () | [ x ] -> x () | _ -> -#if DEBUG - assert false - failwithf "multiple disjoint AST node ranges claimed to contain (%A) from %+A" pos debugObj -#else - ignore debugObj None -#endif /// traverse an implementation file walking all the way down to SynExpr or TypeAbbrev at a particular location /// @@ -283,19 +277,19 @@ module SyntaxTraversal = | SynModuleDecl.NestedModule (decls = synModuleDecls) -> synModuleDecls |> List.map (fun x -> dive x x.Range (traverseSynModuleDecl path)) - |> pick decl + |> pick | SynModuleDecl.Let (isRecursive, synBindingList, range) -> match visitor.VisitLetOrUse(path, isRecursive, traverseSynBinding path, synBindingList, range) with | Some x -> Some x | None -> synBindingList |> List.map (fun x -> dive x x.RangeOfBindingWithRhs (traverseSynBinding path)) - |> pick decl + |> pick | SynModuleDecl.Expr (synExpr, _range) -> traverseSynExpr path synExpr | SynModuleDecl.Types (synTypeDefnList, _range) -> synTypeDefnList |> List.map (fun x -> dive x x.Range (traverseSynTypeDefn path)) - |> pick decl + |> pick | SynModuleDecl.Exception (_synExceptionDefn, _range) -> None | SynModuleDecl.Open (_target, _range) -> None | SynModuleDecl.Attributes (_synAttributes, _range) -> None @@ -312,7 +306,7 @@ module SyntaxTraversal = synModuleDecls |> List.map (fun x -> dive x x.Range (traverseSynModuleDecl path)) - |> pick range mors + |> pick range and traverseSynExpr origPath (expr: SynExpr) = let pick = pick expr.Range @@ -331,7 +325,7 @@ module SyntaxTraversal = [ //dive synExpr synExpr.Range traverseSynExpr // TODO, what is this? dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Const (_synConst, _range) -> None @@ -342,7 +336,7 @@ module SyntaxTraversal = | SynInterpolatedStringPart.String _ -> () | SynInterpolatedStringPart.FillExpr (fillExpr, _) -> yield dive fillExpr fillExpr.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Typed (synExpr, synType, _range) -> match traverseSynExpr synExpr with @@ -351,7 +345,7 @@ module SyntaxTraversal = | SynExpr.Tuple (_, synExprList, _, _range) | SynExpr.ArrayOrList (_, synExprList, _range) -> - synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick expr + synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick | SynExpr.AnonRecd (_isStruct, copyOpt, synExprList, _range) -> [ @@ -371,7 +365,7 @@ module SyntaxTraversal = for _, _, x in synExprList do yield dive x x.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Record (inheritOpt, copyOpt, fields, _range) -> [ @@ -476,7 +470,7 @@ module SyntaxTraversal = | _ -> () ] - |> pick expr + |> pick | SynExpr.New (_, _synType, synExpr, _range) -> traverseSynExpr synExpr | SynExpr.ObjExpr (objType = ty; argOptions = baseCallOpt; bindings = binds; members = ms; extraImpls = ifaces) -> @@ -503,14 +497,14 @@ module SyntaxTraversal = for b in binds do yield dive b b.RangeOfBindingWithRhs (traverseSynBinding path) ] - |> pick expr + |> pick | SynExpr.While (_spWhile, synExpr, synExpr2, _range) -> [ dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.For (identBody = synExpr; toBody = synExpr2; doBody = synExpr3) -> [ @@ -518,7 +512,7 @@ module SyntaxTraversal = dive synExpr2 synExpr2.Range traverseSynExpr dive synExpr3 synExpr3.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.ForEach (_spFor, _spIn, _seqExprOnly, _isFromSource, synPat, synExpr, synExpr2, _range) -> [ @@ -526,7 +520,7 @@ module SyntaxTraversal = dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.ArrayOrListComputed (_, synExpr, _range) -> traverseSynExpr synExpr @@ -559,7 +553,7 @@ module SyntaxTraversal = | SynExpr.MatchLambda (_isExnMatch, _argm, synMatchClauseList, _spBind, _wholem) -> synMatchClauseList |> List.map (fun x -> dive x x.Range (traverseSynMatchClause path)) - |> pick expr + |> pick | SynExpr.Match (expr = synExpr; clauses = synMatchClauseList) -> [ @@ -568,7 +562,7 @@ module SyntaxTraversal = synMatchClauseList |> List.map (fun x -> dive x x.Range (traverseSynMatchClause path)) ] - |> pick expr + |> pick | SynExpr.Do (synExpr, _range) -> traverseSynExpr synExpr @@ -586,13 +580,13 @@ module SyntaxTraversal = dive synExpr2 synExpr2.Range traverseSynExpr dive synExpr synExpr.Range traverseSynExpr ] // reverse the args - |> pick expr + |> pick else [ dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.TypeApp (synExpr, _, _synTypeList, _commas, _, _, _range) -> traverseSynExpr synExpr @@ -605,7 +599,7 @@ module SyntaxTraversal = |> List.map (fun x -> dive x x.RangeOfBindingWithRhs (traverseSynBinding path)) yield dive synExpr synExpr.Range traverseSynExpr ] - |> pick expr + |> pick | x -> x | SynExpr.TryWith (tryExpr = synExpr; withCases = synMatchClauseList) -> @@ -615,14 +609,14 @@ module SyntaxTraversal = synMatchClauseList |> List.map (fun x -> dive x x.Range (traverseSynMatchClause path)) ] - |> pick expr + |> pick | SynExpr.TryFinally (tryExpr = synExpr; finallyExpr = synExpr2) -> [ dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Lazy (synExpr, _range) -> traverseSynExpr synExpr @@ -633,7 +627,7 @@ module SyntaxTraversal = dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.IfThenElse (ifExpr = synExpr; thenExpr = synExpr2; elseExpr = synExprOpt) -> [ @@ -643,7 +637,7 @@ module SyntaxTraversal = | None -> () | Some x -> yield dive x x.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Ident _ident -> None @@ -662,7 +656,7 @@ module SyntaxTraversal = dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.IndexRange (expr1, _, expr2, _, _, _) -> [ @@ -673,7 +667,7 @@ module SyntaxTraversal = | Some e -> dive e e.Range traverseSynExpr | None -> () ] - |> pick expr + |> pick | SynExpr.IndexFromEnd (e, _) -> traverseSynExpr e @@ -682,7 +676,7 @@ module SyntaxTraversal = yield dive synExpr synExpr.Range traverseSynExpr yield dive indexArgs indexArgs.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.DotIndexedSet (synExpr, indexArgs, synExpr2, _, _range, _range2) -> [ @@ -690,21 +684,21 @@ module SyntaxTraversal = yield dive indexArgs indexArgs.Range traverseSynExpr yield dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.JoinIn (synExpr1, _range, synExpr2, _range2) -> [ dive synExpr1 synExpr1.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.NamedIndexedPropertySet (_longIdent, synExpr, synExpr2, _range) -> [ dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.DotNamedIndexedPropertySet (synExpr, _longIdent, synExpr2, synExpr3, _range) -> [ @@ -712,7 +706,7 @@ module SyntaxTraversal = dive synExpr2 synExpr2.Range traverseSynExpr dive synExpr3 synExpr3.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.TypeTest (synExpr, synType, _range) @@ -723,7 +717,7 @@ module SyntaxTraversal = dive synExpr synExpr.Range traverseSynExpr dive synType synType.Range traverseSynType ] - |> pick expr + |> pick | SynExpr.InferredUpcast (synExpr, _range) -> traverseSynExpr synExpr @@ -753,7 +747,7 @@ module SyntaxTraversal = ] yield dive synExpr2 synExpr2.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.MatchBang (expr = synExpr; clauses = synMatchClauseList) -> [ @@ -762,7 +756,7 @@ module SyntaxTraversal = synMatchClauseList |> List.map (fun x -> dive x x.Range (traverseSynMatchClause path)) ] - |> pick expr + |> pick | SynExpr.DoBang (synExpr, _range) -> traverseSynExpr synExpr @@ -885,10 +879,10 @@ module SyntaxTraversal = synMemberDefns |> normalizeMembersToDealWithPeculiaritiesOfGettersAndSetters path (fun _ -> None) ] - |> pick tRange tydef + |> pick tRange and traverseSynMemberDefn path traverseInherit (m: SynMemberDefn) = - let pick (debugObj: obj) = pick m.Range debugObj + let pick = pick m.Range let path = SyntaxNode.SynMemberDefn m :: path match m with @@ -915,14 +909,14 @@ module SyntaxTraversal = | x -> x) dive () synExpr.Range (fun () -> visitor.VisitImplicitInherit(path, traverseSynExpr path, synType, synExpr, range)) ] - |> pick m + |> pick | SynMemberDefn.AutoProperty (synExpr = synExpr) -> traverseSynExpr path synExpr | SynMemberDefn.LetBindings (synBindingList, isRecursive, _, range) -> match visitor.VisitLetOrUse(path, isRecursive, traverseSynBinding path, synBindingList, range) with | None -> synBindingList |> List.map (fun x -> dive x x.RangeOfBindingWithRhs (traverseSynBinding path)) - |> pick m + |> pick | x -> x | SynMemberDefn.AbstractSlot(slotSig = SynValSig (synType = synType)) -> traverseSynType path synType | SynMemberDefn.Interface (interfaceType = synType; members = synMemberDefnsOption) -> @@ -936,7 +930,7 @@ module SyntaxTraversal = x |> normalizeMembersToDealWithPeculiaritiesOfGettersAndSetters path (fun _ -> None) ] - |> pick x + |> pick | ok -> ok | SynMemberDefn.Inherit (synType, _identOption, range) -> traverseInherit (synType, range) | SynMemberDefn.ValField _ -> None @@ -956,7 +950,7 @@ module SyntaxTraversal = yield synExpr ] |> List.map (fun x -> dive x x.Range (traverseSynExpr path))) - |> pick all.Range all + |> pick all.Range visitor.VisitMatchClause(origPath, defaultTraverse, mc) @@ -986,5 +980,5 @@ module SyntaxTraversal = #endif l |> List.map (fun x -> dive x x.Range (traverseSynModuleOrNamespace [])) - |> pick fileRange l + |> pick fileRange | ParsedInput.SigFile _sigFile -> None diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fsi b/src/Compiler/Service/ServiceParseTreeWalk.fsi index 2b9819def4e..f6375329874 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fsi +++ b/src/Compiler/Service/ServiceParseTreeWalk.fsi @@ -159,6 +159,6 @@ module public SyntaxTraversal = val internal dive: node: 'a -> range: 'b -> project: ('a -> 'c) -> 'b * (unit -> 'c) val internal pick: - pos: pos -> outerRange: range -> debugObj: obj -> diveResults: (range * (unit -> 'a option)) list -> 'a option + pos: pos -> outerRange: range -> diveResults: (range * (unit -> 'a option)) list -> 'a option val Traverse: pos: pos * parseTree: ParsedInput * visitor: SyntaxVisitorBase<'T> -> 'T option diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index d41426a5e70..8a57f72e0af 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -474,7 +474,7 @@ module ParsedInput = dive lidwd lidwd.Range (traverseLidOrElse pos None) dive exprRhs exprRhs.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.DotGet (exprLeft, mDot, lidwd, _m) -> let afterDotBeforeLid = mkRange mDot.FileName mDot.End lidwd.Range.Start @@ -484,7 +484,7 @@ module ParsedInput = dive exprLeft afterDotBeforeLid (fun e -> Some(e.Range.End, true)) dive lidwd lidwd.Range (traverseLidOrElse pos (Some exprLeft)) ] - |> pick expr + |> pick | SynExpr.DotSet (exprLeft, lidwd, exprRhs, _m) -> [ @@ -492,14 +492,14 @@ module ParsedInput = dive lidwd lidwd.Range (traverseLidOrElse pos (Some exprLeft)) dive exprRhs exprRhs.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Set (exprLeft, exprRhs, _m) -> [ dive exprLeft exprLeft.Range traverseSynExpr dive exprRhs exprRhs.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.NamedIndexedPropertySet (lidwd, exprIndexer, exprRhs, _m) -> [ @@ -507,7 +507,7 @@ module ParsedInput = dive exprIndexer exprIndexer.Range traverseSynExpr dive exprRhs exprRhs.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.DotNamedIndexedPropertySet (exprLeft, lidwd, exprIndexer, exprRhs, _m) -> [ @@ -516,7 +516,7 @@ module ParsedInput = dive exprIndexer exprIndexer.Range traverseSynExpr dive exprRhs exprRhs.Range traverseSynExpr ] - |> pick expr + |> pick | SynExpr.Const (SynConst.Double _, m) -> if posEq m.End pos then diff --git a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs index 36aabb4c8a0..cd57e22e3c4 100644 --- a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs @@ -35,6 +35,38 @@ let whoSings s = s.Artist Assert.AreEqual(expected, actual) +[] +let ``Hints are shown for lambdas`` () = + let code = """ +let iamboring() = + fun x -> x +""" + let document = getFsDocument code + let expected = [{ Content = ": 'a"; Location = (2, 10) }] + + let actual = getTypeHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints are shown for lambdas with tuples`` () = + let code = """ +let zip4 (l1: 'a list) (l2: 'b list) (l3: 'c list) (l4: 'd list) = + List.zip l1 (List.zip3 l2 l3 l4) + |> List.map (fun (x1, (x2, x3, x4)) -> (x1, x2, x3, x4)) +""" + let document = getFsDocument code + let expected = [ + { Content = ": 'a"; Location = (3, 25) } + { Content = ": 'b"; Location = (3, 30) } + { Content = ": 'c"; Location = (3, 34) } + { Content = ": 'd"; Location = (3, 38) } + ] + + let actual = getTypeHints document + + CollectionAssert.AreEquivalent(expected, actual) + [] let ``Hints are not shown in signature files`` () = let fsiCode = """ From 31f1601e870e051f953b8d8ff4648bcc0b9f2cef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:43:49 +0000 Subject: [PATCH 2/6] Automated command ran: fantomas Co-authored-by: psfinaki <5451366+psfinaki@users.noreply.github.com> --- src/Compiler/Service/ServiceParseTreeWalk.fs | 6 ++---- src/Compiler/Service/ServiceParseTreeWalk.fsi | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index e40911baca0..97e7d20067e 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -258,8 +258,7 @@ module SyntaxTraversal = snd (e) () | [ x ] -> x () - | _ -> - None + | _ -> None /// traverse an implementation file walking all the way down to SynExpr or TypeAbbrev at a particular location /// @@ -344,8 +343,7 @@ module SyntaxTraversal = | x -> x | SynExpr.Tuple (_, synExprList, _, _range) - | SynExpr.ArrayOrList (_, synExprList, _range) -> - synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick + | SynExpr.ArrayOrList (_, synExprList, _range) -> synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick | SynExpr.AnonRecd (_isStruct, copyOpt, synExprList, _range) -> [ diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fsi b/src/Compiler/Service/ServiceParseTreeWalk.fsi index f6375329874..e903ebd53ba 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fsi +++ b/src/Compiler/Service/ServiceParseTreeWalk.fsi @@ -158,7 +158,6 @@ module public SyntaxTraversal = val internal dive: node: 'a -> range: 'b -> project: ('a -> 'c) -> 'b * (unit -> 'c) - val internal pick: - pos: pos -> outerRange: range -> diveResults: (range * (unit -> 'a option)) list -> 'a option + val internal pick: pos: pos -> outerRange: range -> diveResults: (range * (unit -> 'a option)) list -> 'a option val Traverse: pos: pos * parseTree: ParsedInput * visitor: SyntaxVisitorBase<'T> -> 'T option From d005b9c42a1f07534bc770e5b2ec2f679715e904 Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 15 Nov 2022 13:00:11 +0100 Subject: [PATCH 3/6] Squashed commit of the following: commit ceb3acc19529f16514d9e8d5c85271ce6a42aeae Author: Don Syme Date: Mon Nov 14 15:39:35 2022 +0000 Language Service prefers original parse results --- .../Service/FSharpParseFileResults.fs | 10 +++++- .../Service/ServiceInterfaceStubGenerator.fs | 1 + src/Compiler/Service/ServiceParseTreeWalk.fs | 35 ++++++++++++++++--- src/Compiler/Service/ServiceParsedInputOps.fs | 34 ++++++++++++------ src/Compiler/Service/ServiceStructure.fs | 9 +++++ 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Service/FSharpParseFileResults.fs b/src/Compiler/Service/FSharpParseFileResults.fs index 929d38b957c..d086ad5cf7d 100644 --- a/src/Compiler/Service/FSharpParseFileResults.fs +++ b/src/Compiler/Service/FSharpParseFileResults.fs @@ -275,7 +275,13 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput, | Some expr -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos // Capture the body of a lambda, often nested in a call to a collection function - | SynExpr.Lambda (body = body) when rangeContainsPos body.Range pos -> getIdentRangeForFuncExprInApp traverseSynExpr body pos + // Prefer the original parse rather than the one after de-sugaring complex patterns in the lambda arguments. + | SynExpr.Lambda (parsedData = Some (_, body)) when rangeContainsPos body.Range pos -> + getIdentRangeForFuncExprInApp traverseSynExpr body pos + + // Capture the body of a lambda, often nested in a call to a collection function + | SynExpr.Lambda (body = body) when rangeContainsPos body.Range pos -> + getIdentRangeForFuncExprInApp traverseSynExpr body pos | SynExpr.Do (expr, range) when rangeContainsPos range pos -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos @@ -764,6 +770,8 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput, yield! walkExprOpt true whenExpr yield! walkExpr true resultExpr + | SynExpr.Lambda (parsedData = Some (_, bodyExpr)) -> yield! walkExpr true bodyExpr + | SynExpr.Lambda (body = bodyExpr) -> yield! walkExpr true bodyExpr | SynExpr.Match (matchDebugPoint = spBind; expr = inpExpr; clauses = cl) -> diff --git a/src/Compiler/Service/ServiceInterfaceStubGenerator.fs b/src/Compiler/Service/ServiceInterfaceStubGenerator.fs index 55ff01779f5..515ef262335 100644 --- a/src/Compiler/Service/ServiceInterfaceStubGenerator.fs +++ b/src/Compiler/Service/ServiceInterfaceStubGenerator.fs @@ -899,6 +899,7 @@ module InterfaceStubGenerator = | SynExpr.ComputationExpr (_, synExpr, _range) -> walkExpr synExpr + | SynExpr.Lambda (parsedData = Some (_, synExpr)) -> walkExpr synExpr | SynExpr.Lambda (body = synExpr) -> walkExpr synExpr | SynExpr.MatchLambda (_isExnMatch, _argm, synMatchClauseList, _spBind, _wholem) -> diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 97e7d20067e..fe1c981d428 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -259,6 +259,24 @@ module SyntaxTraversal = snd (e) () | [ x ] -> x () | _ -> None + | _ -> + assert false + // printing? + // logging? + // assert? + + // This condition probably arises when using a construct that is de-sugared during parsing. + // We are gradually eliminating these and instead doing the de-sugaring during type checking, or + // else recording the non-de-sugared parsing in the parse tree alongside the de-sugared parsing. + // + // For example, SynExpr.Lambda contains both the parsing after de-sugaring complex patterns, and the + // original parsing. + // + // For nearly all purposes except type checking we want to traverse the original parsing. + + // "multiple disjoint AST node ranges claimed to contain (%A) from %+A" pos debugObj + + None /// traverse an implementation file walking all the way down to SynExpr or TypeAbbrev at a particular location /// @@ -315,6 +333,7 @@ module SyntaxTraversal = let traverseSynExpr = traverseSynExpr path let traverseSynType = traverseSynType path let traversePat = traversePat path + let traversePats = traversePats path match e with @@ -540,6 +559,11 @@ module SyntaxTraversal = if ok.IsSome then ok else traverseSynExpr synExpr + | SynExpr.Lambda (parsedData = Some(argPats, synExpr)) -> + match traversePats argPats with + | None -> traverseSynExpr synExpr + | res -> res + | SynExpr.Lambda (args = synSimplePats; body = synExpr) -> match synSimplePats with | SynSimplePats.SimplePats (pats, _) -> @@ -774,6 +798,9 @@ module SyntaxTraversal = visitor.VisitExpr(origPath, traverseSynExpr origPath, defaultTraverse, expr) + and traversePats origPath (pats: SynPat list) = + pats |> List.tryPick (traversePat origPath) + and traversePat origPath (pat: SynPat) = let defaultTraverse p = let path = SyntaxNode.SynPat p :: origPath @@ -782,15 +809,15 @@ module SyntaxTraversal = | SynPat.Paren (p, _) -> traversePat path p | SynPat.As (p1, p2, _) | SynPat.Or (p1, p2, _, _) - | SynPat.ListCons (p1, p2, _, _) -> [ p1; p2 ] |> List.tryPick (traversePat path) + | SynPat.ListCons (p1, p2, _, _) -> [ p1; p2 ] |> traversePats path | SynPat.Ands (ps, _) | SynPat.Tuple (_, ps, _) - | SynPat.ArrayOrList (_, ps, _) -> ps |> List.tryPick (traversePat path) + | SynPat.ArrayOrList (_, ps, _) -> ps |> traversePats path | SynPat.Attrib (p, _, _) -> traversePat path p | SynPat.LongIdent (argPats = args) -> match args with - | SynArgPats.Pats ps -> ps |> List.tryPick (traversePat path) - | SynArgPats.NamePatPairs (pats = ps) -> ps |> List.map (fun (_, _, pat) -> pat) |> List.tryPick (traversePat path) + | SynArgPats.Pats ps -> ps |> traversePats path + | SynArgPats.NamePatPairs (pats = ps) -> ps |> List.map (fun (_, _, pat) -> pat) |> traversePats path | SynPat.Typed (p, ty, _) -> match traversePat path p with | None -> traverseSynType path ty diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 8a57f72e0af..000a1192917 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -615,12 +615,12 @@ module ParsedInput = and walkPatWithKind (kind: EntityKind option) pat = match pat with - | SynPat.Ands (pats, _) -> List.tryPick walkPat pats - | SynPat.As (pat1, pat2, _) -> List.tryPick walkPat [ pat1; pat2 ] + | SynPat.Ands (pats, _) -> walkPats pats + | SynPat.As (pat1, pat2, _) -> walkPats [ pat1; pat2 ] | SynPat.Typed (pat, t, _) -> walkPat pat |> Option.orElseWith (fun () -> walkType t) | SynPat.Attrib (pat, Attributes attrs, _) -> walkPat pat |> Option.orElseWith (fun () -> List.tryPick walkAttribute attrs) | SynPat.Or (pat1, pat2, _, _) - | SynPat.ListCons (pat1, pat2, _, _) -> List.tryPick walkPat [ pat1; pat2 ] + | SynPat.ListCons (pat1, pat2, _, _) -> walkPats [ pat1; pat2 ] | SynPat.LongIdent (typarDecls = typars; argPats = ConstructorPats pats; range = r) -> ifPosInRange r (fun _ -> kind) |> Option.orElseWith (fun () -> @@ -628,15 +628,17 @@ module ParsedInput = |> Option.bind (fun (ValTyparDecls (typars, constraints, _)) -> List.tryPick walkTyparDecl typars |> Option.orElseWith (fun () -> List.tryPick walkTypeConstraint constraints))) - |> Option.orElseWith (fun () -> List.tryPick walkPat pats) - | SynPat.Tuple (_, pats, _) -> List.tryPick walkPat pats + |> Option.orElseWith (fun () -> walkPats pats) + | SynPat.Tuple (_, pats, _) -> walkPats pats | SynPat.Paren (pat, _) -> walkPat pat - | SynPat.ArrayOrList (_, pats, _) -> List.tryPick walkPat pats + | SynPat.ArrayOrList (_, pats, _) -> walkPats pats | SynPat.IsInst (t, _) -> walkType t | SynPat.QuoteExpr (e, _) -> walkExpr e | _ -> None - and walkPat = walkPatWithKind None + and walkPat pat = walkPatWithKind None pat + + and walkPats pats = List.tryPick walkPat pats and walkBinding bind = let (SynBinding (attributes = Attributes attrs; headPat = pat; returnInfo = returnInfo; expr = e)) = @@ -742,6 +744,8 @@ module ParsedInput = | SynExpr.ComputationExpr (_, e, _) -> walkExprWithKind parentKind e + | SynExpr.Lambda (parsedData = Some (_, e)) -> walkExprWithKind parentKind e + | SynExpr.Lambda (body = e) -> walkExprWithKind parentKind e | SynExpr.MatchLambda (_, _, synMatchClauseList, _, _) -> List.tryPick walkClause synMatchClauseList @@ -1626,11 +1630,15 @@ module ParsedInput = walkMemberSig sign | SynTypeConstraint.WhereSelfConstrained (ty, _) -> walkType ty + and walkPats pats = + for pat in pats do + walkPat pat + and walkPat pat = match pat with | SynPat.Tuple (_, pats, _) | SynPat.ArrayOrList (_, pats, _) - | SynPat.Ands (pats, _) -> List.iter walkPat pats + | SynPat.Ands (pats, _) -> walkPats pats | SynPat.Named (SynIdent (ident, _), _, _, _) -> addIdent ident | SynPat.Typed (pat, t, _) -> walkPat pat @@ -1640,7 +1648,7 @@ module ParsedInput = List.iter walkAttribute attrs | SynPat.As (pat1, pat2, _) | SynPat.Or (pat1, pat2, _, _) - | SynPat.ListCons (pat1, pat2, _, _) -> List.iter walkPat [ pat1; pat2 ] + | SynPat.ListCons (pat1, pat2, _, _) -> walkPats [ pat1; pat2 ] | SynPat.LongIdent (longDotId = ident; typarDecls = typars; argPats = ConstructorPats pats) -> addLongIdentWithDots ident @@ -1649,7 +1657,7 @@ module ParsedInput = List.iter walkTyparDecl typars List.iter walkTypeConstraint constraints) - List.iter walkPat pats + walkPats pats | SynPat.Paren (pat, _) -> walkPat pat | SynPat.IsInst (t, _) -> walkType t | SynPat.QuoteExpr (e, _) -> walkExpr e @@ -1724,9 +1732,15 @@ module ParsedInput = | SynExpr.Assert (e, _) | SynExpr.Lazy (e, _) | SynExpr.YieldOrReturnFrom (_, e, _) -> walkExpr e + + | SynExpr.Lambda (parsedData = Some(argPats, e)) -> + walkPats argPats + walkExpr e + | SynExpr.Lambda (args = pats; body = e) -> walkSimplePats pats walkExpr e + | SynExpr.New (_, t, e, _) | SynExpr.TypeTest (e, t, _) | SynExpr.Upcast (e, t, _) diff --git a/src/Compiler/Service/ServiceStructure.fs b/src/Compiler/Service/ServiceStructure.fs index e9468f63357..67e58afd61d 100644 --- a/src/Compiler/Service/ServiceStructure.fs +++ b/src/Compiler/Service/ServiceStructure.fs @@ -446,7 +446,16 @@ module Structure = rcheck Scope.While Collapse.Below r r parseExpr e + | SynExpr.Lambda (parsedData = Some(argPats, e); range = r) -> + match List.tryLast argPats with + | Some argPat -> + rcheck Scope.Lambda Collapse.Below r (Range.endToEnd argPat.Range r) + | None -> + () + parseExpr e + | SynExpr.Lambda (args = pats; body = e; range = r) -> + match pats with | SynSimplePats.SimplePats (_, pr) | SynSimplePats.Typed (_, _, pr) -> rcheck Scope.Lambda Collapse.Below r (Range.endToEnd pr r) From 56f3177bd8c9ed816addc79e3d21cfbe38f9ca10 Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 15 Nov 2022 13:02:25 +0100 Subject: [PATCH 4/6] Cleanup --- src/Compiler/Service/ServiceParseTreeWalk.fs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index fe1c981d428..8b8d5c1e20b 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -258,13 +258,8 @@ module SyntaxTraversal = snd (e) () | [ x ] -> x () - | _ -> None | _ -> - assert false - // printing? - // logging? - // assert? - +#if DEBUG // This condition probably arises when using a construct that is de-sugared during parsing. // We are gradually eliminating these and instead doing the de-sugaring during type checking, or // else recording the non-de-sugared parsing in the parse tree alongside the de-sugared parsing. @@ -274,8 +269,8 @@ module SyntaxTraversal = // // For nearly all purposes except type checking we want to traverse the original parsing. - // "multiple disjoint AST node ranges claimed to contain (%A) from %+A" pos debugObj - + printf "should happen rarely and disappear with time" +#endif None /// traverse an implementation file walking all the way down to SynExpr or TypeAbbrev at a particular location From 85f9dd48451ab891fcf7614f3705bb0cee911cbf Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 15 Nov 2022 13:18:22 +0100 Subject: [PATCH 5/6] Cleanup --- src/Compiler/Service/ServiceParseTreeWalk.fs | 2 +- .../tests/UnitTests/InlineTypeHintTests.fs | 32 ------------------- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 8b8d5c1e20b..932597c662e 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -269,7 +269,7 @@ module SyntaxTraversal = // // For nearly all purposes except type checking we want to traverse the original parsing. - printf "should happen rarely and disappear with time" + printf "A construct desugared during parsing. Should happen rarely and disappear with time." #endif None diff --git a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs index cd57e22e3c4..36aabb4c8a0 100644 --- a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs @@ -35,38 +35,6 @@ let whoSings s = s.Artist Assert.AreEqual(expected, actual) -[] -let ``Hints are shown for lambdas`` () = - let code = """ -let iamboring() = - fun x -> x -""" - let document = getFsDocument code - let expected = [{ Content = ": 'a"; Location = (2, 10) }] - - let actual = getTypeHints document - - Assert.AreEqual(expected, actual) - -[] -let ``Hints are shown for lambdas with tuples`` () = - let code = """ -let zip4 (l1: 'a list) (l2: 'b list) (l3: 'c list) (l4: 'd list) = - List.zip l1 (List.zip3 l2 l3 l4) - |> List.map (fun (x1, (x2, x3, x4)) -> (x1, x2, x3, x4)) -""" - let document = getFsDocument code - let expected = [ - { Content = ": 'a"; Location = (3, 25) } - { Content = ": 'b"; Location = (3, 30) } - { Content = ": 'c"; Location = (3, 34) } - { Content = ": 'd"; Location = (3, 38) } - ] - - let actual = getTypeHints document - - CollectionAssert.AreEquivalent(expected, actual) - [] let ``Hints are not shown in signature files`` () = let fsiCode = """ From f34dd65db92920c6390a62d5e4d8471347e02d1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 13:11:56 +0000 Subject: [PATCH 6/6] Automated command ran: fantomas Co-authored-by: psfinaki <5451366+psfinaki@users.noreply.github.com> --- src/Compiler/Service/FSharpParseFileResults.fs | 9 ++++----- src/Compiler/Service/ServiceInterfaceStubGenerator.fs | 2 +- src/Compiler/Service/ServiceParseTreeWalk.fs | 2 +- src/Compiler/Service/ServiceParsedInputOps.fs | 4 ++-- src/Compiler/Service/ServiceStructure.fs | 9 ++++----- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Compiler/Service/FSharpParseFileResults.fs b/src/Compiler/Service/FSharpParseFileResults.fs index d086ad5cf7d..d2c911ecb2c 100644 --- a/src/Compiler/Service/FSharpParseFileResults.fs +++ b/src/Compiler/Service/FSharpParseFileResults.fs @@ -276,12 +276,11 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput, // Capture the body of a lambda, often nested in a call to a collection function // Prefer the original parse rather than the one after de-sugaring complex patterns in the lambda arguments. - | SynExpr.Lambda (parsedData = Some (_, body)) when rangeContainsPos body.Range pos -> - getIdentRangeForFuncExprInApp traverseSynExpr body pos + | SynExpr.Lambda(parsedData = Some (_, body)) when rangeContainsPos body.Range pos -> + getIdentRangeForFuncExprInApp traverseSynExpr body pos // Capture the body of a lambda, often nested in a call to a collection function - | SynExpr.Lambda (body = body) when rangeContainsPos body.Range pos -> - getIdentRangeForFuncExprInApp traverseSynExpr body pos + | SynExpr.Lambda (body = body) when rangeContainsPos body.Range pos -> getIdentRangeForFuncExprInApp traverseSynExpr body pos | SynExpr.Do (expr, range) when rangeContainsPos range pos -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos @@ -770,7 +769,7 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput, yield! walkExprOpt true whenExpr yield! walkExpr true resultExpr - | SynExpr.Lambda (parsedData = Some (_, bodyExpr)) -> yield! walkExpr true bodyExpr + | SynExpr.Lambda(parsedData = Some (_, bodyExpr)) -> yield! walkExpr true bodyExpr | SynExpr.Lambda (body = bodyExpr) -> yield! walkExpr true bodyExpr diff --git a/src/Compiler/Service/ServiceInterfaceStubGenerator.fs b/src/Compiler/Service/ServiceInterfaceStubGenerator.fs index 515ef262335..d66ed735a84 100644 --- a/src/Compiler/Service/ServiceInterfaceStubGenerator.fs +++ b/src/Compiler/Service/ServiceInterfaceStubGenerator.fs @@ -899,7 +899,7 @@ module InterfaceStubGenerator = | SynExpr.ComputationExpr (_, synExpr, _range) -> walkExpr synExpr - | SynExpr.Lambda (parsedData = Some (_, synExpr)) -> walkExpr synExpr + | SynExpr.Lambda(parsedData = Some (_, synExpr)) -> walkExpr synExpr | SynExpr.Lambda (body = synExpr) -> walkExpr synExpr | SynExpr.MatchLambda (_isExnMatch, _argm, synMatchClauseList, _spBind, _wholem) -> diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 932597c662e..866d5fb2ecc 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -554,7 +554,7 @@ module SyntaxTraversal = if ok.IsSome then ok else traverseSynExpr synExpr - | SynExpr.Lambda (parsedData = Some(argPats, synExpr)) -> + | SynExpr.Lambda(parsedData = Some (argPats, synExpr)) -> match traversePats argPats with | None -> traverseSynExpr synExpr | res -> res diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 000a1192917..67f5e436ecc 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -744,7 +744,7 @@ module ParsedInput = | SynExpr.ComputationExpr (_, e, _) -> walkExprWithKind parentKind e - | SynExpr.Lambda (parsedData = Some (_, e)) -> walkExprWithKind parentKind e + | SynExpr.Lambda(parsedData = Some (_, e)) -> walkExprWithKind parentKind e | SynExpr.Lambda (body = e) -> walkExprWithKind parentKind e @@ -1733,7 +1733,7 @@ module ParsedInput = | SynExpr.Lazy (e, _) | SynExpr.YieldOrReturnFrom (_, e, _) -> walkExpr e - | SynExpr.Lambda (parsedData = Some(argPats, e)) -> + | SynExpr.Lambda(parsedData = Some (argPats, e)) -> walkPats argPats walkExpr e diff --git a/src/Compiler/Service/ServiceStructure.fs b/src/Compiler/Service/ServiceStructure.fs index 67e58afd61d..91796b8554b 100644 --- a/src/Compiler/Service/ServiceStructure.fs +++ b/src/Compiler/Service/ServiceStructure.fs @@ -446,12 +446,11 @@ module Structure = rcheck Scope.While Collapse.Below r r parseExpr e - | SynExpr.Lambda (parsedData = Some(argPats, e); range = r) -> + | SynExpr.Lambda (parsedData = Some (argPats, e); range = r) -> match List.tryLast argPats with - | Some argPat -> - rcheck Scope.Lambda Collapse.Below r (Range.endToEnd argPat.Range r) - | None -> - () + | Some argPat -> rcheck Scope.Lambda Collapse.Below r (Range.endToEnd argPat.Range r) + | None -> () + parseExpr e | SynExpr.Lambda (args = pats; body = e; range = r) ->