diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index 12a54d572fc..471fb842ab5 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -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)) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index f81d89a900a..3a36122e89e 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -26,7 +26,7 @@ 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 @@ -34,7 +34,7 @@ let SelectImmediateMemberVals g optFilter f (tcref: TyconRef) = // 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 @@ -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 @@ -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. @@ -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 @@ -216,13 +219,13 @@ 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) @@ -230,8 +233,11 @@ let rec GetImmediateIntrinsicPropInfosOfTypeAux (optFilter, ad) g amap m origTy /// 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 = @@ -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 @@ -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 @@ -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 diff --git a/src/Compiler/Checking/InfoReader.fsi b/src/Compiler/Checking/InfoReader.fsi index 3e8ceb927ca..6638c1c39e7 100644 --- a/src/Compiler/Checking/InfoReader.fsi +++ b/src/Compiler/Checking/InfoReader.fsi @@ -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 @@ -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 @@ -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 -> diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 46ea4bce4f5..cab12da5cdf 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -579,6 +579,71 @@ type internal TypeCheckInfo Some(StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)) | _, _ -> None + /// Build a CompletionItem + let CompletionItemWithMoreSetting + (ty: TyconRef voption) + (assemblySymbol: AssemblySymbol voption) + minorPriority + insertText + displayText + (item: ItemWithInst) + = + let kind = + match item.Item with + | Item.DelegateCtor _ + | Item.CtorGroup _ -> CompletionItemKind.Method false + | Item.MethodGroup(_, minfos, _) -> + match minfos with + | [] -> CompletionItemKind.Method false + | minfo :: _ -> CompletionItemKind.Method minfo.IsExtensionMember + | Item.AnonRecdField _ + | Item.RecdField _ + | Item.UnionCaseField _ + | Item.Property _ -> CompletionItemKind.Property + | Item.Event _ -> CompletionItemKind.Event + | Item.ILField _ + | Item.Value _ -> CompletionItemKind.Field + | Item.CustomOperation _ -> CompletionItemKind.CustomOperation + // These items are not given a completion kind. This could be reviewed + | Item.ActivePatternResult _ + | Item.ExnCase _ + | Item.ImplicitOp _ + | Item.ModuleOrNamespaces _ + | Item.Trait _ + | Item.TypeVar _ + | Item.Types _ + | Item.UnionCase _ + | Item.UnqualifiedType _ + | Item.NewDef _ + | Item.SetterArg _ + | Item.CustomBuilder _ + | Item.OtherName _ + | Item.ActivePatternCase _ -> CompletionItemKind.Other + + let isUnresolved = + match assemblySymbol with + | ValueSome x -> Some x.UnresolvedSymbol + | _ -> None + + let ty = + match ty with + | ValueSome x -> Some x + | _ -> None + + { + ItemWithInst = item + MinorPriority = minorPriority + Kind = kind + IsOwnMember = false + Type = ty + Unresolved = isUnresolved + CustomInsertText = insertText + CustomDisplayText = displayText + } + + let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) (item: ItemWithInst) = + CompletionItemWithMoreSetting ty assemblySymbol 0 ValueNone ValueNone item + let CollectParameters (methods: MethInfo list) amap m : Item list = methods |> List.collect (fun meth -> @@ -898,59 +963,6 @@ type internal TypeCheckInfo if p >= 0 then Some p else None - /// Build a CompetionItem - let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) (item: ItemWithInst) = - let kind = - match item.Item with - | Item.DelegateCtor _ - | Item.CtorGroup _ -> CompletionItemKind.Method false - | Item.MethodGroup(_, minfos, _) -> - match minfos with - | [] -> CompletionItemKind.Method false - | minfo :: _ -> CompletionItemKind.Method minfo.IsExtensionMember - | Item.AnonRecdField _ - | Item.RecdField _ - | Item.UnionCaseField _ - | Item.Property _ -> CompletionItemKind.Property - | Item.Event _ -> CompletionItemKind.Event - | Item.ILField _ - | Item.Value _ -> CompletionItemKind.Field - | Item.CustomOperation _ -> CompletionItemKind.CustomOperation - // These items are not given a completion kind. This could be reviewed - | Item.ActivePatternResult _ - | Item.ExnCase _ - | Item.ImplicitOp _ - | Item.ModuleOrNamespaces _ - | Item.Trait _ - | Item.TypeVar _ - | Item.Types _ - | Item.UnionCase _ - | Item.UnqualifiedType _ - | Item.NewDef _ - | Item.SetterArg _ - | Item.CustomBuilder _ - | Item.OtherName _ - | Item.ActivePatternCase _ -> CompletionItemKind.Other - - let isUnresolved = - match assemblySymbol with - | ValueSome x -> Some x.UnresolvedSymbol - | _ -> None - - let ty = - match ty with - | ValueSome x -> Some x - | _ -> None - - { - ItemWithInst = item - MinorPriority = 0 - Kind = kind - IsOwnMember = false - Type = ty - Unresolved = isUnresolved - } - let DefaultCompletionItem item = CompletionItem ValueNone ValueNone item let CompletionItemSuggestedName displayName = @@ -961,6 +973,8 @@ type internal TypeCheckInfo Kind = CompletionItemKind.SuggestedName IsOwnMember = false Unresolved = None + CustomInsertText = ValueNone + CustomDisplayText = ValueNone } let getItem (x: ItemWithInst) = x.Item @@ -1045,45 +1059,340 @@ type internal TypeCheckInfo |> Option.defaultValue completions /// Gets all methods that a type can override, but has not yet done so. - let GetOverridableMethods pos typeNameRange = - let isMethodOverridable alreadyOverridden (candidate: MethInfo) = + let GetOverridableMethods pos ctx (typeNameRange: range) spacesBeforeOverrideKeyword hasThis isStatic = + let checkImplementedSlotDeclareType ty slots = + slots + |> Option.map (List.exists (fun (TSlotSig(declaringType = ty2)) -> typeEquiv g ty ty2)) + |> Option.defaultValue false + + let isMethodOverridable superTy alreadyOverridden (candidate: MethInfo) = not candidate.IsFinal && not ( alreadyOverridden - |> List.exists (MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate) + |> ResizeArray.exists (fun i -> + MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate i + && (tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef + || checkImplementedSlotDeclareType superTy (Option.attempt (fun () -> i.ImplementedSlotSignatures)))) ) + let isMethodOptionOverridable superTy alreadyOverridden candidate = + candidate + |> ValueOption.map (fun i -> isMethodOverridable superTy alreadyOverridden i) + |> ValueOption.defaultValue false + + let isPropertyOverridable superTy alreadyOverridden (candidate: PropInfo) = + if candidate.IsVirtualProperty then + let getterOverridden, setterOverridden = + alreadyOverridden + |> List.filter (fun i -> + PropInfosEquivByNameAndSig EraseNone g amap range0 candidate i + && (tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef + || checkImplementedSlotDeclareType superTy (Option.attempt (fun () -> i.ImplementedSlotSignatures)))) + |> List.fold + (fun (getterOverridden, setterOverridden) i -> getterOverridden || i.HasGetter, setterOverridden || i.HasSetter) + (false, false) + + not getterOverridden, not setterOverridden + else + false, false + + let rec checkOrGenerateArgName (nameSet: HashSet<_>) name = + let name = if String.IsNullOrEmpty name then "arg" else name + + if nameSet.Add(name) then + name + else + checkOrGenerateArgName nameSet $"{name}_{nameSet.Count}" + let (nenv, ad), m = GetBestEnvForPos pos + let denv = nenv.DisplayEnv - sResolutions.CapturedNameResolutions - |> ResizeArray.tryPick (fun r -> - match r.Item with - | Item.Types(_, ty :: _) when equals r.Range typeNameRange && isAppTy g ty -> - let superTy = - (tcrefOfAppTy g ty).TypeContents.tcaug_super |> Option.defaultValue g.obj_ty + let checkMethAbstractAndGetImplementBody (meth: MethInfo) implementBody = + if meth.IsAbstract then + if nenv.DisplayEnv.openTopPathsSorted.Force() |> List.contains [ "System" ] then + "raise (NotImplementedException())" + else + "raise (System.NotImplementedException())" + else + implementBody + + let newlineIndent = + Environment.NewLine + String.make (spacesBeforeOverrideKeyword + 4) ' ' + + let getOverridableMethods superTy (overriddenMethods: MethInfo list) overriddenProperties = + // Do not check a method with same name twice + //type AA() = + // abstract a: unit -> unit + // default _.a() = printfn "A" + //type BB() = + // inherit AA() + // member _.a() = printfn "B" (* This method covered the `AA.a` *) + //type CC() = + // inherit BB() + // override | (* Here should not suggest to override `AA.a` *) + let checkedMethods = ResizeArray(overriddenMethods) + + let isInterface = isInterfaceTy g superTy + + // reuse between props and methods + let argNames = HashSet() + + let overridableProps = + let generatePropertyOverrideBody (prop: PropInfo) getterMeth setterMeth = + argNames.Clear() + + let parameters = + prop.GetParamNamesAndTypes(amap, m) + |> List.map (fun (ParamNameAndType(name, ty)) -> + let name = + name + |> Option.map _.idText + |> Option.defaultValue String.Empty + |> checkOrGenerateArgName argNames + + $"{name}: {stringOfTy denv ty}") + |> String.concat ", " + + let retTy = prop.GetPropertyType(amap, m) + let retTy = stringOfTy denv retTy + + let getter, getterWithBody = + match getterMeth with + | ValueSome meth -> + let implementBody = + checkMethAbstractAndGetImplementBody + meth + ($"base.{prop.DisplayName}" + (if prop.IsIndexer then $"({parameters})" else "")) + + let getter = $"get ({parameters}): {retTy}" + getter, $"{getter} = {implementBody}" + | _ -> String.Empty, String.Empty + + let setter, setterWithBody = + match setterMeth with + | ValueSome meth -> + let argValue = checkOrGenerateArgName argNames "value" + + let implementBody = + checkMethAbstractAndGetImplementBody + meth + ($"base.{prop.DisplayName}" + + (if prop.IsIndexer then $"({parameters})" else String.Empty) + + $" <- {argValue}") + + let parameters = if prop.IsIndexer then $"({parameters}) " else String.Empty + let setter = $"set {parameters}({argValue}: {retTy})" + setter, $"{setter} = {implementBody}" + | _ -> String.Empty, String.Empty + + let keywordAnd = + if getterMeth.IsNone || setterMeth.IsNone then + String.Empty + else + " and " + + let this = if hasThis || prop.IsStatic then String.Empty else "this." + + let getterWithBody = + if String.IsNullOrWhiteSpace getterWithBody then + String.Empty + else + getterWithBody + newlineIndent + + let name = $"{prop.DisplayName} with {getter}{keywordAnd}{setter}" + + let textInCode = + this + + prop.DisplayName + + newlineIndent + + "with " + + getterWithBody + + keywordAnd + + setterWithBody + + name, textInCode + + GetIntrinsicPropInfoWithOverriddenPropOfType + infoReader + None + ad + TypeHierarchy.AllowMultiIntfInstantiations.No + FindMemberFlag.PreferOverrides + range0 + superTy + |> List.choose (fun struct (prop, baseProp) -> + let getterMeth = + if prop.HasGetter then + ValueSome prop.GetterMethod + else + baseProp |> ValueOption.map _.GetterMethod + + let setterMeth = + if prop.HasSetter then + ValueSome prop.SetterMethod + else + baseProp |> ValueOption.map _.SetterMethod + + let isGetterOverridable, isSetterOverridable = + isPropertyOverridable superTy overriddenProperties prop + + let isGetterOverridable = + isGetterOverridable + && isMethodOptionOverridable superTy checkedMethods getterMeth - let overriddenMethods = - GetImmediateIntrinsicMethInfosOfType (None, ad) g amap typeNameRange ty - |> List.filter (fun x -> x.IsDefiniteFSharpOverride) + let isSetterOverridable = + isSetterOverridable + && isMethodOptionOverridable superTy checkedMethods setterMeth - let overridableMethods = - GetIntrinsicMethInfosOfType - infoReader + let canPick = + prop.IsStatic = isStatic && (isGetterOverridable || isSetterOverridable) + + getterMeth |> ValueOption.iter checkedMethods.Add + setterMeth |> ValueOption.iter checkedMethods.Add + + if not canPick then None - ad - TypeHierarchy.AllowMultiIntfInstantiations.No - FindMemberFlag.PreferOverrides - range0 - superTy - |> List.filter (isMethodOverridable overriddenMethods) - |> List.groupBy (fun x -> x.DisplayName) - |> List.map (fun (name, overloads) -> - Item.MethodGroup(name, overloads, None) + else + let getterMeth = if isGetterOverridable then getterMeth else ValueNone + let setterMeth = if isSetterOverridable then setterMeth else ValueNone + let name, textInCode = generatePropertyOverrideBody prop getterMeth setterMeth + + Item.Property( + name, + [ + prop + if baseProp.IsSome then + baseProp.Value + ], + None + ) |> ItemWithNoInst - |> DefaultCompletionItem) + |> CompletionItemWithMoreSetting ValueNone ValueNone -1 (ValueSome textInCode) (ValueSome name) + |> Some) - Some(overridableMethods, nenv.DisplayEnv, m) - | _ -> None) + let overridableMeths = + let generateMethodOverrideBody (meth: MethInfo) = + argNames.Clear() + + let parameters = + meth.GetParamNames() + |> List.zip (meth.GetParamTypes(amap, m, meth.FormalMethodInst)) + |> List.map (fun (types, names) -> + let names = + names + |> List.zip types + |> List.map (fun (ty, name) -> + let name = + name |> Option.defaultValue String.Empty |> checkOrGenerateArgName argNames + + $"{name}: {stringOfTy denv ty}") + |> String.concat ", " + + $"({names})") + |> String.concat " " + + let retTy = meth.GetFSharpReturnType(amap, m, meth.FormalMethodInst) + + let name = $"{meth.DisplayName} {parameters}: {stringOfTy denv retTy}" + + let textInCode = + let nameWithThis = + if hasThis || not meth.IsInstance then + $"{name} = " + else + $"this.{name} = " + + let implementBody = + checkMethAbstractAndGetImplementBody meth $"base.{meth.DisplayName}{parameters}" + + nameWithThis + newlineIndent + implementBody + + name, textInCode + + GetIntrinsicMethInfosOfType + infoReader + None + ad + TypeHierarchy.AllowMultiIntfInstantiations.No + FindMemberFlag.PreferOverrides + range0 + superTy + |> List.choose (fun meth -> + let canPick = + meth.IsInstance <> isStatic + && isMethodOverridable superTy checkedMethods meth + && (not isInterface + || not (tyconRefEq g meth.DeclaringTyconRef g.system_Object_tcref)) + + checkedMethods.Add meth + + if not canPick then + None + else + let name, textInCode = generateMethodOverrideBody meth + + Item.MethodGroup(name, [ meth ], None) + |> ItemWithNoInst + |> CompletionItemWithMoreSetting ValueNone ValueNone -1 (ValueSome textInCode) (ValueSome name) + |> Some) + + overridableProps @ overridableMeths + + let getTyFromTypeNamePos (endPos: pos) = + let nameResItems = + GetPreciseItemsFromNameResolution(endPos.Line, endPos.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes) + + match nameResItems with + | NameResResult.Members(ls, _, _) -> + ls + |> List.tryPick (function + | { Item = Item.Types(_, ty :: _) } -> Some ty + | _ -> None) + | _ -> None + + let ctx = + match ctx with + | MethodOverrideCompletionContext.Class -> + sResolutions.CapturedNameResolutions + |> ResizeArray.tryPick (fun r -> + match r.Item with + | Item.Types(_, ty :: _) when equals r.Range typeNameRange && isAppTy g ty -> + let superTy = + (tcrefOfAppTy g ty).TypeContents.tcaug_super |> Option.defaultValue g.obj_ty + + Some(ty, superTy) + | _ -> None) + + | MethodOverrideCompletionContext.Interface mTy -> + sResolutions.CapturedNameResolutions + |> ResizeArray.tryPick (fun r -> + match r.Item with + | Item.Types(_, ty :: _) when equals r.Range typeNameRange && isAppTy g ty -> + let superTy = getTyFromTypeNamePos mTy.End |> Option.defaultValue g.obj_ty + Some(ty, superTy) + | _ -> None) + | MethodOverrideCompletionContext.ObjExpr m -> + let _, quals = GetExprTypingForPosition(m.End) + + quals + |> Array.tryFind (fun (_, _, _, r) -> posEq m.Start r.Start) + |> Option.map (fun (ty, _, _, _) -> ty, getTyFromTypeNamePos typeNameRange.End |> Option.defaultValue g.obj_ty) + + match ctx with + | Some(ty, superTy) -> + let overriddenMethods = + GetImmediateIntrinsicMethInfosWithExplicitImplOfType (None, ad) g amap typeNameRange ty + |> List.filter (fun x -> x.IsDefiniteFSharpOverride) + + let overriddenProperties = + GetImmediateIntrinsicPropInfosWithExplicitImplOfType (None, ad) g amap typeNameRange ty + |> List.filter (fun x -> x.IsDefiniteFSharpOverride) + + let overridableMethods = + getOverridableMethods superTy overriddenMethods overriddenProperties + + Some(overridableMethods, denv, m) + | _ -> None /// Gets all field identifiers of a union case that can be referred to in a pattern. let GetUnionCaseFields caseIdRange alreadyReferencedFields = @@ -1579,6 +1888,8 @@ type internal TypeCheckInfo IsOwnMember = false Type = None Unresolved = None + CustomInsertText = ValueNone + CustomDisplayText = ValueNone }) match declaredItems with @@ -1641,7 +1952,8 @@ type internal TypeCheckInfo getDeclaredItemsNotInRangeOpWithAllSymbols () |> Option.bind (FilterRelevantItemsBy getItem2 None IsPatternCandidate) - | Some(CompletionContext.MethodOverride enclosingTypeNameRange) -> GetOverridableMethods pos enclosingTypeNameRange + | Some(CompletionContext.MethodOverride(ctx, enclosingTypeNameRange, spacesBeforeOverrideKeyword, hasThis, isStatic)) -> + GetOverridableMethods pos ctx enclosingTypeNameRange spacesBeforeOverrideKeyword hasThis isStatic // Other completions | cc -> diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index 14b506bff86..dd6727a5ba9 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -86,7 +86,9 @@ type CompletionItem = IsOwnMember: bool MinorPriority: int Type: TyconRef option - Unresolved: UnresolvedSymbol option } + Unresolved: UnresolvedSymbol option + CustomInsertText: string voption + CustomDisplayText: string voption } member x.Item = x.ItemWithInst.Item [] @@ -1125,19 +1127,23 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i match u.Namespace with | [||] -> u.DisplayName | ns -> (ns |> String.concat ".") + "." + u.DisplayName - | None -> x.Item.DisplayName) + | None when x.CustomDisplayText.IsSome -> x.CustomDisplayText.Value + | None -> x.Item.DisplayName + ) |> List.map ( let textInDeclList item = match item.Unresolved with | Some u -> u.DisplayName + | None when item.CustomDisplayText.IsSome -> item.CustomDisplayText.Value | None -> item.Item.DisplayNameCore let textInCode (item: CompletionItem) = match item.Item with | Item.TypeVar (name, typar) -> (if typar.StaticReq = Syntax.TyparStaticReq.None then "'" else " ^") + name | _ -> - match item.Unresolved with - | Some u -> u.DisplayName - | None -> item.Item.DisplayName + match item.Unresolved, item.CustomInsertText with + | Some u, _ -> u.DisplayName + | None, ValueSome textInCode -> textInCode + | None, _ -> item.Item.DisplayName if not supportsPreferExtsMethodsOverProperty then // we don't pay the cost of filtering specific to RFC-1137 // nor risk a change in behaviour for the intellisense item list diff --git a/src/Compiler/Service/ServiceDeclarationLists.fsi b/src/Compiler/Service/ServiceDeclarationLists.fsi index 4611ff86a16..3aeaa11112d 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -91,6 +91,9 @@ type internal CompletionItem = Type: TyconRef option Unresolved: UnresolvedSymbol option + + CustomInsertText: string voption + CustomDisplayText: string voption } member Item: Item diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index c395c9422d2..42a461db90d 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -599,9 +599,16 @@ module SyntaxTraversal = | _ -> () for b in binds do yield dive b b.RangeOfBindingWithRhs (traverseSynBinding path) - for SynInterfaceImpl(bindings = binds) in ifaces do + for SynInterfaceImpl(ty, withKeyword, binds, members, range) in ifaces do + let path = + SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(ty, withKeyword, Some members, range)) + :: path + for b in binds do yield dive b b.RangeOfBindingWithRhs (traverseSynBinding path) + + for m in members do + yield dive m m.Range (traverseSynMemberDefn path (fun _ -> None)) ] |> pick expr @@ -984,6 +991,7 @@ module SyntaxTraversal = x |> normalizeMembersToDealWithPeculiaritiesOfGettersAndSetters path (fun _ -> None) ] + |> pick x | ok -> ok | SynMemberDefn.Inherit(synType, _identOption, range) -> traverseInherit (synType, range) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 3f379c0f8b6..0f6f3bc0530 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -70,6 +70,12 @@ type PatternContext = /// Any other position in a pattern that does not need special handling | Other +[] +type MethodOverrideCompletionContext = + | Class + | Interface of mInterfaceName: range + | ObjExpr of mExpr: range + [] type CompletionContext = /// Completion context cannot be determined due to errors @@ -107,7 +113,12 @@ type CompletionContext = | Pattern of context: PatternContext /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of enclosingTypeNameRange: range + | MethodOverride of + ctx: MethodOverrideCompletionContext * + enclosingTypeNameRange: range * + spacesBeforeOverrideKeyword: int * + hasThis: bool * + isStatic: bool type ShortIdent = string @@ -1076,6 +1087,20 @@ module ParsedInput = | Operator "op_Equality" (SynExpr.Ident id, _) -> Some id | _ -> None + let posAfterRangeAndBetweenSpaces (lineStr: string) (m: range) pos = + let rec loop max i = + if i >= lineStr.Length || i >= max then true + elif Char.IsWhiteSpace lineStr[i] then loop max (i + 1) + else false + + posGt pos m.End && pos.Line = m.End.Line && loop pos.Column m.End.Column + + let rangeContainsPosOrIsSpacesBetweenRangeAndPos (lineStr: string) m pos = + rangeContainsPos m pos + // pos is before m + || posLt pos m.Start + || posAfterRangeAndBetweenSpaces lineStr m pos + let findSetters argList = match argList with | SynExpr.Paren(SynExpr.Tuple(false, parameters, _, _), _, _, _) -> @@ -1434,6 +1459,12 @@ module ParsedInput = |> List.tryPick (fun pat -> TryGetCompletionContextInPattern true pat None pos) |> Option.orElseWith (fun () -> defaultTraverse expr) + // { new | } + | SynExpr.ComputationExpr(expr = SynExpr.ArbitraryAfterError _) when + lineStr.Trim().Split(' ') |> Array.contains "new" + -> + Some(CompletionContext.Inherit(InheritanceContext.Unknown, ([], None))) + | _ -> defaultTraverse expr member _.VisitRecordField(path, copyOpt, field) = @@ -1491,36 +1522,123 @@ module ParsedInput = (SynBinding(headPat = headPat; trivia = trivia; returnInfo = returnInfo) as synBinding) ) = - let isOverride leadingKeyword = + let isOverrideOrMember leadingKeyword = + match leadingKeyword with + | SynLeadingKeyword.Override _ + | SynLeadingKeyword.Member _ -> true + | _ -> false + + let isStaticMember leadingKeyword = + match leadingKeyword with + | SynLeadingKeyword.StaticMember _ -> true + | _ -> false + + let isMember leadingKeyword = match leadingKeyword with - | SynLeadingKeyword.Override _ -> true + | SynLeadingKeyword.Member _ -> true | _ -> false - let overrideContext path = + let overrideContext path (mOverride: range) hasThis isStatic isMember = match path with - | _ :: SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ -> - Some(CompletionContext.MethodOverride enclosingType.idRange) + | _ :: SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ when + not isMember + -> + Some( + CompletionContext.MethodOverride( + MethodOverrideCompletionContext.Class, + enclosingType.idRange, + mOverride.StartColumn, + hasThis, + isStatic + ) + ) + | SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: SyntaxNode.SynTypeDefn(SynTypeDefn( + typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ + | _ :: SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: SyntaxNode.SynTypeDefn(SynTypeDefn( + typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ -> + let ty = + match ty with + | SynType.App(typeName = ty) -> ty + | _ -> ty + + Some( + CompletionContext.MethodOverride( + MethodOverrideCompletionContext.Interface ty.Range, + enclosingType.idRange, + mOverride.StartColumn, + hasThis, + isStatic + ) + ) + | SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: (SyntaxNode.SynExpr(SynExpr.ObjExpr _) as expr) :: _ + | _ :: SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: (SyntaxNode.SynExpr(SynExpr.ObjExpr _) as expr) :: _ -> + let ty = + match ty with + | SynType.App(typeName = ty) -> ty + | _ -> ty + + Some( + CompletionContext.MethodOverride( + MethodOverrideCompletionContext.ObjExpr expr.Range, + ty.Range, + mOverride.StartColumn, + hasThis, + isStatic + ) + ) + | SyntaxNode.SynExpr(SynExpr.ObjExpr(objType = ty)) as expr :: _ -> + let ty = + match ty with + | SynType.App(typeName = ty) -> ty + | _ -> ty + + Some( + CompletionContext.MethodOverride( + MethodOverrideCompletionContext.ObjExpr expr.Range, + ty.Range, + mOverride.StartColumn, + hasThis, + isStatic + ) + ) | _ -> Some CompletionContext.Invalid match returnInfo with - | Some(SynBindingReturnInfo(range = m)) when rangeContainsPos m pos -> Some CompletionContext.Type + | Some(SynBindingReturnInfo(range = m)) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> + Some CompletionContext.Type | _ -> match headPat with + // static member | + | SynPat.FromParseError _ when isStaticMember trivia.LeadingKeyword -> + overrideContext path trivia.LeadingKeyword.Range false true false + + // override | + | SynPat.FromParseError _ when isOverrideOrMember trivia.LeadingKeyword && lineStr.[pos.Column - 1] = ' ' -> + overrideContext path trivia.LeadingKeyword.Range false false (isMember trivia.LeadingKeyword) + // override _.| - | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword -> overrideContext path + | SynPat.FromParseError _ when isOverrideOrMember trivia.LeadingKeyword -> + overrideContext path trivia.LeadingKeyword.Range true false (isMember trivia.LeadingKeyword) // override this.| | SynPat.Named(ident = SynIdent(ident = selfId)) when - isOverride trivia.LeadingKeyword && selfId.idRange.End.IsAdjacentTo pos + isOverrideOrMember trivia.LeadingKeyword && selfId.idRange.End.IsAdjacentTo pos -> - overrideContext path + overrideContext path trivia.LeadingKeyword.Range true false (isMember trivia.LeadingKeyword) // override this.ToStr| | SynPat.LongIdent(longDotId = SynLongIdent(id = [ _; methodId ])) when - isOverride trivia.LeadingKeyword && rangeContainsPos methodId.idRange pos + isOverrideOrMember trivia.LeadingKeyword + && rangeContainsPos methodId.idRange pos + -> + overrideContext path trivia.LeadingKeyword.Range true false (isMember trivia.LeadingKeyword) + + // static member A| + | SynPat.LongIdent(longDotId = SynLongIdent(id = [ methodId ])) when + isStaticMember trivia.LeadingKeyword && rangeContainsPos methodId.idRange pos -> - overrideContext path + overrideContext path trivia.LeadingKeyword.Range false true false | SynPat.LongIdent(longDotId = lidwd; argPats = SynArgPats.Pats pats; range = m) when rangeContainsPos m pos -> if rangeContainsPos lidwd.Range pos then @@ -1708,6 +1826,12 @@ module ParsedInput = Some(CompletionContext.ParameterList(att.TypeName.Range.End, findSetters att.ArgExpr)) else None) + + override _.VisitInterfaceSynMemberDefnType(_, synType: SynType) = + match synType with + | SynType.FromParseError(range = m) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> + Some(CompletionContext.Inherit(InheritanceContext.Interface, ([], None))) + | _ -> None } let ctxt = SyntaxTraversal.Traverse(pos, parsedInput, visitor) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 336e6213a7a..427ffda43aa 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -42,6 +42,12 @@ type public PatternContext = /// Any other position in a pattern that does not need special handling | Other +[] +type MethodOverrideCompletionContext = + | Class + | Interface of mInterfaceName: range + | ObjExpr of mExpr: range + [] type public CompletionContext = /// Completion context cannot be determined due to errors @@ -79,7 +85,12 @@ type public CompletionContext = | Pattern of context: PatternContext /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of enclosingTypeNameRange: range + | MethodOverride of + ctx: MethodOverrideCompletionContext * + enclosingTypeNameRange: range * + spacesBeforeOverrideKeyword: int * + hasThis: bool * + isStatic: bool type public ModuleKind = { IsAutoOpen: bool diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 8cc9523b1fd..7bc495e9139 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -2384,8 +2384,23 @@ objectImplementationMember: { let rangeStart = rhs parseState 1 $3 $1 $2 rangeStart } - | opt_attributes staticMemberOrMemberOrOverride error - { [] } + | opt_attributes staticMemberOrMemberOrOverride recover + { let rangeStart = rhs parseState 1 + let memFlagsBuilder, leadingKeyword = $2 + let flags = Some(memFlagsBuilder SynMemberKind.Member) + let xmlDoc = grabXmlDocAtRangeStart (parseState, $1, rangeStart) + let trivia = { LeadingKeyword = leadingKeyword; InlineKeyword = None; EqualsRange = None } + let mMember = rhs parseState 2 + let mEnd = mMember.EndRange + let bindingPat = patFromParseError (SynPat.Wild(mEnd)) + let expr = arbExpr ("objectImplementationMember1", mEnd) + let mWhole = rhs2 parseState 1 2 + let binding = + mkSynBinding + (xmlDoc, bindingPat) + (None, false, false, mWhole, DebugPointAtBinding.NoneAtInvisible, None, expr, mEnd, [], $1, flags, trivia) + + [SynMemberDefn.Member(binding, mWhole)] } | opt_attributes error memberCore opt_ODECLEND { [] } diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 646e067251e..e8647e3f6ba 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2849,8 +2849,16 @@ FSharp.Compiler.EditorServices.CompletionContext+Inherit: FSharp.Compiler.Editor FSharp.Compiler.EditorServices.CompletionContext+Inherit: FSharp.Compiler.EditorServices.InheritanceContext get_context() FSharp.Compiler.EditorServices.CompletionContext+Inherit: System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],Microsoft.FSharp.Core.FSharpOption`1[System.String]] get_path() FSharp.Compiler.EditorServices.CompletionContext+Inherit: System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],Microsoft.FSharp.Core.FSharpOption`1[System.String]] path +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: Boolean get_hasThis() +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: Boolean get_isStatic() +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: Boolean hasThis +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: Boolean isStatic +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext ctx +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext get_ctx() FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: FSharp.Compiler.Text.Range enclosingTypeNameRange FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: FSharp.Compiler.Text.Range get_enclosingTypeNameRange() +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: Int32 get_spacesBeforeOverrideKeyword() +FSharp.Compiler.EditorServices.CompletionContext+MethodOverride: Int32 spacesBeforeOverrideKeyword FSharp.Compiler.EditorServices.CompletionContext+OpenDeclaration: Boolean get_isOpenType() FSharp.Compiler.EditorServices.CompletionContext+OpenDeclaration: Boolean isOpenType FSharp.Compiler.EditorServices.CompletionContext+ParameterList: FSharp.Compiler.Text.Position Item1 @@ -2904,7 +2912,7 @@ FSharp.Compiler.EditorServices.CompletionContext: Boolean get_IsUnionCaseFieldsD FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext AttributeApplication FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext Invalid FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext NewInherit(FSharp.Compiler.EditorServices.InheritanceContext, System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],Microsoft.FSharp.Core.FSharpOption`1[System.String]]) -FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext NewMethodOverride(FSharp.Compiler.Text.Range) +FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext NewMethodOverride(FSharp.Compiler.EditorServices.MethodOverrideCompletionContext, FSharp.Compiler.Text.Range, Int32, Boolean, Boolean) FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext NewOpenDeclaration(Boolean) FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext NewParameterList(FSharp.Compiler.Text.Position, System.Collections.Generic.HashSet`1[System.String]) FSharp.Compiler.EditorServices.CompletionContext: FSharp.Compiler.EditorServices.CompletionContext NewPattern(FSharp.Compiler.EditorServices.PatternContext) @@ -3539,6 +3547,33 @@ FSharp.Compiler.EditorServices.MethodGroupItemParameter: System.String Canonical FSharp.Compiler.EditorServices.MethodGroupItemParameter: System.String ParameterName FSharp.Compiler.EditorServices.MethodGroupItemParameter: System.String get_CanonicalTypeTextForSorting() FSharp.Compiler.EditorServices.MethodGroupItemParameter: System.String get_ParameterName() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext+Tags: Int32 Class +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext+Tags: Int32 Interface +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext+Tags: Int32 ObjExpr +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean Equals(FSharp.Compiler.EditorServices.MethodOverrideCompletionContext) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean Equals(FSharp.Compiler.EditorServices.MethodOverrideCompletionContext, System.Collections.IEqualityComparer) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean Equals(System.Object) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean IsClass +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean IsInterface +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean IsObjExpr +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean get_IsClass() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean get_IsInterface() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Boolean get_IsObjExpr() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext Class +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext NewInterface(FSharp.Compiler.Text.Range) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext NewObjExpr(FSharp.Compiler.Text.Range) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext get_Class() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.EditorServices.MethodOverrideCompletionContext+Tags +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.Text.Range get_mExpr() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.Text.Range get_mInterfaceName() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.Text.Range mExpr +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: FSharp.Compiler.Text.Range mInterfaceName +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Int32 GetHashCode() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Int32 Tag +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: Int32 get_Tag() +FSharp.Compiler.EditorServices.MethodOverrideCompletionContext: System.String ToString() FSharp.Compiler.EditorServices.ModuleKind: Boolean Equals(FSharp.Compiler.EditorServices.ModuleKind) FSharp.Compiler.EditorServices.ModuleKind: Boolean Equals(FSharp.Compiler.EditorServices.ModuleKind, System.Collections.IEqualityComparer) FSharp.Compiler.EditorServices.ModuleKind: Boolean Equals(System.Object) diff --git a/tests/service/data/SyntaxTree/Expression/Object - Class 06.fs.bsl b/tests/service/data/SyntaxTree/Expression/Object - Class 06.fs.bsl index d5e42b7e065..1880c3c044f 100644 --- a/tests/service/data/SyntaxTree/Expression/Object - Class 06.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/Object - Class 06.fs.bsl @@ -8,7 +8,28 @@ ImplFile (ObjExpr (LongIdent (SynLongIdent ([T], [], [None])), Some (Const (Unit, (3,7--3,9)), None), Some (3,10--3,14), [], - [], [], (3,2--3,9), (3,0--4,13)), (3,0--4,13)); + [Member + (SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((4,5), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (Some { IsInstance = true + IsDispatchSlot = false + IsOverrideOrExplicitImpl = true + IsFinal = false + GetterOrSetterIsCompilerGenerated = false + MemberKind = Member }, + SynValInfo + ([[SynArgInfo ([], false, None)]; []], + SynArgInfo ([], false, None)), None), + FromParseError (Wild (4,11--4,11), (4,11--4,11)), None, + ArbitraryAfterError + ("objectImplementationMember1", (4,11--4,11)), + (4,5--4,11), NoneAtInvisible, + { LeadingKeyword = Member (4,5--4,11) + InlineKeyword = None + EqualsRange = None }), (4,5--4,11))], [], (3,2--3,9), + (3,0--4,13)), (3,0--4,13)); Expr (Const (Unit, (6,0--6,2)), (6,0--6,2))], PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, (1,0--6,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), diff --git a/tests/service/data/SyntaxTree/Expression/Object - Class 15.fs.bsl b/tests/service/data/SyntaxTree/Expression/Object - Class 15.fs.bsl index c9b041181af..ec08bebfe92 100644 --- a/tests/service/data/SyntaxTree/Expression/Object - Class 15.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/Object - Class 15.fs.bsl @@ -9,6 +9,27 @@ ImplFile (LongIdent (SynLongIdent ([T], [], [None])), Some (Const (Unit, (3,7--3,9)), None), Some (3,10--3,14), [], [Member + (SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((4,5), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (Some { IsInstance = true + IsDispatchSlot = false + IsOverrideOrExplicitImpl = true + IsFinal = false + GetterOrSetterIsCompilerGenerated = false + MemberKind = Member }, + SynValInfo + ([[SynArgInfo ([], false, None)]; []], + SynArgInfo ([], false, None)), None), + FromParseError (Wild (4,11--4,11), (4,11--4,11)), None, + ArbitraryAfterError + ("objectImplementationMember1", (4,11--4,11)), + (4,5--4,11), NoneAtInvisible, + { LeadingKeyword = Member (4,5--4,11) + InlineKeyword = None + EqualsRange = None }), (4,5--4,11)); + Member (SynBinding (None, Normal, false, false, [], PreXmlDoc ((5,5), FSharp.Compiler.Xml.XmlDocCollector), diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 551b804bc37..09c9d6cefaf 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1853,35 +1853,49 @@ type B () = inherit System.Dynamic.DynamicMetaObjectBinder () override x. + +let _ = + { new System.Dynamic.SetIndexBinder (null) with + member x. + } + +let _ = + { new System.Dynamic.DynamicMetaObjectBinder () with + member this. + } """ // SetIndexBinder inherits from DynamicMetaObjectBinder, but overrides and seals Bind and the ReturnType property - VerifyCompletionListExactly( - fileContents, - "override _.a", - [ - "BindDelegate" - "Equals" - "FallbackSetIndex" - "Finalize" - "GetHashCode" - "ToString" - ] - ) - - VerifyCompletionListExactly( - fileContents, - "override x.", - [ - "Bind" - "BindDelegate" - "Equals" - "Finalize" - "GetHashCode" - "get_ReturnType" - "ToString" - ] - ) + [ "override _.a"; "member x." ] + |> List.iter (fun i -> + VerifyCompletionListExactly( + fileContents, + i, + [ + "BindDelegate (site: System.Runtime.CompilerServices.CallSite<'T>, args: obj array): 'T" + "Equals (obj: obj): bool" + "FallbackSetIndex (target: System.Dynamic.DynamicMetaObject, indexes: System.Dynamic.DynamicMetaObject array, value: System.Dynamic.DynamicMetaObject, errorSuggestion: System.Dynamic.DynamicMetaObject): System.Dynamic.DynamicMetaObject" + "Finalize (): unit" + "GetHashCode (): int" + "ToString (): string" + ] + )) + + [ "override x."; "member this." ] + |> List.iter (fun i -> + VerifyCompletionListExactly( + fileContents, + i, + [ + "ReturnType with get (): System.Type" + "Bind (target: System.Dynamic.DynamicMetaObject, args: System.Dynamic.DynamicMetaObject array): System.Dynamic.DynamicMetaObject" + "BindDelegate (site: System.Runtime.CompilerServices.CallSite<'T>, args: obj array): 'T" + "Equals (obj: obj): bool" + "Finalize (): unit" + "GetHashCode (): int" + "ToString (): string" + ] + )) [] let ``Completion list for override does not contain virtual method if it is already overridden in the same type`` () = @@ -1914,9 +1928,83 @@ type C () = override A1 s = () """ - VerifyCompletionListExactly(fileContents, "override _.", [ "Equals"; "Finalize"; "GetHashCode" ]) - VerifyCompletionListExactly(fileContents, "override x.b", [ "A1"; "A2"; "Equals"; "Finalize"; "GetHashCode"; "ToString" ]) - VerifyCompletionListExactly(fileContents, "override x.c", [ "A2"; "Equals"; "Finalize"; "GetHashCode"; "ToString" ]) + VerifyCompletionListExactly(fileContents, "override _.", [ "Equals (obj: obj): bool"; "Finalize (): unit"; "GetHashCode (): int" ]) + + VerifyCompletionListExactly( + fileContents, + "override x.b", + [ + "A1 (arg: string): unit" + "A2 (): unit" + "Equals (obj: obj): bool" + "Finalize (): unit" + "GetHashCode (): int" + "ToString (): string" + ] + ) + + VerifyCompletionListExactly( + fileContents, + "override x.c", + [ + "A2 (): unit" + "Equals (obj: obj): bool" + "Finalize (): unit" + "GetHashCode (): int" + "ToString (): string" + ] + ) + + [] + let ``Completion list for override in interface implements does not contain method which is already overridden in the same type`` () = + let fileContents = + """ +type IA = + static abstract member A3: unit -> unit + static abstract member A4: unit -> unit + static abstract member P1: value: int -> int with get, set + static abstract member P2: int with get, set + +type IB = + abstract member A1: unit -> unit + abstract member A1: string -> unit + abstract member A2: unit -> unit + abstract member P1: int * bool -> int with get, set + abstract member P2: int with get, set + +type TA() = + interface IA with + static member + static member A3 (): unit = () + static member P2 + with get (): int = raise (System.NotImplementedException()) + interface IB with + member this.A1 (arg1: string): unit = () + member this.P2 + with get (): int = raise (System.NotImplementedException()) + member thisTA. +""" + + VerifyCompletionListExactly( + fileContents, + "static member ", + [ + "P1 with get (value: int): int and set (value: int) (value_1: int)" + "P2 with set (value: int)" + "A4 (): unit" + ] + ) + + VerifyCompletionListExactly( + fileContents, + "member thisTA.", + [ + "P1 with get (arg: int, arg_1: bool): int and set (arg: int, arg_1: bool) (value: int)" + "P2 with set (value: int)" + "A1 (): unit" + "A2 (): unit" + ] + ) [] let ``Completion list for override is empty when the caret is on the self identifier`` () =