diff --git a/eng/Versions.props b/eng/Versions.props index 4d4f0b03af3..7d714d3ceb6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -97,7 +97,7 @@ 4.3.0 4.5.0 - 3.9.0-1.20512.2 + 3.9.0-2.20516.5 $(RoslynVersion) $(RoslynVersion) $(RoslynVersion) diff --git a/global.json b/global.json index 5fed4a7295e..c2dea3f313b 100644 --- a/global.json +++ b/global.json @@ -1,10 +1,9 @@ { "sdk": { - "version": "3.1.302", - "rollForward": "minor" + "version": "3.1.403" }, "tools": { - "dotnet": "3.1.302", + "dotnet": "3.1.403", "vs": { "version": "16.4", "components": [ diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index cdfbf1ee89a..23282e25dd5 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -1198,7 +1198,18 @@ module private 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() @@ -2223,7 +2234,9 @@ let prettyLayoutOfValOrMember denv typarInst v = PrintTastMemberOrVals.prettyLay let prettyLayoutOfValOrMemberNoInst denv v = PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv v -let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v +let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v + +let layoutOfValReturnType denv x = x |> PrintTypes.layoutOfValReturnType denv let prettyLayoutOfInstAndSig denv x = PrintTypes.prettyLayoutOfInstAndSig denv x diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index 8ce4839b3da..464d6c6fa1d 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -2844,7 +2844,7 @@ let tagEntityRefName (xref: EntityRef) name = elif xref.IsFSharpDelegateTycon then tagDelegate name elif xref.IsILEnumTycon || xref.IsFSharpEnumTycon then tagEnum name elif xref.IsStructOrEnumTycon then tagStruct name - elif xref.IsFSharpInterfaceTycon then tagInterface name + elif isInterfaceTyconRef xref then tagInterface name elif xref.IsUnionTycon then tagUnion name elif xref.IsRecordTycon then tagRecord name else tagClass name diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index d70052a74c1..0585c921242 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -6,8 +6,19 @@ open FSharp.Compiler.SyntaxTree open FSharp.Compiler.SyntaxTreeOps open FSharp.Compiler.Range +type TupledArgumentLocation = { IsNamedArgument: bool; ArgumentRange: range } + [] -type FSharpNoteworthyParamInfoLocations(longId: string list, longIdRange: range, openParenLocation: pos, tupleEndLocations: pos list, isThereACloseParen: bool, namedParamNames: string option list) = +type FSharpNoteworthyParamInfoLocations + ( + 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 @@ -28,6 +39,7 @@ type FSharpNoteworthyParamInfoLocations(longId: string list, longIdRange: range, member this.TupleEndLocations = tupleEndLocations member this.IsThereACloseParen = isThereACloseParen member this.NamedParamNames = namedParamNames + member this.ArgumentLocations = argRanges |> Array.ofList [] module internal NoteworthyParamInfoLocationsImpl = @@ -50,7 +62,7 @@ module internal NoteworthyParamInfoLocationsImpl = | _ -> 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) = @@ -86,7 +98,8 @@ module internal NoteworthyParamInfoLocationsImpl = match inner with | None -> if AstTraversal.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 @@ -103,8 +116,12 @@ module internal NoteworthyParamInfoLocationsImpl = match inner with | None -> if AstTraversal.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 @@ -123,14 +140,14 @@ module internal NoteworthyParamInfoLocationsImpl = | SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF if AstTraversal.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 AstTraversal.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 @@ -141,7 +158,7 @@ module internal NoteworthyParamInfoLocationsImpl = | None -> if AstTraversal.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 @@ -153,7 +170,7 @@ module internal NoteworthyParamInfoLocationsImpl = let betweenTheBrackets = mkRange wholem.FileName openm.Start wholem.End if AstTraversal.rangeContainsPosEdgesExclusive betweenTheBrackets pos && args |> List.forall isStaticArg then let commasAndCloseParen = [ for c in commas -> c.End ] @ [ wholem.End ] - Some (FSharpNoteworthyParamInfoLocations(pathOfLid lid, lidm, openm.Start, commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) + Some (FSharpNoteworthyParamInfoLocations(pathOfLid lid, lidm, openm.Start, [], commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) else None | _ -> @@ -169,9 +186,9 @@ module internal NoteworthyParamInfoLocationsImpl = | 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 (FSharpNoteworthyParamInfoLocations(typeName, synType.Range, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd)) + Some (FSharpNoteworthyParamInfoLocations(typeName, synType.Range, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd)) | NotFound, Some cache -> cache | _ -> @@ -190,7 +207,7 @@ module internal NoteworthyParamInfoLocationsImpl = if AstTraversal.rangeContainsPosEdgesExclusive typeArgsm pos then // We found it, dig out ident match digOutIdentFromFuncExpr synExpr with - | Some(lid, lidRange) -> Some (FSharpNoteworthyParamInfoLocations(lid, lidRange, op.idRange.Start, [ wholem.End ], false, [])) + | Some(lid, lidRange) -> Some (FSharpNoteworthyParamInfoLocations(lid, lidRange, op.idRange.Start, [], [ wholem.End ], false, [])) | None -> None else None @@ -205,7 +222,7 @@ module internal NoteworthyParamInfoLocationsImpl = // 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) -> @@ -215,7 +232,7 @@ module internal NoteworthyParamInfoLocationsImpl = // For now, we don't support infix operators. None else - Some (FSharpNoteworthyParamInfoLocations(lid, lidRange, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd)) + Some (FSharpNoteworthyParamInfoLocations(lid, lidRange, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd)) | None -> None | NotFound, Some cache -> cache | _ -> traverseSynExpr synExpr2 @@ -228,7 +245,8 @@ module internal NoteworthyParamInfoLocationsImpl = let typeArgsm = mkRange openm.FileName openm.Start wholem.End if AstTraversal.rangeContainsPosEdgesExclusive typeArgsm pos && tyArgs |> List.forall isStaticArg then let commasAndCloseParen = [ for c in commas -> c.End ] @ [ wholem.End ] - let r = FSharpNoteworthyParamInfoLocations(["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 = FSharpNoteworthyParamInfoLocations(["dummy"], synExpr.Range, openm.Start, argRanges, commasAndCloseParen, closemOpt.IsSome, tyArgs |> List.map digOutIdentFromStaticArg) Some r else None @@ -249,10 +267,10 @@ module internal NoteworthyParamInfoLocationsImpl = // 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 = FSharpNoteworthyParamInfoLocations(typeName, ty.Range, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd) + let r = FSharpNoteworthyParamInfoLocations(typeName, ty.Range, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd) Some r | NotFound -> None else None @@ -272,3 +290,58 @@ type FSharpNoteworthyParamInfoLocations with r | _ -> None +module internal SynExprAppLocationsImpl = + let rec private searchSynArgExpr traverseSynExpr expr ranges = + match expr with + | SynExpr.Const(SynConst.Unit, _) -> + None, None + + | SynExprParen(SynExpr.Tuple (_, exprs, _commas, _tupRange), _, _, _parenRange) -> + let rec loop (exprs: SynExpr list) ranges = + match exprs with + | [] -> ranges + | h::t -> + loop t (h.Range :: ranges) + + let res = loop exprs ranges + Some (res), None + + | SynExprParen(SynExprParen(_, _, _, _) as synExpr, _, _, _parenRange) -> + let r, _cacheOpt = searchSynArgExpr traverseSynExpr synExpr ranges + r, None + + | SynExprParen(SynExpr.App (_, _isInfix, _, _, _range), _, _, parenRange) -> + Some (parenRange :: ranges), None + + | e -> + let inner = traverseSynExpr e + match inner with + | None -> + Some (e.Range :: ranges), Some inner + | _ -> None, Some inner + + let getAllCurriedArgsAtPosition pos parseTree = + AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with + member _.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) = + match expr with + | SynExpr.App (_exprAtomicFlag, _isInfix, funcExpr, argExpr, range) when posEq pos range.Start -> + let isInfixFuncExpr = + match funcExpr with + | SynExpr.App (_, isInfix, _, _, _) -> isInfix + | _ -> false + + if isInfixFuncExpr then + traverseSynExpr funcExpr + else + let workingRanges = + match traverseSynExpr funcExpr with + | Some ranges -> ranges + | None -> [] + + let xResult, cacheOpt = searchSynArgExpr traverseSynExpr argExpr workingRanges + match xResult, cacheOpt with + | Some ranges, _ -> Some ranges + | None, Some cache -> cache + | _ -> traverseSynExpr argExpr + | _ -> defaultTraverse expr }) + |> Option.map List.rev diff --git a/src/fsharp/service/ServiceParamInfoLocations.fsi b/src/fsharp/service/ServiceParamInfoLocations.fsi index 618ced81d12..4ce27fc0169 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fsi +++ b/src/fsharp/service/ServiceParamInfoLocations.fsi @@ -10,6 +10,9 @@ namespace FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Range open FSharp.Compiler.SyntaxTree +/// 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 FSharpNoteworthyParamInfoLocations = @@ -33,8 +36,13 @@ type public FSharpNoteworthyParamInfoLocations = 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 -> FSharpNoteworthyParamInfoLocations option +module internal SynExprAppLocationsImpl = + val getAllCurriedArgsAtPosition: pos: pos -> parseTree: ParsedInput -> range list option diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index bd24fef3fad..3228be7e5cd 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -104,6 +104,61 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option 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() = diff --git a/src/fsharp/service/ServiceUntypedParse.fsi b/src/fsharp/service/ServiceUntypedParse.fsi index b88bc0efec3..2cf813b884f 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -22,11 +22,20 @@ type public FSharpParseFileResults = /// 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 + member FileName: string /// Get declared items and the selected item at the specified location - member GetNavigationItems : unit -> FSharpNavigationItems + member GetNavigationItems: unit -> FSharpNavigationItems /// Return the inner-most range associated with a possible breakpoint location member ValidateBreakpointLocation : pos:pos -> range option diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 59b36bcc0d4..d1919bb6717 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -1645,6 +1645,11 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | M m | C m -> m.IsDispatchSlot | V v -> v.IsDispatchSlot + member _.IsMethod = + match d with + | M _ -> true + | _ -> false + member x.IsProperty = match d with | P _ -> true @@ -2062,6 +2067,11 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | V valRef -> not (SymbolHelpers.isFunction cenv.g valRef.Type) | _ -> false + member x.IsFunction = + match d with + | V valRef -> SymbolHelpers.isFunction cenv.g valRef.Type + | _ -> false + override x.Equals(other: obj) = box x === other || match other with @@ -2082,10 +2092,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 | _,_ -> checkIsResolved() let ty = @@ -2097,8 +2107,26 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = let argtysl = m.GetParamTypes(cenv.amap, range0, m.FormalMethodInst) mkIteratedFunTy (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 + 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 + |> Some + | V v -> + NicePrint.layoutOfValReturnType (displayContext.Contents cenv.g) v + |> Some + member x.GetWitnessPassingInfo() = let witnessInfos = match d with diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index f364f2c1f36..cb21f5a0791 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -772,6 +772,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 @@ -858,7 +861,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 /// matchParameterNumber indicates whether to filter the overloads to match the number of parameters in the current symbol @@ -900,13 +909,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 -> Layout - + member FormatLayout: displayContext: FSharpDisplayContext -> Layout + + /// Format the type using the rules of the given display context + member GetReturnTypeLayout: displayContext: FSharpDisplayContext -> Layout 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/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 1f94595981f..52220db873b 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22437,11 +22437,13 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsOver FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsProperty FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsPropertyGetterMethod FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsPropertySetterMethod +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsMethod FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsSetterMethod FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsTypeFunction FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsUnresolved FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsValCompiledAsMethod FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsValue +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsFunction FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_EventIsStandard() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_HasGetterMethod() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_HasSetterMethod() @@ -22470,11 +22472,13 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_Is FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsProperty() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsPropertyGetterMethod() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsPropertySetterMethod() +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsMethod() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsSetterMethod() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsTypeFunction() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsUnresolved() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsValCompiledAsMethod() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsValue() +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_IsFunction() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: FSharp.Compiler.SourceCodeServices.FSharpAccessibility Accessibility FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: FSharp.Compiler.SourceCodeServices.FSharpAccessibility get_Accessibility() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: FSharp.Compiler.SourceCodeServices.FSharpAssembly Assembly @@ -22500,6 +22504,7 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: FSharp.Compile FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Int32 GetEffectivelySameAsHash() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Int32 GetHashCode() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Internal.Utilities.StructuredFormat.Layout FormatLayout(FSharp.Compiler.SourceCodeServices.FSharpDisplayContext) +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Internal.Utilities.StructuredFormat.Layout] GetReturnTypeLayout(FSharp.Compiler.SourceCodeServices.FSharpDisplayContext) FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] DeclarationLocation FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] ImplementationLocation FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] SignatureLocation @@ -22672,6 +22677,19 @@ FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: pos get_L FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: pos get_OpenParenLocation() FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: pos[] TupleEndLocations FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: pos[] get_TupleEndLocations() +FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: FSharp.Compiler.SourceCodeServices.TupledArgumentLocation[] ArgumentLocations +FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: FSharp.Compiler.SourceCodeServices.TupledArgumentLocation[] get_ArgumentLocations() +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Boolean Equals(FSharp.Compiler.SourceCodeServices.TupledArgumentLocation) +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Boolean Equals(System.Object) +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Boolean IsNamedArgument +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Boolean get_IsNamedArgument() +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Int32 GetHashCode() +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: System.String ToString() +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: Void .ctor(Boolean, range) +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: range ArgumentRange +FSharp.Compiler.SourceCodeServices.TupledArgumentLocation: range get_ArgumentRange() FSharp.Compiler.SourceCodeServices.FSharpObjectExprOverride: FSharp.Compiler.SourceCodeServices.FSharpAbstractSignature Signature FSharp.Compiler.SourceCodeServices.FSharpObjectExprOverride: FSharp.Compiler.SourceCodeServices.FSharpAbstractSignature get_Signature() FSharp.Compiler.SourceCodeServices.FSharpObjectExprOverride: FSharp.Compiler.SourceCodeServices.FSharpExpr Body @@ -22737,6 +22755,9 @@ FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.Sourc FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.SourceCodeServices.FSharpNavigationItems GetNavigationItems() FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] ValidateBreakpointLocation(pos) FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations] FindNoteworthyParamInfoLocations(pos) +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Boolean IsTypeAnnotationGivenAtPosition(pos) +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(pos) +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Range+range]] GetAllArgumentsForFunctionApplicationAtPostion(pos) FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SyntaxTree+ParsedInput] ParseTree FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SyntaxTree+ParsedInput] get_ParseTree() FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: System.String FileName diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 18c7f7318d1..86185b69e51 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -305,3 +305,506 @@ type T = new (x:int) = () """ getTypeMemberRange source |> shouldEqual [ (3, 4), (3, 20) ] + +module FunctionApplicationArguments = + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - Single arg``() = + let source = """ +let f x = () +f 12 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - Multi arg``() = + let source = """ +let f x y z = () +f 1 2 3 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2); (3, 4); (3, 6)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - Multi arg parentheses``() = + let source = """ +let f x y z = () +f (1) (2) (3) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2); (3, 6); (3, 10)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - Multi arg nested parentheses``() = + let source = """ +let f x y z = () +f ((1)) (((2))) ((((3)))) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 3); (3, 10); (3, 19)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - unit``() = + let source = """ +let f () = () +f () +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + Assert.IsTrue(res.IsNone, "Found argument for unit-accepting function, which shouldn't be the case.") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - curried function``() = + let source = """ +let f x y = x + y +f 12 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple value``() = + let source = """ +let f (t: int * int) = () +let t = (1, 2) +f t +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 4 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(4, 2)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple literal``() = + let source = """ +let f (t: int * int) = () +f (1, 2) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 3); (3, 6)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple value with definition that has explicit names``() = + let source = """ +let f ((x, y): int * int) = () +let t = (1, 2) +f t +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 4 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(4, 2)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple literal inside parens``() = + let source = """ +let f (x, y) = () +f ((1, 2)) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 4); (3, 7)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuples with elements as arguments``() = + let source = """ +let f (a, b) = () +f (1, 2) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 3); (3, 6)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - top-level arguments with nested function call``() = + let source = """ +let f x y = x + y +f (f 1 2) 3 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2); (3, 10)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function argument positions``() = + let source = """ +let f x y = x + y +f (f 1 2) 3 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 3) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 5); (3, 7)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function application in infix expression``() = + let source = """ +let addStr x y = string x + y +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 17) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(2, 24)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function application outside of infix expression``() = + let source = """ +let addStr x y = x + string y +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 21) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(2, 28)] + | None -> + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function applications both inside and outside of infix expression``() = + let source = """ +let addStr x y = string x + string y +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 17) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(2, 24)] + | None -> + Assert.Fail("No arguments found in source code") + + + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 28) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(2, 35)] + | None -> + Assert.Fail("No arguments found in source code") + +module TypeAnnotations = + [] + let ``IsTypeAnnotationGivenAtPosition - function - no annotation``() = + let source = """ +let f x = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 6), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGivenAtPosition - function - single arg annotation``() = + let source = """ +let f (x: int) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGivenAtPosition - function - first arg annotated``() = + let source = """ +let f (x: int) y = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 15), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGivenAtPosition - function - second arg annotated``() = + let source = """ +let f x (y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.False(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 9), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - function - all args annotated``() = + let source = """ +let f (x: int) (y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 16), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - lambda function - all args annotated``() = + let source = """ +let f = fun (x: int) (y: string) -> () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 13), "Expected a annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 22), "Expected a annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - constuctor - arg no annotations``() = + let source = """ +type C(x) = class end +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGivenAtPosition - constuctor - first arg unannotated``() = + let source = """ +type C(x, y: string) = class end +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 10), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - constuctor - second arg unannotated``() = + let source = """ +type C(x: int, y) = class end + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 15), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - constuctor - both args annotated``() = + let source = """ +type C(x: int, y: int) = class end + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 15), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method - args no unannotions``() = + let source = """ +type C() = + member _.M(x, y) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 18), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method - first arg annotated``() = + let source = """ +type C() = + member _.M(x: int, y) = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 23), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method - second arg annotated``() = + let source = """ +type C() = + member _.M(x, y: int) = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 18), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method - both args annotated``() = + let source = """ +type C() = + member _.M(x: int, y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 23), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method currying - args no unannotions``() = + let source = """ +type C() = + member _.M x y = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 17), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method currying - first arg annotated``() = + let source = """ +type C() = + member _.M (x: int) y = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 16), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 24), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method currying - second arg annotated``() = + let source = """ +type C() = + member _.M x (y: int) = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 16), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 18), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method currying - both args annotated``() = + let source = """ +type C() = + member _.M (x: int) (y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 16), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 25), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - method - only return type annotated``() = + let source = """ +type C() = + member _.M(x): string = "hello" + x +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGivenAtPosition - tuple - no annotations``() = + let source = """ +let (x, y) = (12, "hello") +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 5), "Expected no annotation for value 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 8), "Expected no annotation for value 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - tuple - first value annotated``() = + let source = """ +let (x: int, y) = (12, "hello") +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 5), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 13), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGivenAtPosition - tuple - second value annotated``() = + let source = """ +let (x, y: string) = (12, "hello") +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 5), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 8), "Expected annotation for argument 'y'") + +module LambdaRecognition = + [] + let ``IsBindingALambdaAtPosition - recognize a lambda``() = + let source = """ +let f = fun x y -> x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "Expected 'f' to be a lambda expression") + + [] + let ``IsBindingALambdaAtPosition - recognize a nested lambda``() = + let source = """ +let f = + fun x -> + fun y -> + x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "Expected 'f' to be a lambda expression") + + [] + let ``IsBindingALambdaAtPosition - recognize a "partial" lambda``() = + let source = """ +let f x = + fun y -> + x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "Expected 'f' to be a lambda expression") + + [] + let ``IsBindingALambdaAtPosition - not a lambda``() = + let source = """ +let f x y = x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "'f' is not a lambda expression'") \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 17e2724d6ee..39c4fca7de6 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -10,8 +10,6 @@ open System.Threading.Tasks open System.Linq open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Editor.Implementation.Debugging -open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Implementation.Debugging diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index cf37cf08aa6..a69b9106bc4 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -2,7 +2,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System open System.Composition open System.Collections.Generic open System.Threading @@ -10,8 +9,6 @@ open System.Threading.Tasks open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor.Implementation.Debugging -open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Implementation.Debugging diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 8bc59c35a19..d66c085a386 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -84,6 +84,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs new file mode 100644 index 00000000000..fc8035597a7 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +namespace Microsoft.VisualStudio.FSharp.Editor + +open System +open System.Collections.Immutable +open System.Threading +open System.ComponentModel.Composition + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints + +open FSharp.Compiler +open FSharp.Compiler.SourceCodeServices +open FSharp.Compiler.Range + +[)>] +type internal FSharpInlineHintsService + [] + ( + checkerProvider: FSharpCheckerProvider, + projectInfoManager: FSharpProjectOptionsManager + ) = + + static let userOpName = "FSharpInlineHints" + + static let getFirstPositionAfterParen (str: string) startPos = + match str with + | null -> -1 + | str when startPos > str.Length -> -1 + | str -> + str.IndexOf('(') + 1 + + interface IFSharpInlineHintsService with + member _.GetInlineHintsAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken) = + asyncMaybe { + do! Option.guard (not (isSignatureFile document.FilePath)) + + let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) + let! sourceText = document.GetTextAsync(cancellationToken) + let! parseFileResults, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, userOpName) + let range = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) + let symbolUses = + checkFileResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) + |> Seq.filter (fun su -> rangeContainsRange range su.RangeAlternate) + + let typeHints = ImmutableArray.CreateBuilder() + let parameterHints = ImmutableArray.CreateBuilder() + + let isValidForTypeHint (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + let isLambdaIfFunction = + funcOrValue.IsFunction && + parseFileResults.IsBindingALambdaAtPosition symbolUse.RangeAlternate.Start + + (funcOrValue.IsValue || isLambdaIfFunction) && + not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.RangeAlternate.Start) && + symbolUse.IsFromDefinition && + not funcOrValue.IsMember && + not funcOrValue.IsMemberThisValue && + not funcOrValue.IsConstructorThisValue && + not (PrettyNaming.IsOperatorName funcOrValue.DisplayName) + + for symbolUse in symbolUses do + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse -> + let typeInfo = ResizeArray() + + let layout = + funcOrValue.GetReturnTypeLayout symbolUse.DisplayContext + |> Option.defaultValue Layout.emptyL + + layout + |> Layout.renderL (Layout.taggedTextListR typeInfo.Add) + |> ignore + + let displayParts = ImmutableArray.CreateBuilder() + displayParts.Add(TaggedText(TextTags.Text, ": ")) + + for tt in typeInfo do + displayParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) + + let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) + + let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray()) + typeHints.Add(hint) + + | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> + let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion symbolUse.RangeAlternate.Start + match appliedArgRangesOpt with + | None -> () + | Some [] -> () + | Some appliedArgRanges -> + let parameters = func.CurriedParameterGroups |> Seq.concat + let appliedArgRanges = appliedArgRanges |> Array.ofList + let definitionArgs = parameters |> Array.ofSeq + + for idx = 0 to appliedArgRanges.Length - 1 do + let appliedArgRange = appliedArgRanges.[idx] + let definitionArgName = definitionArgs.[idx].DisplayName + if not (String.IsNullOrWhiteSpace(definitionArgName)) then + let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) + let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + " =")) + let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts) + parameterHints.Add(hint) + + | :? FSharpMemberOrFunctionOrValue as methodOrConstructor when methodOrConstructor.IsMethod || methodOrConstructor.IsConstructor -> + let endPosForMethod = symbolUse.RangeAlternate.End + let line, _ = Pos.toZ endPosForMethod + let afterParenPosInLine = getFirstPositionAfterParen (sourceText.Lines.[line].ToString()) (endPosForMethod.Column) + let tupledParamInfos = parseFileResults.FindNoteworthyParamInfoLocations(Pos.fromZ line afterParenPosInLine) + let appliedArgRanges = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion symbolUse.RangeAlternate.Start + match tupledParamInfos, appliedArgRanges with + | None, None -> () + + // Prefer looking at the "tupled" view if it exists, even if the other ranges exist. + // M(1, 2) can give results for both, but in that case we want the "tupled" view. + | Some tupledParamInfos, _ -> + let parameters = methodOrConstructor.CurriedParameterGroups |> Seq.concat |> Array.ofSeq + for idx = 0 to parameters.Length - 1 do + let paramLocationInfo = tupledParamInfos.ArgumentLocations.[idx] + let paramName = parameters.[idx].DisplayName + if not paramLocationInfo.IsNamedArgument && not (String.IsNullOrWhiteSpace(paramName)) then + let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, paramLocationInfo.ArgumentRange) + let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, paramName + " =")) + let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts) + parameterHints.Add(hint) + + // This will only happen for curried methods defined in F#. + | _, Some appliedArgRanges -> + let parameters = methodOrConstructor.CurriedParameterGroups |> Seq.concat + let appliedArgRanges = appliedArgRanges |> Array.ofList + let definitionArgs = parameters |> Array.ofSeq + + for idx = 0 to appliedArgRanges.Length - 1 do + let appliedArgRange = appliedArgRanges.[idx] + let definitionArgName = definitionArgs.[idx].DisplayName + if not (String.IsNullOrWhiteSpace(definitionArgName)) then + let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) + let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + " =")) + let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts) + parameterHints.Add(hint) + | _ -> () + + let typeHints = typeHints.ToImmutableArray() + let parameterHints = parameterHints.ToImmutableArray() + + return typeHints.AddRange(parameterHints) + } + |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken)