Skip to content

Commit 7e8f7bb

Browse files
authored
Fix colorization of type name in MyType<Qualified.Name>.StaticMember (dotnet#19800)
* Fix colorization of type name in MyType<Qualified.Name>.S (dotnet#18009) Accept ItemOccurrence.InvalidUse in LegitTypeOccurrence so that the type-name span in expressions like MyType<System.Int32>.S - which name resolution flags as InvalidUse via isWrongItemInExpr - is still classified as a ReferenceType, matching the behavior for the unqualified form MyType<int>.S.
1 parent 85e2e21 commit 7e8f7bb

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

src/Compiler/Service/SemanticClassification.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ module TcResolutionsExtensions =
151151
| ItemOccurrence.UseInType
152152
| ItemOccurrence.UseInAttribute
153153
| ItemOccurrence.Use
154+
| ItemOccurrence.InvalidUse
154155
| ItemOccurrence.Binding
155156
| ItemOccurrence.Pattern
156157
| ItemOccurrence.Open -> Some()

tests/FSharp.Compiler.ComponentTests/FSharpChecker/SemanticClassificationRegressions.fs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,77 @@ type Animal =
143143
(12, 1, 7) // t.IsIdent — RequireQualifiedAccess
144144
(17, 1, 5) ] // this.IsCat — self-referential member
145145

146+
/// (#18009 regression) Static method on a generic type with a *qualified* type argument
147+
/// must still classify the type name as a type.
148+
[<Fact>]
149+
let ``Static method on generic type should classify type name as type`` () =
150+
let source =
151+
"""
152+
module Test
153+
154+
type MyType<'T> =
155+
static member S = 1
156+
157+
let _ = MyType<int>.S
158+
let _ = MyType<System.Int32>.S
159+
"""
160+
161+
let items = getClassifications source
162+
163+
let isMyTypeRefOnLine line (item: SemanticClassificationItem) =
164+
item.Type = SemanticClassificationType.ReferenceType
165+
&& item.Range.StartLine = line
166+
&& item.Range.StartColumn = 8
167+
&& item.Range.EndColumn = 14
168+
169+
let unqualified = items |> Array.filter (isMyTypeRefOnLine 7)
170+
Assert.True(
171+
unqualified.Length = 1,
172+
sprintf
173+
"Expected exactly one ReferenceType classification for MyType on line 7, got: %A"
174+
(items |> Array.filter (fun i -> i.Range.StartLine = 7)
175+
|> Array.map (fun i -> i.Range.StartColumn, i.Range.EndColumn, i.Type))
176+
)
177+
178+
let qualified = items |> Array.filter (isMyTypeRefOnLine 8)
179+
Assert.True(
180+
qualified.Length = 1,
181+
sprintf
182+
"Expected exactly one ReferenceType classification for MyType on line 8, got: %A"
183+
(items |> Array.filter (fun i -> i.Range.StartLine = 8)
184+
|> Array.map (fun i -> i.Range.StartColumn, i.Range.EndColumn, i.Type))
185+
)
186+
187+
/// (#18009 follow-up) Accepting ItemOccurrence.InvalidUse in LegitTypeOccurrence must
188+
/// not cause unresolved identifiers to be classified as types.
189+
[<Fact>]
190+
let ``Undeclared identifier in expression position is not classified as a type`` () =
191+
let source =
192+
"""
193+
module Test
194+
195+
let _ = NotDeclaredAnywhere.S
196+
"""
197+
198+
let items = getClassifications source
199+
200+
let badSpans =
201+
items
202+
|> Array.filter (fun item ->
203+
item.Range.StartLine = 4
204+
&& item.Range.StartColumn = 8
205+
&& item.Range.EndColumn = 27
206+
&& (item.Type = SemanticClassificationType.ReferenceType
207+
|| item.Type = SemanticClassificationType.ValueType
208+
|| item.Type = SemanticClassificationType.Type))
209+
210+
Assert.True(
211+
badSpans.Length = 0,
212+
sprintf
213+
"Undeclared identifier should not be classified as a type, but found: %A"
214+
(badSpans |> Array.map (fun i -> i.Range.StartColumn, i.Range.EndColumn, i.Type))
215+
)
216+
146217
/// (#16982) Delegate `Invoke` synthesized in a delegate declaration must not be classified as Method.
147218
[<Fact>]
148219
let ``Delegate Invoke in declaration not classified as method`` () =

0 commit comments

Comments
 (0)