diff --git a/tests/FSharp.Compiler.Service.Tests/Checker.fs b/tests/FSharp.Compiler.Service.Tests/Checker.fs new file mode 100644 index 00000000000..4768aa89637 --- /dev/null +++ b/tests/FSharp.Compiler.Service.Tests/Checker.fs @@ -0,0 +1,105 @@ +namespace FSharp.Compiler.Service.Tests + +open System +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.EditorServices +open FSharp.Compiler.Text +open FSharp.Compiler.Tokenization + +type SourceContext = + { Source: string + LineText: string + CaretPos: pos } + +type ResolveContext = + { SourceContext: SourceContext + Pos: pos + Names: string list } + + member this.Source = this.SourceContext.Source + member this.LineText = this.SourceContext.LineText + +type CodeCompletionContext = + { SourceContext: SourceContext + Pos: pos + PartialIdentifier: PartialLongName } + + member this.Source = this.SourceContext.Source + member this.LineText = this.SourceContext.LineText + +[] +module SourceContext = + let fromMarkedSource (markedSource: string) : SourceContext = + let lines = markedSource.Split([|"\r\n"; "\n"|], StringSplitOptions.None) + let line = lines |> Seq.findIndex _.Contains("{caret}") + let lineText = lines[line] + let column = lineText.IndexOf("{caret}") + + let source = markedSource.Replace("{caret}", "") + let pos = Position.mkPos (line + 1) (column - 1) + let lineText = lineText.Replace("{caret}", "") + { Source = source; CaretPos = pos; LineText = lineText } + + +[] +module CheckResultsExtensions = + type FSharpCheckFileResults with + member this.GetSymbolUses(context: ResolveContext) = + this.GetSymbolUsesAtLocation(context.Pos.Line, context.Pos.Column, context.LineText, context.Names) + + member this.GetSymbolUse(context: ResolveContext) = + this.GetSymbolUses(context) |> List.exactlyOne + + member this.GetTooltip(context: ResolveContext) = + this.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier) + + member this.GetTooltip(context: ResolveContext, width) = + this.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier, width) + + member this.GetCodeCompletionSuggestions(context: CodeCompletionContext, parseResults: FSharpParseFileResults) = + this.GetDeclarationListInfo(Some parseResults, context.Pos.Line, context.LineText, context.PartialIdentifier) + +[] +module Checker = + let getResolveContext (markedSource: string) = + let context = SourceContext.fromMarkedSource markedSource + let pos = + match QuickParse.GetCompleteIdentifierIsland false context.LineText context.CaretPos.Column with + | Some(_, column, _) -> Position.mkPos context.CaretPos.Line column + | _ -> context.CaretPos + + let plid = QuickParse.GetPartialLongNameEx(context.LineText, pos.Column - 1) + let names = plid.QualifyingIdents @ [plid.PartialIdent] + { SourceContext = context; Pos = pos; Names = names } + + let getCompletionContext (markedSource: string) = + let context = SourceContext.fromMarkedSource markedSource + let plid = QuickParse.GetPartialLongNameEx(context.LineText, context.CaretPos.Column) + let names = plid.QualifyingIdents @ [plid.PartialIdent] + { SourceContext = context; Pos = context.CaretPos; PartialIdentifier = plid } + + let getCheckedResolveContext (markedSource: string) = + let context = getResolveContext markedSource + let _, checkResults = getParseAndCheckResults context.Source + context, checkResults + + let getCompletionInfo (markedSource: string) = + let context = getCompletionContext markedSource + let parseResults, checkResults = getParseAndCheckResults context.Source + checkResults.GetCodeCompletionSuggestions(context, parseResults) + + let getSymbolUses (markedSource: string) = + let context, checkResults = getCheckedResolveContext markedSource + checkResults.GetSymbolUses(context) + + let getSymbolUse (markedSource: string) = + let symbolUses = getSymbolUses markedSource + symbolUses |> List.exactlyOne + + let getTooltipWithOptions (options: string array) (markedSource: string) = + let context = getResolveContext markedSource + let _, checkResults = getParseAndCheckResultsWithOptions options context.Source + checkResults.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier) + + let getTooltip (markedSource: string) = + getTooltipWithOptions [||] markedSource diff --git a/tests/FSharp.Compiler.Service.Tests/Common.fs b/tests/FSharp.Compiler.Service.Tests/Common.fs index 8bc685b544b..74368c9b1f3 100644 --- a/tests/FSharp.Compiler.Service.Tests/Common.fs +++ b/tests/FSharp.Compiler.Service.Tests/Common.fs @@ -342,24 +342,6 @@ let rec allSymbolsInEntities compGen (entities: IList) = yield! allSymbolsInEntities compGen entity.NestedEntities ] -let getCursorPosAndPrepareSource (source: string) : string * string * pos = - let lines = source.Split([|"\r\n"; "\n"|], StringSplitOptions.None) - let line = lines |> Seq.findIndex _.Contains("{caret}") - let lineText = lines[line] - let column = lineText.IndexOf("{caret}") - - let source = source.Replace("{caret}", "") - let lineText = lineText.Replace("{caret}", "") - source, lineText, Position.mkPos (line + 1) (column - 1) - -let getPartialIdentifierAndPrepareSource source = - let source, lineText, pos = getCursorPosAndPrepareSource source - let _, column, _ = QuickParse.GetCompleteIdentifierIsland false lineText pos.Column |> Option.get - let pos = Position.mkPos pos.Line column - let plid = QuickParse.GetPartialLongNameEx(lineText, column - 1) - let names = plid.QualifyingIdents @ [plid.PartialIdent] - source, lineText, pos, plid, names - let getParseResults (source: string) = parseSourceCode("Test.fsx", source) @@ -369,6 +351,9 @@ let getParseResultsOfSignatureFile (source: string) = let getParseAndCheckResults (source: string) = parseAndCheckScript("Test.fsx", source) +let getParseAndCheckResultsWithOptions options source = + parseAndCheckScriptWithOptions ("Test.fsx", source, options) + let getParseAndCheckResultsOfSignatureFile (source: string) = parseAndCheckScript("Test.fsi", source) diff --git a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs index 76106a92b34..c356991673e 100644 --- a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs @@ -1,20 +1,10 @@ module FSharp.Compiler.Service.Tests.CompletionTests -open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.EditorServices open Xunit -let getCompletionInfo source = - let source, lineText, pos = getCursorPosAndPrepareSource source - let parseResults, checkResults = getParseAndCheckResultsPreview source - let plid = QuickParse.GetPartialLongNameEx(lineText, pos.Column) - checkResults.GetDeclarationListInfo(Some parseResults, pos.Line, lineText, plid) - -let getCompletionItemNames (completionInfo: DeclarationListInfo) = - completionInfo.Items |> Array.map (fun item -> item.NameInCode) - let private assertItemsWithNames contains names (completionInfo: DeclarationListInfo) = - let itemNames = getCompletionItemNames completionInfo |> set + let itemNames = completionInfo.Items |> Array.map _.NameInCode |> set for name in names do Assert.True(Set.contains name itemNames = contains) @@ -27,7 +17,7 @@ let assertHasNoItemsWithNames names (completionInfo: DeclarationListInfo) = [] let ``Expr - After record decl 01`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: int } { Fi{caret} } @@ -36,7 +26,7 @@ type Record = { Field: int } [] let ``Expr - After record decl 02`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: int } {caret} @@ -45,7 +35,7 @@ type Record = { Field: int } [] let ``Expr - record - field 01 - anon module`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: int } { Fi{caret} } @@ -54,7 +44,7 @@ type Record = { Field: int } [] let ``Expr - record - field 02 - anon module`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: int } let record = { Field = 1 } @@ -65,7 +55,7 @@ let record = { Field = 1 } [] let ``Expr - record - empty 01`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: int } { {caret} } @@ -74,7 +64,7 @@ type Record = { Field: int } [] let ``Expr - record - empty 02`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: int } let record = { Field = 1 } @@ -85,56 +75,56 @@ let record = { Field = 1 } [] let ``Underscore dot lambda - completion 01`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Len{caret}""" assertHasItemWithNames ["Length"] info [] let ``Underscore dot lambda - completion 02`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ System.DateTime.Now |> _.TimeOfDay.Mill{caret}""" assertHasItemWithNames ["Milliseconds"] info [] let ``Underscore dot lambda - completion 03`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.ToString().Len{caret}""" assertHasItemWithNames ["Length"] info [] let ``Underscore dot lambda - completion 04`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Len{caret}gth.ToString()""" assertHasItemWithNames ["Length"] info [] let ``Underscore dot lambda - completion 05`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Length.ToString().Chars("".Len{caret})""" assertHasItemWithNames ["Length"] info [] let ``Underscore dot lambda - completion 06`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Chars(System.DateTime.UtcNow.Tic{caret}).ToString()""" assertHasItemWithNames ["Ticks"] info [] let ``Underscore dot lambda - completion 07`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Length.ToString().Len{caret}""" assertHasItemWithNames ["Length"] info [] let ``Underscore dot lambda - completion 08`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ System.DateTime.Now |> _.TimeOfDay .Mill{caret}""" @@ -142,21 +132,21 @@ System.DateTime.Now |> _.TimeOfDay [] let ``Underscore dot lambda - completion 09`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Length.ToSt{caret}.Length""" assertHasItemWithNames ["ToString"] info [] let ``Underscore dot lambda - completion 10`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Chars(0).ToStr{caret}.Length""" assertHasItemWithNames ["ToString"] info [] let ``Underscore dot lambda - completion 11`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ open System.Linq [[""]] |> _.Select(_.Head.ToL{caret})""" @@ -165,7 +155,7 @@ open System.Linq [] let ``Underscore dot lambda - completion 12`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ open System.Linq [[[""]]] |> _.Head.Select(_.Head.ToL{caret})""" @@ -174,7 +164,7 @@ open System.Linq [] let ``Underscore dot lambda - completion 13`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ let myFancyFunc (x:string) = x |> _.ToL{caret}""" @@ -182,7 +172,7 @@ let myFancyFunc (x:string) = [] let ``Underscore dot lambda - completion 14`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ let myFancyFunc (x:System.DateTime) = x |> _.TimeOfDay.Mill{caret} @@ -191,14 +181,14 @@ let myFancyFunc (x:System.DateTime) = [] let ``Underscore dot lambda - completion 15`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ let _a = 5 "" |> _{caret}.Length.ToString() """ assertHasItemWithNames ["_a"] info [] let ``Underscore dot lambda - No prefix 01`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ let s = "" [s] |> List.map _.{caret} """ @@ -206,21 +196,21 @@ let s = "" [] let ``Underscore dot lambda - No prefix 02`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ System.DateTime.Now |> _.TimeOfDay.{caret}""" assertHasItemWithNames ["Milliseconds"] info [] let ``Underscore dot lambda - No prefix 03`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ "" |> _.Length.ToString().{caret}""" assertHasItemWithNames ["Length"] info [] let ``Type decl - Record - Field type 01`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ type Record = { Field: {caret} } """ assertHasItemWithNames ["string"] info @@ -228,7 +218,7 @@ type Record = { Field: {caret} } [] let ``Expr - Qualifier 01`` () = - let info = getCompletionInfo """ + let info = Checker.getCompletionInfo """ let f (s: string) = s.Trim().{caret} s.Trim() @@ -239,8 +229,7 @@ let f (s: string) = [] let ``Expr - Qualifier 02`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ let f (s: string) = s.Trim() s.Trim().{caret} @@ -251,8 +240,7 @@ let f (s: string) = [] let ``Expr - Qualifier 03`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ let f (s: string) = s.Trim() s.Trim() @@ -263,8 +251,7 @@ let f (s: string) = [] let ``Expr - Qualifier 04`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ type T() = do System.String.Empty.ToString().L{caret} @@ -273,24 +260,21 @@ type T() = [] let ``Expr - Qualifier 05`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ System.String.Empty.ToString().{caret} """ assertHasItemWithNames ["Length"] info [] let ``Expr - Qualifier 06`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ System.String.Empty.ToString().L{caret} """ assertHasItemWithNames ["Length"] info [] let ``Expr - Qualifier 07`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ type T() = do System.String.Empty.ToString().L{caret} @@ -300,8 +284,7 @@ type T() = [] let ``Import - Ns 01`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ namespace Ns type Rec1 = { F: int } @@ -321,8 +304,7 @@ module M = [] let ``Import - Ns 02 - Rec`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ namespace Ns type Rec1 = { F: int } @@ -342,8 +324,7 @@ module M = [] let ``Import - Ns 03 - Rec`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ namespace Ns type Rec1 = { F: int } @@ -363,8 +344,7 @@ module rec M = [] let ``Not in scope 01`` () = - let info = - getCompletionInfo """ + let info = Checker.getCompletionInfo """ namespace Ns1 type E = diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index fc325ec28aa..4f5cdab60f5 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -25,6 +25,7 @@ XunitSetup.fs + diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index 181cbfe40af..2da98810e0a 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -9,7 +9,6 @@ open FSharp.Test.Assert open Xunit module ActivePatterns = - let completePatternInput = """ let (|True|False|) = function | true -> True @@ -515,18 +514,18 @@ module FSharpMemberOrFunctionOrValue = [] let ``Both Set and Get symbols are present`` () = - let _, checkResults = getParseAndCheckResults """ + let context, checkResults = Checker.getCheckedResolveContext """ namespace Foo type Foo = - member _.X + member _.X{caret} with get (y: int) : string = "" and set (a: int) (b: float) = () """ // "X" resolves a symbol but it will either be the property, get or set symbol. // Use get_ or set_ to differentiate. - let xPropertySymbol, _ = checkResults.GetSymbolUsesAtLocation(5, 14, " member _.X", [ "X" ]) |> List.pick pickPropertySymbol + let xPropertySymbol, _ = checkResults.GetSymbolUses(context) |> List.pick pickPropertySymbol Assert.True xPropertySymbol.IsProperty Assert.True xPropertySymbol.HasGetterMethod @@ -545,20 +544,14 @@ type Foo = | symbol -> failwith $"Expected {symbol} to be FSharpMemberOrFunctionOrValue" [] - let ``AutoProperty with get,set has property symbol!`` () = - let _, checkResults = getParseAndCheckResults """ + let ``AutoProperty with get, set has property symbol 01`` () = + let symbols = Checker.getSymbolUses """ namespace Foo type Foo = - member val AutoPropGetSet = 0 with get, set + member val AutoPropGe{caret}tSet = 0 with get, set """ - - let symbols = checkResults.GetSymbolUsesAtLocation(5, 29, " member val AutoPropGetSet = 0 with get, set", ["AutoPropGetSet"]) - - let autoPropertySymbol, mAutoPropSymbolUse = - symbols - |> List.pick pickPropertySymbol - + let autoPropertySymbol, mAutoPropSymbolUse = symbols |> List.pick pickPropertySymbol Assert.True autoPropertySymbol.IsProperty Assert.True autoPropertySymbol.HasGetterMethod Assert.True autoPropertySymbol.HasSetterMethod @@ -566,39 +559,34 @@ type Foo = Assert.True (autoPropertySymbol.SetterMethod.CompiledName.StartsWith("set_")) assertRange (5, 15) (5, 29) mAutoPropSymbolUse - let symbols = - symbols - |> List.choose chooseMemberOrFunctionOrValue - + let symbols = symbols |> List.choose chooseMemberOrFunctionOrValue match symbols with - | [ propMfv - setMfv - getMfv ] -> + | [ propMfv; setMfv; getMfv ] -> Assert.True propMfv.IsProperty Assert.True(getMfv.CompiledName.StartsWith("get_")) Assert.True(setMfv.CompiledName.StartsWith("set_")) | _ -> failwith $"Expected three symbols, got %A{symbols}" - - // The setter should have a symbol for the generated parameter `v`. - let setVMfv = - checkResults.GetSymbolUsesAtLocation(5, 29, " member val AutoPropGetSet = 0 with get, set", ["v"]) - |> List.tryExactlyOne - |> Option.map chooseMemberOrFunctionOrValue - if Option.isNone setVMfv then failwith "No generated v symbol for the setter was found" + [] + let ``AutoProperty with get, set has property symbol 02`` () = + let symbol = Checker.getSymbolUse """ +namespace Foo + +type Foo = + member val AutoPropGetSet{caret} = 0 with get, set +""" + // The setter should have a symbol for the generated parameter `v`. + let setVMfv = symbol |> chooseMemberOrFunctionOrValue + if Option.isNone setVMfv then + failwith "No generated v symbol for the setter was found" [] let ``Property symbol is resolved for property`` () = - let source = """ + let symbols = Checker.getSymbolUses """ type X(y: string) = - member val Y = y with get, set + member val Y{caret} = y with get, set """ - - let _, checkResults = getParseAndCheckResults source - let propSymbol, _ = - checkResults.GetSymbolUsesAtLocation(3, 16, " member val Y = y with get, set", [ "Y" ]) - |> List.pick pickPropertySymbol - + let propSymbol, _ = symbols |> List.pick pickPropertySymbol Assert.True propSymbol.IsProperty Assert.True propSymbol.HasGetterMethod Assert.True propSymbol.HasSetterMethod @@ -606,11 +594,11 @@ type X(y: string) = [] let ``Multiple relevant symbols for type name`` () = - let _, checkResults = getParseAndCheckResults """ + let context, checkResults = Checker.getCheckedResolveContext """ // This is a generated file; the original input is 'FSInteractiveSettings.txt' namespace FSInteractiveSettings -type internal SR () = +type internal SR{caret} () = static let mutable swallowResourceText = false @@ -619,11 +607,8 @@ type internal SR () = and set (b) = swallowResourceText <- b // END BOILERPLATE """ - - let symbols = - checkResults.GetSymbolUsesAtLocation(5, 16, "type internal SR () =", [ "" ]) - |> List.map (fun su -> su.Symbol) - + let context = { context with Names = [""] } // Override the context to get the extra symbols + let symbols = checkResults.GetSymbolUses(context) |> List.map _.Symbol match symbols with | [ :? FSharpMemberOrFunctionOrValue as cctor :? FSharpMemberOrFunctionOrValue as ctor @@ -635,79 +620,58 @@ type internal SR () = [] let ``AutoProperty with get has get symbol attached to property name`` () = - let _, checkResults = getParseAndCheckResults """ + let symbolUse = Checker.getSymbolUse """ namespace Foo type Foo() = - member val Bar = 0 with get + member val B{caret}ar = 0 with get """ - - let autoPropertySymbolUses = - checkResults.GetSymbolUsesAtLocation(5, 18, " member val Bar = 0 with get", ["Bar"]) - |> List.map (fun su -> su.Symbol) - - match autoPropertySymbolUses with - | [ :? FSharpMemberOrFunctionOrValue as mfv ] -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as mfv -> Assert.True mfv.IsPropertyGetterMethod assertRange (5, 15) (5, 18) mfv.SignatureLocation.Value | symbols -> failwith $"Unexpected symbols, got %A{symbols}" [] let ``Property with get has symbol attached to property name`` () = - let _, checkResults = getParseAndCheckResults """ + let symbolUse = Checker.getSymbolUse """ namespace F type Foo() = let mutable b = 0 - member this.Count with get () = b + member this.Coun{caret}t with get () = b """ - - let getSymbolUses = - checkResults.GetSymbolUsesAtLocation(6, 21, " member this.Count with get () = b", ["Count"]) - |> List.map (fun su -> su.Symbol) - - match getSymbolUses with - | [ :? FSharpMemberOrFunctionOrValue as mfv ] -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as mfv -> Assert.True mfv.IsPropertyGetterMethod assertRange (6, 16) (6, 21) mfv.SignatureLocation.Value | symbols -> failwith $"Unexpected symbols, got %A{symbols}" [] let ``Property with set has symbol attached to property name`` () = - let _, checkResults = getParseAndCheckResults """ + let symbolUse = Checker.getSymbolUse """ namespace F type Foo() = let mutable b = 0 - member this.Count with set (v:int) = b <- v + member this.Coun{caret}t with set (v:int) = b <- v """ - - let _all = checkResults.GetAllUsesOfAllSymbolsInFile() - - let getSymbolUses = - checkResults.GetSymbolUsesAtLocation(6, 21, " member this.Count with set (v:int) = b <- v", ["Count"]) - |> List.map (fun su -> su.Symbol) - - match getSymbolUses with - | [ :? FSharpMemberOrFunctionOrValue as mfv ] -> + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as mfv -> Assert.True mfv.IsPropertySetterMethod assertRange (6, 16) (6, 21) mfv.SignatureLocation.Value | symbols -> failwith $"Unexpected symbols, got %A{symbols}" [] let ``Property with set/get has property symbol`` () = - let _, checkResults = getParseAndCheckResults """ + let symbolUses = Checker.getSymbolUses """ namespace F type Foo() = let mutable b = 0 - member this.Count with set (v:int) = b <- v and get () = b + member this.Coun{caret}t with set (v:int) = b <- v and get () = b """ - - let propSymbol, _ = - checkResults.GetSymbolUsesAtLocation(6, 21, " member this.Count with set (v:int) = b <- v", ["Count"]) - |> List.pick pickPropertySymbol - + let propSymbol, _ = symbolUses |> List.pick pickPropertySymbol Assert.True propSymbol.IsProperty Assert.True propSymbol.HasGetterMethod Assert.True propSymbol.HasSetterMethod @@ -715,22 +679,18 @@ type Foo() = [] let ``Property usage is reported properly`` () = - let _, checkResults = getParseAndCheckResults """ + let context, checkResults = Checker.getCheckedResolveContext """ module X type Foo() = let mutable b = 0 - member x.Name + member x.Nam{caret}e with get() = 0 and set (v: int) = () ignore (Foo().Name) """ - - let propertySymbolUse, _ = - checkResults.GetSymbolUsesAtLocation(6, 17, " member x.Name", ["Name"]) - |> List.pick pickPropertySymbol - + let propertySymbolUse, _ = checkResults.GetSymbolUses(context) |> List.pick pickPropertySymbol let usages = checkResults.GetUsesOfSymbolInFile(propertySymbolUse) Assert.Equal(2, usages.Length) Assert.True usages.[0].IsFromDefinition @@ -738,39 +698,31 @@ ignore (Foo().Name) [] let ``Property symbol is present after critical error`` () = - let _, checkResults = getParseAndCheckResults """ + let context, checkResults = Checker.getCheckedResolveContext """ module X let _ = UnresolvedName type X() = - member val Y = 1 with get, set + member val Y{caret} = 1 with get, set """ - - let _propertySymbolUse, _ = - checkResults.GetSymbolUsesAtLocation(7, 16, " member val Y = 1 with get, set", ["Y"]) - |> List.pick pickPropertySymbol - - Assert.False (Array.isEmpty checkResults.Diagnostics) + let _propertySymbolUse, _ = checkResults.GetSymbolUses(context) |> List.pick pickPropertySymbol + Assert.False(Array.isEmpty checkResults.Diagnostics) [] let ``Property symbol is present after critical error in property`` () = - let _, checkResults = getParseAndCheckResults """ + let context, checkResults = Checker.getCheckedResolveContext """ module Z type X() = - member val Y = UnresolvedName with get, set + member val Y{caret} = UnresolvedName with get, set """ - - let _propertySymbolUse, _ = - checkResults.GetSymbolUsesAtLocation(5, 16, " member val Y = UnresolvedName with get, set", ["Y"]) - |> List.pick pickPropertySymbol - + let _propertySymbolUse, _ = checkResults.GetSymbolUses(context) |> List.pick pickPropertySymbol Assert.False (Array.isEmpty checkResults.Diagnostics) [] let ``Property symbol on interface implementation`` () = - let _, checkResults = getParseAndCheckResults """ + let context, checkResults = Checker.getCheckedResolveContext """ module Z type I = @@ -778,13 +730,9 @@ type I = type T() = interface I with - member val P = 1 with get, set + member val P{caret} = 1 with get, set """ - - let _propertySymbolUse, mProp = - checkResults.GetSymbolUsesAtLocation(9, 20, " member val P = 1 with get, set", ["P"]) - |> List.pick pickPropertySymbol - + let _propertySymbolUse, mProp = checkResults.GetSymbolUses(context) |> List.pick pickPropertySymbol assertRange (9, 19) (9, 20) mProp @@ -1003,118 +951,79 @@ let f (r: {| A: int; C: int |}) = Assert.Equal(5, getSymbolUses.Length) - [] - let ``Symbols for fields in nested copy-and-update are present`` () = - let _, checkResults = getParseAndCheckResults """ -type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - -let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.Bar = 2; Zoo.Bar = 3; Foo = 4 } -""" - - let line = "let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.Bar = 2; Zoo.Bar = 3; Foo = 4 }" - - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 47, line, [ "Zoo" ]) - |> List.exactlyOne - + let private checkFieldUsage name typeName range source = + let fieldSymbolUse = Checker.getSymbolUse source match fieldSymbolUse.Symbol with | :? FSharpField as field -> - Assert.Equal ("Zoo", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 44) (4, 47) fieldSymbolUse.Range + Assert.Equal(name, field.Name) + Assert.Equal(typeName, field.DeclaringEntity.Value.CompiledName) + assertRange (fst range) (snd range) fieldSymbolUse.Range | _ -> failwith "Symbol was not FSharpField" + [] + let ``Nested copy-and-update 01`` () = + checkFieldUsage "Zoo" "RecordA`1" ((4, 44), (4, 47)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 51, line, [ "Foo" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Foo", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 48) (4, 51) fieldSymbolUse.Range - - | _ -> failwith "Symbol was not FSharpField" - - - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 60, line, [ "Zoo" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Zoo", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 57) (4, 60) fieldSymbolUse.Range - - | _ -> failwith "Symbol was not FSharpField" - +let nestedFunc (a: RecordA) = { a with Zo{caret}o.Foo = 1; Zoo.Zoo.Bar = 2; Zoo.Bar = 3; Foo = 4 } +""" - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 64, line, [ "Zoo" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Zoo", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 61) (4, 64) fieldSymbolUse.Range + [] + let ``Nested copy-and-update 02`` () = + checkFieldUsage "Foo" "RecordA`1" ((4, 48), (4, 51)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - | _ -> failwith "Symbol was not FSharpField" +let nestedFunc (a: RecordA) = { a with Zoo.Fo{caret}o = 1; Zoo.Zoo.Bar = 2; Zoo.Bar = 3; Foo = 4 } +""" + [] + let ``Nested copy-and-update 03`` () = + checkFieldUsage "Zoo" "RecordA`1" ((4, 57), (4, 60)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 68, line, [ "Bar" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Bar", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 65) (4, 68) fieldSymbolUse.Range +let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Z{caret}oo.Zoo.Bar = 2; Zoo.Bar = 3; Foo = 4 } +""" - | _ -> failwith "Symbol was not FSharpField" + [] + let ``Nested copy-and-update 04`` () = + checkFieldUsage "Zoo" "RecordA`1" ((4, 61), (4, 64)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } +let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zo{caret}o.Bar = 2; Zoo.Bar = 3; Foo = 4 } +""" - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 77, line, [ "Zoo" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Zoo", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 74) (4, 77) fieldSymbolUse.Range + [] + let ``Nested copy-and-update 05`` () = + checkFieldUsage "Bar" "RecordA`1" ((4, 65), (4, 68)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - | _ -> failwith "Symbol was not FSharpField" +let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.B{caret}ar = 2; Zoo.Bar = 3; Foo = 4 } +""" + [] + let ``Nested copy-and-update 06`` () = + checkFieldUsage "Zoo" "RecordA`1" ((4, 74), (4, 77)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 81, line, [ "Bar" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Bar", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 78) (4, 81) fieldSymbolUse.Range +let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.Bar = 2; Z{caret}oo.Bar = 3; Foo = 4 } +""" - | _ -> failwith "Symbol was not FSharpField" + [] + let ``Nested copy-and-update 07`` () = + checkFieldUsage "Bar" "RecordA`1" ((4, 78), (4, 81)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } +let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.Bar = 2; Zoo.B{caret}ar = 3; Foo = 4 } +""" - let fieldSymbolUse = - checkResults.GetSymbolUsesAtLocation(4, 90, line, [ "Foo" ]) - |> List.exactlyOne - - match fieldSymbolUse.Symbol with - | :? FSharpField as field -> - Assert.Equal ("Foo", field.Name) - Assert.Equal ("RecordA`1", field.DeclaringEntity.Value.CompiledName) - assertRange (4, 87) (4, 90) fieldSymbolUse.Range + [] + let ``Nested copy-and-update 08`` () = + checkFieldUsage "Foo" "RecordA`1" ((4, 87), (4, 90)) """ +type RecordA<'a> = { Foo: 'a; Bar: int; Zoo: RecordA<'a> } - | _ -> failwith "Symbol was not FSharpField" +let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.Bar = 2; Zoo.Bar = 3; Fo{caret}o = 4 } +""" module ComputationExpressions = [] @@ -1294,14 +1203,11 @@ type T() = module Event = [] let ``CLIEvent member does not produce additional property symbol`` () = - let _, checkResults = getParseAndCheckResults """ -type T() = + let allSymbols = Checker.getSymbolUses """ +type T() = [] - member this.Event = Event().Publish + member this.Ev{caret}ent = Event().Publish """ - let allSymbols = - checkResults.GetSymbolUsesAtLocation(4, 21, " member this.Event = Event().Publish", [ "Event" ]) - let hasPropertySymbols = allSymbols |> List.exists (fun su -> diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index 90a74ed3e4e..9fd5f364b0c 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -6,31 +6,24 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.Text -open FSharp.Compiler.Tokenization open FSharp.Compiler.EditorServices open FSharp.Compiler.Symbols open FSharp.Test open Xunit let testXmlDocFallbackToSigFileWhileInImplFile sigSource implSource (expectedContent: string) = - let implSource, lineText, pos, plid, names = getPartialIdentifierAndPrepareSource implSource + let context = Checker.getResolveContext implSource let files = Map.ofArray - [| "A.fsi", - SourceText.ofString sigSource - - - "A.fs", - SourceText.ofString implSource |] + [| "A.fsi", SourceText.ofString sigSource + "A.fs", SourceText.ofString context.Source |] let documentSource fileName = Map.tryFind fileName files |> async.Return let projectOptions = let _, projectOptions = mkTestFileAndOptions Array.empty - - { projectOptions with - SourceFiles = [| "A.fsi"; "A.fs" |] } + { projectOptions with SourceFiles = [| "A.fsi"; "A.fs" |] } let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom documentSource, @@ -43,9 +36,7 @@ let testXmlDocFallbackToSigFileWhileInImplFile sigSource implSource (expectedCon match checkResult with | _, FSharpCheckFileAnswer.Succeeded(checkResults) -> // Get the tooltip for (line, colAtEndOfNames) in the implementation file - let (ToolTipText tooltipElements) = - checkResults.GetToolTip(pos.Line, pos.Column, lineText, names, FSharpTokenTag.Identifier) - + let (ToolTipText tooltipElements) = checkResults.GetTooltip(context) match tooltipElements with | ToolTipElement.Group [ element ] :: _ -> match element.XmlDoc with @@ -264,11 +255,8 @@ let a = 23 let testToolTipSquashing source = - let source, lineText, pos, plid, names = getPartialIdentifierAndPrepareSource source - let files = - Map.ofArray - [| "A.fs", - SourceText.ofString source |] + let context = Checker.getResolveContext source + let files = Map.ofArray [| "A.fs", SourceText.ofString context.Source |] let documentSource fileName = Map.tryFind fileName files |> async.Return @@ -288,13 +276,10 @@ let testToolTipSquashing source = match checkResult with | _, FSharpCheckFileAnswer.Succeeded(checkResults) -> - // Get the tooltip for `bar` - let (ToolTipText tooltipElements) = - checkResults.GetToolTip(pos.Line, pos.Column, lineText, names, FSharpTokenTag.Identifier) + let (ToolTipText tooltipElements) = checkResults.GetTooltip(context) + let (ToolTipText tooltipElementsSquashed) = checkResults.GetTooltip(context, 10) - let (ToolTipText tooltipElementsSquashed) = - checkResults.GetToolTip(pos.Line, pos.Column, lineText, names, FSharpTokenTag.Identifier, 10) match tooltipElements, tooltipElementsSquashed with | groups, groupsSquashed -> let breaks = @@ -388,56 +373,53 @@ let taggedTextsToString (t: TaggedText array) = t |> Array.map (fun taggedText -> taggedText.Text) |> String.concat "" + let assertAndExtractTooltip (ToolTipText(items)) = Assert.Equal(1,items.Length) - match items.[0] with + match items[0] with | ToolTipElement.Group [ singleElement ] -> let toolTipText = singleElement.MainDescription |> taggedTextsToString toolTipText, singleElement.XmlDoc, singleElement.Remarks |> Option.map taggedTextsToString - | _ -> failwith $"Expected group, got {items.[0]}" + | _ -> failwith $"Expected group, got {items[0]}" let assertAndGetSingleToolTipText items = let text,_xml,_remarks = assertAndExtractTooltip items text -let normalize (s:string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n") +let normalize (s: string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n") [] let ``Auto property should display a single tool tip`` () = - let source = """ + Checker.getTooltip """ namespace Foo /// Some comment on class type Bar() = /// Some comment on class member - member val Foo = "bla" with get, set + member val Fo{caret}o = "bla" with get, set """ - let checkResults = getCheckResults source Array.empty - checkResults.GetToolTip(7, 18, " member val Foo = \"bla\" with get, set", [ "Foo" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldBeEquivalentTo "property Bar.Foo: string with get, set" [] let ``Should display nullable Csharp code analysis annotations on method argument`` () = - - let source = """module Foo -let exists() = System.IO.Path.Exists(null:string) + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + +let exists() = System.IO.Path.Exist{caret}s(null:string) """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(2, 36, "let exists() = System.IO.Path.Exists(null:string)", [ "Exists" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldBeEquivalentTo "System.IO.Path.Exists([] path: string | null) : bool" [] let ``Should display xml doc on a nullable BLC method`` () = - - let source = """module Foo -let exists() = System.IO.Path.Exists(null:string) + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + +let exists() = System.IO.Path.Exi{caret}sts(null:string) """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(2, 36, "let exists() = System.IO.Path.Exists(null:string)", [ "Exists" ], FSharpTokenTag.Identifier) |> assertAndExtractTooltip |> fun (text,xml,_remarks) -> text |> Assert.shouldBeEquivalentTo "System.IO.Path.Exists([] path: string | null) : bool" @@ -448,15 +430,14 @@ let exists() = System.IO.Path.Exists(null:string) [] let ``Should display xml doc on fsharp hosted nullable function`` () = - - let source = """module Foo + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + /// This is a xml doc above myFunc let myFunc(x:string|null) : string | null = x -let exists() = myFunc(null) +let exists() = myFu{caret}nc(null) """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(5, 21, "let exists() = myFunc(null)", [ "myFunc" ], FSharpTokenTag.Identifier) |> assertAndExtractTooltip |> fun (text,xml,remarks) -> match xml with @@ -469,78 +450,76 @@ let exists() = myFunc(null) [] let ``Should display nullable Csharp code analysis annotations on method return type`` () = - - let source = """module Foo -let getPath() = System.IO.Path.GetFileName(null:string) + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + +let getPath() = System.IO.Path.GetFile{caret}Name(null:string) """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(2, 42, "let getPath() = System.IO.Path.GetFileName(null:string)", [ "GetFileName" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldBeEquivalentTo ("""[] System.IO.Path.GetFileName(path: string | null) : string | null""" |> normalize) [] let ``Should display nullable Csharp code analysis annotations on TryParse pattern`` () = - let source = """module Foo -let success,version = System.Version.TryParse(null) + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + +let success,version = System.Version.TryPar{caret}se(null) """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(2, 45, "let success,version = System.Version.TryParse(null)", [ "TryParse" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText - |> Assert.shouldBeEquivalentTo ("""System.Version.TryParse([] input: string | null, [] result: byref) : bool""") + |> Assert.shouldBeEquivalentTo """System.Version.TryParse([] input: string | null, [] result: byref) : bool""" [] let ``Display with nullable annotations can be squashed`` () = - let source = """module Foo -let success,version = System.Version.TryParse(null) + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + +let success,version = System.Version.Try{caret}Parse(null) """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(2, 45, "let success,version = System.Version.TryParse(null)", [ "TryParse" ], FSharpTokenTag.Identifier,width=100) |> assertAndGetSingleToolTipText - |> Assert.shouldBeEquivalentTo ("""System.Version.TryParse([] input: string | null, - [] result: byref) : bool""" |> normalize) + |> Assert.shouldBeEquivalentTo ("""System.Version.TryParse([] input: string | null, [] result: byref) : bool""" |> normalize) [] let ``Allows ref struct is shown on BCL interface declaration`` () = - let source = """module Foo + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + open System -let myAction : Action | null = null +let myAction : Acti{caret}on | null = null """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(3, 21, "let myAction : Action | null = null", [ "Action" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldStartWith ("""type Action<'T (allows ref struct)>""" |> normalize) [] let ``Allows ref struct is shown for each T on BCL interface declaration`` () = - let source = """module Foo + Checker.getTooltipWithOptions [|"--checknulls+";"--langversion:preview"|] """ +module Foo + open System -let myAction : Action | null = null +let myAction : Acti{caret}on | null = null """ - let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|] - checkResults.GetToolTip(3, 21, "let myAction : Action | null = null", [ "Action" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldStartWith ("""type Action<'T1,'T2,'T3,'T4 (allows ref struct and allows ref struct and allows ref struct and allows ref struct)>""" |> normalize) [] let ``Allows ref struct is shown on BCL method usage`` () = - let source = """module Foo + Checker.getTooltip """ +module Foo + open System open System.Collections.Generic -let doIt (dict:Dictionary<'a,'b>) = dict.GetAlternateLookup<'a,'b,ReadOnlySpan>() +let doIt (dict:Dictionary<'a,'b>) = dict.GetAltern{caret}ateLookup<'a,'b,ReadOnlySpan>() """ - let checkResults = getCheckResults source [|"--langversion:preview"|] - checkResults.GetToolTip(4, 59, "let doIt (dict:Dictionary<'a,'b>) = dict.GetAlternateLookup<'a,'b,ReadOnlySpan>()", [ "GetAlternateLookup" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldContain ("""'TAlternateKey (allows ref struct)""" |> normalize) [] let ``Allows ref struct is not shown on BCL interface usage`` () = - let source = """module Foo + Checker.getTooltip """ +module Foo + open System -let doIt(myAction : Action) = myAction.Invoke(42) +let doIt(myAction : Action) = myAc{caret}tion.Invoke(42) """ - let checkResults = getCheckResults source [|"--langversion:preview"|] - checkResults.GetToolTip(3, 43, "let doIt(myAction : Action) = myAction.Invoke(42)", [ "myAction" ], FSharpTokenTag.Identifier) |> assertAndGetSingleToolTipText |> Assert.shouldBeEquivalentTo ("""val myAction: Action""" |> normalize) \ No newline at end of file