From 0ca3de18e45915fbaa37d29cc4a22e9e00798a1d Mon Sep 17 00:00:00 2001 From: kerams Date: Mon, 7 Nov 2022 21:04:15 +0100 Subject: [PATCH 1/7] Add parameter name hints for discriminated unions --- src/Compiler/Service/FSharpCheckerResults.fs | 2 ++ src/Compiler/Service/FSharpCheckerResults.fsi | 3 ++ .../src/FSharp.Editor/Hints/HintService.fs | 6 ++++ .../Hints/InlineParameterNameHints.fs | 33 +++++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 0abff6d4d04..94b3794294b 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -242,6 +242,8 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti member _.IsFromDispatchSlotImplementation = itemOcc = ItemOccurence.Implemented + member _.IsFromUse = itemOcc = ItemOccurence.Use + member _.IsFromComputationExpression = match symbol.Item, itemOcc with // 'seq' in 'seq { ... }' gets colored as keywords diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 6d60a75cc5b..b3def4d9b87 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -171,6 +171,9 @@ type public FSharpSymbolUse = /// Indicates if the reference is in open statement member IsFromOpenStatement: bool + /// Indicates if the reference is used??? eh, todo + member IsFromUse: bool + /// The file name the reference occurs in member FileName: string diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index c04ccef51fd..853f5669e10 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -23,6 +23,12 @@ module HintService = InlineParameterNameHints.getHints parseResults symbol symbolUse + | :? FSharpUnionCase as symbol + when hintKinds |> Set.contains HintKind.ParameterNameHint + && symbolUse.IsFromUse -> + + InlineParameterNameHints.getHintsForUnionCase parseResults symbol symbolUse + // we'll be adding other stuff gradually here | _ -> [] diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs index 4e4baa5687d..6ba204d3c16 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs @@ -17,9 +17,19 @@ module InlineParameterNameHints = Parts = [ TaggedText(TextTag.Text, $"{parameter.DisplayName} = ") ] } + let private getHint' (range: range, parameter: string) = + { + Kind = HintKind.ParameterNameHint + Range = range.StartRange + Parts = [ TaggedText(TextTag.Text, $"{parameter} = ") ] + } + let private doesParameterNameExist (parameter: FSharpParameter) = parameter.DisplayName <> "" + let private doesParameterNameExist' (parameter: string) = + parameter <> "" + let isValidForHint (symbol: FSharpMemberOrFunctionOrValue) = // is there a better way? let isNotBuiltInOperator = @@ -47,3 +57,26 @@ module InlineParameterNameHints = // this is the case at least for custom operators | None -> [] + + let getHintsForUnionCase + (parseResults: FSharpParseFileResults) + (symbol: FSharpUnionCase) + (symbolUse: FSharpSymbolUse) = + + let fieldNames = symbol.Fields |> Seq.map (fun x -> x.Name) |> Seq.toList + + // If a case does not use field names, don't even bother getting applied argument ranges + if fieldNames |> List.exists doesParameterNameExist' |> not then + [] + else + let ranges = parseResults.GetAllArgumentsForFunctionApplicationAtPosition symbolUse.Range.Start + + // When not all field values are provided (as the user is typing), don't show anything yet + match ranges with + | Some ranges when ranges.Length = fieldNames.Length -> + fieldNames + |> List.zip ranges + |> List.where (snd >> doesParameterNameExist') + |> List.map getHint' + + | _ -> [] From bdcea9b66c0fa41f7d2121610baa3ac09fa7dd73 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 8 Nov 2022 08:21:55 +0100 Subject: [PATCH 2/7] Refactor, update tests --- ...erService.SurfaceArea.netstandard.expected | 2 ++ .../src/FSharp.Editor/Hints/HintService.fs | 6 ++-- .../Hints/InlineParameterNameHints.fs | 32 +++++++++++-------- .../src/FSharp.Editor/Hints/RoslynAdapter.fs | 29 +++++++++-------- .../UnitTests/InlineParameterNameHintTests.fs | 28 ++++++++++++---- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected index be3829e8faf..52360afde2a 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected @@ -2163,6 +2163,7 @@ FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromOpenStatement FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromPattern FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromType FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsPrivateToFile +FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromUse FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromAttribute() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromComputationExpression() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromDefinition() @@ -2171,6 +2172,7 @@ FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromOpenStatement() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromPattern() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromType() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsPrivateToFile() +FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromUse() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpDisplayContext DisplayContext FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpDisplayContext get_DisplayContext() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpSymbol Symbol diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index 853f5669e10..31c8724d6c4 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -19,13 +19,13 @@ module HintService = | :? FSharpMemberOrFunctionOrValue as symbol when hintKinds |> Set.contains HintKind.ParameterNameHint - && InlineParameterNameHints.isValidForHint symbol -> + && InlineParameterNameHints.isMemberOrFunctionOrValueValidForHint symbol -> - InlineParameterNameHints.getHints parseResults symbol symbolUse + InlineParameterNameHints.getHintsForMemberOrFunctionOrValue parseResults symbol symbolUse | :? FSharpUnionCase as symbol when hintKinds |> Set.contains HintKind.ParameterNameHint - && symbolUse.IsFromUse -> + && InlineParameterNameHints.isUnionCaseValidForHint symbolUse -> InlineParameterNameHints.getHintsForUnionCase parseResults symbol symbolUse diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs index 6ba204d3c16..ca41a949626 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs @@ -10,27 +10,27 @@ open Hints module InlineParameterNameHints = - let private getHint (range: range, parameter: FSharpParameter) = + let private getParameterHint (range: range, parameter: FSharpParameter) = { Kind = HintKind.ParameterNameHint Range = range.StartRange Parts = [ TaggedText(TextTag.Text, $"{parameter.DisplayName} = ") ] } - let private getHint' (range: range, parameter: string) = + let private getFieldHint (range: range, field: FSharpField) = { Kind = HintKind.ParameterNameHint Range = range.StartRange - Parts = [ TaggedText(TextTag.Text, $"{parameter} = ") ] + Parts = [ TaggedText(TextTag.Text, $"{field.Name} = ") ] } let private doesParameterNameExist (parameter: FSharpParameter) = parameter.DisplayName <> "" - let private doesParameterNameExist' (parameter: string) = - parameter <> "" + let private doesFieldNameExist (field: FSharpField) = + not field.IsNameGenerated - let isValidForHint (symbol: FSharpMemberOrFunctionOrValue) = + let isMemberOrFunctionOrValueValidForHint (symbol: FSharpMemberOrFunctionOrValue) = // is there a better way? let isNotBuiltInOperator = symbol.DeclaringEntity @@ -39,7 +39,11 @@ module InlineParameterNameHints = symbol.IsFunction && isNotBuiltInOperator // arguably, hints for those would be rather useless - let getHints + let isUnionCaseValidForHint (symbolUse: FSharpSymbolUse) = + // is the union case being used as a constructor + symbolUse.IsFromUse + + let getHintsForMemberOrFunctionOrValue (parseResults: FSharpParseFileResults) (symbol: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = @@ -52,7 +56,7 @@ module InlineParameterNameHints = parameters |> Seq.zip ranges |> Seq.where (snd >> doesParameterNameExist) - |> Seq.map getHint + |> Seq.map getParameterHint |> Seq.toList // this is the case at least for custom operators @@ -63,20 +67,20 @@ module InlineParameterNameHints = (symbol: FSharpUnionCase) (symbolUse: FSharpSymbolUse) = - let fieldNames = symbol.Fields |> Seq.map (fun x -> x.Name) |> Seq.toList + let fields = Seq.toList symbol.Fields // If a case does not use field names, don't even bother getting applied argument ranges - if fieldNames |> List.exists doesParameterNameExist' |> not then + if fields |> List.exists doesFieldNameExist |> not then [] else let ranges = parseResults.GetAllArgumentsForFunctionApplicationAtPosition symbolUse.Range.Start // When not all field values are provided (as the user is typing), don't show anything yet match ranges with - | Some ranges when ranges.Length = fieldNames.Length -> - fieldNames + | Some ranges when ranges.Length = fields.Length -> + fields |> List.zip ranges - |> List.where (snd >> doesParameterNameExist') - |> List.map getHint' + |> List.where (snd >> doesFieldNameExist) + |> List.map getFieldHint | _ -> [] diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index cddf8d250f0..b6026d6da44 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -22,19 +22,22 @@ type internal RoslynAdapter interface IFSharpInlineHintsService with member _.GetInlineHintsAsync(document, _, cancellationToken) = async { - let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask let hintKinds = OptionParser.getHintKinds settings.Advanced - let! nativeHints = - HintService.getHintsForDocument - document - hintKinds - userOpName - cancellationToken - - let roslynHints = - nativeHints - |> Seq.map (NativeToRoslynHintConverter.convert sourceText) - - return roslynHints.ToImmutableArray() + if hintKinds.IsEmpty then + return ImmutableArray.Empty + else + let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask + let! nativeHints = + HintService.getHintsForDocument + document + hintKinds + userOpName + cancellationToken + + let roslynHints = + nativeHints + |> Seq.map (NativeToRoslynHintConverter.convert sourceText) + + return roslynHints.ToImmutableArray() } |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs index 9fbebcd1396..30fadbc0600 100644 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -159,17 +159,31 @@ let wrapped = WrappedThing 42 Assert.IsEmpty(result) [] -let ``Hints are not (yet) shown for dicrimanted unions`` () = +let ``Hints are shown for discrimanted unions`` () = let code = """ type Shape = - | Square of side : float - | Circle of radius : float - -let circle = Circle 42 + | Square of side: int + | Rectangle of width: int * height: int + | Triangle of side1: int * int * side3: int + | Circle of int + +let a = Square 1 +let b = Rectangle (1, 2) +let c = Triangle (1, 2, 3) +let d = Circle 1 +let invalidTriangle = Triangle (1, 2) """ let document = getFsDocument code - let result = getParameterNameHints document + let expected = [ + { Content = "side = "; Location = (7, 16) } + { Content = "width = "; Location = (8, 20) } + { Content = "height = "; Location = (8, 23) } + { Content = "side1 = "; Location = (9, 19) } + { Content = "side3 = "; Location = (9, 25) } + ] - Assert.IsEmpty(result) + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) From f77d3a6d23707ad4b99b5bb480e3a8f581c19911 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 8 Nov 2022 11:43:19 +0100 Subject: [PATCH 3/7] Fix --- src/Compiler/Service/FSharpCheckerResults.fsi | 2 +- vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index b3def4d9b87..bbf14830f39 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -171,7 +171,7 @@ type public FSharpSymbolUse = /// Indicates if the reference is in open statement member IsFromOpenStatement: bool - /// Indicates if the reference is used??? eh, todo + /// Indicates if the reference is used for example at a call site member IsFromUse: bool /// The file name the reference occurs in diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs index 30fadbc0600..501f7de13b2 100644 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -159,7 +159,7 @@ let wrapped = WrappedThing 42 Assert.IsEmpty(result) [] -let ``Hints are shown for discrimanted unions`` () = +let ``Hints are shown for discriminated unions`` () = let code = """ type Shape = | Square of side: int From 947b15d841d383a83031b93508d2493a9db1073d Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 8 Nov 2022 13:20:20 +0100 Subject: [PATCH 4/7] Address comments --- .../UnitTests/InlineParameterNameHintTests.fs | 47 +++++++++++++++---- .../UnitTests/OverallHintExperienceTests.fs | 10 ++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs index 501f7de13b2..eb1378f620a 100644 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -159,31 +159,60 @@ let wrapped = WrappedThing 42 Assert.IsEmpty(result) [] -let ``Hints are shown for discriminated unions`` () = +let ``Hints are shown for discriminated union case fields with explicit names`` () = let code = """ type Shape = | Square of side: int | Rectangle of width: int * height: int - | Triangle of side1: int * int * side3: int - | Circle of int let a = Square 1 let b = Rectangle (1, 2) +""" + let document = getFsDocument code + + let expected = [ + { Content = "side = "; Location = (5, 16) } + { Content = "width = "; Location = (6, 20) } + { Content = "height = "; Location = (6, 23) } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints for discriminated union case fields are not shown when names are generated`` () = + let code = """ +type Shape = + | Triangle of side1: int * int * side3: int + | Circle of int + let c = Triangle (1, 2, 3) let d = Circle 1 -let invalidTriangle = Triangle (1, 2) """ let document = getFsDocument code let expected = [ - { Content = "side = "; Location = (7, 16) } - { Content = "width = "; Location = (8, 20) } - { Content = "height = "; Location = (8, 23) } - { Content = "side1 = "; Location = (9, 19) } - { Content = "side3 = "; Location = (9, 25) } + { Content = "side1 = "; Location = (5, 19) } + { Content = "side3 = "; Location = (5, 25) } ] let actual = getParameterNameHints document Assert.AreEqual(expected, actual) +[] +let ``Hints for discriminated union case fields are not shown provided arguments don't match the expected count`` () = + let code = """ +type Shape = + | Triangle of side1: int * side2: int * side3: int + | Circle of int + +let c = Triangle (1, 2) +""" + let document = getFsDocument code + + let actual = getParameterNameHints document + + Assert.IsEmpty(result) + diff --git a/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs b/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs index fb1dcbe8017..af8c8d8d71c 100644 --- a/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs +++ b/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs @@ -15,12 +15,22 @@ type Song = { Artist: string; Title: string } let whoSings song = song.Artist let artist = whoSings { Artist = "Květy"; Title = "Je podzim" } + +type Shape = + | Square of side: int + | Rectangle of width: int * height: int + +let a = Square 1 +let b = Rectangle (1, 2) """ let document = getFsDocument code let expected = [ { Content = ": Song"; Location = (2, 18) } { Content = ": string"; Location = (4, 11) } { Content = "song = "; Location = (4, 23) } + { Content = "side = "; Location = (10, 16) } + { Content = "width = "; Location = (11, 20) } + { Content = "height = "; Location = (11, 23) } ] let actual = getAllHints document From 3457a012f6f565424678f43035d6444068616a36 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 8 Nov 2022 13:21:24 +0100 Subject: [PATCH 5/7] Fix --- vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs index eb1378f620a..c1dade4646c 100644 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -214,5 +214,5 @@ let c = Triangle (1, 2) let actual = getParameterNameHints document - Assert.IsEmpty(result) + Assert.IsEmpty(actual) From 39821fe0bc75c37d83e52af556669fcd9d9d5fc0 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 8 Nov 2022 13:21:55 +0100 Subject: [PATCH 6/7] Sigh --- vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs index c1dade4646c..a073cb0481a 100644 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -202,7 +202,7 @@ let d = Circle 1 Assert.AreEqual(expected, actual) [] -let ``Hints for discriminated union case fields are not shown provided arguments don't match the expected count`` () = +let ``Hints for discriminated union case fields are not shown when provided arguments don't match the expected count`` () = let code = """ type Shape = | Triangle of side1: int * side2: int * side3: int From d3564eca2a28a8b10644fa7077fafa5aba931c8f Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 8 Nov 2022 15:21:39 +0100 Subject: [PATCH 7/7] Ignore Cons for union case field hints --- vsintegration/src/FSharp.Editor/Hints/HintService.fs | 2 +- .../FSharp.Editor/Hints/InlineParameterNameHints.fs | 5 +++-- .../tests/UnitTests/InlineParameterNameHintTests.fs | 12 ++++++++++++ .../tests/UnitTests/OverallHintExperienceTests.fs | 4 +++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index 31c8724d6c4..2ec037a04d3 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -25,7 +25,7 @@ module HintService = | :? FSharpUnionCase as symbol when hintKinds |> Set.contains HintKind.ParameterNameHint - && InlineParameterNameHints.isUnionCaseValidForHint symbolUse -> + && InlineParameterNameHints.isUnionCaseValidForHint symbol symbolUse -> InlineParameterNameHints.getHintsForUnionCase parseResults symbol symbolUse diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs index ca41a949626..42ad4252e75 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs @@ -39,9 +39,10 @@ module InlineParameterNameHints = symbol.IsFunction && isNotBuiltInOperator // arguably, hints for those would be rather useless - let isUnionCaseValidForHint (symbolUse: FSharpSymbolUse) = - // is the union case being used as a constructor + let isUnionCaseValidForHint (symbol: FSharpUnionCase) (symbolUse: FSharpSymbolUse) = + // is the union case being used as a constructor and is it not Cons symbolUse.IsFromUse + && symbol.DisplayName <> "(::)" let getHintsForMemberOrFunctionOrValue (parseResults: FSharpParseFileResults) diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs index a073cb0481a..323c0fd88b8 100644 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -216,3 +216,15 @@ let c = Triangle (1, 2) Assert.IsEmpty(actual) +[] +let ``Hints for discriminated union case fields are not shown for Cons`` () = + let code = """ +type X = + member _.Test() = 42 :: [42; 42] +""" + let document = getFsDocument code + + let actual = getParameterNameHints document + + Assert.IsEmpty(actual) + diff --git a/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs b/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs index af8c8d8d71c..65d47a21029 100644 --- a/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs +++ b/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs @@ -26,11 +26,13 @@ let b = Rectangle (1, 2) let document = getFsDocument code let expected = [ { Content = ": Song"; Location = (2, 18) } - { Content = ": string"; Location = (4, 11) } { Content = "song = "; Location = (4, 23) } + { Content = ": string"; Location = (4, 11) } { Content = "side = "; Location = (10, 16) } + { Content = ": Shape"; Location = (10, 6) } { Content = "width = "; Location = (11, 20) } { Content = "height = "; Location = (11, 23) } + { Content = ": Shape"; Location = (11, 6) } ] let actual = getAllHints document