diff --git a/src/Compiler/Service/SemanticClassification.fs b/src/Compiler/Service/SemanticClassification.fs index 8fe61e20dd..5dbce47dc6 100644 --- a/src/Compiler/Service/SemanticClassification.fs +++ b/src/Compiler/Service/SemanticClassification.fs @@ -151,6 +151,7 @@ module TcResolutionsExtensions = | ItemOccurrence.UseInType | ItemOccurrence.UseInAttribute | ItemOccurrence.Use + | ItemOccurrence.InvalidUse | ItemOccurrence.Binding | ItemOccurrence.Pattern | ItemOccurrence.Open -> Some() diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SemanticClassificationRegressions.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SemanticClassificationRegressions.fs index 8af90a38e5..84aff53a4f 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SemanticClassificationRegressions.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SemanticClassificationRegressions.fs @@ -136,3 +136,74 @@ type Animal = (7, 2, 8) // s.IsCircle && s.IsSquare — two on same line (12, 1, 7) // t.IsIdent — RequireQualifiedAccess (17, 1, 5) ] // this.IsCat — self-referential member + +/// (#18009 regression) Static method on a generic type with a *qualified* type argument +/// must still classify the type name as a type. +[] +let ``Static method on generic type should classify type name as type`` () = + let source = + """ +module Test + +type MyType<'T> = + static member S = 1 + +let _ = MyType.S +let _ = MyType.S +""" + + let items = getClassifications source + + let isMyTypeRefOnLine line (item: SemanticClassificationItem) = + item.Type = SemanticClassificationType.ReferenceType + && item.Range.StartLine = line + && item.Range.StartColumn = 8 + && item.Range.EndColumn = 14 + + let unqualified = items |> Array.filter (isMyTypeRefOnLine 7) + Assert.True( + unqualified.Length = 1, + sprintf + "Expected exactly one ReferenceType classification for MyType on line 7, got: %A" + (items |> Array.filter (fun i -> i.Range.StartLine = 7) + |> Array.map (fun i -> i.Range.StartColumn, i.Range.EndColumn, i.Type)) + ) + + let qualified = items |> Array.filter (isMyTypeRefOnLine 8) + Assert.True( + qualified.Length = 1, + sprintf + "Expected exactly one ReferenceType classification for MyType on line 8, got: %A" + (items |> Array.filter (fun i -> i.Range.StartLine = 8) + |> Array.map (fun i -> i.Range.StartColumn, i.Range.EndColumn, i.Type)) + ) + +/// (#18009 follow-up) Accepting ItemOccurrence.InvalidUse in LegitTypeOccurrence must +/// not cause unresolved identifiers to be classified as types. +[] +let ``Undeclared identifier in expression position is not classified as a type`` () = + let source = + """ +module Test + +let _ = NotDeclaredAnywhere.S +""" + + let items = getClassifications source + + let badSpans = + items + |> Array.filter (fun item -> + item.Range.StartLine = 4 + && item.Range.StartColumn = 8 + && item.Range.EndColumn = 27 + && (item.Type = SemanticClassificationType.ReferenceType + || item.Type = SemanticClassificationType.ValueType + || item.Type = SemanticClassificationType.Type)) + + Assert.True( + badSpans.Length = 0, + sprintf + "Undeclared identifier should not be classified as a type, but found: %A" + (badSpans |> Array.map (fun i -> i.Range.StartColumn, i.Range.EndColumn, i.Type)) + )