Skip to content

Commit f5e27c5

Browse files
authored
Fix nullable types formatting in FSharpType.Format and tooltips to include parentheses (#18842)
1 parent 6a1e4af commit f5e27c5

File tree

23 files changed

+101
-57
lines changed

23 files changed

+101
-57
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
* Fix active pattern typechecking regression. ([Issue #18638](https://github.com/dotnet/fsharp/issues/18638), [PR #18642](https://github.com/dotnet/fsharp/pull/18642))
1919
* Fix nullness warnings when casting non-nullable values to `IEquatable<T>` to match C# behavior. ([Issue #18759](https://github.com/dotnet/fsharp/issues/18759))
2020
* Fix IsByRefLikeAttribute types being incorrectly suppressed in completion lists. Types like `Span<T>` and `ReadOnlySpan<T>` now appear correctly in IntelliSense.
21-
2221
* Fix SRTP nullness constraint resolution for types imported from older assemblies. AmbivalentToNull types now use legacy F# nullness rules instead of always satisfying `'T : null` constraints. ([Issue #18390](https://github.com/dotnet/fsharp/issues/18390), [Issue #18344](https://github.com/dotnet/fsharp/issues/18344))
2322
* Fix Show XML doc for enum fields in external metadata ([Issue #17939](https://github.com/dotnet/fsharp/issues/17939#issuecomment-3137410105), [PR #18800](https://github.com/dotnet/fsharp/pull/18800))
23+
* Fix nullable types formatting in `FSharpType.Format` and tooltips to include parentheses. ([PR #18842](https://github.com/dotnet/fsharp/pull/18842))
2424
* TypeMismatchDiagnosticExtendedData: fix expected and actual types calculation. ([Issue ](https://github.com/dotnet/fsharp/pull/18851))
2525

2626
### Changed

src/Compiler/Checking/NicePrint.fs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ module internal PrintUtilities =
145145
| [x] -> [resultFunction x (layoutFunction x)]
146146
| x :: rest -> [ resultFunction x (layoutFunction x -- leftL (tagText (match rest.Length with 1 -> FSComp.SR.nicePrintOtherOverloads1() | n -> FSComp.SR.nicePrintOtherOverloadsN(n)))) ]
147147
| _ -> []
148+
149+
let showNullness (denv: DisplayEnv) (nullness: Nullness) =
150+
denv.showNullnessAnnotations <> Some false && nullness.Evaluate() = NullnessInfo.WithNull
148151

149152
let tagEntityRefName(denv: DisplayEnv) (xref: EntityRef) name =
150153
if xref.IsNamespace then tagNamespace name
@@ -942,15 +945,12 @@ module PrintTypes =
942945
| [arg] -> layoutTypeWithInfoAndPrec denv env 2 arg ^^ tcL
943946
| args -> bracketIfL (prec <= 1) (bracketL (layoutTypesWithInfoAndPrec denv env 2 SepL.comma args) --- tcL)
944947

945-
and layoutNullness (denv: DisplayEnv) part2 (nullness: Nullness) =
948+
and layoutNullness (denv: DisplayEnv) part2 (nullness: Nullness) prec =
946949
// Show nullness annotations unless explicitly turned off
947-
if denv.showNullnessAnnotations <> Some false then
948-
match nullness.Evaluate() with
949-
| NullnessInfo.WithNull -> part2 ^^ wordL (tagPunctuation "|") ^^ wordL (tagKeyword "null")
950-
| NullnessInfo.WithoutNull -> part2
951-
| NullnessInfo.AmbivalentToNull -> part2 //^^ wordL (tagText "__maybenull")
950+
if showNullness denv nullness then
951+
part2 ^^ wordL (tagPunctuation "|") ^^ wordL (tagKeyword "null") |> bracketIfL (prec <= 3)
952952
else
953-
part2
953+
part2 // if NullnessInfo.AmbivalentToNull -> part2 ^^ wordL (tagText "__maybenull")
954954

955955
/// Layout a type, taking precedence into account to insert brackets where needed
956956
and layoutTypeWithInfoAndPrec denv env prec ty =
@@ -1014,7 +1014,7 @@ module PrintTypes =
10141014
prefix
10151015
args
10161016

1017-
let part2 = layoutNullness denv part1 nullness
1017+
let part2 = layoutNullness denv part1 nullness prec
10181018

10191019
part2
10201020
// Layout a tuple type
@@ -1046,14 +1046,15 @@ module PrintTypes =
10461046
let retTyL = layoutTypeWithInfoAndPrec denv env 5 retTy
10471047
let argTysL = argTys |> List.map (layoutTypeWithInfoAndPrec denv env 4)
10481048
let funcTyL = curriedLayoutsL arrow argTysL retTyL
1049-
let part1 = bracketIfL (prec <= 4) funcTyL
1050-
let part2 = layoutNullness denv part1 nullness
1049+
let showNull = showNullness denv nullness
1050+
let part1 = bracketIfL (prec <= 4 || showNull) funcTyL
1051+
let part2 = layoutNullness denv part1 nullness prec
10511052
part2
10521053

10531054
// Layout a type variable .
10541055
| TType_var (r, nullness) ->
10551056
let part1 = layoutTyparRefWithInfo denv env r
1056-
let part2 = layoutNullness denv part1 nullness
1057+
let part2 = layoutNullness denv part1 nullness prec
10571058
part2
10581059

10591060
| TType_measure unt -> layoutMeasure denv unt
@@ -1104,8 +1105,13 @@ module PrintTypes =
11041105

11051106
// Layout an unnamed argument
11061107
// Cannot have any attributes
1107-
| None, _, _ ->
1108-
layoutTypeWithInfoAndPrec denv env 2 ty
1108+
| None, _, _ ->
1109+
let prec =
1110+
match ty with
1111+
| TType_tuple _ -> 2
1112+
| _ -> 4
1113+
1114+
layoutTypeWithInfoAndPrec denv env prec ty
11091115

11101116
// Layout a named argument
11111117
| Some id, _, _ ->
@@ -2945,7 +2951,7 @@ let minimalStringOfType denv ty =
29452951
let ty, _cxs = PrettyTypes.PrettifyType denv.g ty
29462952
let denv = suppressNullnessAnnotations denv
29472953
let denvMin = { denv with showInferenceTyparAnnotations=false; showStaticallyResolvedTyparAnnotations=false }
2948-
showL (PrintTypes.layoutTypeWithInfoAndPrec denvMin SimplifyTypes.typeSimplificationInfo0 2 ty)
2954+
showL (PrintTypes.layoutTypeWithInfoAndPrec denvMin SimplifyTypes.typeSimplificationInfo0 5 ty)
29492955

29502956
let minimalStringOfTypeWithNullness denv ty =
29512957
minimalStringOfType {denv with showNullnessAnnotations = Some true} ty

tests/FSharp.Compiler.ComponentTests/Conformance/InferenceProcedures/ByrefSafetyAnalysis/ByrefSafetyAnalysis.fs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,29 +69,29 @@ module ByrefSafetyAnalysis =
6969
(Error 438, Line 113, Col 23, Line 113, Col 33, "Duplicate method. The method 'TestMethod' has the same name and signature as another method in type 'NegativeTests.TestNegativeOverloading'.")
7070
(Error 412, Line 121, Col 18, Line 121, Col 22, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
7171
(Error 412, Line 123, Col 18, Line 123, Col 23, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
72-
(Error 3301, Line 125, Col 9, Line 125, Col 14, "The function or method has an invalid return type '(byref<int> * int)'. This is not permitted by the rules of Common IL.")
72+
(Error 3301, Line 125, Col 9, Line 125, Col 14, "The function or method has an invalid return type 'byref<int> * int'. This is not permitted by the rules of Common IL.")
7373
(Error 412, Line 125, Col 34, Line 125, Col 39, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
7474
(Error 418, Line 125, Col 35, Line 125, Col 36, "The byref typed value 'x' cannot be used at this point")
75-
(Error 3301, Line 127, Col 9, Line 127, Col 14, "The function or method has an invalid return type '(byref<int> -> unit)'. This is not permitted by the rules of Common IL.")
75+
(Error 3301, Line 127, Col 9, Line 127, Col 14, "The function or method has an invalid return type 'byref<int> -> unit'. This is not permitted by the rules of Common IL.")
7676
(Error 412, Line 129, Col 14, Line 129, Col 15, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
77-
(Error 3300, Line 131, Col 17, Line 131, Col 18, "The parameter 'x' has an invalid type '((byref<int> -> unit) * int)'. This is not permitted by the rules of Common IL.")
78-
(Error 3300, Line 133, Col 17, Line 133, Col 18, "The parameter 'x' has an invalid type '(byref<int> -> unit)'. This is not permitted by the rules of Common IL.")
79-
(Error 3300, Line 133, Col 41, Line 133, Col 42, "The parameter 'y' has an invalid type '(byref<int> * int)'. This is not permitted by the rules of Common IL.")
80-
(Error 3300, Line 139, Col 36, Line 139, Col 39, "The parameter 'tup' has an invalid type '(inref<int> * int)'. This is not permitted by the rules of Common IL.")
77+
(Error 3300, Line 131, Col 17, Line 131, Col 18, "The parameter 'x' has an invalid type '(byref<int> -> unit) * int'. This is not permitted by the rules of Common IL.")
78+
(Error 3300, Line 133, Col 17, Line 133, Col 18, "The parameter 'x' has an invalid type 'byref<int> -> unit'. This is not permitted by the rules of Common IL.")
79+
(Error 3300, Line 133, Col 41, Line 133, Col 42, "The parameter 'y' has an invalid type 'byref<int> * int'. This is not permitted by the rules of Common IL.")
80+
(Error 3300, Line 139, Col 36, Line 139, Col 39, "The parameter 'tup' has an invalid type 'inref<int> * int'. This is not permitted by the rules of Common IL.")
8181
(Error 412, Line 140, Col 13, Line 140, Col 33, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
82-
(Error 3300, Line 142, Col 37, Line 142, Col 38, "The parameter 'x' has an invalid type '(byref<int> -> unit)'. This is not permitted by the rules of Common IL.")
82+
(Error 3300, Line 142, Col 37, Line 142, Col 38, "The parameter 'x' has an invalid type 'byref<int> -> unit'. This is not permitted by the rules of Common IL.")
8383
(Error 3300, Line 144, Col 37, Line 144, Col 38, "The parameter 'x' has an invalid type 'byref<int> option'. This is not permitted by the rules of Common IL.")
8484
(Error 3300, Line 146, Col 17, Line 146, Col 18, "The parameter 'x' has an invalid type 'byref<int> option'. This is not permitted by the rules of Common IL.")
8585
(Error 412, Line 151, Col 13, Line 151, Col 14, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
8686
(Error 412, Line 151, Col 17, Line 151, Col 30, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
87-
(Error 3301, Line 154, Col 9, Line 154, Col 15, "The function or method has an invalid return type '(byref<int> -> unit)'. This is not permitted by the rules of Common IL.")
87+
(Error 3301, Line 154, Col 9, Line 154, Col 15, "The function or method has an invalid return type 'byref<int> -> unit'. This is not permitted by the rules of Common IL.")
8888
(Error 412, Line 155, Col 9, Line 155, Col 22, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
8989
(Error 412, Line 158, Col 13, Line 158, Col 14, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
9090
(Error 412, Line 160, Col 13, Line 160, Col 26, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
9191
(Error 412, Line 165, Col 9, Line 165, Col 22, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
9292
(Error 412, Line 169, Col 13, Line 169, Col 14, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
9393
(Error 412, Line 169, Col 17, Line 169, Col 28, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
94-
(Error 3301, Line 172, Col 9, Line 172, Col 15, "The function or method has an invalid return type '(int -> byref<int> -> unit)'. This is not permitted by the rules of Common IL.")
94+
(Error 3301, Line 172, Col 9, Line 172, Col 15, "The function or method has an invalid return type 'int -> byref<int> -> unit'. This is not permitted by the rules of Common IL.")
9595
(Error 412, Line 173, Col 9, Line 173, Col 20, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
9696
(Error 412, Line 176, Col 13, Line 176, Col 14, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
9797
(Error 412, Line 178, Col 13, Line 178, Col 24, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
@@ -102,7 +102,7 @@ module ByrefSafetyAnalysis =
102102
(Error 412, Line 191, Col 13, Line 191, Col 14, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
103103
(Error 412, Line 193, Col 13, Line 193, Col 28, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
104104
(Error 412, Line 198, Col 9, Line 198, Col 24, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
105-
(Error 3301, Line 201, Col 9, Line 201, Col 15, "The function or method has an invalid return type '(byref<int> * int)'. This is not permitted by the rules of Common IL.")
105+
(Error 3301, Line 201, Col 9, Line 201, Col 15, "The function or method has an invalid return type 'byref<int> * int'. This is not permitted by the rules of Common IL.")
106106
(Error 412, Line 203, Col 10, Line 203, Col 15, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
107107
(Error 421, Line 203, Col 11, Line 203, Col 12, "The address of the variable 'x' cannot be used at this point")
108108
(Error 412, Line 206, Col 9, Line 206, Col 18, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
@@ -807,7 +807,7 @@ type outref<'T> with
807807
|> compile
808808
|> shouldFail
809809
|> withDiagnostics [
810-
(Error 3300, Line 5, Col 18, Line 5, Col 19, "The parameter 'f' has an invalid type '(byref<int> -> 'a)'. This is not permitted by the rules of Common IL.")
810+
(Error 3300, Line 5, Col 18, Line 5, Col 19, "The parameter 'f' has an invalid type 'byref<int> -> 'a'. This is not permitted by the rules of Common IL.")
811811
(Error 424, Line 7, Col 6, Line 7, Col 13, "The address of an array element cannot be used at this point")
812812
(Error 412, Line 9, Col 19, Line 9, Col 20, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")
813813
(Error 412, Line 11, Col 19, Line 11, Col 20, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL.")

tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnionCasePatternMatchingErrors.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ let myVal =
2424
\tf1: int list
2525
\t{| X: string |}
2626
\tf3: U
27-
\tf4: (int * System.String)")
27+
\tf4: int * System.String")
2828

2929
[<Fact>]
3030
let ``Union matching error - Named args - Name used twice`` () =

tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ let main _args =
103103
|> withDiagnostics
104104
[ Error 3265, Line 13, Col 13, Line 13, Col 60, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for '{| x: int |}'. Nullness warnings won't be reported correctly for such types."
105105
Error 3265, Line 14, Col 13, Line 14, Col 51, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for 'System.Int32'. Nullness warnings won't be reported correctly for such types."
106-
Error 3265, Line 15, Col 13, Line 15, Col 59, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for '(int * float)'. Nullness warnings won't be reported correctly for such types."
106+
Error 3265, Line 15, Col 13, Line 15, Col 59, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for 'int * float'. Nullness warnings won't be reported correctly for such types."
107107
Error 3265, Line 16, Col 13, Line 16, Col 67, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for 'struct (int * float)'. Nullness warnings won't be reported correctly for such types."
108108
Error 3265, Line 17, Col 13, Line 17, Col 57, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for 'int<mykg>'. Nullness warnings won't be reported correctly for such types."
109109
Error 3265, Line 18, Col 13, Line 18, Col 57, "Application of method 'Deserialize' attempted to create a nullable type ('T | null) for 'int<mykg>'. Nullness warnings won't be reported correctly for such types."]
@@ -152,7 +152,7 @@ let doNotWarnOnDowncastRepeatedNestedNullable(o:objnull) = o :? list<((AB | null
152152
[ Error 3264, Line 4, Col 39, Line 4, Col 47, "Nullness warning: Downcasting from 'objnull' into 'AB' can introduce unexpected null values. Cast to 'AB|null' instead or handle the null before downcasting."
153153
Error 3060, Line 5, Col 42, Line 5, Col 59, "This type test or downcast will erase the provided type 'AB | null' to the type 'AB'"
154154
Error 3060, Line 6, Col 41, Line 6, Col 55, "This type test or downcast will erase the provided type 'AB | null' to the type 'AB'"
155-
Error 3060, Line 7, Col 51, Line 7, Col 97, "This type test or downcast will erase the provided type 'List<AB | null array | null> | null' to the type 'List<AB array>'"]
155+
Error 3060, Line 7, Col 51, Line 7, Col 97, "This type test or downcast will erase the provided type 'List<(AB | null) array | null> | null' to the type 'List<AB array>'"]
156156

157157

158158
[<Fact>]
@@ -611,7 +611,7 @@ let f6(x: 'a | null when 'a:null) = ()
611611
[ Error 3261, Line 3, Col 11, Line 3, Col 32, "Nullness warning: The type 'string option' uses 'null' as a representation value but a non-null type is expected."
612612
Error 3260, Line 4, Col 11, Line 4, Col 21, "The type 'int' does not support a nullness qualification."
613613
Error 43, Line 4, Col 11, Line 4, Col 21, "A generic construct requires that the type 'int' have reference semantics, but it does not, i.e. it is a struct"
614-
Error 3260, Line 5, Col 11, Line 5, Col 25, "The type '('a * 'b)' does not support a nullness qualification."
614+
Error 3260, Line 5, Col 11, Line 5, Col 25, "The type ''a * 'b' does not support a nullness qualification."
615615
Error 3261, Line 6, Col 11, Line 6, Col 28, "Nullness warning: The type ''a option' uses 'null' as a representation value but a non-null type is expected."
616616
Error 43, Line 7, Col 28, Line 7, Col 37, "The constraints 'struct' and 'not struct' are inconsistent"
617617
Error 43, Line 8, Col 26, Line 8, Col 33, "The constraints 'null' and 'not null' are inconsistent"]
@@ -1013,7 +1013,7 @@ myNullReturningFunction myValOfY |> ignore
10131013
[Error 3261, Line 17, Col 25, Line 17, Col 34, "Nullness warning: The type 'string' does not support 'null'."
10141014
Error 193, Line 19, Col 26, Line 19, Col 45, "The type 'System.DateTime' does not have 'null' as a proper value"
10151015
Error 1, Line 20, Col 25, Line 20, Col 36, "The type '{| Anon: 'a |}' does not have 'null' as a proper value"
1016-
Error 1, Line 21, Col 26, Line 21, Col 31, "The type '('a * 'b * 'c)' does not have 'null' as a proper value"
1016+
Error 1, Line 21, Col 26, Line 21, Col 31, "The type ''a * 'b * 'c' does not have 'null' as a proper value"
10171017
Error 1, Line 23, Col 25, Line 23, Col 33, "The type 'Y' does not have 'null' as a proper value"]
10181018

10191019

@@ -1084,8 +1084,8 @@ looseFunc(maybeTuple2) |> ignore
10841084
|> shouldFail
10851085
|> withDiagnostics
10861086
[ Error 43, Line 21, Col 12, Line 21, Col 16, "The constraints 'null' and 'not null' are inconsistent"
1087-
Error 3260, Line 27, Col 18, Line 27, Col 34, "The type '(int * int)' does not support a nullness qualification."
1088-
Error 43, Line 27, Col 37, Line 27, Col 41, "The type '(int * int)' does not have 'null' as a proper value"
1087+
Error 3260, Line 27, Col 18, Line 27, Col 34, "The type 'int * int' does not support a nullness qualification."
1088+
Error 43, Line 27, Col 37, Line 27, Col 41, "The type 'int * int' does not have 'null' as a proper value"
10891089
Error 3261, Line 29, Col 12, Line 29, Col 19, "Nullness warning: The type 'MyDu | null' supports 'null' but a non-null type is expected."
10901090
Error 3261, Line 30, Col 12, Line 30, Col 21, "Nullness warning: The type 'MyRecord | null' supports 'null' but a non-null type is expected."
10911091
Error 43, Line 40, Col 36, Line 40, Col 40, "The type 'Maybe<int * int>' does not have 'null' as a proper value"]

0 commit comments

Comments
 (0)