diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index 78fe4bb4c97..b0a39cd469d 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -1105,7 +1105,18 @@ module PrintTypes = let prettyLayoutOfTypeNoConstraints denv ty = let ty, _cxs = PrettyTypes.PrettifyType denv.g ty - layoutTypeWithInfoAndPrec denv SimplifyTypes.typeSimplificationInfo0 5 ty + layoutTypeWithInfoAndPrec denv SimplifyTypes.typeSimplificationInfo0 5 ty + + let layoutOfValReturnType denv (v: ValRef) = + match v.ValReprInfo with + | None -> + let _, tau = v.TypeScheme + let _argtysl, rty = stripFunTy denv.g tau + layoutReturnType denv SimplifyTypes.typeSimplificationInfo0 rty + | Some (ValReprInfo(_typars, argInfos, _retInfo)) -> + let tau = v.TauType + let _c, rty = GetTopTauTypeInFSharpForm denv.g argInfos tau Range.range0 + layoutReturnType denv SimplifyTypes.typeSimplificationInfo0 rty let layoutAssemblyName _denv (ty: TType) = ty.GetAssemblyName() @@ -2435,7 +2446,9 @@ let prettyLayoutOfValOrMember denv infoReader typarInst v = PrintTastMemberOrVal let prettyLayoutOfValOrMemberNoInst denv infoReader v = PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv infoReader v -let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v +let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v + +let layoutOfValReturnType denv v = v |> PrintTypes.layoutOfValReturnType denv let prettyLayoutOfInstAndSig denv x = PrintTypes.prettyLayoutOfInstAndSig denv x diff --git a/src/fsharp/NicePrint.fsi b/src/fsharp/NicePrint.fsi index 5726a7f229b..78a2e39f74d 100644 --- a/src/fsharp/NicePrint.fsi +++ b/src/fsharp/NicePrint.fsi @@ -109,6 +109,8 @@ val prettyLayoutOfValOrMemberNoInst: denv:DisplayEnv -> infoReader:InfoReader -> val prettyLayoutOfMemberNoInstShort: denv:DisplayEnv -> v:Val -> Layout +val layoutOfValReturnType: denv:DisplayEnv -> v:ValRef -> Layout + val prettyLayoutOfInstAndSig: denv:DisplayEnv -> TyparInst * TTypes * TType -> TyparInst * (TTypes * TType) * (Layout list * Layout) * Layout val minimalStringsOfTwoTypes: denv:DisplayEnv -> t1:TType -> t2:TType -> string * string * string diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index bec75f547cd..b9bfc6a2b76 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -8,8 +8,19 @@ open FSharp.Compiler.Text.Range open FSharp.Compiler.Syntax open FSharp.Compiler.SyntaxTreeOps +type TupledArgumentLocation = { IsNamedArgument: bool; ArgumentRange: range } + [] -type ParameterLocations(longId: string list, longIdRange: range, openParenLocation: pos, tupleEndLocations: pos list, isThereACloseParen: bool, namedParamNames: string option list) = +type ParameterLocations + ( + longId: string list, + longIdRange: range, + openParenLocation: pos, + argRanges: TupledArgumentLocation list, + tupleEndLocations: pos list, + isThereACloseParen: bool, + namedParamNames: string option list + ) = let tupleEndLocations = Array.ofList tupleEndLocations let namedParamNames = Array.ofList namedParamNames @@ -30,6 +41,7 @@ type ParameterLocations(longId: string list, longIdRange: range, openParenLocati member this.TupleEndLocations = tupleEndLocations member this.IsThereACloseParen = isThereACloseParen member this.NamedParamNames = namedParamNames + member this.ArgumentLocations = argRanges |> Array.ofList [] module internal ParameterLocationsImpl = @@ -54,7 +66,7 @@ module internal ParameterLocationsImpl = | _ -> None type FindResult = - | Found of openParen: pos * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool + | Found of openParen: pos * argRanges: TupledArgumentLocation list * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool | NotFound let digOutIdentFromStaticArg (StripParenTypes synType) = @@ -90,7 +102,8 @@ module internal ParameterLocationsImpl = match inner with | None -> if SyntaxTraversal.rangeContainsPosLeftEdgeExclusiveAndRightEdgeInclusive parenRange pos then - Found (parenRange.Start, [(parenRange.End, getNamedParamName synExpr)], rpRangeOpt.IsSome), None + let argRanges = [{ IsNamedArgument = (getNamedParamName synExpr).IsSome; ArgumentRange = synExpr.Range }] + Found (parenRange.Start, argRanges, [(parenRange.End, getNamedParamName synExpr)], rpRangeOpt.IsSome), None else NotFound, None | _ -> NotFound, None @@ -107,8 +120,12 @@ module internal ParameterLocationsImpl = match inner with | None -> if SyntaxTraversal.rangeContainsPosLeftEdgeExclusiveAndRightEdgeInclusive parenRange pos then + // argRange, isNamed + let argRanges = + synExprList + |> List.map (fun e -> { IsNamedArgument = (getNamedParamName e).IsSome; ArgumentRange = e.Range }) let commasAndCloseParen = ((synExprList, commaRanges@[parenRange]) ||> List.map2 (fun e c -> c.End, getNamedParamName e)) - let r = Found (parenRange.Start, commasAndCloseParen, rpRangeOpt.IsSome) + let r = Found (parenRange.Start, argRanges, commasAndCloseParen, rpRangeOpt.IsSome) r, None else NotFound, None @@ -127,14 +144,14 @@ module internal ParameterLocationsImpl = | SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF if SyntaxTraversal.rangeContainsPosEdgesExclusive range pos then - let r = Found (range.Start, [(range.End, None)], false) + let r = Found (range.Start, [], [(range.End, None)], false) r, None else NotFound, None | SynExpr.Const (SynConst.Unit, unitRange) -> if SyntaxTraversal.rangeContainsPosEdgesExclusive unitRange pos then - let r = Found (unitRange.Start, [(unitRange.End, None)], true) + let r = Found (unitRange.Start, [], [(unitRange.End, None)], true) r, None else NotFound, None @@ -145,7 +162,7 @@ module internal ParameterLocationsImpl = | None -> if SyntaxTraversal.rangeContainsPosEdgesExclusive e.Range pos then // any other expression doesn't start with parens, so if it was the target of an App, then it must be a single argument e.g. "f x" - Found (e.Range.Start, [ (e.Range.End, None) ], false), Some inner + Found (e.Range.Start, [], [ (e.Range.End, None) ], false), Some inner else NotFound, Some inner | _ -> NotFound, Some inner @@ -157,7 +174,7 @@ module internal ParameterLocationsImpl = let betweenTheBrackets = mkRange wholem.FileName openm.Start wholem.End if SyntaxTraversal.rangeContainsPosEdgesExclusive betweenTheBrackets pos && args |> List.forall isStaticArg then let commasAndCloseParen = [ for c in commas -> c.End ] @ [ wholem.End ] - Some (ParameterLocations(pathOfLid lid, lidm, openm.Start, commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) + Some (ParameterLocations(pathOfLid lid, lidm, openm.Start, [], commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) else None | _ -> @@ -173,9 +190,9 @@ module internal ParameterLocationsImpl = | SynExpr.New (_, synType, synExpr, _) -> let constrArgsResult, cacheOpt = searchSynArgExpr traverseSynExpr pos synExpr match constrArgsResult, cacheOpt with - | Found(parenLoc, args, isThereACloseParen), _ -> + | Found(parenLoc, argRanges, commasAndCloseParen, isThereACloseParen), _ -> let typeName = getTypeName synType - Some (ParameterLocations(typeName, synType.Range, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd)) + Some (ParameterLocations(typeName, synType.Range, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd)) | NotFound, Some cache -> cache | _ -> @@ -194,7 +211,7 @@ module internal ParameterLocationsImpl = if SyntaxTraversal.rangeContainsPosEdgesExclusive typeArgsm pos then // We found it, dig out ident match digOutIdentFromFuncExpr synExpr with - | Some(lid, lidRange) -> Some (ParameterLocations(lid, lidRange, op.idRange.Start, [ wholem.End ], false, [])) + | Some(lid, lidRange) -> Some (ParameterLocations(lid, lidRange, op.idRange.Start, [], [ wholem.End ], false, [])) | None -> None else None @@ -209,7 +226,7 @@ module internal ParameterLocationsImpl = // Search the argument let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos synExpr2 match xResult, cacheOpt with - | Found(parenLoc, args, isThereACloseParen), _ -> + | Found(parenLoc, argRanges, commasAndCloseParen, isThereACloseParen), _ -> // We found it, dig out ident match digOutIdentFromFuncExpr synExpr with | Some(lid, lidRange) -> @@ -219,7 +236,7 @@ module internal ParameterLocationsImpl = // For now, we don't support infix operators. None else - Some (ParameterLocations(lid, lidRange, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd)) + Some (ParameterLocations(lid, lidRange, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd)) | None -> None | NotFound, Some cache -> cache | _ -> traverseSynExpr synExpr2 @@ -232,7 +249,8 @@ module internal ParameterLocationsImpl = let typeArgsm = mkRange openm.FileName openm.Start wholem.End if SyntaxTraversal.rangeContainsPosEdgesExclusive typeArgsm pos && tyArgs |> List.forall isStaticArg then let commasAndCloseParen = [ for c in commas -> c.End ] @ [ wholem.End ] - let r = ParameterLocations(["dummy"], synExpr.Range, openm.Start, commasAndCloseParen, closemOpt.IsSome, tyArgs |> List.map digOutIdentFromStaticArg) + let argRanges = tyArgs |> List.map (fun tyarg -> { IsNamedArgument = false; ArgumentRange = tyarg.Range }) + let r = ParameterLocations(["dummy"], synExpr.Range, openm.Start, argRanges, commasAndCloseParen, closemOpt.IsSome, tyArgs |> List.map digOutIdentFromStaticArg) Some r else None @@ -253,10 +271,10 @@ module internal ParameterLocationsImpl = // inherit ty(expr) --- treat it like an application (constructor call) let xResult, _cacheOpt = searchSynArgExpr defaultTraverse pos expr match xResult with - | Found(parenLoc, args, isThereACloseParen) -> + | Found(parenLoc, argRanges, commasAndCloseParen, isThereACloseParen) -> // we found it, dig out ident let typeName = getTypeName ty - let r = ParameterLocations(typeName, ty.Range, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd) + let r = ParameterLocations(typeName, ty.Range, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd) Some r | NotFound -> None else None @@ -276,7 +294,6 @@ type ParameterLocations with r | _ -> None - module internal SynExprAppLocationsImpl = let rec private searchSynArgExpr traverseSynExpr expr ranges = match expr with diff --git a/src/fsharp/service/ServiceParamInfoLocations.fsi b/src/fsharp/service/ServiceParamInfoLocations.fsi index 77fa2b46326..bc381eda2e9 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fsi +++ b/src/fsharp/service/ServiceParamInfoLocations.fsi @@ -10,6 +10,9 @@ namespace FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text +/// Represents the location of a tupled argument, which can optionally be a named argument. +type TupledArgumentLocation = { IsNamedArgument: bool; ArgumentRange: range } + /// Represents the locations relevant to activating parameter info in an IDE [] type public ParameterLocations = @@ -33,7 +36,10 @@ type public ParameterLocations = member IsThereACloseParen : bool /// Either empty or a name if an actual named parameter; f(0,a=4,?b=None) would be [|None; Some "a"; Some "b"|] - member NamedParamNames : string option [] + member NamedParamNames : string option [] + + /// Array of locations for each argument, and a flag if that argument is named + member ArgumentLocations: TupledArgumentLocation [] /// Find the information about parameter info locations at a particular source location static member Find : pos * ParsedInput -> ParameterLocations option diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs new file mode 100755 index 00000000000..3228be7e5cd --- /dev/null +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -0,0 +1,1471 @@ +// 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 + +[] +type InheritanceOrigin = + | Class + | Interface + | Unknown + +[] +type InheritanceContext = + | Class + | Interface + | Unknown + +[] +type RecordContext = + | CopyOnUpdate of range * CompletionPath // range of copy-expr + current field + | Constructor of string // typename + | New of CompletionPath + +[] +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 + | AttributeApplication + | OpenDeclaration of isOpenType: bool + /// completing pattern type (e.g. foo (x: |)) + | PatternType + +//---------------------------------------------------------------------------- +// FSharpParseFileResults +//---------------------------------------------------------------------------- + +[] +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.FindNoteworthyParamInfoLocations pos = + match input with + | Some input -> FSharpNoteworthyParamInfoLocations.Find(pos, input) + | _ -> None + + member scope.GetAllArgumentsForFunctionApplicationAtPostion pos = + match input with + | Some input -> SynExprAppLocationsImpl.getAllCurriedArgsAtPosition pos input + | None -> None + + member scope.IsTypeAnnotationGivenAtPosition pos = + match input with + | Some parseTree -> + let res = + AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with + member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = + match expr with + | SynExpr.Typed (_expr, _typeExpr, range) when posEq range.Start pos -> + Some range + | _ -> defaultTraverse expr + + override _.VisitSimplePats(pats) = + match pats with + | [] -> None + | _ -> + let exprFunc pat = + match pat with + | SynSimplePat.Typed (_pat, _targetExpr, range) when posEq range.Start pos -> + Some range + | _ -> + None + + pats |> List.tryPick exprFunc + + override _.VisitPat(defaultTraverse, pat) = + match pat with + | SynPat.Typed (_pat, _targetType, range) when posEq range.Start pos -> + Some range + | _ -> defaultTraverse pat }) + res.IsSome + | None -> false + + member scope.IsBindingALambdaAtPosition pos = + match input with + | Some parseTree -> + let res = + AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with + member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = + defaultTraverse expr + + override _.VisitBinding(defaultTraverse, binding) = + match binding with + | SynBinding.Binding(_, _, _, _, _, _, _, _, _, expr, range, _) when posEq range.Start pos -> + match expr with + | SynExpr.Lambda _ -> Some range + | _ -> None + | _ -> defaultTraverse binding }) + res.IsSome + | None -> false + + /// 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 } + +[] +type EntityKind = + | Attribute + | Type + | FunctionOrValue of isActivePattern: bool + | Module of ModuleKind + override x.ToString() = sprintf "%A" x + +module UntypedParseImpl = + + let emptyStringSet = HashSet() + + 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 + + and walkTypeDefn (TypeDefn (info, repr, members, _)) = + walkComponentInfo false info + |> Option.orElse (walkTypeDefnRepr repr) + |> Option.orElse (List.tryPick walkMember members) + + and walkSynModuleDecl isTopLevel (decl: SynModuleDecl) = + match decl with + | SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace isTopLevel fragment + | SynModuleDecl.NestedModule(info, _, modules, _, range) -> + walkComponentInfo true info + |> Option.orElse (ifPosInRange range (fun _ -> List.tryPick (walkSynModuleDecl false) modules)) + | SynModuleDecl.Open _ -> None + | SynModuleDecl.Let (_, bindings, _) -> List.tryPick walkBinding bindings + | SynModuleDecl.DoExpr (_, expr, _) -> walkExpr expr + | SynModuleDecl.Types (types, _) -> List.tryPick walkTypeDefn types + | _ -> None + + match input with + | ParsedInput.SigFile _ -> None + | ParsedInput.ImplFile input -> walkImplFileInput input + + type internal TS = AstTraversal.TraverseStep + /// Matches the most nested [< and >] pair. + let insideAttributeApplicationRegex = Regex(@"(?<=\[\<)(?(.*?))(?=\>\])", RegexOptions.Compiled ||| RegexOptions.ExplicitCapture) + + /// Try to determine completion context for the given pair (row, columns) + let TryGetCompletionContext (pos, parsedInput: ParsedInput, lineStr: string) : CompletionContext option = + + match GetEntityKind(pos, parsedInput) with + | Some EntityKind.Attribute -> Some CompletionContext.AttributeApplication + | _ -> + + let parseLid (LongIdentWithDots(lid, dots)) = + let rec collect plid (parts : Ident list) (dots : range list) = + match parts, dots with + | [], _ -> Some (plid, None) + | x :: xs, ds -> + if rangeContainsPos x.idRange pos then + // pos lies with the range of current identifier + let s = x.idText.Substring(0, pos.Column - x.idRange.Start.Column) + let residue = if s.Length <> 0 then Some s else None + Some (plid, residue) + elif posGt x.idRange.Start pos then + // can happen if caret is placed after dot but before the existing identifier A. $ B + // return accumulated plid with no residue + Some (plid, None) + else + match ds with + | [] -> + // pos lies after the id and no dots found - return accumulated plid and current id as residue + Some (plid, Some (x.idText)) + | d :: ds -> + if posGeq pos d.End then + // pos lies after the dot - proceed to the next identifier + collect ((x.idText) :: plid) xs ds + else + // pos after the id but before the dot + // A $.B - return nothing + None + + match collect [] lid dots with + | Some (parts, residue) -> + Some ((List.rev parts), residue) + | None -> None + + let (|Class|Interface|Struct|Unknown|Invalid|) synAttributes = + let (|SynAttr|_|) name (attr : SynAttribute) = + match attr with + | {TypeName = LongIdentWithDots([x], _)} when x.idText = name -> Some () + | _ -> None + + let rec getKind isClass isInterface isStruct = + function + | [] -> isClass, isInterface, isStruct + | (SynAttr "Class") :: xs -> getKind true isInterface isStruct xs + | (SynAttr "AbstractClass") :: xs -> getKind true isInterface isStruct xs + | (SynAttr "Interface") :: xs -> getKind isClass true isStruct xs + | (SynAttr "Struct") :: xs -> getKind isClass isInterface true xs + | _ :: xs -> getKind isClass isInterface isStruct xs + + match getKind false false false synAttributes with + | false, false, false -> Unknown + | true, false, false -> Class + | false, true, false -> Interface + | false, false, true -> Struct + | _ -> Invalid + + let GetCompletionContextForInheritSynMember ((ComponentInfo(Attributes synAttributes, _, _, _, _, _, _, _)), typeDefnKind : SynTypeDefnKind, completionPath) = + + let success k = Some (CompletionContext.Inherit (k, completionPath)) + + // if kind is specified - take it + // if kind is non-specified + // - try to obtain it from attribute + // - if no attributes present - infer kind from members + match typeDefnKind with + | TyconClass -> + match synAttributes with + | Class | Unknown -> success InheritanceContext.Class + | _ -> Some CompletionContext.Invalid // non-matching attributes + | TyconInterface -> + match synAttributes with + | Interface | Unknown -> success InheritanceContext.Interface + | _ -> Some CompletionContext.Invalid // non-matching attributes + | TyconStruct -> + // display nothing for structs + Some CompletionContext.Invalid + | TyconUnspecified -> + match synAttributes with + | Class -> success InheritanceContext.Class + | Interface -> success InheritanceContext.Interface + | Unknown -> + // user do not specify kind explicitly or via attributes + success InheritanceContext.Unknown + | _ -> + // unable to uniquely detect kind from the attributes - return invalid context + Some CompletionContext.Invalid + | _ -> None + + let (|Operator|_|) name e = + match e with + | SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, true, SynExpr.Ident ident, lhs, _), rhs, _) + when ident.idText = name -> Some (lhs, rhs) + | _ -> None + + // checks if we are in rhs of the range operator + let isInRhsOfRangeOp (p : AstTraversal.TraversePath) = + match p with + | TS.Expr(Operator "op_Range" _) :: _ -> true + | _ -> false + + let (|Setter|_|) e = + match e with + | Operator "op_Equality" (SynExpr.Ident id, _) -> Some id + | _ -> None + + let findSetters argList = + match argList with + | SynExpr.Paren (SynExpr.Tuple (false, parameters, _, _), _, _, _) -> + let setters = HashSet() + for p in parameters do + match p with + | Setter id -> ignore(setters.Add id.idText) + | _ -> () + setters + | _ -> emptyStringSet + + let endOfLastIdent (lid: LongIdentWithDots) = + let last = List.last lid.Lid + last.idRange.End + + let endOfClosingTokenOrLastIdent (mClosing: range option) (lid : LongIdentWithDots) = + match mClosing with + | Some m -> m.End + | None -> endOfLastIdent lid + + let endOfClosingTokenOrIdent (mClosing: range option) (id : Ident) = + match mClosing with + | Some m -> m.End + | None -> id.idRange.End + + let (|NewObjectOrMethodCall|_|) e = + match e with + | (SynExpr.New (_, SynType.LongIdent typeName, arg, _)) -> + // new A() + Some (endOfLastIdent typeName, findSetters arg) + | (SynExpr.New (_, SynType.App(StripParenTypes (SynType.LongIdent typeName), _, _, _, mGreaterThan, _, _), arg, _)) -> + // new A<_>() + Some (endOfClosingTokenOrLastIdent mGreaterThan typeName, findSetters arg) + | (SynExpr.App (_, false, SynExpr.Ident id, arg, _)) -> + // A() + Some (id.idRange.End, findSetters arg) + | (SynExpr.App (_, false, SynExpr.TypeApp (SynExpr.Ident id, _, _, _, mGreaterThan, _, _), arg, _)) -> + // A<_>() + Some (endOfClosingTokenOrIdent mGreaterThan id, findSetters arg) + | (SynExpr.App (_, false, SynExpr.LongIdent (_, lid, _, _), arg, _)) -> + // A.B() + Some (endOfLastIdent lid, findSetters arg) + | (SynExpr.App (_, false, SynExpr.TypeApp (SynExpr.LongIdent (_, lid, _, _), _, _, _, mGreaterThan, _, _), arg, _)) -> + // A.B<_>() + Some (endOfClosingTokenOrLastIdent mGreaterThan lid, findSetters arg) + | _ -> None + + let isOnTheRightOfComma (elements: SynExpr list) (commas: range list) current = + let rec loop elements (commas: range list) = + match elements with + | x :: xs -> + match commas with + | c :: cs -> + if x === current then posLt c.End pos || posEq c.End pos + else loop xs cs + | _ -> false + | _ -> false + loop elements commas + + let (|PartOfParameterList|_|) precedingArgument path = + match path with + | TS.Expr(SynExpr.Paren _) :: TS.Expr(NewObjectOrMethodCall args) :: _ -> + if Option.isSome precedingArgument then None else Some args + | TS.Expr(SynExpr.Tuple (false, elements, commas, _)) :: TS.Expr(SynExpr.Paren _) :: TS.Expr(NewObjectOrMethodCall args) :: _ -> + match precedingArgument with + | None -> Some args + | Some e -> + // if expression is passed then + // 1. find it in among elements of the tuple + // 2. find corresponding comma + // 3. check that current position is past the comma + // this is used for cases like (a = something-here.) if the cursor is after . + // in this case this is not object initializer completion context + if isOnTheRightOfComma elements commas e then Some args else None + | _ -> None + + let walker = + { + new AstTraversal.AstVisitorBase<_>() with + member __.VisitExpr(path, _, defaultTraverse, expr) = + + if isInRhsOfRangeOp path then + match defaultTraverse expr with + | None -> Some CompletionContext.RangeOperator // nothing was found - report that we were in the context of range operator + | x -> x // ok, we found something - return it + else + match expr with + // new A($) + | SynExpr.Const (SynConst.Unit, m) when rangeContainsPos m pos -> + match path with + | TS.Expr(NewObjectOrMethodCall args) :: _ -> + Some (CompletionContext.ParameterList args) + | _ -> + defaultTraverse expr + // new (... A$) + | SynExpr.Ident id when id.idRange.End = pos -> + match path with + | PartOfParameterList None args -> + Some (CompletionContext.ParameterList args) + | _ -> + defaultTraverse expr + // new (A$ = 1) + // new (A = 1, $) + | Setter id when id.idRange.End = pos || rangeBeforePos expr.Range pos -> + let precedingArgument = if id.idRange.End = pos then None else Some expr + match path with + | PartOfParameterList precedingArgument args-> + Some (CompletionContext.ParameterList args) + | _ -> + defaultTraverse expr + + | _ -> defaultTraverse expr + + member __.VisitRecordField(path, copyOpt, field) = + let contextFromTreePath completionPath = + // detect records usage in constructor + match path with + | TS.Expr(_) :: TS.Binding(_) :: TS.MemberDefn(_) :: TS.TypeDefn(SynTypeDefn.TypeDefn(ComponentInfo(_, _, _, [id], _, _, _, _), _, _, _)) :: _ -> + RecordContext.Constructor(id.idText) + | _ -> RecordContext.New completionPath + match field with + | Some field -> + match parseLid field with + | Some completionPath -> + let recordContext = + match copyOpt with + | Some (s : SynExpr) -> RecordContext.CopyOnUpdate(s.Range, completionPath) + | None -> contextFromTreePath completionPath + Some (CompletionContext.RecordField recordContext) + | None -> None + | None -> + let recordContext = + match copyOpt with + | Some s -> RecordContext.CopyOnUpdate(s.Range, ([], None)) + | None -> contextFromTreePath ([], None) + Some (CompletionContext.RecordField recordContext) + + member __.VisitInheritSynMemberDefn(componentInfo, typeDefnKind, synType, _members, _range) = + match synType with + | SynType.LongIdent lidwd -> + match parseLid lidwd with + | Some completionPath -> GetCompletionContextForInheritSynMember (componentInfo, typeDefnKind, completionPath) + | None -> Some (CompletionContext.Invalid) // A $ .B -> no completion list + + | _ -> None + + member __.VisitBinding(defaultTraverse, (Binding(headPat = headPat) as synBinding)) = + + let visitParam = function + | SynPat.Named (range = range) when rangeContainsPos range pos -> + // parameter without type hint, no completion + Some CompletionContext.Invalid + | SynPat.Typed(SynPat.Named(SynPat.Wild range, _, _, _, _), _, _) when rangeContainsPos range pos -> + // parameter with type hint, but we are on its name, no completion + Some CompletionContext.Invalid + | _ -> defaultTraverse synBinding + + match headPat with + | SynPat.LongIdent(longDotId = lidwd) when rangeContainsPos lidwd.Range pos -> + // let fo|o x = () + Some CompletionContext.Invalid + | SynPat.LongIdent(_, _, _, ctorArgs, _, _) -> + match ctorArgs with + | SynArgPats.Pats pats -> + pats |> List.tryPick (fun pat -> + match pat with + | SynPat.Paren(pat, _) -> + match pat with + | SynPat.Tuple(_, pats, _) -> + pats |> List.tryPick visitParam + | _ -> visitParam pat + | SynPat.Wild range when rangeContainsPos range pos -> + // let foo (x| + Some CompletionContext.Invalid + | _ -> visitParam pat + ) + | _ -> defaultTraverse synBinding + | SynPat.Named(range = range) when rangeContainsPos range pos -> + // let fo|o = 1 + Some CompletionContext.Invalid + | _ -> defaultTraverse synBinding + + member __.VisitHashDirective range = + if rangeContainsPos range pos then Some CompletionContext.Invalid + else None + + member __.VisitModuleOrNamespace(SynModuleOrNamespace(longId = idents)) = + match List.tryLast idents with + | Some lastIdent when pos.Line = lastIdent.idRange.EndLine && lastIdent.idRange.EndColumn >= 0 && pos.Column <= lineStr.Length -> + let stringBetweenModuleNameAndPos = lineStr.[lastIdent.idRange.EndColumn..pos.Column - 1] + if stringBetweenModuleNameAndPos |> Seq.forall (fun x -> x = ' ' || x = '.') then + Some CompletionContext.Invalid + else None + | _ -> None + + member __.VisitComponentInfo(ComponentInfo(range = range)) = + if rangeContainsPos range pos then Some CompletionContext.Invalid + else None + + member __.VisitLetOrUse(_, _, bindings, range) = + match bindings with + | [] when range.StartLine = pos.Line -> Some CompletionContext.Invalid + | _ -> None + + member __.VisitSimplePats pats = + pats |> List.tryPick (fun pat -> + match pat with + | SynSimplePat.Id(range = range) + | SynSimplePat.Typed(SynSimplePat.Id(range = range), _, _) when rangeContainsPos range pos -> + Some CompletionContext.Invalid + | _ -> None) + + member __.VisitModuleDecl(defaultTraverse, decl) = + match decl with + | SynModuleDecl.Open(target, m) -> + // in theory, this means we're "in an open" + // in practice, because the parse tree/walkers do not handle attributes well yet, need extra check below to ensure not e.g. $here$ + // open System + // [ true + | SynOpenDeclTarget.ModuleOrNamespace _ -> false + Some (CompletionContext.OpenDeclaration isOpenType) + else + None + | _ -> defaultTraverse decl + + member __.VisitType(defaultTraverse, ty) = + match ty with + | SynType.LongIdent _ when rangeContainsPos ty.Range pos -> + Some CompletionContext.PatternType + | _ -> defaultTraverse ty + } + + AstTraversal.Traverse(pos, parsedInput, walker) + // Uncompleted attribute applications are not presented in the AST in any way. So, we have to parse source string. + |> Option.orElseWith (fun _ -> + let cutLeadingAttributes (str: string) = + // cut off leading attributes, i.e. we cut "[]" to " >]" + match str.LastIndexOf ';' with + | -1 -> str + | idx when idx < str.Length -> str.[idx + 1..].TrimStart() + | _ -> "" + + let isLongIdent = Seq.forall (fun c -> IsIdentifierPartCharacter c || c = '.' || c = ':') // ':' may occur in "[]" + + // match the most nested paired [< and >] first + let matches = + insideAttributeApplicationRegex.Matches lineStr + |> Seq.cast + |> Seq.filter (fun m -> m.Index <= pos.Column && m.Index + m.Length >= pos.Column) + |> Seq.toArray + + if not (Array.isEmpty matches) then + matches + |> Seq.tryPick (fun m -> + let g = m.Groups.["attribute"] + let col = pos.Column - g.Index + if col >= 0 && col < g.Length then + let str = g.Value.Substring(0, col).TrimStart() // cut other rhs attributes + let str = cutLeadingAttributes str + if isLongIdent str then + Some CompletionContext.AttributeApplication + else None + else None) + else + // Paired [< and >] were not found, try to determine that we are after [< without closing >] + match lineStr.LastIndexOf("[<", StringComparison.Ordinal) with + | -1 -> None + | openParenIndex when pos.Column >= openParenIndex + 2 -> + let str = lineStr.[openParenIndex + 2..pos.Column - 1].TrimStart() + let str = cutLeadingAttributes str + if isLongIdent str then + Some CompletionContext.AttributeApplication + else None + | _ -> None) + + /// Check if we are at an "open" declaration + let GetFullNameOfSmallestModuleOrNamespaceAtPoint (parsedInput: ParsedInput, pos: pos) = + let mutable path = [] + let visitor = + { new AstTraversal.AstVisitorBase() with + override this.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = + // don't need to keep going, namespaces and modules never appear inside Exprs + None + override this.VisitModuleOrNamespace(SynModuleOrNamespace(longId = longId; range = range)) = + if rangeContainsPos range pos then + path <- path @ longId + None // we should traverse the rest of the AST to find the smallest module + } + AstTraversal.Traverse(pos, parsedInput, visitor) |> ignore + path |> List.map (fun x -> x.idText) |> List.toArray diff --git a/src/fsharp/service/ServiceUntypedParse.fsi b/src/fsharp/service/ServiceUntypedParse.fsi new file mode 100755 index 00000000000..2cf813b884f --- /dev/null +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +//---------------------------------------------------------------------------- +// API to the compiler as an incremental service for parsing, +// type checking and intellisense-like environment-reporting. +//---------------------------------------------------------------------------- + +namespace FSharp.Compiler.SourceCodeServices + +open System.Collections.Generic + +open FSharp.Compiler.Range +open FSharp.Compiler.SyntaxTree + +[] +/// Represents the results of parsing an F# file +type public FSharpParseFileResults = + + /// The syntax tree resulting from the parse + member ParseTree : ParsedInput option + + /// Notable parse info for ParameterInfo at a given location + member FindNoteworthyParamInfoLocations : pos:pos -> FSharpNoteworthyParamInfoLocations option + + /// Gets the ranges of all arguments, if they can be found, for a function application at the given position. + member GetAllArgumentsForFunctionApplicationAtPostion: pos: pos -> range list option + + /// Determines if the expression or pattern at the given position has a type annotation + member IsTypeAnnotationGivenAtPosition: pos -> bool + + /// Determines if the binding at the given position is bound to a lambda expression + member IsBindingALambdaAtPosition: pos -> bool + + /// Name of the file for which this information were created + member FileName: string + + /// Get declared items and the selected item at the specified location + member GetNavigationItems: unit -> FSharpNavigationItems + + /// Return the inner-most range associated with a possible breakpoint location + member ValidateBreakpointLocation : pos:pos -> range option + + /// When these files change then the build is invalid + member DependencyFiles : string[] + + /// Get the errors and warnings for the parse + member Errors : FSharpErrorInfo[] + + /// Indicates if any errors occurred during the parse + member ParseHadErrors : bool + + internal new: errors: FSharpErrorInfo[] * input: ParsedInput option * parseHadErrors: bool * dependencyFiles: string[] -> FSharpParseFileResults + +/// Information about F# source file names +module public SourceFile = + + /// Whether or not this file is compilable + val IsCompilable : string -> bool + + /// Whether or not this file should be a single-file project + val MustBeSingleFileProject : string -> bool + +type public CompletionPath = string list * string option // plid * residue + +[] +type public InheritanceContext = + | Class + | Interface + | Unknown + +[] +type public RecordContext = + | CopyOnUpdate of range * CompletionPath // range + | Constructor of string // typename + | New of CompletionPath + +[] +type public 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 + + | AttributeApplication + + | OpenDeclaration of isOpenType: bool + + /// completing pattern type (e.g. foo (x: |)) + | PatternType + +type public ModuleKind = { IsAutoOpen: bool; HasModuleSuffix: bool } + +[] +type public EntityKind = + | Attribute + | Type + | FunctionOrValue of isActivePattern:bool + | Module of ModuleKind + +// implementation details used by other code in the compiler +module public UntypedParseImpl = + val TryFindExpressionASTLeftOfDotLeftOfCursor : pos * ParsedInput option -> (pos * bool) option + + val GetRangeOfExprLeftOfDot : pos * ParsedInput option -> range option + + val TryFindExpressionIslandInPosition : pos * ParsedInput option -> string option + + val TryGetCompletionContext : pos * ParsedInput * lineStr: string -> CompletionContext option + + val GetEntityKind: pos * ParsedInput -> EntityKind option + + val GetFullNameOfSmallestModuleOrNamespaceAtPoint : ParsedInput * pos -> string[] + +// implementation details used by other code in the compiler +module internal SourceFileImpl = + + val IsInterfaceFile : string -> bool + + val AdditionalDefinesForUseInEditor: isInteractive: bool -> string list + diff --git a/src/fsharp/symbols/SymbolHelpers.fsi b/src/fsharp/symbols/SymbolHelpers.fsi index d31bc529493..86005da7134 100755 --- a/src/fsharp/symbols/SymbolHelpers.fsi +++ b/src/fsharp/symbols/SymbolHelpers.fsi @@ -127,6 +127,7 @@ namespace FSharp.Compiler.Symbols // Implementation details used by other code in the compiler module internal SymbolHelpers = + val ParamNameAndTypesOfUnaryCustomOperation : TcGlobals -> MethInfo -> ParamNameAndType list val GetXmlCommentForItem : InfoReader -> range -> Item -> FSharpXmlDoc diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 5502a4e232e..c5feba4a2c2 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -1787,7 +1787,12 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | M m | C m -> m.IsDispatchSlot | V v -> v.IsDispatchSlot - member _.IsProperty = + member _.IsMethod = + match d with + | M _ -> true + | _ -> false + + member x.IsProperty = match d with | P _ -> true | _ -> false @@ -2224,10 +2229,10 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = prefix + x.LogicalName with _ -> "??" - member x.FormatLayout (context:FSharpDisplayContext) = + member x.FormatLayout (displayContext: FSharpDisplayContext) = match x.IsMember, d with | true, V v -> - NicePrint.prettyLayoutOfMemberNoInstShort { (context.Contents cenv.g) with showMemberContainers=true } v.Deref + NicePrint.prettyLayoutOfMemberNoInstShort { (displayContext.Contents cenv.g) with showMemberContainers=true } v.Deref |> LayoutRender.toArray | _,_ -> checkIsResolved() @@ -2240,9 +2245,29 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = let argtysl = m.GetParamTypes(cenv.amap, range0, m.FormalMethodInst) mkIteratedFunTy cenv.g (List.map (mkRefTupledTy cenv.g) argtysl) rty | V v -> v.TauType - NicePrint.prettyLayoutOfTypeNoCx (context.Contents cenv.g) ty + NicePrint.prettyLayoutOfTypeNoCx (displayContext.Contents cenv.g) ty |> LayoutRender.toArray + member x.GetReturnTypeLayout (displayContext: FSharpDisplayContext) = + match x.IsMember, d with + | true, _ -> + None + | false, _ -> + checkIsResolved() + match d with + | E _ + | P _ + | C _ -> None + | M m -> + let rty = m.GetFSharpReturnTy(cenv.amap, range0, m.FormalMethodInst) + NicePrint.layoutType (displayContext.Contents cenv.g) rty + |> LayoutRender.toArray + |> Some + | V v -> + NicePrint.layoutOfValReturnType (displayContext.Contents cenv.g) v + |> LayoutRender.toArray + |> Some + member x.GetWitnessPassingInfo() = let witnessInfos = match d with diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index ffeea9af46f..87af638e879 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -787,6 +787,9 @@ type FSharpMemberOrFunctionOrValue = /// Indicates if this is a property member member IsProperty: bool + /// Indicates if this is a method member + member IsMethod : bool + /// Indicates if this is a property and there exists an associated getter method member HasGetterMethod: bool @@ -865,7 +868,13 @@ type FSharpMemberOrFunctionOrValue = /// Get the name as presented in F# error messages and documentation member DisplayName: string - member CurriedParameterGroups: IList> + /// List of list of parameters, where each nested item represents a defined parameter + /// + /// Typically, there is only one nested list. + /// However, code such as 'f (a, b) (c, d)' contains two groups, each with two parameters. + /// In that example, there is a list made up of two lists, each with a parameter. + /// + member CurriedParameterGroups : IList> /// Gets the overloads for the current method. /// @@ -909,13 +918,19 @@ type FSharpMemberOrFunctionOrValue = /// Indicated if this is a value member IsValue: bool + + /// Indicated if this is a function + member IsFunction : bool /// Indicates if this is a constructor. member IsConstructor: bool /// Format the type using the rules of the given display context - member FormatLayout: context: FSharpDisplayContext -> TaggedText[] - + member FormatLayout: displayContext: FSharpDisplayContext -> TaggedText[] + + /// Format the type using the rules of the given display context + member GetReturnTypeLayout: displayContext: FSharpDisplayContext -> TaggedText[] option + /// Check if this method has an entrpoint that accepts witness arguments and if so return /// the name of that entrypoint and information about the additional witness arguments member GetWitnessPassingInfo: unit -> (string * IList) option diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected index f72fc2a7864..64d3492d90e 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected @@ -3440,9 +3440,11 @@ FSharp.Compiler.EditorServices.OpenStatementInsertionPoint: System.String ToStri FSharp.Compiler.EditorServices.ParameterLocations FSharp.Compiler.EditorServices.ParameterLocations: Boolean IsThereACloseParen FSharp.Compiler.EditorServices.ParameterLocations: Boolean get_IsThereACloseParen() +FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.EditorServices.TupledArgumentLocation[] ArgumentLocations FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.Text.Position LongIdEndLocation FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.Text.Position LongIdStartLocation FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.Text.Position OpenParenLocation +FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.EditorServices.TupledArgumentLocation[] get_ArgumentLocations() FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.Text.Position get_LongIdEndLocation() FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.Text.Position get_LongIdStartLocation() FSharp.Compiler.EditorServices.ParameterLocations: FSharp.Compiler.Text.Position get_OpenParenLocation() @@ -3980,6 +3982,18 @@ FSharp.Compiler.EditorServices.ToolTipText: Int32 get_Tag() FSharp.Compiler.EditorServices.ToolTipText: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.EditorServices.ToolTipElement] Item FSharp.Compiler.EditorServices.ToolTipText: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.EditorServices.ToolTipElement] get_Item() FSharp.Compiler.EditorServices.ToolTipText: System.String ToString() +FSharp.Compiler.EditorServices.TupledArgumentLocation +FSharp.Compiler.EditorServices.TupledArgumentLocation: Boolean Equals(FSharp.Compiler.EditorServices.TupledArgumentLocation) +FSharp.Compiler.EditorServices.TupledArgumentLocation: Boolean Equals(System.Object) +FSharp.Compiler.EditorServices.TupledArgumentLocation: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.EditorServices.TupledArgumentLocation: Boolean IsNamedArgument +FSharp.Compiler.EditorServices.TupledArgumentLocation: Boolean get_IsNamedArgument() +FSharp.Compiler.EditorServices.TupledArgumentLocation: FSharp.Compiler.Text.Range ArgumentRange +FSharp.Compiler.EditorServices.TupledArgumentLocation: FSharp.Compiler.Text.Range get_ArgumentRange() +FSharp.Compiler.EditorServices.TupledArgumentLocation: Int32 GetHashCode() +FSharp.Compiler.EditorServices.TupledArgumentLocation: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.EditorServices.TupledArgumentLocation: System.String ToString() +FSharp.Compiler.EditorServices.TupledArgumentLocation: Void .ctor(Boolean, FSharp.Compiler.Text.Range) FSharp.Compiler.EditorServices.UnresolvedSymbol FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(FSharp.Compiler.EditorServices.UnresolvedSymbol) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(System.Object) @@ -4822,6 +4836,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsInstanceMember FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsInstanceMemberInCompiledCode FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsMember FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsMemberThisValue +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsMethod FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsModuleValueOrMember FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsMutable FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsOverrideOrExplicitInterfaceImplementation @@ -4852,6 +4867,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsInstanceMem FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsInstanceMemberInCompiledCode() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsMember() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsMemberThisValue() +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsMethod() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsModuleValueOrMember() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsMutable() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsOverrideOrExplicitInterfaceImplementation() @@ -4908,6 +4924,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.Collections.Generi FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpGenericParameter] get_GenericParameters() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpParameter]] CurriedParameterGroups FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpParameter]] get_CurriedParameterGroups() +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]] GetReturnTypeLayout(FSharp.Compiler.Symbols.FSharpDisplayContext) FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.String CompiledName FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.String DisplayName FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: System.String LogicalName diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 907f6f0262a..d4a78ca1041 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -10,4 +10,4 @@ type SurfaceAreaTest() = member _.VerifyArea() = let expectedFile = System.IO.Path.Combine(__SOURCE_DIRECTORY__,"FSharp.CompilerService.SurfaceArea.netstandard.expected") let actualFile = System.IO.Path.Combine(__SOURCE_DIRECTORY__,"FSharp.CompilerService.SurfaceArea.netstandard.actual") - SurfaceArea.verify expectedFile actualFile \ No newline at end of file + SurfaceArea.verify expectedFile actualFile diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index ef7642666e9..c83268e100c 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -1474,4 +1474,4 @@ let f x = let f x y = x + y """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "'f' is not a lambda expression'") + Assert.IsFalse(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "'f' is not a lambda expression'")