Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
### Fixed

* Fix the closing `]` of an open-ended slice (`xs[0..]`) being colored as a method by the editor: the synthesized `GetSlice` / `SetSlice` member lookup now uses a synthetic identifier range so the classifier does not pick it up. ([Issue #19905 item 5](https://github.com/dotnet/fsharp/issues/19905))
* Fix computation-expression builder identifier being classified as a plain value (instead of `ComputationExpression`) when the CE is used inside a `[ for … do … ]` list / array comprehension. ([Issue #19905 item 2](https://github.com/dotnet/fsharp/issues/19905))
* Fix semantic classification range overrun for generic method and constructor calls - the `<.>` type arguments (and on ctors the entire argument list) no longer pick up the surrounding `Method` / `DisposableType` color. ([Issue #19905 items 3, 4, 6](https://github.com/dotnet/fsharp/issues/19905))
* Fix wide-range `Method` semantic classification covering the whole `delegate of . -> .` clause inside an F# delegate declaration. ([Issue #19905 item 1](https://github.com/dotnet/fsharp/issues/19905))
* Tooltip "Full name" now shows demangled companion module names (e.g. `MyType.func` instead of `MyTypeModule.func`). ([Issue #17335](https://github.com/dotnet/fsharp/issues/17335), [PR #19867](https://github.com/dotnet/fsharp/pull/19867))
* Fix internal error (FS0193) when calling an indexed property setter with a named argument that matches an indexer parameter. ([Issue #16034](https://github.com/dotnet/fsharp/issues/16034), [PR #19851](https://github.com/dotnet/fsharp/pull/19851))
* Fix missing FS1182 ("unused binding") warning for unused `let` function bindings inside class types. ([Issue #13849](https://github.com/dotnet/fsharp/issues/13849), [PR #19805](https://github.com/dotnet/fsharp/pull/19805))
Expand Down
19 changes: 15 additions & 4 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6930,7 +6930,15 @@ and TcIndexingThen cenv env overallTy mWholeExpr mDot tpenv setInfo synLeftExprO
match setInfo with
// expr1.[expr2]
| None ->
[ DelayedDotLookup([ ident(nm, mWholeExpr)], mWholeExpr)
// For slice getters (isIndex=false: synthesized GetSlice call), syntheticize the
// synthesized identifier range so the resolver does not emit a Method classification
// covering the closing ']' of an open-ended slice (#19905 item 5). For the regular
// Item indexer (isIndex=true) keep the real range so symbol uses remain visible to
// IDE features.
let mIdent =
if isIndex then mWholeExpr
else mWholeExpr.MakeSynthetic()
[ DelayedDotLookup([ ident(nm, mIdent)], mIdent)
DelayedApp(ExprAtomicFlag.Atomic, true, synLeftExprOpt, MakeIndexParam None, mWholeExpr)
yield! delayed ]

Expand All @@ -6942,7 +6950,10 @@ and TcIndexingThen cenv env overallTy mWholeExpr mDot tpenv setInfo synLeftExprO
MakeDelayedSet(expr3, mWholeExpr)
yield! delayed ]
else
[ DelayedDotLookup([ident("SetSlice", mOfLeftOfSet)], mOfLeftOfSet)
// SetSlice synthesized identifier: syntheticize the range to suppress the
// wide-range Method classification on the closing ']' (#19905 item 5).
let mSyntheticSet = mOfLeftOfSet.MakeSynthetic()
[ DelayedDotLookup([ident("SetSlice", mSyntheticSet)], mSyntheticSet)
DelayedApp(ExprAtomicFlag.Atomic, true, synLeftExprOpt, MakeIndexParam (Some expr3), mWholeExpr)
yield! delayed ]

Expand Down Expand Up @@ -9217,7 +9228,7 @@ and TcMethodItemThen (cenv: cenv) overallTy env item methodName minfos tpenv mIt
// FUTURE: can we do better than emptyTyparInst here, in order to display instantiations
// of type variables in the quick info provided in the IDE? But note we haven't yet even checked if the
// number of type arguments is correct...
CallNameResolutionSink cenv.tcSink (mExprAndTypeArgs, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Use, env.eAccessRights)
CallNameResolutionSink cenv.tcSink (mItem, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Use, env.eAccessRights)

match otherDelayed with
| DelayedApp(atomicFlag, _, _, arg, mExprAndArg) :: otherDelayed ->
Expand Down Expand Up @@ -9251,7 +9262,7 @@ and TcCtorItemThen (cenv: cenv) overallTy env item nm minfos tinstEnclosing tpen
| DelayedTypeApp(tyargs, _mTypeArgs, mExprAndTypeArgs) :: DelayedApp(_, _, _, arg, mExprAndArg) :: otherDelayed ->

let objTyAfterTyArgs, tpenv = TcNestedTypeApplication cenv NewTyparsOK CheckCxs ItemOccurrence.UseInType WarnOnIWSAM.Yes env tpenv mExprAndTypeArgs objTy tinstEnclosing tyargs
CallExprHasTypeSink cenv.tcSink (mExprAndArg, env.NameEnv, objTyAfterTyArgs, env.eAccessRights)
CallExprHasTypeSink cenv.tcSink (mItem, env.NameEnv, objTyAfterTyArgs, env.eAccessRights)
let itemAfterTyArgs, minfosAfterTyArgs =
#if !NO_TYPEPROVIDERS
Comment thread
T-Gro marked this conversation as resolved.
// If the type is provided and took static arguments then the constructor will have changed
Expand Down
83 changes: 65 additions & 18 deletions src/Compiler/Service/SemanticClassification.fs
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,22 @@ module TcResolutionsExtensions =

// Custom builders like 'async { }' are both Item.Value and Item.CustomBuilder.
// We should prefer the latter, otherwise they would not get classified as CEs.
// Inside list/array comprehensions the same range can host more than 2 CNRs; we
// still want to keep the CustomBuilder ones if any are present.
let takeCustomBuilder (cnrs: CapturedNameResolution[]) =
assert (cnrs.Length > 0)

if cnrs.Length = 1 then
cnrs
elif cnrs.Length = 2 then
match cnrs[0].Item, cnrs[1].Item with
| Item.Value _, Item.CustomBuilder _ -> [| cnrs[1] |]
| Item.CustomBuilder _, Item.Value _ -> [| cnrs[0] |]
| _ -> cnrs
else
cnrs
let customBuilders =
cnrs
|> Array.filter (fun cnr ->
match cnr.Item with
| Item.CustomBuilder _ -> true
| _ -> false)

if customBuilders.Length > 0 then customBuilders else cnrs

let resolutions =
match range with
Expand All @@ -206,6 +210,34 @@ module TcResolutionsExtensions =

let results = ImmutableArray.CreateBuilder()

// (#19905 items 3 and 6) For Method/Property classifications, the CNR range may
// span an entire dotted/generic prefix (e.g. `MyType.Method` or
// `MailboxProcessor<int>.Start`). The wide range is needed for find-references and
// goto-definition, but classification should color only the trailing identifier.
// Narrow to the last `displayName.Length` characters of the range when the source
// span is wider than the identifier and the range lies on a single line.
let narrowMethodOrPropertyRange (displayName: string) (m: range) =
if m.StartLine = m.EndLine
&& not (System.String.IsNullOrEmpty displayName)
&& displayName.Length > 0
&& m.EndColumn - m.StartColumn > displayName.Length
&& m.EndColumn >= displayName.Length then
mkRange m.FileName (Position.mkPos m.EndLine (m.EndColumn - displayName.Length)) m.End
else
m

// (#19905 item 4) For CtorGroup classifications on `new T<typeArgs>(...)`, the CNR
// range covers the whole type-with-args. Narrow to the leading type-name portion so
// DisposableType/ctor colors do not extend over the `<.>` type arguments.
let narrowCtorRange (displayName: string) (m: range) =
if m.StartLine = m.EndLine
&& not (System.String.IsNullOrEmpty displayName)
&& displayName.Length > 0
&& m.EndColumn - m.StartColumn > displayName.Length then
mkRange m.FileName m.Start (Position.mkPos m.StartLine (m.StartColumn + displayName.Length))
else
m

let inline add m (typ: SemanticClassificationType) =
if duplicates.Add m then
results.Add(SemanticClassificationItem((m, typ)))
Expand All @@ -228,7 +260,19 @@ module TcResolutionsExtensions =
elif vref.IsPropertyGetterMethod || vref.IsPropertySetterMethod then
add m SemanticClassificationType.Property
elif vref.IsMember then
add m SemanticClassificationType.Method
// (#19905 item 1) Suppress wide-range Method classification produced by
// the parser-synthesized SynValSig for an F# delegate's `Invoke` abstract
// slot. Its idRange covers the whole `delegate of . -> .` clause, which
// would otherwise paint keywords/punctuation as if they were a method call.
let isSynthesizedDelegateInvoke =
let name = vref.LogicalName

(name = "Invoke" || name = "BeginInvoke" || name = "EndInvoke")
&& vref.IsDispatchSlot
&& vref.MemberApparentEntity.IsFSharpDelegateTycon

if not isSynthesizedDelegateInvoke then
add m SemanticClassificationType.Method
elif IsOperatorDisplayName vref.DisplayName then
add m SemanticClassificationType.Operator
else
Expand Down Expand Up @@ -269,29 +313,30 @@ module TcResolutionsExtensions =
else
add m SemanticClassificationType.RecordField

| Item.Property(info = pinfo :: _), _, m ->
| Item.Property(info = pinfo :: _) as item, _, m ->
if not pinfo.IsIndexer then
add m SemanticClassificationType.Property
add (narrowMethodOrPropertyRange item.DisplayNameCore m) SemanticClassificationType.Property

| Item.CtorGroup(_, minfos), _, m ->
| Item.CtorGroup(_, minfos) as item, _, m ->
let mNarrow = narrowCtorRange item.DisplayNameCore m
match minfos with
| [] -> add m SemanticClassificationType.ConstructorForReferenceType
| [] -> add mNarrow SemanticClassificationType.ConstructorForReferenceType
| _ ->
if
minfos
|> List.forall (fun minfo -> isDisposableTy g amap minfo.ApparentEnclosingType)
then
add m SemanticClassificationType.DisposableType
add mNarrow SemanticClassificationType.DisposableType
elif minfos |> List.forall (fun minfo -> isStructTy g minfo.ApparentEnclosingType) then
add m SemanticClassificationType.ConstructorForValueType
add mNarrow SemanticClassificationType.ConstructorForValueType
else
add m SemanticClassificationType.ConstructorForReferenceType
add mNarrow SemanticClassificationType.ConstructorForReferenceType

| Item.DelegateCtor _, _, m -> add m SemanticClassificationType.ConstructorForReferenceType

| Item.MethodGroup(_, minfos, _), _, m ->
| Item.MethodGroup(_, minfos, _) as item, _, m ->
match minfos with
| [] -> add m SemanticClassificationType.Method
| [] -> add (narrowMethodOrPropertyRange item.DisplayNameCore m) SemanticClassificationType.Method
| _ ->
let isSynthesizedDelegateMemberInDecl =
minfos
Expand All @@ -302,15 +347,17 @@ module TcResolutionsExtensions =
&& minfo.ApparentEnclosingTyconRef.IsFSharpDelegateTycon
&& rangeContainsRange minfo.ApparentEnclosingTyconRef.Range m)

let mNarrow = narrowMethodOrPropertyRange item.DisplayNameCore m

if isSynthesizedDelegateMemberInDecl then
()
elif
minfos
|> List.forall (fun minfo -> minfo.IsExtensionMember || minfo.IsCSharpStyleExtensionMember)
then
add m SemanticClassificationType.ExtensionMethod
add mNarrow SemanticClassificationType.ExtensionMethod
else
add m SemanticClassificationType.Method
add mNarrow SemanticClassificationType.Method

// Special case measures for struct types
| Item.Types(_, AppTy g (tyconRef, TType_measure _ :: _) :: _), LegitTypeOccurrence, m when
Expand Down
Loading
Loading