Skip to content

Commit 79e8531

Browse files
Classify nameof<'T> & match … with nameof ident -> … correctly (#18300)
* Resolve `nameof` in `nameof<'T>` * Resolve `nameof` in `match … with nameof ident -> …` * Add `nameof` classification tests * Update release notes * Might as well make them compilable * Show `nameof` as keyword in `nameof<…>` even when the type parameter is invalid * Touchup
1 parent 96771a8 commit 79e8531

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
### Fixed
2+
* Fix classification of `nameof` in `nameof<'T>`, `match … with nameof ident -> …`. ([Issue #10026](https://github.com/dotnet/fsharp/issues/10026), [PR #18300](https://github.com/dotnet/fsharp/pull/18300))
23
* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877))
34
* Fix optimizer internal error for records with static fields ([Issue #18165](https://github.com/dotnet/fsharp/issues/18165), [PR #18280](https://github.com/dotnet/fsharp/pull/18280))
45
* Fix nullness warning with flexible types ([Issue #18056](https://github.com/dotnet/fsharp/issues/18056), [PR #18266](https://github.com/dotnet/fsharp/pull/18266))
@@ -25,4 +26,4 @@
2526
* Added nullability annotations to `.Using` builder method for `async`, `task` and compiler-internal builders ([PR #18292](https://github.com/dotnet/fsharp/pull/18292))
2627

2728
### Breaking Changes
28-
* Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/17877))
29+
* Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/17877))

src/Compiler/Checking/CheckPatterns.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,10 @@ and TcPatLongIdentNewDef warnOnUpperForId warnOnUpper (cenv: cenv) env ad valRep
583583
| [arg]
584584
when g.langVersion.SupportsFeature LanguageFeature.NameOf && IsNameOf cenv env ad m id ->
585585
match TcNameOfExpr cenv env tpenv (ConvSynPatToSynExpr arg) with
586-
| Expr.Const(Const.String s, m, _) -> TcConstPat warnOnUpper cenv env vFlags patEnv ty (SynConst.String(s, SynStringKind.Regular, m)) m
586+
| Expr.Const(Const.String s, m, _) ->
587+
// Record the resolution of the `nameof` usage so that we can classify it correctly later.
588+
CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, Item.Value g.nameof_vref, emptyTyparInst, ItemOccurrence.Use, env.eAccessRights)
589+
TcConstPat warnOnUpper cenv env vFlags patEnv ty (SynConst.String(s, SynStringKind.Regular, m)) m
587590
| _ -> failwith "Impossible: TcNameOfExpr must return an Expr.Const of type string"
588591

589592
| _ ->

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9265,6 +9265,12 @@ and TcValueItemThen cenv overallTy env vref tpenv mItem afterResolution delayed
92659265
// Allow `nameof<'T>` for a generic parameter
92669266
match vref with
92679267
| _ when isNameOfValRef g vref && g.langVersion.SupportsFeature LanguageFeature.NameOf ->
9268+
// Record the resolution of the `nameof` usage so that we can classify it correctly later.
9269+
do
9270+
match afterResolution with
9271+
| AfterResolution.RecordResolution (_, callSink, _, _) -> callSink emptyTyparInst
9272+
| AfterResolution.DoNothing -> ()
9273+
92689274
match tys with
92699275
| [SynType.Var(SynTypar(id, _, false) as tp, _m)] ->
92709276
let _tpR, tpenv = TcTypeOrMeasureParameter None cenv env ImplicitlyBoundTyparsAllowed.NoNewTypars tpenv tp

vsintegration/tests/FSharp.Editor.Tests/SemanticClassificationServiceTests.fs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ type SemanticClassificationServiceTests() =
4343
match ranges |> List.tryFind (fun item -> Range.rangeContainsPos item.Range markerPos) with
4444
| None -> failwith "Cannot find colorization data for end of marker"
4545
| Some item ->
46-
FSharpClassificationTypes.getClassificationTypeName item.Type
47-
|> Assert.shouldBeEqualWith classificationType "Classification data doesn't match for end of marker"
46+
let actual = FSharpClassificationTypes.getClassificationTypeName item.Type
47+
48+
actual
49+
|> Assert.shouldBeEqualWith
50+
classificationType
51+
$"Classification data doesn't match for end of marker: {classificationType} ≠ {actual} ({item.Type})"
4852

4953
let verifyNoClassificationDataAtEndOfMarker (fileContents: string, marker: string, classificationType: string) =
5054
let text = SourceText.From(fileContents)
@@ -183,3 +187,56 @@ let g() =
183187
"""
184188

185189
verifyNoClassificationDataAtEndOfMarker (sourceText, marker, classificationType)
190+
191+
[<Theory>]
192+
[<InlineData("(*1*)", ClassificationTypeNames.Keyword)>]
193+
[<InlineData("(*2*)", ClassificationTypeNames.Keyword)>]
194+
[<InlineData("(*3*)", ClassificationTypeNames.Keyword)>]
195+
[<InlineData("(*4*)", ClassificationTypeNames.LocalName)>]
196+
[<InlineData("(*5*)", ClassificationTypeNames.LocalName)>]
197+
[<InlineData("(*6*)", ClassificationTypeNames.LocalName)>]
198+
[<InlineData("(*7*)", ClassificationTypeNames.Identifier)>]
199+
[<InlineData("(*8*)", ClassificationTypeNames.Identifier)>]
200+
[<InlineData("(*9*)", ClassificationTypeNames.ClassName)>]
201+
[<InlineData("(*10*)", ClassificationTypeNames.ClassName)>]
202+
[<InlineData("(*11*)", ClassificationTypeNames.ClassName)>]
203+
[<InlineData("(*12*)", ClassificationTypeNames.ClassName)>]
204+
[<InlineData("(*13*)", ClassificationTypeNames.ClassName)>]
205+
[<InlineData("(*14*)", ClassificationTypeNames.TypeParameterName)>]
206+
[<InlineData("(*15*)", ClassificationTypeNames.TypeParameterName)>]
207+
[<InlineData("(*16*)", ClassificationTypeNames.Keyword)>]
208+
[<InlineData("(*17*)", ClassificationTypeNames.Keyword)>]
209+
[<InlineData("(*18*)", ClassificationTypeNames.Keyword)>]
210+
member _.``nameof ident, nameof<'T>, match with nameof ident``(marker: string, classificationType: string) =
211+
let sourceText =
212+
"""
213+
module ``Normal usage of nameof should show up as a keyword`` =
214+
let f x = (*1*)nameof x
215+
let g (x : 'T) = (*2*)nameof<'T>
216+
let h x y = match x with (*3*)nameof y -> () | _ -> ()
217+
218+
module ``Redefined nameof should shadow the intrinsic one`` =
219+
let a x = match x with (*4*)nameof -> ()
220+
let b (*5*)nameof = (*6*)nameof
221+
let (*7*)nameof = "redefined"
222+
let _ = (*8*)nameof
223+
224+
type (*9*)nameof () = class end
225+
let _ = (*10*)nameof ()
226+
let _ = new (*11*)nameof ()
227+
228+
module (*12*)nameof =
229+
let f x = x
230+
231+
let _ = (*13*)nameof.f 3
232+
233+
let c (x : '(*14*)nameof) = x
234+
let d (x : (*15*)'nameof) = x
235+
236+
module ``It should still show up as a keyword even if the type parameter is invalid`` =
237+
let _ = (*16*)nameof<>
238+
let a (x : 'a) (y : 'b) = (*17*)nameof<'c> // FS0039: The type parameter 'c is not defined.
239+
let _ = (*18*)nameof<int> // FS3250: Expression does not have a name.
240+
"""
241+
242+
verifyClassificationAtEndOfMarker (sourceText, marker, classificationType)

0 commit comments

Comments
 (0)