Skip to content

Commit f6e24e8

Browse files
ijklamvzarytovskii
andauthored
Improve completion after method/property override (#17292)
Co-authored-by: Vlad Zarytovskii <vzaritovsky@hotmail.com>
1 parent db21cf2 commit f6e24e8

File tree

14 files changed

+874
-145
lines changed

14 files changed

+874
-145
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@
3535
* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16822](https://github.com/dotnet/fsharp/pull/16822))
3636
* Use AsyncLocal instead of ThreadStatic to hold Cancellable.Token ([PR #17156](https://github.com/dotnet/fsharp/pull/17156))
3737
* 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))
38+
* Improve completion after method/property override ([PR #17292](https://github.com/dotnet/fsharp/pull/17292))
3839
* Support lazy custom attributes calculation for `ILTypeDef` public API, improve `ExtensionAttribute` presence detecting perf. ([PR #16168](https://github.com/dotnet/fsharp/pull/16168))

src/Compiler/Checking/InfoReader.fs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ open FSharp.Compiler.TypeHierarchy
2626
open FSharp.Compiler.TypeRelations
2727

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

56-
let rec GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m origTy metadataTy =
56+
let rec GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy metadataTy =
5757

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

9595
/// Query the immediate methods of an F# type, not taking into account inherited methods. The optFilter
9696
/// parameter is an optional name to restrict the set of properties returned.
9797
let GetImmediateIntrinsicMethInfosOfType (optFilter, ad) g amap m ty =
98-
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m ty ty
98+
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m false ty ty
99+
100+
let GetImmediateIntrinsicMethInfosWithExplicitImplOfType (optFilter, ad) g amap m ty =
101+
GetImmediateIntrinsicMethInfosOfTypeAux (optFilter, ad) g amap m true ty ty
99102

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

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

188-
let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy metadataTy =
191+
let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy metadataTy =
189192

190193
let pinfos =
191194
match metadataOfTy g metadataTy with
@@ -216,22 +219,25 @@ let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy
216219
// In this case convert to the .NET Tuple type that carries metadata and try again
217220
if isAnyTupleTy g metadataTy || isFunTy g metadataTy then
218221
let betterMetadataTy = convertToTypeWithMetadataIfPossible g metadataTy
219-
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy betterMetadataTy
222+
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m withExplicitImpl origTy betterMetadataTy
220223
else
221224
match tryTcrefOfAppTy g metadataTy with
222225
| ValueNone -> []
223226
| ValueSome tcref ->
224227
let propCollector = PropertyCollector(g, amap, m, origTy, optFilter, ad)
225-
SelectImmediateMemberVals g None (fun membInfo vref -> propCollector.Collect(membInfo, vref); None) tcref |> ignore
228+
SelectImmediateMemberVals g None (fun membInfo vref -> propCollector.Collect(membInfo, vref); None) withExplicitImpl tcref |> ignore
226229
propCollector.Close()
227230

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

231234
/// Query the immediate properties of an F# type, not taking into account inherited properties. The optFilter
232235
/// parameter is an optional name to restrict the set of properties returned.
233-
let rec GetImmediateIntrinsicPropInfosOfType (optFilter, ad) g amap m ty =
234-
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m ty ty
236+
let GetImmediateIntrinsicPropInfosOfType (optFilter, ad) g amap m ty =
237+
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m false ty ty
238+
239+
let GetImmediateIntrinsicPropInfosWithExplicitImplOfType (optFilter, ad) g amap m ty =
240+
GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m true ty ty
235241

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

664+
//type A() =
665+
// abstract E: int with get, set
666+
// default val E = 0 with get
667+
// Will get (A::E with get, A::E with get, set)
668+
// -----
669+
//type A() =
670+
// member val A = 0 with get, set
671+
//type B() =
672+
// inherit A()
673+
// static member val A = 0
674+
// Will get (static B::A, None)
675+
static let FilterOverridesOfPropInfosWithOverridenProp findFlag g amap m props =
676+
let checkProp prop prop2 =
677+
not(obj.ReferenceEquals(prop, prop2)) &&
678+
PropInfosEquivByNameAndSig EraseNone g amap m prop prop2 &&
679+
if prop.HasGetter && prop.HasSetter then false
680+
elif prop.HasGetter then prop2.HasSetter
681+
elif prop.HasSetter then prop2.HasGetter
682+
else false
683+
684+
let rec findPropBefore prop hasMetTheProp =
685+
function
686+
| props :: t when hasMetTheProp ->
687+
match props |> List.tryFind (checkProp prop) with
688+
| Some p -> ValueSome p
689+
| None -> findPropBefore prop true t
690+
| props :: t ->
691+
if props |> List.exists (fun i -> obj.ReferenceEquals(prop, i)) then
692+
match props |> List.tryFind (checkProp prop) with
693+
| Some p -> ValueSome p
694+
| None -> findPropBefore prop true t
695+
else findPropBefore prop false t
696+
| _ -> ValueNone
697+
698+
props
699+
|> FilterOverridesOfPropInfos findFlag g amap m
700+
|> List.map (List.map (fun prop -> struct(prop, if findFlag = FindMemberFlag.IgnoreOverrides || prop.IsNewSlot then ValueNone else findPropBefore prop false props)))
701+
658702
/// Exclude methods from super types which have the same signature as a method in a more specific type.
659703
static let ExcludeHiddenOfMethInfosImpl g amap m (minfos: MethInfo list list) =
660704
minfos
@@ -905,6 +949,12 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this =
905949
member infoReader.GetIntrinsicPropInfosOfType optFilter ad allowMultiIntfInst findFlag m ty =
906950
infoReader.GetIntrinsicPropInfoSetsOfType optFilter ad allowMultiIntfInst findFlag m ty |> List.concat
907951

952+
/// Get the flattened list of intrinsic properties in the hierarchy
953+
member infoReader.GetIntrinsicPropInfoWithOverriddenPropOfType optFilter ad allowMultiIntfInst findFlag m ty =
954+
infoReader.GetRawIntrinsicPropertySetsOfType(optFilter, ad, allowMultiIntfInst, m, ty)
955+
|> FilterOverridesOfPropInfosWithOverridenProp findFlag infoReader.g infoReader.amap m
956+
|> List.concat
957+
908958
member _.GetTraitInfosInType optFilter ty =
909959
GetImmediateTraitsInfosOfType optFilter g ty
910960

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

1011+
let GetIntrinsicPropInfoWithOverriddenPropOfType (infoReader: InfoReader) optFilter ad allowMultiIntfInst findFlag m ty =
1012+
infoReader.GetIntrinsicPropInfoWithOverriddenPropOfType optFilter ad allowMultiIntfInst findFlag m ty
1013+
9611014
let TryFindIntrinsicNamedItemOfType (infoReader: InfoReader) (nm, ad, includeConstraints) findFlag m ty =
9621015
infoReader.TryFindIntrinsicNamedItemOfType (nm, ad, includeConstraints) findFlag m ty
9631016

src/Compiler/Checking/InfoReader.fsi

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ val GetImmediateIntrinsicMethInfosOfType:
3434
ty: TType ->
3535
MethInfo list
3636

37+
/// Query the immediate methods of an F# type, not taking into account inherited methods. The optFilter
38+
/// parameter is an optional name to restrict the set of properties returned.
39+
val GetImmediateIntrinsicMethInfosWithExplicitImplOfType:
40+
optFilter: string option * ad: AccessorDomain ->
41+
g: TcGlobals ->
42+
amap: ImportMap ->
43+
m: range ->
44+
ty: TType ->
45+
MethInfo list
46+
3747
/// A helper type to help collect properties.
3848
///
3949
/// Join up getters and setters which are not associated in the F# data structure
@@ -55,6 +65,16 @@ val GetImmediateIntrinsicPropInfosOfType:
5565
ty: TType ->
5666
PropInfo list
5767

68+
/// Query the immediate properties of an F# type, not taking into account inherited properties. The optFilter
69+
/// parameter is an optional name to restrict the set of properties returned.
70+
val GetImmediateIntrinsicPropInfosWithExplicitImplOfType:
71+
optFilter: string option * ad: AccessorDomain ->
72+
g: TcGlobals ->
73+
amap: ImportMap ->
74+
m: range ->
75+
ty: TType ->
76+
PropInfo list
77+
5878
/// Checks whether the given type has an indexer property.
5979
val IsIndexerType: g: TcGlobals -> amap: ImportMap -> ty: TType -> bool
6080

@@ -261,6 +281,17 @@ val GetIntrinsicPropInfosOfType:
261281
ty: TType ->
262282
PropInfo list
263283

284+
/// 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.
285+
val GetIntrinsicPropInfoWithOverriddenPropOfType:
286+
infoReader: InfoReader ->
287+
optFilter: string option ->
288+
ad: AccessorDomain ->
289+
allowMultiIntfInst: AllowMultiIntfInstantiations ->
290+
findFlag: FindMemberFlag ->
291+
m: range ->
292+
ty: TType ->
293+
struct (PropInfo * PropInfo voption) list
294+
264295
/// Perform type-directed name resolution of a particular named member in an F# type
265296
val TryFindIntrinsicNamedItemOfType:
266297
infoReader: InfoReader ->

0 commit comments

Comments
 (0)