Skip to content

Commit

Permalink
Improve completion after method/property override (#17292)
Browse files Browse the repository at this point in the history
Co-authored-by: Vlad Zarytovskii <[email protected]>
  • Loading branch information
ijklam and vzarytovskii authored Jun 27, 2024
1 parent db21cf2 commit f6e24e8
Show file tree
Hide file tree
Showing 14 changed files with 874 additions and 145 deletions.
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@
* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16822](https://github.com/dotnet/fsharp/pull/16822))
* Use AsyncLocal instead of ThreadStatic to hold Cancellable.Token ([PR #17156](https://github.com/dotnet/fsharp/pull/17156))
* Showing and inserting correct name of entities from unopened namespace/module ([Issue #14375](https://github.com/dotnet/fsharp/issues/14375), [PR #17261](https://github.com/dotnet/fsharp/pull/17261))
* Improve completion after method/property override ([PR #17292](https://github.com/dotnet/fsharp/pull/17292))
* Support lazy custom attributes calculation for `ILTypeDef` public API, improve `ExtensionAttribute` presence detecting perf. ([PR #16168](https://github.com/dotnet/fsharp/pull/16168))
77 changes: 65 additions & 12 deletions src/Compiler/Checking/InfoReader.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ open FSharp.Compiler.TypeHierarchy
open FSharp.Compiler.TypeRelations

/// Use the given function to select some of the member values from the members of an F# type
let SelectImmediateMemberVals g optFilter f (tcref: TyconRef) =
let SelectImmediateMemberVals g optFilter f withExplicitImpl (tcref: TyconRef) =
let chooser (vref: ValRef) =
match vref.MemberInfo with
// The 'when' condition is a workaround for the fact that values providing
// override and interface implementations are published in inferred module types
// These cannot be selected directly via the "." notation.
// However, it certainly is useful to be able to publish these values, as we can in theory
// optimize code to make direct calls to these methods.
| Some membInfo when not (ValRefIsExplicitImpl g vref) ->
| Some membInfo when withExplicitImpl || not (ValRefIsExplicitImpl g vref) ->
f membInfo vref
| _ ->
None
Expand All @@ -53,7 +53,7 @@ let TrySelectMemberVal g optFilter ty pri _membInfo (vref: ValRef) =
else
None

let rec GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m origTy metadataTy =
let rec GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy metadataTy =

let minfos =
match metadataOfTy g metadataTy with
Expand All @@ -77,25 +77,28 @@ let rec GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m origTy
// In this case convert to the .NET Tuple type that carries metadata and try again
if isAnyTupleTy g metadataTy then
let betterMetadataTy = convertToTypeWithMetadataIfPossible g metadataTy
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m origTy betterMetadataTy
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy betterMetadataTy
// Function types support methods FSharpFunc<_, _>.FromConverter and friends from .NET metadata,
// but not instance methods (you can't write "f.Invoke(x)", you have to write "f x")
elif isFunTy g metadataTy then
let betterMetadataTy = convertToTypeWithMetadataIfPossible g metadataTy
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m origTy betterMetadataTy
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy betterMetadataTy
|> List.filter (fun minfo -> not minfo.IsInstance)
else
match tryTcrefOfAppTy g metadataTy with
| ValueNone -> []
| ValueSome tcref ->
SelectImmediateMemberVals g optFilter (TrySelectMemberVal g optFilter origTy None) tcref
SelectImmediateMemberVals g optFilter (TrySelectMemberVal g optFilter origTy None) withExplicitImpl tcref
let minfos = minfos |> List.filter (IsMethInfoAccessible amap m ad)
minfos

/// Query the immediate methods of an F# type, not taking into account inherited methods. The optFilter
/// parameter is an optional name to restrict the set of properties returned.
let GetImmediateIntrinsicMethInfosOfType (optFilter, ad) g amap m ty =
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m ty ty
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m false ty ty

let GetImmediateIntrinsicMethInfosWithExplicitImplOfType (optFilter, ad) g amap m ty =
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m true ty ty

/// Query the immediate methods of an F# type, not taking into account inherited methods. The optFilter
/// parameter is an optional name to restrict the set of properties returned.
Expand Down Expand Up @@ -185,7 +188,7 @@ type PropertyCollector(g, amap, m, ty, optFilter, ad) =

member _.Close() = [ for KeyValue(_, pinfo) in props -> pinfo ]

let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy metadataTy =
let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy metadataTy =

let pinfos =
match metadataOfTy g metadataTy with
Expand Down Expand Up @@ -216,22 +219,25 @@ let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy
// In this case convert to the .NET Tuple type that carries metadata and try again
if isAnyTupleTy g metadataTy || isFunTy g metadataTy then
let betterMetadataTy = convertToTypeWithMetadataIfPossible g metadataTy
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy betterMetadataTy
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy betterMetadataTy
else
match tryTcrefOfAppTy g metadataTy with
| ValueNone -> []
| ValueSome tcref ->
let propCollector = PropertyCollector(g, amap, m, origTy, optFilter, ad)
SelectImmediateMemberVals g None (fun membInfo vref -> propCollector.Collect(membInfo, vref); None) tcref |> ignore
SelectImmediateMemberVals g None (fun membInfo vref -> propCollector.Collect(membInfo, vref); None) withExplicitImpl tcref |> ignore
propCollector.Close()

let pinfos = pinfos |> List.filter (IsPropInfoAccessible g amap m ad)
pinfos

/// Query the immediate properties of an F# type, not taking into account inherited properties. The optFilter
/// parameter is an optional name to restrict the set of properties returned.
let rec GetImmediateIntrinsicPropInfosOfType (optFilter, ad) g amap m ty =
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m ty ty
let GetImmediateIntrinsicPropInfosOfType (optFilter, ad) g amap m ty =
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m false ty ty

let GetImmediateIntrinsicPropInfosWithExplicitImplOfType (optFilter, ad) g amap m ty =
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m true ty ty

// Checks whether the given type has an indexer property.
let IsIndexerType g amap ty =
Expand Down Expand Up @@ -655,6 +661,44 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this =
PropInfosEquivByNameAndSig EraseNone g amap m,
(fun pinfo -> pinfo.PropertyName))

//type A() =
// abstract E: int with get, set
// default val E = 0 with get
// Will get (A::E with get, A::E with get, set)
// -----
//type A() =
// member val A = 0 with get, set
//type B() =
// inherit A()
// static member val A = 0
// Will get (static B::A, None)
static let FilterOverridesOfPropInfosWithOverridenProp findFlag g amap m props =
let checkProp prop prop2 =
not(obj.ReferenceEquals(prop, prop2)) &&
PropInfosEquivByNameAndSig EraseNone g amap m prop prop2 &&
if prop.HasGetter && prop.HasSetter then false
elif prop.HasGetter then prop2.HasSetter
elif prop.HasSetter then prop2.HasGetter
else false

let rec findPropBefore prop hasMetTheProp =
function
| props :: t when hasMetTheProp ->
match props |> List.tryFind (checkProp prop) with
| Some p -> ValueSome p
| None -> findPropBefore prop true t
| props :: t ->
if props |> List.exists (fun i -> obj.ReferenceEquals(prop, i)) then
match props |> List.tryFind (checkProp prop) with
| Some p -> ValueSome p
| None -> findPropBefore prop true t
else findPropBefore prop false t
| _ -> ValueNone

props
|> FilterOverridesOfPropInfos findFlag g amap m
|> List.map (List.map (fun prop -> struct(prop, if findFlag = FindMemberFlag.IgnoreOverrides || prop.IsNewSlot then ValueNone else findPropBefore prop false props)))

/// Exclude methods from super types which have the same signature as a method in a more specific type.
static let ExcludeHiddenOfMethInfosImpl g amap m (minfos: MethInfo list list) =
minfos
Expand Down Expand Up @@ -905,6 +949,12 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this =
member infoReader.GetIntrinsicPropInfosOfType optFilter ad allowMultiIntfInst findFlag m ty =
infoReader.GetIntrinsicPropInfoSetsOfType optFilter ad allowMultiIntfInst findFlag m ty |> List.concat

/// Get the flattened list of intrinsic properties in the hierarchy
member infoReader.GetIntrinsicPropInfoWithOverriddenPropOfType optFilter ad allowMultiIntfInst findFlag m ty =
infoReader.GetRawIntrinsicPropertySetsOfType(optFilter, ad, allowMultiIntfInst, m, ty)
|> FilterOverridesOfPropInfosWithOverridenProp findFlag infoReader.g infoReader.amap m
|> List.concat

member _.GetTraitInfosInType optFilter ty =
GetImmediateTraitsInfosOfType optFilter g ty

Expand Down Expand Up @@ -958,6 +1008,9 @@ let GetIntrinsicMethInfosOfType (infoReader: InfoReader) optFilter ad allowMulti
let GetIntrinsicPropInfosOfType (infoReader: InfoReader) optFilter ad allowMultiIntfInst findFlag m ty =
infoReader.GetIntrinsicPropInfosOfType optFilter ad allowMultiIntfInst findFlag m ty

let GetIntrinsicPropInfoWithOverriddenPropOfType (infoReader: InfoReader) optFilter ad allowMultiIntfInst findFlag m ty =
infoReader.GetIntrinsicPropInfoWithOverriddenPropOfType optFilter ad allowMultiIntfInst findFlag m ty

let TryFindIntrinsicNamedItemOfType (infoReader: InfoReader) (nm, ad, includeConstraints) findFlag m ty =
infoReader.TryFindIntrinsicNamedItemOfType (nm, ad, includeConstraints) findFlag m ty

Expand Down
31 changes: 31 additions & 0 deletions src/Compiler/Checking/InfoReader.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ val GetImmediateIntrinsicMethInfosOfType:
ty: TType ->
MethInfo list

/// Query the immediate methods of an F# type, not taking into account inherited methods. The optFilter
/// parameter is an optional name to restrict the set of properties returned.
val GetImmediateIntrinsicMethInfosWithExplicitImplOfType:
optFilter: string option * ad: AccessorDomain ->
g: TcGlobals ->
amap: ImportMap ->
m: range ->
ty: TType ->
MethInfo list

/// A helper type to help collect properties.
///
/// Join up getters and setters which are not associated in the F# data structure
Expand All @@ -55,6 +65,16 @@ val GetImmediateIntrinsicPropInfosOfType:
ty: TType ->
PropInfo list

/// Query the immediate properties of an F# type, not taking into account inherited properties. The optFilter
/// parameter is an optional name to restrict the set of properties returned.
val GetImmediateIntrinsicPropInfosWithExplicitImplOfType:
optFilter: string option * ad: AccessorDomain ->
g: TcGlobals ->
amap: ImportMap ->
m: range ->
ty: TType ->
PropInfo list

/// Checks whether the given type has an indexer property.
val IsIndexerType: g: TcGlobals -> amap: ImportMap -> ty: TType -> bool

Expand Down Expand Up @@ -261,6 +281,17 @@ val GetIntrinsicPropInfosOfType:
ty: TType ->
PropInfo list

/// Get the flattened list of intrinsic properties in the hierarchy. If the PropInfo is get-only or set-only, try to find its setter or getter from the hierarchy.
val GetIntrinsicPropInfoWithOverriddenPropOfType:
infoReader: InfoReader ->
optFilter: string option ->
ad: AccessorDomain ->
allowMultiIntfInst: AllowMultiIntfInstantiations ->
findFlag: FindMemberFlag ->
m: range ->
ty: TType ->
struct (PropInfo * PropInfo voption) list

/// Perform type-directed name resolution of a particular named member in an F# type
val TryFindIntrinsicNamedItemOfType:
infoReader: InfoReader ->
Expand Down
Loading

0 comments on commit f6e24e8

Please sign in to comment.