Skip to content

Commit 006f91e

Browse files
authored
Bugfix : make sure nullness does not break XmlDoc info import for methods and types (#17741)
* Remove nullness signal in string-based type encoding of a symbol (since it is used for xmldoc lookup) * release notes * Discard unused values
1 parent b9df594 commit 006f91e

File tree

5 files changed

+64
-27
lines changed

5 files changed

+64
-27
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.100.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* Add missing byte chars notations, enforce limits in decimal notation in byte char & string (Issues [#15867](https://github.com/dotnet/fsharp/issues/15867), [#15868](https://github.com/dotnet/fsharp/issues/15868), [#15869](https://github.com/dotnet/fsharp/issues/15869), [PR #15898](https://github.com/dotnet/fsharp/pull/15898))
1717
* Parentheses analysis: keep extra parentheses around unit & tuples in method definitions. ([PR #17618](https://github.com/dotnet/fsharp/pull/17618))
1818
* Fix IsUnionCaseTester throwing for non-methods/properties [#17301](https://github.com/dotnet/fsharp/pull/17634)
19+
* Fix xmlc doc tooltip display for nullable types [#17741](https://github.com/dotnet/fsharp/pull/17741)
1920
* Consider `open type` used when the type is an enum and any of the enum cases is used unqualified. ([PR #17628](https://github.com/dotnet/fsharp/pull/17628))
2021
* Guard for possible StackOverflowException when typechecking non recursive modules and namespaces ([PR #17654](https://github.com/dotnet/fsharp/pull/17654))
2122
* Nullable - fix for processing System.Nullable types with nesting ([PR #17736](https://github.com/dotnet/fsharp/pull/17736))

src/Compiler/TypedTree/TcGlobals.fs

+1-5
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,7 @@ type TcGlobals(
193193
realsig: bool) =
194194

195195
let v_langFeatureNullness = langVersion.SupportsFeature LanguageFeature.NullnessChecking
196-
197-
let v_renderNullness = checkNullness && v_langFeatureNullness
198-
196+
199197
let v_knownWithNull =
200198
if v_langFeatureNullness then KnownWithNull else KnownAmbivalentToNull
201199

@@ -1113,8 +1111,6 @@ type TcGlobals(
11131111

11141112
member _.langFeatureNullness = v_langFeatureNullness
11151113

1116-
member _.renderNullnessAnnotations = v_renderNullness
1117-
11181114
member _.knownWithNull = v_knownWithNull
11191115

11201116
member _.knownWithoutNull = v_knownWithoutNull

src/Compiler/TypedTree/TcGlobals.fsi

-2
Original file line numberDiff line numberDiff line change
@@ -1055,8 +1055,6 @@ type internal TcGlobals =
10551055

10561056
member reference_equality_inner_vref: FSharp.Compiler.TypedTree.ValRef
10571057

1058-
member renderNullnessAnnotations: bool
1059-
10601058
member reraise_info: IntrinsicValRef
10611059

10621060
member reraise_vref: FSharp.Compiler.TypedTree.ValRef

src/Compiler/TypedTree/TypedTreeOps.fs

+11-14
Original file line numberDiff line numberDiff line change
@@ -8926,10 +8926,7 @@ let typarEnc _g (gtpsType, gtpsMethod) typar =
89268926
warning(InternalError("Typar not found during XmlDoc generation", typar.Range))
89278927
"``0"
89288928

8929-
let nullnessEnc (g:TcGlobals) (nullness:Nullness) =
8930-
if g.renderNullnessAnnotations then nullness.ToFsharpCodeString() else ""
8931-
8932-
let rec typeEnc g (gtpsType, gtpsMethod) ty =
8929+
let rec typeEnc g (gtpsType, gtpsMethod) ty =
89338930
let stripped = stripTyEqnsAndMeasureEqns g ty
89348931
match stripped with
89358932
| TType_forall _ ->
@@ -8943,26 +8940,26 @@ let rec typeEnc g (gtpsType, gtpsMethod) ty =
89438940
let ety = destNativePtrTy g ty
89448941
typeEnc g (gtpsType, gtpsMethod) ety + "*"
89458942

8946-
| TType_app (_, _, nullness) when isArrayTy g ty ->
8943+
| TType_app (_, _, _nullness) when isArrayTy g ty ->
89478944
let tcref, tinst = destAppTy g ty
89488945
let rank = rankOfArrayTyconRef g tcref
89498946
let arraySuffix = "[" + String.concat ", " (List.replicate (rank-1) "0:") + "]"
8950-
typeEnc g (gtpsType, gtpsMethod) (List.head tinst) + arraySuffix + nullnessEnc g nullness
8947+
typeEnc g (gtpsType, gtpsMethod) (List.head tinst) + arraySuffix
89518948

89528949
| TType_ucase (_, tinst)
89538950
| TType_app (_, tinst, _) ->
8954-
let tyName,nullness =
8951+
let tyName =
89558952
let ty = stripTyEqnsAndMeasureEqns g ty
89568953
match ty with
8957-
| TType_app (tcref, _tinst, nullness) ->
8954+
| TType_app (tcref, _tinst, _nullness) ->
89588955
// Generic type names are (name + "`" + digits) where name does not contain "`".
89598956
// In XML doc, when used in type instances, these do not use the ticks.
89608957
let path = Array.toList (fullMangledPathToTyconRef tcref) @ [tcref.CompiledName]
8961-
textOfPath (List.map DemangleGenericTypeName path),nullness
8958+
textOfPath (List.map DemangleGenericTypeName path)
89628959
| _ ->
89638960
assert false
89648961
failwith "impossible"
8965-
tyName + tyargsEnc g (gtpsType, gtpsMethod) tinst + nullnessEnc g nullness
8962+
tyName + tyargsEnc g (gtpsType, gtpsMethod) tinst
89668963

89678964
| TType_anon (anonInfo, tinst) ->
89688965
sprintf "%s%s" anonInfo.ILTypeRef.FullName (tyargsEnc g (gtpsType, gtpsMethod) tinst)
@@ -8973,11 +8970,11 @@ let rec typeEnc g (gtpsType, gtpsMethod) ty =
89738970
else
89748971
sprintf "System.Tuple%s"(tyargsEnc g (gtpsType, gtpsMethod) tys)
89758972

8976-
| TType_fun (domainTy, rangeTy, nullness) ->
8977-
"Microsoft.FSharp.Core.FSharpFunc" + tyargsEnc g (gtpsType, gtpsMethod) [domainTy; rangeTy] + nullnessEnc g nullness
8973+
| TType_fun (domainTy, rangeTy, _nullness) ->
8974+
"Microsoft.FSharp.Core.FSharpFunc" + tyargsEnc g (gtpsType, gtpsMethod) [domainTy; rangeTy]
89788975

8979-
| TType_var (typar, nullness) ->
8980-
typarEnc g (gtpsType, gtpsMethod) typar + nullnessEnc g nullness
8976+
| TType_var (typar, _nullness) ->
8977+
typarEnc g (gtpsType, gtpsMethod) typar
89818978

89828979
| TType_measure _ -> "?"
89838980

tests/FSharp.Compiler.Service.Tests/TooltipTests.fs

+51-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ open FSharp.Compiler.Text
99
open FSharp.Compiler.Tokenization
1010
open FSharp.Compiler.EditorServices
1111
open FSharp.Compiler.Symbols
12+
open FSharp.Compiler.Xml
1213
open FSharp.Test
1314
open Xunit
1415

@@ -398,16 +399,24 @@ let getCheckResults source options =
398399
let _, checkResults = parseAndCheckFile fileName source options
399400
checkResults
400401

401-
let assertAndGetSingleToolTipText (ToolTipText(items)) =
402+
403+
let taggedTextsToString (t: TaggedText array) =
404+
t
405+
|> Array.map (fun taggedText -> taggedText.Text)
406+
|> String.concat ""
407+
let assertAndExtractTooltip (ToolTipText(items)) =
402408
Assert.Equal(1,items.Length)
403409
match items.[0] with
404-
| ToolTipElement.Group [ { MainDescription = description } ] ->
410+
| ToolTipElement.Group [ singleElement ] ->
405411
let toolTipText =
406-
description
407-
|> Array.map (fun taggedText -> taggedText.Text)
408-
|> String.concat ""
409-
toolTipText
412+
singleElement.MainDescription
413+
|> taggedTextsToString
414+
toolTipText, singleElement.XmlDoc, singleElement.Remarks |> Option.map taggedTextsToString
410415
| _ -> failwith $"Expected group, got {items.[0]}"
416+
417+
let assertAndGetSingleToolTipText items =
418+
let text,_xml,_remarks = assertAndExtractTooltip items
419+
text
411420

412421
let normalize (s:string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n")
413422

@@ -436,6 +445,42 @@ let exists() = System.IO.Path.Exists(null:string)
436445
checkResults.GetToolTip(2, 36, "let exists() = System.IO.Path.Exists(null:string)", [ "Exists" ], FSharpTokenTag.Identifier)
437446
|> assertAndGetSingleToolTipText
438447
|> Assert.shouldBeEquivalentTo "System.IO.Path.Exists([<NotNullWhenAttribute (true)>] path: string | null) : bool"
448+
449+
[<FactForNETCOREAPP>]
450+
let ``Should display xml doc on a nullable BLC method`` () =
451+
452+
let source = """module Foo
453+
let exists() = System.IO.Path.Exists(null:string)
454+
"""
455+
let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|]
456+
checkResults.GetToolTip(2, 36, "let exists() = System.IO.Path.Exists(null:string)", [ "Exists" ], FSharpTokenTag.Identifier)
457+
|> assertAndExtractTooltip
458+
|> fun (text,xml,remarks) ->
459+
text |> Assert.shouldBeEquivalentTo "System.IO.Path.Exists([<NotNullWhenAttribute (true)>] path: string | null) : bool"
460+
match xml with
461+
| FSharpXmlDoc.FromXmlFile (_dll,sigPath) -> sigPath |> Assert.shouldBeEquivalentTo "M:System.IO.Path.Exists(System.String)"
462+
| _ -> failwith $"Xml wrong type %A{xml}"
463+
464+
465+
[<FactForNETCOREAPP>]
466+
let ``Should display xml doc on fsharp hosted nullable function`` () =
467+
468+
let source = """module Foo
469+
/// This is a xml doc above myFunc
470+
let myFunc(x:string|null) : string | null = x
471+
472+
let exists() = myFunc(null)
473+
"""
474+
let checkResults = getCheckResults source [|"--checknulls+";"--langversion:preview"|]
475+
checkResults.GetToolTip(5, 21, "let exists() = myFunc(null)", [ "myFunc" ], FSharpTokenTag.Identifier)
476+
|> assertAndExtractTooltip
477+
|> fun (text,xml,remarks) ->
478+
match xml with
479+
| FSharpXmlDoc.FromXmlText t ->
480+
t.UnprocessedLines |> Assert.shouldBeEquivalentTo [|" This is a xml doc above myFunc"|]
481+
| _ -> failwith $"xml was %A{xml}"
482+
text |> Assert.shouldBeEquivalentTo "val myFunc: x: string | null -> string | null"
483+
remarks |> Assert.shouldBeEquivalentTo (Some "Full name: Foo.myFunc")
439484

440485

441486
[<FactForNETCOREAPP>]

0 commit comments

Comments
 (0)