From 3567144f9aaee57b0a8f1e79e5a9c720c43fa1e3 Mon Sep 17 00:00:00 2001 From: cartermp Date: Fri, 16 Oct 2020 15:02:50 -0700 Subject: [PATCH 01/26] Initial skeleton + dependencies updates --- eng/Versions.props | 2 +- global.json | 5 +- .../Debugging/BreakpointResolutionService.fs | 2 - .../Debugging/LanguageDebugInfoService.fs | 3 -- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../FSharp.Editor/InlineHints/InlineHints.fs | 47 +++++++++++++++++++ 6 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs 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/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..63177ab0b9d 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -53,6 +53,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..9e5975e4fbe --- /dev/null +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +namespace Microsoft.VisualStudio.FSharp.Editor + +open System.Composition +open System.Collections.Immutable +open System.Threading + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints + +open FSharp.Compiler +open FSharp.Compiler.Range +open FSharp.Compiler.SourceCodeServices +open FSharp.Compiler.SyntaxTree + +[)>] +type internal FSharpInlineHintsService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = + + static let userOpName = "FSharpInlineHints" + + interface IFSharpInlineHintsService with + member _.GetInlineHintsAsync(document: Document, _textSpan: TextSpan, cancellationToken: CancellationToken) = + asyncMaybe { + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) + let! sourceText = document.GetTextAsync(cancellationToken) + //let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + //let _textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) + //let _textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) + //let _fcsTextLineNumber = Line.fromZ textLinePos.Line + //let! _symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, textSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! _parseFileResults, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, userOpName) + let! symbols = checkFileResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync + //let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland, userOpName=userOpName) + + let hints = + [| + for symbol in symbols do + // let givenRange = RoslynHelpers.TextSpanToFSharpRange + let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbol.RangeAlternate) + FSharpInlineHint(TextSpan(symbolSpan.Start, 0), ImmutableArray.Create(TaggedText(TextTags.Text, symbol.Symbol.DisplayName + ":"))) + |] + + return hints.ToImmutableArray() + } + |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) \ No newline at end of file From 8b4b8cb392046b07fba3459de13759c23eb8f0d1 Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 20 Oct 2020 21:11:46 -0700 Subject: [PATCH 02/26] Initial proof of concept --- src/fsharp/service/FSharpCheckerResults.fs | 13 ++ src/fsharp/service/FSharpCheckerResults.fsi | 4 + .../src/FSharp.Editor/FSharp.Editor.fsproj | 2 +- .../FSharp.Editor/InlineHints/InlineHints.fs | 118 ++++++++++++++---- 4 files changed, 112 insertions(+), 25 deletions(-) diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index c1e0b96d002..d096cb54245 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1885,6 +1885,19 @@ type FSharpCheckFileResults FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) }) + member __.GetAllUsesOfAllSymbolsInFileWithinRange(outerRange: range, ?cancellationToken: CancellationToken) = + threadSafeOp + (fun () -> [| |]) + (fun scope -> + let cenv = scope.SymbolEnv + [| + for symbolUseChunk in scope.ScopeSymbolUses.AllUsesOfSymbols do + for symbolUse in symbolUseChunk do + cancellationToken |> Option.iter (fun ct -> ct.ThrowIfCancellationRequested()) + if rangeContainsRange outerRange symbolUse.Range && symbolUse.ItemOccurence <> ItemOccurence.RelatedText then + let symbol = FSharpSymbol.Create(cenv, symbolUse.Item) + FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) |]) + member __.GetUsesOfSymbolInFile(symbol:FSharpSymbol, ?cancellationToken: CancellationToken) = threadSafeOp (fun () -> [| |]) diff --git a/src/fsharp/service/FSharpCheckerResults.fsi b/src/fsharp/service/FSharpCheckerResults.fsi index 95e8156b748..f486fe5fa37 100644 --- a/src/fsharp/service/FSharpCheckerResults.fsi +++ b/src/fsharp/service/FSharpCheckerResults.fsi @@ -212,6 +212,10 @@ type public FSharpCheckFileResults = /// Get all textual usages of all symbols throughout the file member GetAllUsesOfAllSymbolsInFile : ?cancellationToken: CancellationToken -> seq + /// Get all textual usages of all symbols throughout a typechecked file that fall within a given range. + /// The range in the typechecked document that all symbols must by defined within + member GetAllUsesOfAllSymbolsInFileWithinRange : outerRange: range -> Async + /// Get the textual usages that resolved to the given symbol throughout the file member GetUsesOfSymbolInFile : symbol:FSharpSymbol * ?cancellationToken: CancellationToken -> FSharpSymbolUse[] diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 63177ab0b9d..d66c085a386 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -53,7 +53,6 @@ - @@ -85,6 +84,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 9e5975e4fbe..69d56624cfd 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -1,47 +1,117 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace Microsoft.VisualStudio.FSharp.Editor -open System.Composition +open Microsoft.VisualStudio.Shell + +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.Range open FSharp.Compiler.SourceCodeServices -open FSharp.Compiler.SyntaxTree [)>] -type internal FSharpInlineHintsService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = +type internal FSharpInlineHintsService + [] + ( + checkerProvider: FSharpCheckerProvider, + [)>] serviceProvider: IServiceProvider, + projectInfoManager: FSharpProjectOptionsManager + ) = static let userOpName = "FSharpInlineHints" interface IFSharpInlineHintsService with - member _.GetInlineHintsAsync(document: Document, _textSpan: TextSpan, cancellationToken: CancellationToken) = + member _.GetInlineHintsAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken) = asyncMaybe { - let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) + let! textVersion = document.GetTextVersionAsync(cancellationToken) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - //let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - //let _textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) - //let _textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) - //let _fcsTextLineNumber = Line.fromZ textLinePos.Line - //let! _symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, textSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let! _parseFileResults, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, userOpName) - let! symbols = checkFileResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync - //let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland, userOpName=userOpName) - - let hints = - [| - for symbol in symbols do - // let givenRange = RoslynHelpers.TextSpanToFSharpRange - let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbol.RangeAlternate) - FSharpInlineHint(TextSpan(symbolSpan.Start, 0), ImmutableArray.Create(TaggedText(TextTags.Text, symbol.Symbol.DisplayName + ":"))) - |] - - return hints.ToImmutableArray() + let range = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) + let! symbolUses = checkFileResults.GetAllUsesOfAllSymbolsInFileWithinRange(range) |> liftAsync + + let typeHints = ImmutableArray.CreateBuilder() + + // todo - get these at some point I guess + // most likely need to work with the parse tree API + // since there is no good way to tell if a symbol is a parameter (declared or used) + let _parameterHints = ImmutableArray.CreateBuilder() + + for symbolUse in symbolUses do + if symbolUse.IsFromDefinition then + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as x when x.IsValue && not x.IsMemberThisValue && not x.IsConstructorThisValue -> + let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) + + // TODO: look for if 'x' is SynExpr.Typed perhaps? + + // This does not correctly "classify" non-F# types + // TODO deal with that, probably via a new API? + let typeLayout = x.FormatLayout symbolUse.DisplayContext + + let taggedText = ResizeArray() + + Layout.renderL (Layout.taggedTextListR taggedText.Add) typeLayout |> ignore + + let displayParts = ImmutableArray.CreateBuilder() + displayParts.Add(TaggedText(TextTags.Text, ": ")) + + taggedText + |> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text) + |> Seq.map (fun (tag, text) -> TaggedText(tag, text)) + |> Seq.iter (fun tt -> displayParts.Add(tt)) + + // TODO - this is not actually correct + // We need to get QuickInfo for the actual type we pull out, not the value + // This code is correct for parameter name hints though, if that gets done! + let callBack position = + fun _ _ -> + asyncMaybe { + let! quickInfo = + FSharpAsyncQuickInfoSource.ProvideQuickInfo( + checkerProvider.Checker, + document.Id, + sourceText, + document.FilePath, + position, + parsingOptions, + projectOptions, + textVersion.GetHashCode(), + document.FSharpOptions.LanguageServicePerformance) + + let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService) + let mainDesc, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo + + let descriptionParts = ImmutableArray.CreateBuilder() + + mainDesc + |> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text) + |> Seq.map (fun (tag, text) -> TaggedText(tag, text)) + |> Seq.iter (fun tt -> descriptionParts.Add(tt)) + + docs + |> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text) + |> Seq.map (fun (tag, text) -> TaggedText(tag, text)) + |> Seq.iter (fun tt -> descriptionParts.Add(tt)) + + return (descriptionParts.ToImmutableArray()) + } + |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) + + let getDescriptionAsync position = Func(callBack position) + + let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) + typeHints.Add(hint) + | _ -> () + + return typeHints.ToImmutableArray() } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) \ No newline at end of file + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) From 6b36be521eb33b2899c8f1a7a105fb767711675d Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 20 Oct 2020 22:30:38 -0700 Subject: [PATCH 03/26] Cleanup, tag interfaces correctly, surface area --- src/fsharp/TypedTreeOps.fs | 2 +- .../FSharp.Editor/InlineHints/InlineHints.fs | 37 +++++++------------ 2 files changed, 14 insertions(+), 25 deletions(-) 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/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 69d56624cfd..f88078e0b17 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -46,26 +46,18 @@ type internal FSharpInlineHintsService for symbolUse in symbolUses do if symbolUse.IsFromDefinition then match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as x when x.IsValue && not x.IsMemberThisValue && not x.IsConstructorThisValue -> - let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) - - // TODO: look for if 'x' is SynExpr.Typed perhaps? - - // This does not correctly "classify" non-F# types - // TODO deal with that, probably via a new API? - let typeLayout = x.FormatLayout symbolUse.DisplayContext - - let taggedText = ResizeArray() + | :? FSharpMemberOrFunctionOrValue as value when value.IsValue && not value.IsMemberThisValue && not value.IsConstructorThisValue -> + let typeInfo = ResizeArray() - Layout.renderL (Layout.taggedTextListR taggedText.Add) typeLayout |> ignore + value.FormatLayout symbolUse.DisplayContext + |> Layout.renderL (Layout.taggedTextListR typeInfo.Add) + |> ignore let displayParts = ImmutableArray.CreateBuilder() displayParts.Add(TaggedText(TextTags.Text, ": ")) - - taggedText - |> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text) - |> Seq.map (fun (tag, text) -> TaggedText(tag, text)) - |> Seq.iter (fun tt -> displayParts.Add(tt)) + + for tt in typeInfo do + displayParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) // TODO - this is not actually correct // We need to get QuickInfo for the actual type we pull out, not the value @@ -90,15 +82,11 @@ type internal FSharpInlineHintsService let descriptionParts = ImmutableArray.CreateBuilder() - mainDesc - |> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text) - |> Seq.map (fun (tag, text) -> TaggedText(tag, text)) - |> Seq.iter (fun tt -> descriptionParts.Add(tt)) + for tt in mainDesc do + descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) - docs - |> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text) - |> Seq.map (fun (tag, text) -> TaggedText(tag, text)) - |> Seq.iter (fun tt -> descriptionParts.Add(tt)) + for tt in docs do + descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) return (descriptionParts.ToImmutableArray()) } @@ -106,6 +94,7 @@ type internal FSharpInlineHintsService |> RoslynHelpers.StartAsyncAsTask(cancellationToken) let getDescriptionAsync position = Func(callBack position) + let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) typeHints.Add(hint) From b67ebdbcd5e2dc7fe4a76e0f977b4bd9b65a469a Mon Sep 17 00:00:00 2001 From: cartermp Date: Wed, 21 Oct 2020 23:04:45 -0700 Subject: [PATCH 04/26] Basic, buggy support for parameter names --- .../service/ServiceParamInfoLocations.fs | 47 ++++++ .../service/ServiceParamInfoLocations.fsi | 2 + src/fsharp/service/ServiceUntypedParse.fs | 8 + src/fsharp/service/ServiceUntypedParse.fsi | 7 +- src/fsharp/symbols/Symbols.fs | 22 +++ src/fsharp/symbols/Symbols.fsi | 6 + .../FSharp.Editor/InlineHints/InlineHints.fs | 151 +++++++++++------- 7 files changed, 180 insertions(+), 63 deletions(-) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index d70052a74c1..9779b593a52 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -272,3 +272,50 @@ type FSharpNoteworthyParamInfoLocations with r | _ -> None +module internal FunctionApplicationArgumentLocationsImpl = + + type private FindResult = + /// Ranges for each parameter found in a function application + | Found of range list + | NotFound + + let rec private searchSynArgExpr traverseSynExpr _pos expr ranges = + match expr with + + /// TODO - need to handle things like tuples, parens using functions as inputs, etc. + // should probably be test-driven! + + | SynExprParen(SynExprParen(_, _, _, _) as _synExpr, _, _, parenRange) -> + Found(parenRange :: ranges), None + + | SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF + Found(range :: ranges), None + + | e -> + let inner = traverseSynExpr e + match inner with + | None -> + Found (e.Range :: ranges), Some inner + | _ -> NotFound, Some inner + + let findFSharpFunctionArgInfos 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) -> + let isInfixFuncExpr = + match funcExpr with + | SynExpr.App (_, isInfix, _, _, _) -> isInfix + | _ -> false + + let workingRanges = match traverseSynExpr funcExpr with Some ranges -> ranges | None -> [] + + if isInfix || isInfixFuncExpr then + None + else + let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos argExpr workingRanges + match xResult, cacheOpt with + | Found ranges, _ -> Some ranges + | NotFound, Some cache -> cache + | _ -> traverseSynExpr argExpr + | _ -> defaultTraverse expr }) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fsi b/src/fsharp/service/ServiceParamInfoLocations.fsi index 618ced81d12..3f5cfefa232 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fsi +++ b/src/fsharp/service/ServiceParamInfoLocations.fsi @@ -38,3 +38,5 @@ type public FSharpNoteworthyParamInfoLocations = /// Find the information about parameter info locations at a particular source location static member Find : pos * ParsedInput -> FSharpNoteworthyParamInfoLocations option +module internal FunctionApplicationArgumentLocationsImpl = + val findFSharpFunctionArgInfos: pos: pos -> parseTree: ParsedInput -> range list option diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index bd24fef3fad..751a3f7cdc8 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -104,6 +104,14 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option match input with | Some input -> FSharpNoteworthyParamInfoLocations.Find(pos, input) | _ -> None + + member scope.GetAllArgumentsForFunctionApplication pos = + match input with + | Some input -> + // TODO - ideally we shouldn't be having to reverse it here I guess + FunctionApplicationArgumentLocationsImpl.findFSharpFunctionArgInfos pos input + |> Option.map List.rev + | None -> None /// 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..e3db1d81be8 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -22,11 +22,14 @@ type public FSharpParseFileResults = /// Notable parse info for ParameterInfo at a given location member FindNoteworthyParamInfoLocations : pos:pos -> FSharpNoteworthyParamInfoLocations option + // doot doot yeet yeet + member GetAllArgumentsForFunctionApplication: pos: pos -> range list option + /// 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..087b9898d95 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -1927,6 +1927,23 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = |> makeReadOnlyCollection ] |> makeReadOnlyCollection + member x.PossibleArgumentList = + checkIsResolved() + match d with + | P _ | E _ | M _ | C _ -> None + | V v -> + match v.ValReprInfo with + | None -> + // the "pass a single function to a function case"? + let _, tau = v.TypeScheme + if isFunTy cenv.g tau then + Some [v.DisplayName] + else + None + | Some info -> + info.ArgNames + + member x.ReturnParameter = checkIsResolved() match d with @@ -2062,6 +2079,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 diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index f364f2c1f36..6c402501c75 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -860,6 +860,9 @@ type FSharpMemberOrFunctionOrValue = member CurriedParameterGroups: IList> + /// doop doop yeet yeet + member PossibleArgumentList : string list option + /// Gets the overloads for the current method /// matchParameterNumber indicates whether to filter the overloads to match the number of parameters in the current symbol member Overloads: bool -> IList option @@ -900,6 +903,9 @@ type FSharpMemberOrFunctionOrValue = /// Indicated if this is a value member IsValue: bool + + /// Indicated if this is a value + member IsFunction : bool /// Indicates if this is a constructor. member IsConstructor: bool diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index f88078e0b17..23c8a60a78a 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -32,75 +32,104 @@ type internal FSharpInlineHintsService let! textVersion = document.GetTextVersionAsync(cancellationToken) let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - let! _parseFileResults, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, userOpName) + let! parseFileResults, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, userOpName) let range = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let! symbolUses = checkFileResults.GetAllUsesOfAllSymbolsInFileWithinRange(range) |> liftAsync let typeHints = ImmutableArray.CreateBuilder() - - // todo - get these at some point I guess - // most likely need to work with the parse tree API - // since there is no good way to tell if a symbol is a parameter (declared or used) - let _parameterHints = ImmutableArray.CreateBuilder() + let parameterHints = ImmutableArray.CreateBuilder() + + let isValidValue (value: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + value.IsValue && + not value.IsMemberThisValue && + not value.IsConstructorThisValue && + symbolUse.IsFromDefinition for symbolUse in symbolUses do - if symbolUse.IsFromDefinition then - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as value when value.IsValue && not value.IsMemberThisValue && not value.IsConstructorThisValue -> - let typeInfo = ResizeArray() + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as value when isValidValue value symbolUse -> + let typeInfo = ResizeArray() - value.FormatLayout symbolUse.DisplayContext - |> Layout.renderL (Layout.taggedTextListR typeInfo.Add) - |> ignore + value.FormatLayout symbolUse.DisplayContext + |> 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)) - - // TODO - this is not actually correct - // We need to get QuickInfo for the actual type we pull out, not the value - // This code is correct for parameter name hints though, if that gets done! - let callBack position = - fun _ _ -> - asyncMaybe { - let! quickInfo = - FSharpAsyncQuickInfoSource.ProvideQuickInfo( - checkerProvider.Checker, - document.Id, - sourceText, - document.FilePath, - position, - parsingOptions, - projectOptions, - textVersion.GetHashCode(), - document.FSharpOptions.LanguageServicePerformance) - - let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService) - let mainDesc, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo - - let descriptionParts = ImmutableArray.CreateBuilder() - - for tt in mainDesc do - descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) - - for tt in docs do - descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) - - return (descriptionParts.ToImmutableArray()) - } - |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) - - let getDescriptionAsync position = Func(callBack position) - let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) - - let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) - typeHints.Add(hint) - | _ -> () - - return typeHints.ToImmutableArray() + let displayParts = ImmutableArray.CreateBuilder() + displayParts.Add(TaggedText(TextTags.Text, ": ")) + + for tt in typeInfo do + displayParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) + + // TODO - this is not actually correct + // We need to get QuickInfo for the actual type we pull out, not the value + // This code is correct for parameter name hints though, if that gets done! + let callBack position = + fun _ _ -> + asyncMaybe { + let! quickInfo = + FSharpAsyncQuickInfoSource.ProvideQuickInfo( + checkerProvider.Checker, + document.Id, + sourceText, + document.FilePath, + position, + parsingOptions, + projectOptions, + textVersion.GetHashCode(), + document.FSharpOptions.LanguageServicePerformance) + + let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService) + let mainDesc, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo + + let descriptionParts = ImmutableArray.CreateBuilder() + + for tt in mainDesc do + descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) + + for tt in docs do + descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) + + return (descriptionParts.ToImmutableArray()) + } + |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) + + let getDescriptionAsync position = Func(callBack position) + let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) + + let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) + typeHints.Add(hint) + | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> + let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start + match appliedArgRangesOpt with + | Some [] -> () + | Some appliedArgRanges -> + match func.PossibleArgumentList with + | Some [] -> () + | Some definitionArgNames -> + + let appliedArgRanges = appliedArgRanges |> Array.ofList + let definitionArgNames = definitionArgNames |> Array.ofList + + for idx = 0 to appliedArgRanges.Length - 1 do + let appliedArgRange = appliedArgRanges.[idx] + let definitionArgName = definitionArgNames.[idx] + let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) + let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + ": ")) + let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) + parameterHints.Add(hint) + | _ -> + () + | None -> + () + + () + | _ -> () + + let typeHints = typeHints.ToImmutableArray() + let parameterHints = parameterHints.ToImmutableArray() + + return typeHints.AddRange(parameterHints) } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) |> RoslynHelpers.StartAsyncAsTask(cancellationToken) From 7f09a76b7cb7e3a6ade194d1cb00ffc72b889241 Mon Sep 17 00:00:00 2001 From: cartermp Date: Thu, 22 Oct 2020 18:11:46 -0700 Subject: [PATCH 05/26] Preliminary support for not adding hints to already-annotated values --- src/fsharp/service/ServiceUntypedParse.fs | 19 +++++++++++++++++++ src/fsharp/service/ServiceUntypedParse.fsi | 3 +++ .../FSharp.Editor/InlineHints/InlineHints.fs | 9 +++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 751a3f7cdc8..879c7e5f534 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -112,6 +112,25 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option FunctionApplicationArgumentLocationsImpl.findFSharpFunctionArgInfos pos input |> Option.map List.rev | None -> None + + member scope.IsTypeAnnotationGiven 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 _.VisitPat(defaultTraverse, pat) = + match pat with + | SynPat.Typed (_pat, _targetType, range) -> + rangeContainsPos range pos + |> Some + | _ -> + defaultTraverse pat }) + 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 e3db1d81be8..2245fd95a02 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -25,6 +25,9 @@ type public FSharpParseFileResults = // doot doot yeet yeet member GetAllArgumentsForFunctionApplication: pos: pos -> range list option + // doot doot yeet yeet + member IsTypeAnnotationGiven: pos -> bool + /// Name of the file for which this information were created member FileName: string diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 23c8a60a78a..57417a4282b 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -43,7 +43,8 @@ type internal FSharpInlineHintsService value.IsValue && not value.IsMemberThisValue && not value.IsConstructorThisValue && - symbolUse.IsFromDefinition + symbolUse.IsFromDefinition && + not (parseFileResults.IsTypeAnnotationGiven symbolUse.RangeAlternate.Start) for symbolUse in symbolUses do match symbolUse.Symbol with @@ -62,7 +63,7 @@ type internal FSharpInlineHintsService // TODO - this is not actually correct // We need to get QuickInfo for the actual type we pull out, not the value - // This code is correct for parameter name hints though, if that gets done! + // But it does demonstrate a possible way to do this let callBack position = fun _ _ -> asyncMaybe { @@ -99,6 +100,7 @@ type internal FSharpInlineHintsService let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) typeHints.Add(hint) + | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start match appliedArgRangesOpt with @@ -124,6 +126,9 @@ type internal FSharpInlineHintsService () () + + // TODO - support method calls, ctors, etc + | _ -> () let typeHints = typeHints.ToImmutableArray() From e129f583b386c0b1b995dfaab9e663e0620f3f0e Mon Sep 17 00:00:00 2001 From: cartermp Date: Thu, 22 Oct 2020 18:24:23 -0700 Subject: [PATCH 06/26] don't show for typed value decls --- src/fsharp/service/ServiceUntypedParse.fs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 879c7e5f534..e65bd7c0c0f 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -119,7 +119,11 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option let res = AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with member __.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = - defaultTraverse(expr) + match expr with + | SynExpr.Typed (_expr, _typeExpr, range) -> + rangeContainsPos range pos + |> Some + | _ -> defaultTraverse(expr) override _.VisitPat(defaultTraverse, pat) = match pat with From fcc4be6a3093bcc4226992197060a2702568007a Mon Sep 17 00:00:00 2001 From: cartermp Date: Fri, 23 Oct 2020 12:33:34 -0700 Subject: [PATCH 07/26] No sig files, handle more typed cases --- src/fsharp/service/ServiceUntypedParse.fs | 30 ++++++++++++------- .../FSharp.Editor/InlineHints/InlineHints.fs | 20 ++++++++----- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index e65bd7c0c0f..6d81f851899 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -118,20 +118,30 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | Some parseTree -> let res = AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with - member __.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = + member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = match expr with - | SynExpr.Typed (_expr, _typeExpr, range) -> - rangeContainsPos range pos - |> Some - | _ -> defaultTraverse(expr) + | SynExpr.Typed (_expr, _typeExpr, range) when rangeContainsPos range pos -> + Some range + | _ -> defaultTraverse expr + + override _.VisitSimplePats(pats) = + match pats with + | [] -> None + | _ -> + let exprFunc pat = + match pat with + | SynSimplePat.Typed (_pat, _targetExpr, range) when rangeContainsPos range pos -> + Some range + | _ -> + None + + pats |> List.tryPick exprFunc override _.VisitPat(defaultTraverse, pat) = match pat with - | SynPat.Typed (_pat, _targetType, range) -> - rangeContainsPos range pos - |> Some - | _ -> - defaultTraverse pat }) + | SynPat.Typed (_pat, _targetType, range) when rangeContainsPos range pos -> + Some range + | _ -> defaultTraverse pat }) res.IsSome | None -> false diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 57417a4282b..76d33f6856f 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -29,6 +29,8 @@ type internal FSharpInlineHintsService interface IFSharpInlineHintsService with member _.GetInlineHintsAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken) = asyncMaybe { + do! Option.guard (not (isSignatureFile document.FilePath)) + let! textVersion = document.GetTextVersionAsync(cancellationToken) let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) @@ -39,19 +41,21 @@ type internal FSharpInlineHintsService let typeHints = ImmutableArray.CreateBuilder() let parameterHints = ImmutableArray.CreateBuilder() - let isValidValue (value: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = - value.IsValue && - not value.IsMemberThisValue && - not value.IsConstructorThisValue && + let isValid (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + not (parseFileResults.IsTypeAnnotationGiven symbolUse.RangeAlternate.Start) && symbolUse.IsFromDefinition && - not (parseFileResults.IsTypeAnnotationGiven symbolUse.RangeAlternate.Start) + (funcOrValue.IsValue || funcOrValue.IsFunction) && + not funcOrValue.IsMember && + not funcOrValue.IsMemberThisValue && + not funcOrValue.IsConstructorThisValue && + not (PrettyNaming.IsOperatorName funcOrValue.DisplayName) - for symbolUse in symbolUses do + for symbolUse in symbolUses |> Array.distinctBy (fun su -> su.RangeAlternate) do match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as value when isValidValue value symbolUse -> + | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValid funcOrValue symbolUse -> let typeInfo = ResizeArray() - value.FormatLayout symbolUse.DisplayContext + funcOrValue.FormatLayout symbolUse.DisplayContext |> Layout.renderL (Layout.taggedTextListR typeInfo.Add) |> ignore From 626f7ff970aa1a0001f57cf7eed4d6784bc56685 Mon Sep 17 00:00:00 2001 From: cartermp Date: Fri, 23 Oct 2020 15:24:07 -0700 Subject: [PATCH 08/26] Be precise about type annotations when looking for them in the syntax tree --- src/fsharp/service/ServiceUntypedParse.fs | 6 +- .../FSharp.Editor/InlineHints/InlineHints.fs | 55 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 6d81f851899..4d4fdff7f08 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -120,7 +120,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = match expr with - | SynExpr.Typed (_expr, _typeExpr, range) when rangeContainsPos range pos -> + | SynExpr.Typed (_expr, _typeExpr, range) when posEq range.Start pos -> Some range | _ -> defaultTraverse expr @@ -130,7 +130,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | _ -> let exprFunc pat = match pat with - | SynSimplePat.Typed (_pat, _targetExpr, range) when rangeContainsPos range pos -> + | SynSimplePat.Typed (_pat, _targetExpr, range) when posEq range.Start pos -> Some range | _ -> None @@ -139,7 +139,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option override _.VisitPat(defaultTraverse, pat) = match pat with - | SynPat.Typed (_pat, _targetType, range) when rangeContainsPos range pos -> + | SynPat.Typed (_pat, _targetType, range) when posEq range.Start pos -> Some range | _ -> defaultTraverse pat }) res.IsSome diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 76d33f6856f..df1b3f2c9a5 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -41,7 +41,7 @@ type internal FSharpInlineHintsService let typeHints = ImmutableArray.CreateBuilder() let parameterHints = ImmutableArray.CreateBuilder() - let isValid (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + let isValidForTypeHint (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = not (parseFileResults.IsTypeAnnotationGiven symbolUse.RangeAlternate.Start) && symbolUse.IsFromDefinition && (funcOrValue.IsValue || funcOrValue.IsFunction) && @@ -52,7 +52,7 @@ type internal FSharpInlineHintsService for symbolUse in symbolUses |> Array.distinctBy (fun su -> su.RangeAlternate) do match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValid funcOrValue symbolUse -> + | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse -> let typeInfo = ResizeArray() funcOrValue.FormatLayout symbolUse.DisplayContext @@ -105,33 +105,30 @@ type internal FSharpInlineHintsService let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) typeHints.Add(hint) - | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> - let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start - match appliedArgRangesOpt with - | Some [] -> () - | Some appliedArgRanges -> - match func.PossibleArgumentList with - | Some [] -> () - | Some definitionArgNames -> - - let appliedArgRanges = appliedArgRanges |> Array.ofList - let definitionArgNames = definitionArgNames |> Array.ofList - - for idx = 0 to appliedArgRanges.Length - 1 do - let appliedArgRange = appliedArgRanges.[idx] - let definitionArgName = definitionArgNames.[idx] - let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) - let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + ": ")) - let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) - parameterHints.Add(hint) - | _ -> - () - | None -> - () - - () - - // TODO - support method calls, ctors, etc + // parameter name hints, disabled for now + //| :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> + // let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start + // match appliedArgRangesOpt with + // | Some [] -> () + // | Some appliedArgRanges -> + // match func.PossibleArgumentList with + // | Some [] -> () + // | Some definitionArgNames -> + + // let appliedArgRanges = appliedArgRanges |> Array.ofList + // let definitionArgNames = definitionArgNames |> Array.ofList + + // for idx = 0 to appliedArgRanges.Length - 1 do + // let appliedArgRange = appliedArgRanges.[idx] + // let definitionArgName = definitionArgNames.[idx] + // let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) + // let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + ": ")) + // let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) + // parameterHints.Add(hint) + // | _ -> + // () + // | None -> + // () | _ -> () From 1c9afe65412629b0a33fd0f23b41a7f0b2246c77 Mon Sep 17 00:00:00 2001 From: cartermp Date: Fri, 23 Oct 2020 16:35:00 -0700 Subject: [PATCH 09/26] More annotations fixity --- src/fsharp/service/ServiceUntypedParse.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 4d4fdff7f08..c3f12b7b825 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -120,7 +120,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option 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 -> + | SynExpr.Typed (_expr, _typeExpr, range) when posGeq range.Start pos -> Some range | _ -> defaultTraverse expr @@ -130,7 +130,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | _ -> let exprFunc pat = match pat with - | SynSimplePat.Typed (_pat, _targetExpr, range) when posEq range.Start pos -> + | SynSimplePat.Typed (_pat, _targetExpr, range) when posGeq range.Start pos -> Some range | _ -> None @@ -139,7 +139,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option override _.VisitPat(defaultTraverse, pat) = match pat with - | SynPat.Typed (_pat, _targetType, range) when posEq range.Start pos -> + | SynPat.Typed (_pat, _targetType, range) when posGeq range.Start pos -> Some range | _ -> defaultTraverse pat }) res.IsSome From 57f82460855864aaec6eeac2fa6b70d66aacc3b0 Mon Sep 17 00:00:00 2001 From: cartermp Date: Fri, 23 Oct 2020 17:48:35 -0700 Subject: [PATCH 10/26] Hints show only the return type for functions --- src/fsharp/NicePrint.fs | 21 ++++++++++++++++++- src/fsharp/symbols/Symbols.fs | 18 +++++++++++++++- src/fsharp/symbols/Symbols.fsi | 5 ++++- .../FSharp.Editor/InlineHints/InlineHints.fs | 10 ++++++++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index cdfbf1ee89a..c6bb924c8b4 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -1198,7 +1198,24 @@ 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 prettyLayoutOfReturnType denv (v: Val) = + //let ty, _cxs = PrettyTypes.PrettifyType denv. g ty + + let tps, tau = v.TypeScheme + + // adjust the type in case this is the 'this' pointer stored in a reference cell + let tau = StripSelfRefCell(denv.g, v.BaseOrThisInfo, tau) + + let (_prettyTyparInst, _prettyTypars, _prettyTauTy), cxs = PrettyTypes.PrettifyInstAndTyparsAndType denv.g (emptyTyparInst, tps, tau) + + + let env = SimplifyTypes.CollectInfo true [tau] cxs + + let _argInfos, rty = GetTopTauTypeInFSharpForm denv.g (arityOfVal v).ArgInfos tau v.Range + + layoutReturnType denv env rty let layoutAssemblyName _denv (ty: TType) = ty.GetAssemblyName() @@ -2203,6 +2220,8 @@ let prettyLayoutOfType denv x = x |> PrintTypes.prettyLayoutOfType denv let prettyLayoutOfTypeNoCx denv x = x |> PrintTypes.prettyLayoutOfTypeNoConstraints denv +let prettyLayoutOfReturnType denv x = x |> PrintTypes.prettyLayoutOfReturnType denv + let prettyStringOfTy denv x = x |> PrintTypes.prettyLayoutOfType denv |> showL let prettyStringOfTyNoCx denv x = x |> PrintTypes.prettyLayoutOfTypeNoConstraints denv |> showL diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 087b9898d95..a611d1f53fe 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -2120,7 +2120,23 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = mkIteratedFunTy (List.map (mkRefTupledTy cenv.g) argtysl) rty | V v -> v.TauType NicePrint.prettyLayoutOfTypeNoCx (context.Contents cenv.g) ty - + + // TODO - this is NOT fully implemented, as far as I can tell + member x.GetReturnTypeLayout (denv: FSharpDisplayContext) = + match x.IsMember, d with + | true, _ -> + None + | false, _ -> + checkIsResolved() + match d with + | E _e -> None + | P _p -> None + | M _m | C _m -> + None + | V v -> + NicePrint.prettyLayoutOfReturnType (denv.Contents cenv.g) v.Deref + |> Some + member x.GetWitnessPassingInfo() = let witnessInfos = match d with diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 6c402501c75..71d751b3555 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -912,7 +912,10 @@ type FSharpMemberOrFunctionOrValue = /// Format the type using the rules of the given display context member FormatLayout: context: FSharpDisplayContext -> Layout - + + /// Format the type using the rules of the given display context + member GetReturnTypeLayout: context: 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/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index df1b3f2c9a5..2e2aefa4cd3 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -55,7 +55,15 @@ type internal FSharpInlineHintsService | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse -> let typeInfo = ResizeArray() - funcOrValue.FormatLayout symbolUse.DisplayContext + // TODO - this must surely be simpler to do + let layout = + if funcOrValue.IsFunction then + funcOrValue.GetReturnTypeLayout symbolUse.DisplayContext + |> Option.defaultValue Layout.emptyL + else + funcOrValue.FormatLayout symbolUse.DisplayContext + + layout |> Layout.renderL (Layout.taggedTextListR typeInfo.Add) |> ignore From 7dfc1b825c2f8a57ecba230753fbd0d60b18844d Mon Sep 17 00:00:00 2001 From: cartermp Date: Sat, 24 Oct 2020 15:38:25 -0700 Subject: [PATCH 11/26] Cleanup and surface tests --- src/fsharp/NicePrint.fs | 32 ++++++++----------- .../service/ServiceParamInfoLocations.fs | 2 ++ src/fsharp/service/ServiceUntypedParse.fs | 5 +-- src/fsharp/symbols/Symbols.fs | 4 +-- .../SurfaceArea.netstandard.fs | 8 +++++ .../FSharp.Editor/InlineHints/InlineHints.fs | 10 ++---- 6 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index c6bb924c8b4..0506cd855a3 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -1200,22 +1200,16 @@ module private PrintTypes = let ty, _cxs = PrettyTypes.PrettifyType denv.g ty layoutTypeWithInfoAndPrec denv SimplifyTypes.typeSimplificationInfo0 5 ty - let prettyLayoutOfReturnType denv (v: Val) = - //let ty, _cxs = PrettyTypes.PrettifyType denv. g ty - - let tps, tau = v.TypeScheme - - // adjust the type in case this is the 'this' pointer stored in a reference cell - let tau = StripSelfRefCell(denv.g, v.BaseOrThisInfo, tau) - - let (_prettyTyparInst, _prettyTypars, _prettyTauTy), cxs = PrettyTypes.PrettifyInstAndTyparsAndType denv.g (emptyTyparInst, tps, tau) - - - let env = SimplifyTypes.CollectInfo true [tau] cxs - - let _argInfos, rty = GetTopTauTypeInFSharpForm denv.g (arityOfVal v).ArgInfos tau v.Range - - layoutReturnType denv env rty + let prettyLayoutOfValReturnType 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() @@ -2220,8 +2214,6 @@ let prettyLayoutOfType denv x = x |> PrintTypes.prettyLayoutOfType denv let prettyLayoutOfTypeNoCx denv x = x |> PrintTypes.prettyLayoutOfTypeNoConstraints denv -let prettyLayoutOfReturnType denv x = x |> PrintTypes.prettyLayoutOfReturnType denv - let prettyStringOfTy denv x = x |> PrintTypes.prettyLayoutOfType denv |> showL let prettyStringOfTyNoCx denv x = x |> PrintTypes.prettyLayoutOfTypeNoConstraints denv |> showL @@ -2242,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 prettyLayoutOfValReturnType denv x = x |> PrintTypes.prettyLayoutOfValReturnType denv let prettyLayoutOfInstAndSig denv x = PrintTypes.prettyLayoutOfInstAndSig denv x diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index 9779b593a52..7ab8c26d9e6 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -298,6 +298,7 @@ module internal FunctionApplicationArgumentLocationsImpl = Found (e.Range :: ranges), Some inner | _ -> NotFound, Some inner + // TODO - doesn't handle infix cases like this: 'string x + y', where 'x' should have a label let findFSharpFunctionArgInfos pos parseTree = AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with member _.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) = @@ -319,3 +320,4 @@ module internal FunctionApplicationArgumentLocationsImpl = | NotFound, Some cache -> cache | _ -> traverseSynExpr argExpr | _ -> defaultTraverse expr }) + |> Option.map List.rev diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index c3f12b7b825..a540893f164 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -107,10 +107,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option member scope.GetAllArgumentsForFunctionApplication pos = match input with - | Some input -> - // TODO - ideally we shouldn't be having to reverse it here I guess - FunctionApplicationArgumentLocationsImpl.findFSharpFunctionArgInfos pos input - |> Option.map List.rev + | Some input -> FunctionApplicationArgumentLocationsImpl.findFSharpFunctionArgInfos pos input | None -> None member scope.IsTypeAnnotationGiven pos = diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index a611d1f53fe..79acd17c7e8 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -2120,8 +2120,8 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = mkIteratedFunTy (List.map (mkRefTupledTy cenv.g) argtysl) rty | V v -> v.TauType NicePrint.prettyLayoutOfTypeNoCx (context.Contents cenv.g) ty - - // TODO - this is NOT fully implemented, as far as I can tell + + // TODO - consider the other types member x.GetReturnTypeLayout (denv: FSharpDisplayContext) = match x.IsMember, d with | true, _ -> diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 1f94595981f..19af946db4c 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22442,6 +22442,7 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean IsType 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() @@ -22475,6 +22476,7 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Boolean get_Is 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 +22502,8 @@ 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.FSharpOption1[Internal.Utilities.StructuredFormat.Layout] GetReturnTypeLayout(FSharp.Compiler.SourceCodeServices.FSharpDisplayContext) +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption1[Internal.Utilities.StructuredFormat.Layout] 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 @@ -22521,6 +22525,8 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collect FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpGenericParameter] get_GenericParameters() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] CurriedParameterGroups FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] get_CurriedParameterGroups() +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption1[Microsoft.FSharp.Collections.FSharpList1[System.String]] PossibleArgumentList +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption1[Microsoft.FSharp.Collections.FSharpList1[System.String]] get_PossibleArgumentList() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] ElaboratedXmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] XmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] get_ElaboratedXmlDoc() @@ -22737,6 +22743,8 @@ 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 IsTypeAnnotationGiven(pos) +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Range+range]] GetAllArgumentsForFunctionApplication(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/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 2e2aefa4cd3..d80bad05fe7 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -54,14 +54,10 @@ type internal FSharpInlineHintsService match symbolUse.Symbol with | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse -> let typeInfo = ResizeArray() - - // TODO - this must surely be simpler to do + let layout = - if funcOrValue.IsFunction then - funcOrValue.GetReturnTypeLayout symbolUse.DisplayContext - |> Option.defaultValue Layout.emptyL - else - funcOrValue.FormatLayout symbolUse.DisplayContext + funcOrValue.GetReturnTypeLayout symbolUse.DisplayContext + |> Option.defaultValue Layout.emptyL layout |> Layout.renderL (Layout.taggedTextListR typeInfo.Add) From 47ed27a729c45f692c565e1a20bf05bab908eaf5 Mon Sep 17 00:00:00 2001 From: cartermp Date: Sat, 24 Oct 2020 16:11:04 -0700 Subject: [PATCH 12/26] Match names --- src/fsharp/symbols/Symbols.fs | 8 ++++---- src/fsharp/symbols/Symbols.fsi | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 79acd17c7e8..1d3e8c72acf 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -2104,10 +2104,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 = @@ -2119,10 +2119,10 @@ 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 // TODO - consider the other types - member x.GetReturnTypeLayout (denv: FSharpDisplayContext) = + member x.GetReturnTypeLayout (displayContext: FSharpDisplayContext) = match x.IsMember, d with | true, _ -> None diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 71d751b3555..5397e9ca168 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -911,6 +911,7 @@ type FSharpMemberOrFunctionOrValue = member IsConstructor: bool /// Format the type using the rules of the given display context +<<<<<<< HEAD member FormatLayout: context: FSharpDisplayContext -> Layout /// Format the type using the rules of the given display context @@ -919,6 +920,13 @@ type FSharpMemberOrFunctionOrValue = /// 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 +======= + member FormatLayout : displayContext: FSharpDisplayContext -> Layout + + /// Format the type using the rules of the given display context + member GetReturnTypeLayout : displayContext: FSharpDisplayContext -> Layout option + +>>>>>>> ac5c7ef5f... Match names /// A subtype of FSharpSymbol that represents a parameter [] From 51dcd6fd17aaa39b8f11a22e8df3531964d505a4 Mon Sep 17 00:00:00 2001 From: cartermp Date: Sat, 24 Oct 2020 16:47:12 -0700 Subject: [PATCH 13/26] Basic tests for arg names --- tests/service/ServiceUntypedParseTests.fs | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 18c7f7318d1..55e4eabc362 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -305,3 +305,68 @@ type T = new (x:int) = () """ getTypeMemberRange source |> shouldEqual [ (3, 4), (3, 20) ] + + [] + let ``GetAllArgumentsForFunctionApplication - Single arg``() = + let source = """ +let f x = () +f 12 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2)] + | None -> + Assert.Fail("No functions found for source code - test is likely incorrect") + + + [] + let ``GetAllArgumentsForFunctionApplication - Multi arg``() = + let source = """ +let f x y z = () +f 1 2 3 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2); (3, 4); (3, 6)] + | None -> + Assert.Fail("No functions found for source code - test is likely incorrect") + + [] + let ``GetAllArgumentsForFunctionApplication - Multi arg parentheses``() = + let source = """ +let f x y z = () +f (1) (2) (3) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2); (3, 6); (3, 10)] + | None -> + Assert.Fail("No functions found for source code - test is likely incorrect") + + [] + let ``GetAllArgumentsForFunctionApplication - Multi arg nested parentheses``() = + let source = """ +let f x y z = () +f ((1)) (((2))) ((((3)))) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + match res with + | Some res -> + res + |> List.map (tups >> fst) + |> shouldEqual [(3, 2); (3, 8); (3, 16)] + | None -> + Assert.Fail("No functions found for source code - test is likely incorrect") From d2502db130898f0197573191ff09f1daf0d8bcc1 Mon Sep 17 00:00:00 2001 From: cartermp Date: Mon, 26 Oct 2020 12:54:18 -0700 Subject: [PATCH 14/26] Fix issues with nested functions + more tests --- .../service/ServiceParamInfoLocations.fs | 10 +- tests/service/ServiceUntypedParseTests.fs | 132 +++++++++++++++++- .../FSharp.Editor/InlineHints/InlineHints.fs | 47 +++---- 3 files changed, 159 insertions(+), 30 deletions(-) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index 7ab8c26d9e6..88463902279 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -285,12 +285,18 @@ module internal FunctionApplicationArgumentLocationsImpl = /// TODO - need to handle things like tuples, parens using functions as inputs, etc. // should probably be test-driven! - | SynExprParen(SynExprParen(_, _, _, _) as _synExpr, _, _, parenRange) -> + | SynExprParen(SynExprParen(_, _, _, _), _, _, parenRange) -> Found(parenRange :: ranges), None | SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF Found(range :: ranges), None + | SynExpr.Const(SynConst.Unit, _) -> + NotFound, None + + | SynExprParen(SynExpr.App (_, _isInfix, _, _, _range), _, _, parenRange) -> + Found (parenRange :: ranges), None + | e -> let inner = traverseSynExpr e match inner with @@ -303,7 +309,7 @@ module internal FunctionApplicationArgumentLocationsImpl = AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with member _.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) = match expr with - | SynExpr.App (_exprAtomicFlag, isInfix, funcExpr, argExpr, _range) -> + | SynExpr.App (_exprAtomicFlag, isInfix, funcExpr, argExpr, range) when posEq pos range.Start -> let isInfixFuncExpr = match funcExpr with | SynExpr.App (_, isInfix, _, _, _) -> isInfix diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 55e4eabc362..ef09b153ddb 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -320,7 +320,7 @@ f 12 |> List.map (tups >> fst) |> shouldEqual [(3, 2)] | None -> - Assert.Fail("No functions found for source code - test is likely incorrect") + Assert.Fail("No arguments found in source code") [] @@ -337,7 +337,7 @@ f 1 2 3 |> List.map (tups >> fst) |> shouldEqual [(3, 2); (3, 4); (3, 6)] | None -> - Assert.Fail("No functions found for source code - test is likely incorrect") + Assert.Fail("No arguments found in source code") [] let ``GetAllArgumentsForFunctionApplication - Multi arg parentheses``() = @@ -353,7 +353,7 @@ f (1) (2) (3) |> List.map (tups >> fst) |> shouldEqual [(3, 2); (3, 6); (3, 10)] | None -> - Assert.Fail("No functions found for source code - test is likely incorrect") + Assert.Fail("No arguments found in source code") [] let ``GetAllArgumentsForFunctionApplication - Multi arg nested parentheses``() = @@ -369,4 +369,128 @@ f ((1)) (((2))) ((((3)))) |> List.map (tups >> fst) |> shouldEqual [(3, 2); (3, 8); (3, 16)] | None -> - Assert.Fail("No functions found for source code - test is likely incorrect") + Assert.Fail("No arguments found in source code") + + [] + let ``GetAllArgumentsForFunctionApplication - unit``() = + let source = """ +let f () = () +f () +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + Assert.IsTrue(res.IsNone, "Found argument for unit-accepting function, which shouldn't be the case.") + + [] + let ``GetAllArgumentsForFunctionApplication - curried function``() = + let source = """ +let f x y = x + y +f 12 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - tuple value``() = + let source = """ +let f (t: int * int) = () +let t = (1, 2) +f t +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - tuple literal``() = + let source = """ +let f (t: int * int) = () +f (1, 2) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - 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.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - tuple literal with definition that has explicit names``() = + let source = """ +let f ((x, y): int * int) = () +f (1, 2) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - 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.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - nested function argument positions``() = + let source = """ +let f x y = x + y +f (f 1 2) 3 +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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") \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index d80bad05fe7..2b3cd641c0f 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -109,30 +109,29 @@ type internal FSharpInlineHintsService let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) typeHints.Add(hint) - // parameter name hints, disabled for now - //| :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> - // let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start - // match appliedArgRangesOpt with - // | Some [] -> () - // | Some appliedArgRanges -> - // match func.PossibleArgumentList with - // | Some [] -> () - // | Some definitionArgNames -> - - // let appliedArgRanges = appliedArgRanges |> Array.ofList - // let definitionArgNames = definitionArgNames |> Array.ofList - - // for idx = 0 to appliedArgRanges.Length - 1 do - // let appliedArgRange = appliedArgRanges.[idx] - // let definitionArgName = definitionArgNames.[idx] - // let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) - // let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + ": ")) - // let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) - // parameterHints.Add(hint) - // | _ -> - // () - // | None -> - // () + | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> + let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start + match appliedArgRangesOpt with + | Some [] -> () + | Some appliedArgRanges -> + match func.PossibleArgumentList with + | Some [] -> () + | Some definitionArgNames -> + + let appliedArgRanges = appliedArgRanges |> Array.ofList + let definitionArgNames = definitionArgNames |> Array.ofList + + for idx = 0 to appliedArgRanges.Length - 1 do + let appliedArgRange = appliedArgRanges.[idx] + let definitionArgName = definitionArgNames.[idx] + let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) + let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + ": ")) + let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) + parameterHints.Add(hint) + | _ -> + () + | None -> + () | _ -> () From a9644144c26a08e71ce480646fed1c610c3683f0 Mon Sep 17 00:00:00 2001 From: cartermp Date: Mon, 26 Oct 2020 17:09:08 -0700 Subject: [PATCH 15/26] More testing and fix a bug with annotating return types for methods --- src/fsharp/service/ServiceUntypedParse.fs | 6 +- tests/service/ServiceUntypedParseTests.fs | 202 +++++++++++++++++++++- 2 files changed, 203 insertions(+), 5 deletions(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index a540893f164..50f68c5ef7b 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -117,7 +117,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = match expr with - | SynExpr.Typed (_expr, _typeExpr, range) when posGeq range.Start pos -> + | SynExpr.Typed (_expr, _typeExpr, range) when posEq range.Start pos -> Some range | _ -> defaultTraverse expr @@ -127,7 +127,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | _ -> let exprFunc pat = match pat with - | SynSimplePat.Typed (_pat, _targetExpr, range) when posGeq range.Start pos -> + | SynSimplePat.Typed (_pat, _targetExpr, range) when posEq range.Start pos -> Some range | _ -> None @@ -136,7 +136,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option override _.VisitPat(defaultTraverse, pat) = match pat with - | SynPat.Typed (_pat, _targetType, range) when posGeq range.Start pos -> + | SynPat.Typed (_pat, _targetType, range) when posEq range.Start pos -> Some range | _ -> defaultTraverse pat }) res.IsSome diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index ef09b153ddb..ebfe652e714 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -306,6 +306,8 @@ type T = """ getTypeMemberRange source |> shouldEqual [ (3, 4), (3, 20) ] +module FunctionApplicationArguments = + [] let ``GetAllArgumentsForFunctionApplication - Single arg``() = let source = """ @@ -322,7 +324,6 @@ f 12 | None -> Assert.Fail("No arguments found in source code") - [] let ``GetAllArgumentsForFunctionApplication - Multi arg``() = let source = """ @@ -493,4 +494,201 @@ f (f 1 2) 3 |> List.map (tups >> fst) |> shouldEqual [(3, 5); (3, 7)] | None -> - Assert.Fail("No arguments found in source code") \ No newline at end of file + Assert.Fail("No arguments found in source code") + +module TypeAnnotations = + [] + let ``IsTypeAnnotationGiven - function - no annotation``() = + let source = """ +let f x = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 6), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGiven - function - single arg annotation``() = + let source = """ +let f (x: int) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGiven - function - first arg annotated``() = + let source = """ +let f (x: int) y = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 15), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGiven - function - second arg annotated``() = + let source = """ +let f x (y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.False(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 9), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - function - all args annotated``() = + let source = """ +let f (x: int) (y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 16), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - constuctor - arg no annotations``() = + let source = """ +type C(x) = class end +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGiven - constuctor - first arg unannotated``() = + let source = """ +type C(x, y: string) = class end +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 10), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - constuctor - second arg unannotated``() = + let source = """ +type C(x: int, y) = class end + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 15), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - constuctor - both args annotated``() = + let source = """ +type C(x: int, y: int) = class end + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 15), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method - args no unannotions``() = + let source = """ +type C() = + member _.M(x, y) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 18), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method - first arg annotated``() = + let source = """ +type C() = + member _.M(x: int, y) = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 23), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method - second arg annotated``() = + let source = """ +type C() = + member _.M(x, y: int) = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 18), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method - both args annotated``() = + let source = """ +type C() = + member _.M(x: int, y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 23), "Expected annotation for argument 'y'") + + + + [] + let ``IsTypeAnnotationGiven - method currying - args no unannotions``() = + let source = """ +type C() = + member _.M x y = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 17), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method currying - first arg annotated``() = + let source = """ +type C() = + member _.M (x: int) y = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 16), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 24), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method currying - second arg annotated``() = + let source = """ +type C() = + member _.M x (y: int) = () + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 16), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 18), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method currying - both args annotated``() = + let source = """ +type C() = + member _.M (x: int) (y: string) = () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 16), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 25), "Expected annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - method - only return type annotated``() = + let source = """ +type C() = + member _.M(x): string = "hello" + x +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") + + [] + let ``IsTypeAnnotationGiven - tuple - no annotations``() = + let source = """ +let (x, y) = (12, "hello") +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected no annotation for value 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 8), "Expected no annotation for value 'y'") + + [] + let ``IsTypeAnnotationGiven - tuple - first value annotated``() = + let source = """ +let (x: int, y) = (12, "hello") +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 13), "Expected no annotation for argument 'y'") + + [] + let ``IsTypeAnnotationGiven - tuple - second value annotated``() = + let source = """ +let (x, y: string) = (12, "hello") +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected no annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 8), "Expected annotation for argument 'y'") From 3169656bf28e9b3aa90bd9bc4710d6224bcc4528 Mon Sep 17 00:00:00 2001 From: cartermp Date: Mon, 26 Oct 2020 18:53:48 -0700 Subject: [PATCH 16/26] Add failing test for infix exprs --- .../service/ServiceParamInfoLocations.fs | 47 ++++++++----------- tests/service/ServiceUntypedParseTests.fs | 17 ++++++- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index 88463902279..a3e9d1a10b4 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -273,57 +273,50 @@ type FSharpNoteworthyParamInfoLocations with | _ -> None module internal FunctionApplicationArgumentLocationsImpl = - - type private FindResult = - /// Ranges for each parameter found in a function application - | Found of range list - | NotFound - let rec private searchSynArgExpr traverseSynExpr _pos expr ranges = match expr with - - /// TODO - need to handle things like tuples, parens using functions as inputs, etc. - // should probably be test-driven! - | SynExprParen(SynExprParen(_, _, _, _), _, _, parenRange) -> - Found(parenRange :: ranges), None + Some(parenRange :: ranges), None | SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF - Found(range :: ranges), None + Some(range :: ranges), None | SynExpr.Const(SynConst.Unit, _) -> - NotFound, None + None, None | SynExprParen(SynExpr.App (_, _isInfix, _, _, _range), _, _, parenRange) -> - Found (parenRange :: ranges), None + Some (parenRange :: ranges), None | e -> let inner = traverseSynExpr e match inner with | None -> - Found (e.Range :: ranges), Some inner - | _ -> NotFound, Some inner + Some (e.Range :: ranges), Some inner + | _ -> None, Some inner - // TODO - doesn't handle infix cases like this: 'string x + y', where 'x' should have a label + // TODO - doesn't handle infix cases like this: 'string x + y', properly let findFSharpFunctionArgInfos 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 = + let _isInfixFuncExpr = match funcExpr with | SynExpr.App (_, isInfix, _, _, _) -> isInfix | _ -> false - let workingRanges = match traverseSynExpr funcExpr with Some ranges -> ranges | None -> [] + let workingRanges = + if isInfix then + [] + else + match traverseSynExpr funcExpr with + | Some ranges -> ranges + | None -> [] - if isInfix || isInfixFuncExpr then - None - else - let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos argExpr workingRanges - match xResult, cacheOpt with - | Found ranges, _ -> Some ranges - | NotFound, Some cache -> cache - | _ -> traverseSynExpr argExpr + let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos 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/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index ebfe652e714..5de4e56ad86 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -496,6 +496,21 @@ f (f 1 2) 3 | None -> Assert.Fail("No arguments found in source code") + [] + let ``GetAllArgumentsForFunctionApplication - nested function application in infix expression``() = + let source = """ +let addStr x y = string x + y +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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") + module TypeAnnotations = [] let ``IsTypeAnnotationGiven - function - no annotation``() = @@ -615,8 +630,6 @@ type C() = Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected annotation for argument 'x'") Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 23), "Expected annotation for argument 'y'") - - [] let ``IsTypeAnnotationGiven - method currying - args no unannotions``() = let source = """ From b565539c47259cf412f319377e873458d1e6d38c Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 27 Oct 2020 14:02:28 -0700 Subject: [PATCH 17/26] Tests and fixes for exprs in infix operators --- .../service/ServiceParamInfoLocations.fs | 26 ++++++------ tests/service/ServiceUntypedParseTests.fs | 40 +++++++++++++++++++ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index a3e9d1a10b4..b58589df681 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -294,29 +294,29 @@ module internal FunctionApplicationArgumentLocationsImpl = Some (e.Range :: ranges), Some inner | _ -> None, Some inner - // TODO - doesn't handle infix cases like this: 'string x + y', properly let findFSharpFunctionArgInfos 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 = + | SynExpr.App (_exprAtomicFlag, _isInfix, funcExpr, argExpr, range) when posEq pos range.Start -> + let isInfixFuncExpr = match funcExpr with | SynExpr.App (_, isInfix, _, _, _) -> isInfix | _ -> false - let workingRanges = - if isInfix then - [] - else + if isInfixFuncExpr then + traverseSynExpr funcExpr + else + + let workingRanges = match traverseSynExpr funcExpr with | Some ranges -> ranges | None -> [] - - let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos argExpr workingRanges - match xResult, cacheOpt with - | Some ranges, _ -> Some ranges - | None, Some cache -> cache - | _ -> traverseSynExpr argExpr + + let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos 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/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 5de4e56ad86..e96e8b8e5b0 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -511,6 +511,46 @@ let addStr x y = string x + y | None -> Assert.Fail("No arguments found in source code") + [] + let ``GetAllArgumentsForFunctionApplication - nested function application outside of infix expression``() = + let source = """ +let addStr x y = x + string y +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.GetAllArgumentsForFunctionApplication (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 ``GetAllArgumentsForFunctionApplication - 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.GetAllArgumentsForFunctionApplication (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.GetAllArgumentsForFunctionApplication (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 ``IsTypeAnnotationGiven - function - no annotation``() = From a8e289bc16a9a660ee365a6c8f9c65f51c6aaf2e Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 27 Oct 2020 16:17:44 -0700 Subject: [PATCH 18/26] QuickInfo is scoped out + surface area --- .../SurfaceArea.netstandard.fs | 10 ++--- .../FSharp.Editor/InlineHints/InlineHints.fs | 44 +------------------ 2 files changed, 7 insertions(+), 47 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 19af946db4c..c776ac6e513 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22502,8 +22502,8 @@ 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.FSharpOption1[Internal.Utilities.StructuredFormat.Layout] GetReturnTypeLayout(FSharp.Compiler.SourceCodeServices.FSharpDisplayContext) -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption1[Internal.Utilities.StructuredFormat.Layout] +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Internal.Utilities.StructuredFormat.Layout] +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 @@ -22525,8 +22525,8 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collect FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpGenericParameter] get_GenericParameters() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] CurriedParameterGroups FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] get_CurriedParameterGroups() -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption1[Microsoft.FSharp.Collections.FSharpList1[System.String]] PossibleArgumentList -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption1[Microsoft.FSharp.Collections.FSharpList1[System.String]] get_PossibleArgumentList() +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList1[System.String]] PossibleArgumentList +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList1[System.String]] get_PossibleArgumentList() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] ElaboratedXmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] XmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] get_ElaboratedXmlDoc() @@ -22744,7 +22744,7 @@ FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.Sourc 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 IsTypeAnnotationGiven(pos) -FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Range+range]] GetAllArgumentsForFunctionApplication(pos) +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Range+range]] GetAllArgumentsForFunctionApplication(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/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 2b3cd641c0f..20dccd55020 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.Shell - -open System open System.Collections.Immutable open System.Threading open System.ComponentModel.Composition @@ -20,7 +17,6 @@ type internal FSharpInlineHintsService [] ( checkerProvider: FSharpCheckerProvider, - [)>] serviceProvider: IServiceProvider, projectInfoManager: FSharpProjectOptionsManager ) = @@ -31,8 +27,7 @@ type internal FSharpInlineHintsService asyncMaybe { do! Option.guard (not (isSignatureFile document.FilePath)) - let! textVersion = document.GetTextVersionAsync(cancellationToken) - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) + 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) @@ -69,44 +64,9 @@ type internal FSharpInlineHintsService for tt in typeInfo do displayParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) - // TODO - this is not actually correct - // We need to get QuickInfo for the actual type we pull out, not the value - // But it does demonstrate a possible way to do this - let callBack position = - fun _ _ -> - asyncMaybe { - let! quickInfo = - FSharpAsyncQuickInfoSource.ProvideQuickInfo( - checkerProvider.Checker, - document.Id, - sourceText, - document.FilePath, - position, - parsingOptions, - projectOptions, - textVersion.GetHashCode(), - document.FSharpOptions.LanguageServicePerformance) - - let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService) - let mainDesc, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo - - let descriptionParts = ImmutableArray.CreateBuilder() - - for tt in mainDesc do - descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) - - for tt in docs do - descriptionParts.Add(TaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text)) - - return (descriptionParts.ToImmutableArray()) - } - |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) - - let getDescriptionAsync position = Func(callBack position) let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) - let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start) + let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray()) typeHints.Add(hint) | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> From 16c0b935622ccdf3c78b5103184c4cc97241e971 Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 27 Oct 2020 17:11:07 -0700 Subject: [PATCH 19/26] Add IsMethod and change parameter name hints to be more like names params --- src/fsharp/symbols/Symbols.fs | 5 +++++ src/fsharp/symbols/Symbols.fsi | 3 +++ .../src/FSharp.Editor/InlineHints/InlineHints.fs | 10 +++------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 1d3e8c72acf..378cc487e7d 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 diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 5397e9ca168..796ffd12110 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 diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 20dccd55020..0fa3b3454c8 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -72,12 +72,12 @@ type internal FSharpInlineHintsService | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start match appliedArgRangesOpt with + | None -> () | Some [] -> () | Some appliedArgRanges -> match func.PossibleArgumentList with | Some [] -> () | Some definitionArgNames -> - let appliedArgRanges = appliedArgRanges |> Array.ofList let definitionArgNames = definitionArgNames |> Array.ofList @@ -85,14 +85,10 @@ type internal FSharpInlineHintsService let appliedArgRange = appliedArgRanges.[idx] let definitionArgName = definitionArgNames.[idx] let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) - let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + ": ")) + let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + " =")) let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) parameterHints.Add(hint) - | _ -> - () - | None -> - () - + | _ -> () | _ -> () let typeHints = typeHints.ToImmutableArray() From 7e9eded2d6e4408df571296cfb668e8262ff88e2 Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 27 Oct 2020 21:39:25 -0700 Subject: [PATCH 20/26] Only show type hints for lambdas + tests + surface --- src/fsharp/service/FSharpCheckerResults.fsi | 3 +- src/fsharp/service/ServiceUntypedParse.fs | 21 ++++++++- src/fsharp/service/ServiceUntypedParse.fsi | 5 +- .../SurfaceArea.netstandard.fs | 6 ++- tests/service/ServiceUntypedParseTests.fs | 47 +++++++++++++++++++ .../FSharp.Editor/InlineHints/InlineHints.fs | 8 +++- 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/fsharp/service/FSharpCheckerResults.fsi b/src/fsharp/service/FSharpCheckerResults.fsi index f486fe5fa37..1bc59bbedcd 100644 --- a/src/fsharp/service/FSharpCheckerResults.fsi +++ b/src/fsharp/service/FSharpCheckerResults.fsi @@ -214,7 +214,8 @@ type public FSharpCheckFileResults = /// Get all textual usages of all symbols throughout a typechecked file that fall within a given range. /// The range in the typechecked document that all symbols must by defined within - member GetAllUsesOfAllSymbolsInFileWithinRange : outerRange: range -> Async + /// An optional token to cancel the operation + member GetAllUsesOfAllSymbolsInFileWithinRange : outerRange: range * ?cancellationToken: CancellationToken -> FSharpSymbolUse[] /// Get the textual usages that resolved to the given symbol throughout the file member GetUsesOfSymbolInFile : symbol:FSharpSymbol * ?cancellationToken: CancellationToken -> FSharpSymbolUse[] diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 50f68c5ef7b..a8ce78a9a0c 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -140,8 +140,25 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option Some range | _ -> defaultTraverse pat }) res.IsSome - | None -> - false + | None -> false + + member scope.IsBindingALambda(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 2245fd95a02..369f4e27292 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -25,9 +25,12 @@ type public FSharpParseFileResults = // doot doot yeet yeet member GetAllArgumentsForFunctionApplication: pos: pos -> range list option - // doot doot yeet yeet + // Determines if the expression or pattern at the given position has a type annotation member IsTypeAnnotationGiven: pos -> bool + // Determines if the binding at a given position is a lambda expression + member IsBindingALambda: pos -> bool + /// Name of the file for which this information were created member FileName: string diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index c776ac6e513..74c22d068f3 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22437,6 +22437,7 @@ 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 @@ -22471,6 +22472,7 @@ 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() @@ -22525,8 +22527,8 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collect FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpGenericParameter] get_GenericParameters() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] CurriedParameterGroups FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] get_CurriedParameterGroups() -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList1[System.String]] PossibleArgumentList -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList1[System.String]] get_PossibleArgumentList() +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[System.String]] PossibleArgumentList +FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[System.String]] get_PossibleArgumentList() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] ElaboratedXmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] XmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] get_ElaboratedXmlDoc() diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index e96e8b8e5b0..0b740f1dab2 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -595,6 +595,15 @@ let f (x: int) (y: string) = () Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 16), "Expected annotation for argument 'y'") + [] + let ``IsTypeAnnotationGiven - lambda function - all args annotated``() = + let source = """ +let f = fun (x: int) (y: string) -> () +""" + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 13), "Expected a annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 22), "Expected a annotation for argument 'y'") + [] let ``IsTypeAnnotationGiven - constuctor - arg no annotations``() = let source = """ @@ -745,3 +754,41 @@ let (x, y: string) = (12, "hello") let parseFileResults, _ = getParseAndCheckResults source Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected no annotation for argument 'x'") Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 8), "Expected annotation for argument 'y'") + +module LambdaRecognition = + [] + let ``IsBindingALambda - recognize a lambda``() = + let source = """ +let f = fun x y -> x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsBindingALambda (mkPos 2 4), "Expected 'f' to be a lambda expression") + + [] + let ``IsBindingALambda - recognize a nested lambda``() = + let source = """ +let f = + fun x -> + fun y -> + x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsBindingALambda (mkPos 2 4), "Expected 'f' to be a lambda expression") + + [] + let ``IsBindingALambda - recognize a "partial" lambda``() = + let source = """ +let f x = + fun y -> + x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsTrue(parseFileResults.IsBindingALambda (mkPos 2 4), "Expected 'f' to be a lambda expression") + + [] + let ``IsBindingALambda - not a lambda``() = + let source = """ +let f x y = x + y + """ + let parseFileResults, _ = getParseAndCheckResults source + Assert.IsFalse(parseFileResults.IsBindingALambda (mkPos 2 4), "'f' is not a lambda expression'") \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 0fa3b3454c8..ad8afb1fff5 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -31,15 +31,19 @@ type internal FSharpInlineHintsService 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.GetAllUsesOfAllSymbolsInFileWithinRange(range) |> liftAsync + let symbolUses = checkFileResults.GetAllUsesOfAllSymbolsInFileWithinRange(range, cancellationToken) let typeHints = ImmutableArray.CreateBuilder() let parameterHints = ImmutableArray.CreateBuilder() let isValidForTypeHint (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + let isLambdaIfFunction = + funcOrValue.IsFunction && + parseFileResults.IsBindingALambda symbolUse.RangeAlternate.Start + + (funcOrValue.IsValue || isLambdaIfFunction) && not (parseFileResults.IsTypeAnnotationGiven symbolUse.RangeAlternate.Start) && symbolUse.IsFromDefinition && - (funcOrValue.IsValue || funcOrValue.IsFunction) && not funcOrValue.IsMember && not funcOrValue.IsMemberThisValue && not funcOrValue.IsConstructorThisValue && From 0ce8403d0f0916d60571b03427ffcc8c826a63e9 Mon Sep 17 00:00:00 2001 From: cartermp Date: Wed, 28 Oct 2020 18:17:44 -0700 Subject: [PATCH 21/26] Preliminary support for labels for methods --- .../service/ServiceParamInfoLocations.fs | 47 ++++++++++++------- .../service/ServiceParamInfoLocations.fsi | 5 +- .../FSharp.Editor/InlineHints/InlineHints.fs | 38 ++++++++++++++- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index b58589df681..e6756b4d308 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -7,7 +7,15 @@ open FSharp.Compiler.SyntaxTreeOps open FSharp.Compiler.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: {| IsNamedArgument: bool; ArgumentRange: range |} list, + tupleEndLocations: pos list, + isThereACloseParen: bool, + namedParamNames: string option list) = let tupleEndLocations = Array.ofList tupleEndLocations let namedParamNames = Array.ofList namedParamNames @@ -28,6 +36,7 @@ type FSharpNoteworthyParamInfoLocations(longId: string list, longIdRange: range, member this.TupleEndLocations = tupleEndLocations member this.IsThereACloseParen = isThereACloseParen member this.NamedParamNames = namedParamNames + member this.ArgLocations = argRanges |> Array.ofList [] module internal NoteworthyParamInfoLocationsImpl = @@ -50,7 +59,7 @@ module internal NoteworthyParamInfoLocationsImpl = | _ -> None type FindResult = - | Found of openParen: pos * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool + | Found of openParen: pos * argRanges: {| IsNamedArgument: bool; ArgumentRange: range |} list * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool | NotFound let digOutIdentFromStaticArg (StripParenTypes synType) = @@ -86,7 +95,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 +113,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 +137,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 +155,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 +167,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, [](* TODO - figure out if need to do anything here *), commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) else None | _ -> @@ -169,9 +183,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 +204,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, [] (* TODO - figure out if need to do anything here *), [ wholem.End ], false, [])) | None -> None else None @@ -205,7 +219,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 +229,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 +242,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 +264,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 diff --git a/src/fsharp/service/ServiceParamInfoLocations.fsi b/src/fsharp/service/ServiceParamInfoLocations.fsi index 3f5cfefa232..6491339140b 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fsi +++ b/src/fsharp/service/ServiceParamInfoLocations.fsi @@ -33,7 +33,10 @@ 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 ArgLocations: {| IsNamedArgument: bool; ArgumentRange: range |} [] /// Find the information about parameter info locations at a particular source location static member Find : pos * ParsedInput -> FSharpNoteworthyParamInfoLocations option diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index ad8afb1fff5..aca20a2e0cd 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -1,6 +1,7 @@ // 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 @@ -11,6 +12,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints open FSharp.Compiler open FSharp.Compiler.SourceCodeServices +open FSharp.Compiler.Range [)>] type internal FSharpInlineHintsService @@ -22,6 +24,13 @@ type internal FSharpInlineHintsService 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 { @@ -88,11 +97,36 @@ type internal FSharpInlineHintsService for idx = 0 to appliedArgRanges.Length - 1 do let appliedArgRange = appliedArgRanges.[idx] let definitionArgName = definitionArgNames.[idx] - let appledArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) + let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange) let displayParts = ImmutableArray.Create(TaggedText(TextTags.Text, definitionArgName + " =")) - let hint = FSharpInlineHint(TextSpan(appledArgSpan.Start, 0), displayParts) + let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts) parameterHints.Add(hint) | _ -> () + + | :? FSharpMemberOrFunctionOrValue as meth when meth.IsMethod -> + // get position that is +1 column from first `(` on the method symbol line + let endPosForMethod = symbolUse.RangeAlternate.End + let line, _ = Pos.toZ endPosForMethod + let afterParenPosInLine = getFirstPositionAfterParen (sourceText.Lines.[line].ToString()) (endPosForMethod.Column) + + + let paramInfos = parseFileResults.FindNoteworthyParamInfoLocations(Pos.fromZ line afterParenPosInLine) + match paramInfos with + | None -> () + | Some paramInfos -> + // TODO - how the hell to handle the nested ones? + let groups = meth.CurriedParameterGroups + if groups.Count = 0 || groups.Count > 1 then + () + else + for idx = 0 to groups.[0].Count - 1 do + let paramLocationInfo = paramInfos.ArgLocations.[idx] + let paramName = groups.[0].[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) | _ -> () let typeHints = typeHints.ToImmutableArray() From 50e7d9d0cc86e00f987ffb1cbc83f6d03b795ce9 Mon Sep 17 00:00:00 2001 From: cartermp Date: Thu, 29 Oct 2020 17:13:50 -0700 Subject: [PATCH 22/26] Cleanup, handle method params properly, constructors --- .../service/ServiceParamInfoLocations.fs | 44 +++++++----- .../service/ServiceParamInfoLocations.fsi | 5 +- src/fsharp/service/ServiceUntypedParse.fs | 2 +- src/fsharp/service/ServiceUntypedParse.fsi | 8 +-- src/fsharp/symbols/Symbols.fs | 17 ----- src/fsharp/symbols/Symbols.fsi | 18 ++--- .../SurfaceArea.netstandard.fs | 19 +++-- tests/service/ServiceUntypedParseTests.fs | 58 +++++++++------ .../FSharp.Editor/InlineHints/InlineHints.fs | 71 +++++++++++-------- 9 files changed, 136 insertions(+), 106 deletions(-) diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index e6756b4d308..51cdef489aa 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -6,16 +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, - argRanges: {| IsNamedArgument: bool; ArgumentRange: range |} list, + argRanges: TupledArgumentLocation list, tupleEndLocations: pos list, isThereACloseParen: bool, - namedParamNames: string option list) = + namedParamNames: string option list + ) = let tupleEndLocations = Array.ofList tupleEndLocations let namedParamNames = Array.ofList namedParamNames @@ -59,7 +62,7 @@ module internal NoteworthyParamInfoLocationsImpl = | _ -> None type FindResult = - | Found of openParen: pos * argRanges: {| IsNamedArgument: bool; ArgumentRange: range |} list * 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) = @@ -95,7 +98,7 @@ module internal NoteworthyParamInfoLocationsImpl = match inner with | None -> if AstTraversal.rangeContainsPosLeftEdgeExclusiveAndRightEdgeInclusive parenRange pos then - let argRanges = [{| IsNamedArgument = (getNamedParamName synExpr).IsSome; ArgumentRange = synExpr.Range |}] + let argRanges = [{ IsNamedArgument = (getNamedParamName synExpr).IsSome; ArgumentRange = synExpr.Range }] Found (parenRange.Start, argRanges, [(parenRange.End, getNamedParamName synExpr)], rpRangeOpt.IsSome), None else NotFound, None @@ -116,7 +119,7 @@ module internal NoteworthyParamInfoLocationsImpl = // argRange, isNamed let argRanges = synExprList - |> List.map (fun e -> {| IsNamedArgument = (getNamedParamName e).IsSome; ArgumentRange = e.Range |}) + |> 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, argRanges, commasAndCloseParen, rpRangeOpt.IsSome) r, None @@ -167,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, [](* TODO - figure out if need to do anything here *), commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) + Some (FSharpNoteworthyParamInfoLocations(pathOfLid lid, lidm, openm.Start, [], commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg)) else None | _ -> @@ -204,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, [] (* TODO - figure out if need to do anything here *), [ wholem.End ], false, [])) + | Some(lid, lidRange) -> Some (FSharpNoteworthyParamInfoLocations(lid, lidRange, op.idRange.Start, [], [ wholem.End ], false, [])) | None -> None else None @@ -242,7 +245,7 @@ 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 argRanges = tyArgs |> List.map (fun tyarg -> {| IsNamedArgument = false; ArgumentRange = tyarg.Range |}) + 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 @@ -288,17 +291,25 @@ type FSharpNoteworthyParamInfoLocations with | _ -> None module internal FunctionApplicationArgumentLocationsImpl = - let rec private searchSynArgExpr traverseSynExpr _pos expr ranges = + let rec private searchSynArgExpr traverseSynExpr expr ranges = match expr with - | SynExprParen(SynExprParen(_, _, _, _), _, _, parenRange) -> - Some(parenRange :: ranges), None - - | SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF - Some(range :: ranges), None - | 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 @@ -322,13 +333,12 @@ module internal FunctionApplicationArgumentLocationsImpl = if isInfixFuncExpr then traverseSynExpr funcExpr else - let workingRanges = match traverseSynExpr funcExpr with | Some ranges -> ranges | None -> [] - let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos argExpr workingRanges + let xResult, cacheOpt = searchSynArgExpr traverseSynExpr argExpr workingRanges match xResult, cacheOpt with | Some ranges, _ -> Some ranges | None, Some cache -> cache diff --git a/src/fsharp/service/ServiceParamInfoLocations.fsi b/src/fsharp/service/ServiceParamInfoLocations.fsi index 6491339140b..78e8fca1947 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 = @@ -36,7 +39,7 @@ type public FSharpNoteworthyParamInfoLocations = member NamedParamNames : string option [] /// Array of locations for each argument, and a flag if that argument is named - member ArgLocations: {| IsNamedArgument: bool; ArgumentRange: range |} [] + member ArgLocations: TupledArgumentLocation [] /// Find the information about parameter info locations at a particular source location static member Find : pos * ParsedInput -> FSharpNoteworthyParamInfoLocations option diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index a8ce78a9a0c..410e2e78176 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -105,7 +105,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | Some input -> FSharpNoteworthyParamInfoLocations.Find(pos, input) | _ -> None - member scope.GetAllArgumentsForFunctionApplication pos = + member scope.GetAllArgumentsForFunctionApplicationAtPostion pos = match input with | Some input -> FunctionApplicationArgumentLocationsImpl.findFSharpFunctionArgInfos pos input | None -> None diff --git a/src/fsharp/service/ServiceUntypedParse.fsi b/src/fsharp/service/ServiceUntypedParse.fsi index 369f4e27292..3d85232146e 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -22,13 +22,13 @@ type public FSharpParseFileResults = /// Notable parse info for ParameterInfo at a given location member FindNoteworthyParamInfoLocations : pos:pos -> FSharpNoteworthyParamInfoLocations option - // doot doot yeet yeet - member GetAllArgumentsForFunctionApplication: pos: pos -> range list 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 + /// Determines if the expression or pattern at the given position has a type annotation member IsTypeAnnotationGiven: pos -> bool - // Determines if the binding at a given position is a lambda expression + /// Determines if the binding at a given position is a lambda expression member IsBindingALambda: pos -> bool /// Name of the file for which this information were created diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 378cc487e7d..5195694b593 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -1932,23 +1932,6 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = |> makeReadOnlyCollection ] |> makeReadOnlyCollection - member x.PossibleArgumentList = - checkIsResolved() - match d with - | P _ | E _ | M _ | C _ -> None - | V v -> - match v.ValReprInfo with - | None -> - // the "pass a single function to a function case"? - let _, tau = v.TypeScheme - if isFunTy cenv.g tau then - Some [v.DisplayName] - else - None - | Some info -> - info.ArgNames - - member x.ReturnParameter = checkIsResolved() match d with diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 796ffd12110..82ef5cb7ce6 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -861,10 +861,14 @@ type FSharpMemberOrFunctionOrValue = /// Get the name as presented in F# error messages and documentation member DisplayName: string +<<<<<<< HEAD member CurriedParameterGroups: IList> - - /// doop doop yeet yeet - member PossibleArgumentList : string list option +======= + /// List of list of parameters. 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> +>>>>>>> 8dc6b59f9... Cleanup, handle method params properly, constructors /// Gets the overloads for the current method /// matchParameterNumber indicates whether to filter the overloads to match the number of parameters in the current symbol @@ -914,7 +918,6 @@ type FSharpMemberOrFunctionOrValue = member IsConstructor: bool /// Format the type using the rules of the given display context -<<<<<<< HEAD member FormatLayout: context: FSharpDisplayContext -> Layout /// Format the type using the rules of the given display context @@ -923,13 +926,6 @@ type FSharpMemberOrFunctionOrValue = /// 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 -======= - member FormatLayout : displayContext: FSharpDisplayContext -> Layout - - /// Format the type using the rules of the given display context - member GetReturnTypeLayout : displayContext: FSharpDisplayContext -> Layout option - ->>>>>>> ac5c7ef5f... Match names /// A subtype of FSharpSymbol that represents a parameter [] diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 74c22d068f3..ba47fc3357e 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22504,7 +22504,6 @@ 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] 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 @@ -22527,8 +22526,6 @@ FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collect FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpGenericParameter] get_GenericParameters() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] CurriedParameterGroups FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.Collections.Generic.IList`1[FSharp.Compiler.SourceCodeServices.FSharpParameter]] get_CurriedParameterGroups() -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[System.String]] PossibleArgumentList -FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[System.String]] get_PossibleArgumentList() FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] ElaboratedXmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] XmlDoc FSharp.Compiler.SourceCodeServices.FSharpMemberOrFunctionOrValue: System.Collections.Generic.IList`1[System.String] get_ElaboratedXmlDoc() @@ -22680,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[] ArgLocations +FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: FSharp.Compiler.SourceCodeServices.TupledArgumentLocation[] get_ArgLocations() +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 @@ -22746,7 +22756,8 @@ FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.Sourc 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 IsTypeAnnotationGiven(pos) -FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Range+range]] GetAllArgumentsForFunctionApplication(pos) +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Boolean IsBindingALambda(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 0b740f1dab2..599556d1dac 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -315,7 +315,7 @@ let f x = () f 12 """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res @@ -331,7 +331,7 @@ let f x y z = () f 1 2 3 """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res @@ -347,7 +347,7 @@ let f x y z = () f (1) (2) (3) """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res @@ -363,12 +363,12 @@ let f x y z = () f ((1)) (((2))) ((((3)))) """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res |> List.map (tups >> fst) - |> shouldEqual [(3, 2); (3, 8); (3, 16)] + |> shouldEqual [(3, 3); (3, 10); (3, 19)] | None -> Assert.Fail("No arguments found in source code") @@ -379,7 +379,7 @@ let f () = () f () """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) Assert.IsTrue(res.IsNone, "Found argument for unit-accepting function, which shouldn't be the case.") [] @@ -389,7 +389,7 @@ let f x y = x + y f 12 """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res @@ -406,7 +406,7 @@ let t = (1, 2) f t """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 4 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 4 0) match res with | Some res -> res @@ -422,12 +422,12 @@ let f (t: int * int) = () f (1, 2) """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res |> List.map (tups >> fst) - |> shouldEqual [(3, 2)] + |> shouldEqual [(3, 3); (3, 6)] | None -> Assert.Fail("No arguments found in source code") @@ -439,7 +439,7 @@ let t = (1, 2) f t """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 4 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 4 0) match res with | Some res -> res @@ -449,18 +449,34 @@ f t Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - tuple literal with definition that has explicit names``() = + let ``GetAllArgumentsForFunctionApplication - tuple literal inside parens``() = let source = """ -let f ((x, y): int * int) = () +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 ``GetAllArgumentsForFunctionApplication - tuples with elements as arguments``() = + let source = """ +let f (a, b) = () f (1, 2) """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res |> List.map (tups >> fst) - |> shouldEqual [(3, 2)] + |> shouldEqual [(3, 3); (3, 6)] | None -> Assert.Fail("No arguments found in source code") @@ -471,7 +487,7 @@ let f x y = x + y f (f 1 2) 3 """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 0) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 0) match res with | Some res -> res @@ -487,7 +503,7 @@ let f x y = x + y f (f 1 2) 3 """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 3 3) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 3 3) match res with | Some res -> res @@ -502,7 +518,7 @@ f (f 1 2) 3 let addStr x y = string x + y """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 2 17) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 17) match res with | Some res -> res @@ -517,7 +533,7 @@ let addStr x y = string x + y let addStr x y = x + string y """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 2 21) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 21) match res with | Some res -> res @@ -532,7 +548,7 @@ let addStr x y = x + string y let addStr x y = string x + string y """ let parseFileResults, _ = getParseAndCheckResults source - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 2 17) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 17) match res with | Some res -> res @@ -542,7 +558,7 @@ let addStr x y = string x + string y Assert.Fail("No arguments found in source code") - let res = parseFileResults.GetAllArgumentsForFunctionApplication (mkPos 2 28) + let res = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion (mkPos 2 28) match res with | Some res -> res diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index aca20a2e0cd..7b0908a7d49 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -5,6 +5,7 @@ open System open System.Collections.Immutable open System.Threading open System.ComponentModel.Composition +open System.Linq open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text @@ -83,50 +84,60 @@ type internal FSharpInlineHintsService typeHints.Add(hint) | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> - let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplication symbolUse.RangeAlternate.Start + let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion symbolUse.RangeAlternate.Start match appliedArgRangesOpt with | None -> () | Some [] -> () | Some appliedArgRanges -> - match func.PossibleArgumentList with - | Some [] -> () - | Some definitionArgNames -> - let appliedArgRanges = appliedArgRanges |> Array.ofList - let definitionArgNames = definitionArgNames |> Array.ofList - - for idx = 0 to appliedArgRanges.Length - 1 do - let appliedArgRange = appliedArgRanges.[idx] - let definitionArgName = definitionArgNames.[idx] + 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 meth when meth.IsMethod -> - // get position that is +1 column from first `(` on the method symbol line + | :? 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 to "tupled" view. + | Some tupledParamInfos, _ -> + let parameters = methodOrConstructor.CurriedParameterGroups |> Seq.concat |> Array.ofSeq + for idx = 0 to parameters.Length - 1 do + let paramLocationInfo = tupledParamInfos.ArgLocations.[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 - let paramInfos = parseFileResults.FindNoteworthyParamInfoLocations(Pos.fromZ line afterParenPosInLine) - match paramInfos with - | None -> () - | Some paramInfos -> - // TODO - how the hell to handle the nested ones? - let groups = meth.CurriedParameterGroups - if groups.Count = 0 || groups.Count > 1 then - () - else - for idx = 0 to groups.[0].Count - 1 do - let paramLocationInfo = paramInfos.ArgLocations.[idx] - let paramName = groups.[0].[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) + 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() From 258aa23e9768c052c3f03a295d64be30d799da41 Mon Sep 17 00:00:00 2001 From: cartermp Date: Sun, 1 Nov 2020 16:59:46 -0800 Subject: [PATCH 23/26] Feedback --- src/fsharp/NicePrint.fs | 4 +- .../service/ServiceParamInfoLocations.fs | 6 +- .../service/ServiceParamInfoLocations.fsi | 6 +- src/fsharp/service/ServiceUntypedParse.fs | 6 +- src/fsharp/service/ServiceUntypedParse.fsi | 6 +- src/fsharp/symbols/Symbols.fs | 14 +- src/fsharp/symbols/Symbols.fsi | 9 +- .../SurfaceArea.netstandard.fs | 8 +- tests/service/ServiceUntypedParseTests.fs | 172 +++++++++--------- .../FSharp.Editor/InlineHints/InlineHints.fs | 9 +- 10 files changed, 120 insertions(+), 120 deletions(-) diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index 0506cd855a3..23282e25dd5 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -1200,7 +1200,7 @@ module private PrintTypes = let ty, _cxs = PrettyTypes.PrettifyType denv.g ty layoutTypeWithInfoAndPrec denv SimplifyTypes.typeSimplificationInfo0 5 ty - let prettyLayoutOfValReturnType denv (v: ValRef) = + let layoutOfValReturnType denv (v: ValRef) = match v.ValReprInfo with | None -> let _, tau = v.TypeScheme @@ -2236,7 +2236,7 @@ let prettyLayoutOfValOrMemberNoInst denv v = PrintTastMemberOrVals.prettyLayoutO let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v -let prettyLayoutOfValReturnType denv x = x |> PrintTypes.prettyLayoutOfValReturnType denv +let layoutOfValReturnType denv x = x |> PrintTypes.layoutOfValReturnType denv let prettyLayoutOfInstAndSig denv x = PrintTypes.prettyLayoutOfInstAndSig denv x diff --git a/src/fsharp/service/ServiceParamInfoLocations.fs b/src/fsharp/service/ServiceParamInfoLocations.fs index 51cdef489aa..0585c921242 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fs +++ b/src/fsharp/service/ServiceParamInfoLocations.fs @@ -39,7 +39,7 @@ type FSharpNoteworthyParamInfoLocations member this.TupleEndLocations = tupleEndLocations member this.IsThereACloseParen = isThereACloseParen member this.NamedParamNames = namedParamNames - member this.ArgLocations = argRanges |> Array.ofList + member this.ArgumentLocations = argRanges |> Array.ofList [] module internal NoteworthyParamInfoLocationsImpl = @@ -290,7 +290,7 @@ type FSharpNoteworthyParamInfoLocations with r | _ -> None -module internal FunctionApplicationArgumentLocationsImpl = +module internal SynExprAppLocationsImpl = let rec private searchSynArgExpr traverseSynExpr expr ranges = match expr with | SynExpr.Const(SynConst.Unit, _) -> @@ -320,7 +320,7 @@ module internal FunctionApplicationArgumentLocationsImpl = Some (e.Range :: ranges), Some inner | _ -> None, Some inner - let findFSharpFunctionArgInfos pos parseTree = + let getAllCurriedArgsAtPosition pos parseTree = AstTraversal.Traverse(pos, parseTree, { new AstTraversal.AstVisitorBase<_>() with member _.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) = match expr with diff --git a/src/fsharp/service/ServiceParamInfoLocations.fsi b/src/fsharp/service/ServiceParamInfoLocations.fsi index 78e8fca1947..4ce27fc0169 100755 --- a/src/fsharp/service/ServiceParamInfoLocations.fsi +++ b/src/fsharp/service/ServiceParamInfoLocations.fsi @@ -39,10 +39,10 @@ type public FSharpNoteworthyParamInfoLocations = member NamedParamNames : string option [] /// Array of locations for each argument, and a flag if that argument is named - member ArgLocations: TupledArgumentLocation [] + member ArgumentLocations: TupledArgumentLocation [] /// Find the information about parameter info locations at a particular source location static member Find : pos * ParsedInput -> FSharpNoteworthyParamInfoLocations option -module internal FunctionApplicationArgumentLocationsImpl = - val findFSharpFunctionArgInfos: pos: pos -> parseTree: ParsedInput -> range list 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 410e2e78176..3228be7e5cd 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -107,10 +107,10 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option member scope.GetAllArgumentsForFunctionApplicationAtPostion pos = match input with - | Some input -> FunctionApplicationArgumentLocationsImpl.findFSharpFunctionArgInfos pos input + | Some input -> SynExprAppLocationsImpl.getAllCurriedArgsAtPosition pos input | None -> None - member scope.IsTypeAnnotationGiven pos = + member scope.IsTypeAnnotationGivenAtPosition pos = match input with | Some parseTree -> let res = @@ -142,7 +142,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option res.IsSome | None -> false - member scope.IsBindingALambda(pos) = + member scope.IsBindingALambdaAtPosition pos = match input with | Some parseTree -> let res = diff --git a/src/fsharp/service/ServiceUntypedParse.fsi b/src/fsharp/service/ServiceUntypedParse.fsi index 3d85232146e..2cf813b884f 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -26,10 +26,10 @@ type public FSharpParseFileResults = member GetAllArgumentsForFunctionApplicationAtPostion: pos: pos -> range list option /// Determines if the expression or pattern at the given position has a type annotation - member IsTypeAnnotationGiven: pos -> bool + member IsTypeAnnotationGivenAtPosition: pos -> bool - /// Determines if the binding at a given position is a lambda expression - member IsBindingALambda: 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 diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 5195694b593..d1919bb6717 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -2109,7 +2109,6 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | V v -> v.TauType NicePrint.prettyLayoutOfTypeNoCx (displayContext.Contents cenv.g) ty - // TODO - consider the other types member x.GetReturnTypeLayout (displayContext: FSharpDisplayContext) = match x.IsMember, d with | true, _ -> @@ -2117,12 +2116,15 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | false, _ -> checkIsResolved() match d with - | E _e -> None - | P _p -> None - | M _m | C _m -> - None + | 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.prettyLayoutOfReturnType (denv.Contents cenv.g) v.Deref + NicePrint.layoutOfValReturnType (displayContext.Contents cenv.g) v |> Some member x.GetWitnessPassingInfo() = diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 82ef5cb7ce6..49f79c4fab8 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -861,14 +861,13 @@ type FSharpMemberOrFunctionOrValue = /// Get the name as presented in F# error messages and documentation member DisplayName: string -<<<<<<< HEAD - member CurriedParameterGroups: IList> -======= - /// List of list of parameters. Typically, there is only one nested list. + /// 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> ->>>>>>> 8dc6b59f9... Cleanup, handle method params properly, constructors /// Gets the overloads for the current method /// matchParameterNumber indicates whether to filter the overloads to match the number of parameters in the current symbol diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index ba47fc3357e..52220db873b 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22677,8 +22677,8 @@ 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[] ArgLocations -FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations: FSharp.Compiler.SourceCodeServices.TupledArgumentLocation[] get_ArgLocations() +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) @@ -22755,8 +22755,8 @@ 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 IsTypeAnnotationGiven(pos) -FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Boolean IsBindingALambda(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() diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 599556d1dac..86185b69e51 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -309,7 +309,7 @@ type T = module FunctionApplicationArguments = [] - let ``GetAllArgumentsForFunctionApplication - Single arg``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - Single arg``() = let source = """ let f x = () f 12 @@ -325,7 +325,7 @@ f 12 Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - Multi arg``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - Multi arg``() = let source = """ let f x y z = () f 1 2 3 @@ -341,7 +341,7 @@ f 1 2 3 Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - Multi arg parentheses``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - Multi arg parentheses``() = let source = """ let f x y z = () f (1) (2) (3) @@ -357,7 +357,7 @@ f (1) (2) (3) Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - Multi arg nested parentheses``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - Multi arg nested parentheses``() = let source = """ let f x y z = () f ((1)) (((2))) ((((3)))) @@ -373,7 +373,7 @@ f ((1)) (((2))) ((((3)))) Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - unit``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - unit``() = let source = """ let f () = () f () @@ -383,7 +383,7 @@ f () Assert.IsTrue(res.IsNone, "Found argument for unit-accepting function, which shouldn't be the case.") [] - let ``GetAllArgumentsForFunctionApplication - curried function``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - curried function``() = let source = """ let f x y = x + y f 12 @@ -399,7 +399,7 @@ f 12 Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - tuple value``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple value``() = let source = """ let f (t: int * int) = () let t = (1, 2) @@ -416,7 +416,7 @@ f t Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - tuple literal``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple literal``() = let source = """ let f (t: int * int) = () f (1, 2) @@ -432,7 +432,7 @@ f (1, 2) Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - tuple value with definition that has explicit names``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple value with definition that has explicit names``() = let source = """ let f ((x, y): int * int) = () let t = (1, 2) @@ -449,7 +449,7 @@ f t Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - tuple literal inside parens``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuple literal inside parens``() = let source = """ let f (x, y) = () f ((1, 2)) @@ -465,7 +465,7 @@ f ((1, 2)) Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - tuples with elements as arguments``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - tuples with elements as arguments``() = let source = """ let f (a, b) = () f (1, 2) @@ -481,7 +481,7 @@ f (1, 2) Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - top-level arguments with nested function call``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - top-level arguments with nested function call``() = let source = """ let f x y = x + y f (f 1 2) 3 @@ -497,7 +497,7 @@ f (f 1 2) 3 Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - nested function argument positions``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function argument positions``() = let source = """ let f x y = x + y f (f 1 2) 3 @@ -513,7 +513,7 @@ f (f 1 2) 3 Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - nested function application in infix expression``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function application in infix expression``() = let source = """ let addStr x y = string x + y """ @@ -528,7 +528,7 @@ let addStr x y = string x + y Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - nested function application outside of infix expression``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function application outside of infix expression``() = let source = """ let addStr x y = x + string y """ @@ -543,7 +543,7 @@ let addStr x y = x + string y Assert.Fail("No arguments found in source code") [] - let ``GetAllArgumentsForFunctionApplication - nested function applications both inside and outside of infix expression``() = + let ``GetAllArgumentsForFunctionApplicationAtPostion - nested function applications both inside and outside of infix expression``() = let source = """ let addStr x y = string x + string y """ @@ -569,219 +569,219 @@ let addStr x y = string x + string y module TypeAnnotations = [] - let ``IsTypeAnnotationGiven - function - no annotation``() = + let ``IsTypeAnnotationGivenAtPosition - function - no annotation``() = let source = """ let f x = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 6), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 6), "Expected no annotation for argument 'x'") [] - let ``IsTypeAnnotationGiven - function - single arg annotation``() = + let ``IsTypeAnnotationGivenAtPosition - function - single arg annotation``() = let source = """ let f (x: int) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") + Assert.IsTrue(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected annotation for argument 'x'") [] - let ``IsTypeAnnotationGiven - function - first arg annotated``() = + let ``IsTypeAnnotationGivenAtPosition - function - first arg annotated``() = let source = """ let f (x: int) y = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 15), "Expected no annotation for argument 'x'") + 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 ``IsTypeAnnotationGiven - function - second arg annotated``() = + let ``IsTypeAnnotationGivenAtPosition - function - second arg annotated``() = let source = """ let f x (y: string) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.False(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected no annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 9), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - function - all args annotated``() = + let ``IsTypeAnnotationGivenAtPosition - function - all args annotated``() = let source = """ let f (x: int) (y: string) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 16), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - lambda function - all args annotated``() = + let ``IsTypeAnnotationGivenAtPosition - lambda function - all args annotated``() = let source = """ let f = fun (x: int) (y: string) -> () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 13), "Expected a annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 22), "Expected a annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - constuctor - arg no annotations``() = + let ``IsTypeAnnotationGivenAtPosition - constuctor - arg no annotations``() = let source = """ type C(x) = class end """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 2 7), "Expected no annotation for argument 'x'") [] - let ``IsTypeAnnotationGiven - constuctor - first arg unannotated``() = + let ``IsTypeAnnotationGivenAtPosition - constuctor - first arg unannotated``() = let source = """ type C(x, y: string) = class end """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected no annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 10), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - constuctor - second arg unannotated``() = + let ``IsTypeAnnotationGivenAtPosition - constuctor - second arg unannotated``() = let source = """ type C(x: int, y) = class end """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 15), "Expected no annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - constuctor - both args annotated``() = + let ``IsTypeAnnotationGivenAtPosition - constuctor - both args annotated``() = let source = """ type C(x: int, y: int) = class end """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 7), "Expected annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 15), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method - args no unannotions``() = + let ``IsTypeAnnotationGivenAtPosition - method - args no unannotions``() = let source = """ type C() = member _.M(x, y) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 18), "Expected no annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method - first arg annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method - first arg annotated``() = let source = """ type C() = member _.M(x: int, y) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 23), "Expected no annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method - second arg annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method - second arg annotated``() = let source = """ type C() = member _.M(x, y: int) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 18), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method - both args annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method - both args annotated``() = let source = """ type C() = member _.M(x: int, y: string) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 23), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method currying - args no unannotions``() = + let ``IsTypeAnnotationGivenAtPosition - method currying - args no unannotions``() = let source = """ type C() = member _.M x y = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 17), "Expected no annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method currying - first arg annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method currying - first arg annotated``() = let source = """ type C() = member _.M (x: int) y = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 16), "Expected annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 24), "Expected no annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method currying - second arg annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method currying - second arg annotated``() = let source = """ type C() = member _.M x (y: int) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 16), "Expected no annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 18), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method currying - both args annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method currying - both args annotated``() = let source = """ type C() = member _.M (x: int) (y: string) = () """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 16), "Expected annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 3 25), "Expected annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - method - only return type annotated``() = + let ``IsTypeAnnotationGivenAtPosition - method - only return type annotated``() = let source = """ type C() = member _.M(x): string = "hello" + x """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 3 15), "Expected no annotation for argument 'x'") + Assert.IsFalse(parseFileResults.IsTypeAnnotationGivenAtPosition (mkPos 3 15), "Expected no annotation for argument 'x'") [] - let ``IsTypeAnnotationGiven - tuple - no annotations``() = + let ``IsTypeAnnotationGivenAtPosition - tuple - no annotations``() = let source = """ let (x, y) = (12, "hello") """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected no annotation for value 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 8), "Expected no annotation for value 'y'") + 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 ``IsTypeAnnotationGiven - tuple - first value annotated``() = + let ``IsTypeAnnotationGivenAtPosition - tuple - first value annotated``() = let source = """ let (x: int, y) = (12, "hello") """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected annotation for argument 'x'") - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 13), "Expected no annotation for argument 'y'") + 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 ``IsTypeAnnotationGiven - tuple - second value annotated``() = + let ``IsTypeAnnotationGivenAtPosition - tuple - second value annotated``() = let source = """ let (x, y: string) = (12, "hello") """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsTypeAnnotationGiven (mkPos 2 5), "Expected no annotation for argument 'x'") - Assert.IsTrue(parseFileResults.IsTypeAnnotationGiven (mkPos 2 8), "Expected annotation for argument 'y'") + 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 ``IsBindingALambda - recognize a lambda``() = + let ``IsBindingALambdaAtPosition - recognize a lambda``() = let source = """ let f = fun x y -> x + y """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsBindingALambda (mkPos 2 4), "Expected 'f' to be a lambda expression") + Assert.IsTrue(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "Expected 'f' to be a lambda expression") [] - let ``IsBindingALambda - recognize a nested lambda``() = + let ``IsBindingALambdaAtPosition - recognize a nested lambda``() = let source = """ let f = fun x -> @@ -789,22 +789,22 @@ let f = x + y """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsBindingALambda (mkPos 2 4), "Expected 'f' to be a lambda expression") + Assert.IsTrue(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "Expected 'f' to be a lambda expression") [] - let ``IsBindingALambda - recognize a "partial" lambda``() = + let ``IsBindingALambdaAtPosition - recognize a "partial" lambda``() = let source = """ let f x = fun y -> x + y """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsTrue(parseFileResults.IsBindingALambda (mkPos 2 4), "Expected 'f' to be a lambda expression") + Assert.IsTrue(parseFileResults.IsBindingALambdaAtPosition (mkPos 2 4), "Expected 'f' to be a lambda expression") [] - let ``IsBindingALambda - not a lambda``() = + let ``IsBindingALambdaAtPosition - not a lambda``() = let source = """ let f x y = x + y """ let parseFileResults, _ = getParseAndCheckResults source - Assert.IsFalse(parseFileResults.IsBindingALambda (mkPos 2 4), "'f' is not a lambda expression'") \ No newline at end of file + 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/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index 7b0908a7d49..eecf469189e 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -5,7 +5,6 @@ open System open System.Collections.Immutable open System.Threading open System.ComponentModel.Composition -open System.Linq open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text @@ -49,10 +48,10 @@ type internal FSharpInlineHintsService let isValidForTypeHint (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = let isLambdaIfFunction = funcOrValue.IsFunction && - parseFileResults.IsBindingALambda symbolUse.RangeAlternate.Start + parseFileResults.IsBindingALambdaAtPosition symbolUse.RangeAlternate.Start (funcOrValue.IsValue || isLambdaIfFunction) && - not (parseFileResults.IsTypeAnnotationGiven symbolUse.RangeAlternate.Start) && + not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.RangeAlternate.Start) && symbolUse.IsFromDefinition && not funcOrValue.IsMember && not funcOrValue.IsMemberThisValue && @@ -112,11 +111,11 @@ type internal FSharpInlineHintsService | 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 to "tupled" view. + // 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.ArgLocations.[idx] + 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) From 86c25b9af7def569f46295a33cbfee56f23c9123 Mon Sep 17 00:00:00 2001 From: cartermp Date: Wed, 4 Nov 2020 15:54:36 -0800 Subject: [PATCH 24/26] Param names --- src/fsharp/symbols/Symbols.fsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 49f79c4fab8..94efa34afb5 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -917,10 +917,10 @@ type FSharpMemberOrFunctionOrValue = 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: context: FSharpDisplayContext -> Layout option + 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 From 2fef548cf3b2e0e4bfbd25eaa4809ae8498ceac2 Mon Sep 17 00:00:00 2001 From: cartermp Date: Fri, 6 Nov 2020 12:39:09 -0800 Subject: [PATCH 25/26] Update with latest --- src/fsharp/service/FSharpCheckerResults.fs | 13 ------------- src/fsharp/service/FSharpCheckerResults.fsi | 5 ----- .../src/FSharp.Editor/InlineHints/InlineHints.fs | 6 ++++-- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index d096cb54245..c1e0b96d002 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1885,19 +1885,6 @@ type FSharpCheckFileResults FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) }) - member __.GetAllUsesOfAllSymbolsInFileWithinRange(outerRange: range, ?cancellationToken: CancellationToken) = - threadSafeOp - (fun () -> [| |]) - (fun scope -> - let cenv = scope.SymbolEnv - [| - for symbolUseChunk in scope.ScopeSymbolUses.AllUsesOfSymbols do - for symbolUse in symbolUseChunk do - cancellationToken |> Option.iter (fun ct -> ct.ThrowIfCancellationRequested()) - if rangeContainsRange outerRange symbolUse.Range && symbolUse.ItemOccurence <> ItemOccurence.RelatedText then - let symbol = FSharpSymbol.Create(cenv, symbolUse.Item) - FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) |]) - member __.GetUsesOfSymbolInFile(symbol:FSharpSymbol, ?cancellationToken: CancellationToken) = threadSafeOp (fun () -> [| |]) diff --git a/src/fsharp/service/FSharpCheckerResults.fsi b/src/fsharp/service/FSharpCheckerResults.fsi index 1bc59bbedcd..95e8156b748 100644 --- a/src/fsharp/service/FSharpCheckerResults.fsi +++ b/src/fsharp/service/FSharpCheckerResults.fsi @@ -212,11 +212,6 @@ type public FSharpCheckFileResults = /// Get all textual usages of all symbols throughout the file member GetAllUsesOfAllSymbolsInFile : ?cancellationToken: CancellationToken -> seq - /// Get all textual usages of all symbols throughout a typechecked file that fall within a given range. - /// The range in the typechecked document that all symbols must by defined within - /// An optional token to cancel the operation - member GetAllUsesOfAllSymbolsInFileWithinRange : outerRange: range * ?cancellationToken: CancellationToken -> FSharpSymbolUse[] - /// Get the textual usages that resolved to the given symbol throughout the file member GetUsesOfSymbolInFile : symbol:FSharpSymbol * ?cancellationToken: CancellationToken -> FSharpSymbolUse[] diff --git a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs index eecf469189e..fc8035597a7 100644 --- a/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs +++ b/vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs @@ -40,7 +40,9 @@ type internal FSharpInlineHintsService 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.GetAllUsesOfAllSymbolsInFileWithinRange(range, cancellationToken) + let symbolUses = + checkFileResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) + |> Seq.filter (fun su -> rangeContainsRange range su.RangeAlternate) let typeHints = ImmutableArray.CreateBuilder() let parameterHints = ImmutableArray.CreateBuilder() @@ -58,7 +60,7 @@ type internal FSharpInlineHintsService not funcOrValue.IsConstructorThisValue && not (PrettyNaming.IsOperatorName funcOrValue.DisplayName) - for symbolUse in symbolUses |> Array.distinctBy (fun su -> su.RangeAlternate) do + for symbolUse in symbolUses do match symbolUse.Symbol with | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse -> let typeInfo = ResizeArray() From feaacf73673a231c90c463fcc3d6848578961212 Mon Sep 17 00:00:00 2001 From: Phillip Carter Date: Mon, 16 Nov 2020 11:35:01 -0800 Subject: [PATCH 26/26] Update src/fsharp/symbols/Symbols.fsi Co-authored-by: Chet Husk --- src/fsharp/symbols/Symbols.fsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index 94efa34afb5..cb21f5a0791 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -910,7 +910,7 @@ type FSharpMemberOrFunctionOrValue = /// Indicated if this is a value member IsValue: bool - /// Indicated if this is a value + /// Indicated if this is a function member IsFunction : bool /// Indicates if this is a constructor.