From 03e7cabfa4b609a9f2573e585e29a11e7ce4ab1d Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sat, 25 May 2024 23:02:03 +0800 Subject: [PATCH 01/28] show related completion after = or <> like C# --- src/Compiler/Service/FSharpCheckerResults.fs | 54 ++++++++++++++++--- .../Service/ServiceDeclarationLists.fs | 5 +- .../Service/ServiceDeclarationLists.fsi | 2 + src/Compiler/Service/ServiceParsedInputOps.fs | 12 ++++- .../Service/ServiceParsedInputOps.fsi | 3 ++ 5 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 46ea4bce4f5..9913cba130a 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -899,7 +899,7 @@ 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 CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) insertFullName (item: ItemWithInst) = let kind = match item.Item with | Item.DelegateCtor _ @@ -949,9 +949,10 @@ type internal TypeCheckInfo IsOwnMember = false Type = ty Unresolved = isUnresolved + InsertFullName = insertFullName } - let DefaultCompletionItem item = CompletionItem ValueNone ValueNone item + let DefaultCompletionItem item = CompletionItem ValueNone ValueNone false item let CompletionItemSuggestedName displayName = { @@ -961,6 +962,7 @@ type internal TypeCheckInfo Kind = CompletionItemKind.SuggestedName IsOwnMember = false Unresolved = None + InsertFullName = false } let getItem (x: ItemWithInst) = x.Item @@ -1258,7 +1260,7 @@ type internal TypeCheckInfo | NameResResult.Cancel(denv, m) -> Some([], denv, m) | NameResResult.Members(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m)) -> // lookup based on name resolution results successful - Some(items |> List.map (CompletionItem (getType ()) ValueNone), denv, m) + Some(items |> List.map (CompletionItem (getType ()) ValueNone false), denv, m) | _ -> match origLongIdentOpt with | None -> None @@ -1299,7 +1301,7 @@ type internal TypeCheckInfo isNil plid -> // lookup based on expression typings successful - Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone), denv, m) + Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone false), denv, m) | ExprTypingsResult.NoneBecauseThereWereTypeErrors, _ -> // There was an error, e.g. we have "." and there is an error determining the type of // In this case, we don't want any of the fallback logic, rather, we want to produce zero results. @@ -1323,17 +1325,17 @@ type internal TypeCheckInfo // First, use unfiltered name resolution items, if they're not empty | NameResResult.Members(items, denv, m), _, _ when not (isNil items) -> // lookup based on name resolution results successful - ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone), denv, m) + ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone false), denv, m) // If we have nonempty items from environment that were resolved from a type, then use them... // (that's better than the next case - here we'd return 'int' as a type) | _, FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), _ when not (isNil items) -> // lookup based on name and environment successful - ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone), denv, m) + ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone false), denv, m) // Try again with the qualItems | _, _, ExprTypingsResult.Some(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), ty) -> - ValueSome(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone), denv, m) + ValueSome(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone false), denv, m) | _ -> ValueNone @@ -1362,7 +1364,7 @@ type internal TypeCheckInfo -> globalItemsFiltered |> List.map (fun globalItem -> - CompletionItem (getType ()) (ValueSome globalItem) (ItemWithNoInst globalItem.Symbol.Item)) + CompletionItem (getType ()) (ValueSome globalItem) false (ItemWithNoInst globalItem.Symbol.Item)) |> fun r -> ValueSome(r, denv, m) | _ -> ValueNone | _ -> ValueNone // do not return unresolved items after dot @@ -1579,6 +1581,7 @@ type internal TypeCheckInfo IsOwnMember = false Type = None Unresolved = None + InsertFullName = false }) match declaredItems with @@ -1643,6 +1646,41 @@ type internal TypeCheckInfo | Some(CompletionContext.MethodOverride enclosingTypeNameRange) -> GetOverridableMethods pos enclosingTypeNameRange + | Some(CompletionContext.CaretAfterOperator m) -> + let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () + let _, quals = GetExprTypingForPosition(m.End) + let bestQual = + quals + |> Array.tryFind (fun (_, _, _, r) -> posEq m.Start r.Start) + + match bestQual with + | Some bestQual -> + let ty, nenv, ad, m = bestQual + + let targets = + ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m) + + let items = ResolveCompletionsInType ncenv nenv targets m ad true ty + let items = items |> List.map ItemWithNoInst + let items = items |> RemoveDuplicateItems g + let items = + items + |> RemoveExplicitlySuppressed g + |> List.filter ( + function + | { Item = Item.Value _ } + | { Item = Item.ILField _ } + | { Item = Item.UnionCase _ } + | { Item = Item.Property _ } -> true + | _ -> false) + + match declaredItems with + | Some (declaredItems, _, _) -> + Some((items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true)) @ declaredItems, nenv.DisplayEnv, m) + | None -> + Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true), nenv.DisplayEnv, m) + | None -> declaredItems + // Other completions | cc -> match residueOpt |> Option.bind Seq.tryHead with diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index 14b506bff86..aec332f261b 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -86,7 +86,8 @@ type CompletionItem = IsOwnMember: bool MinorPriority: int Type: TyconRef option - Unresolved: UnresolvedSymbol option } + Unresolved: UnresolvedSymbol option + InsertFullName: bool } member x.Item = x.ItemWithInst.Item [] @@ -1130,6 +1131,7 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i let textInDeclList item = match item.Unresolved with | Some u -> u.DisplayName + | None when item.InsertFullName && item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" | None -> item.Item.DisplayNameCore let textInCode (item: CompletionItem) = match item.Item with @@ -1137,6 +1139,7 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i | _ -> match item.Unresolved with | Some u -> u.DisplayName + | None when item.InsertFullName && item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" | None -> item.Item.DisplayName if not supportsPreferExtsMethodsOverProperty then // we don't pay the cost of filtering specific to RFC-1137 diff --git a/src/Compiler/Service/ServiceDeclarationLists.fsi b/src/Compiler/Service/ServiceDeclarationLists.fsi index 4611ff86a16..0bd9588d36b 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -91,6 +91,8 @@ type internal CompletionItem = Type: TyconRef option Unresolved: UnresolvedSymbol option + + InsertFullName: bool } member Item: Item diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 3f379c0f8b6..6c95952577d 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -109,6 +109,9 @@ type CompletionContext = /// Completing a method override (e.g. override this.ToStr|) | MethodOverride of enclosingTypeNameRange: range + /// (s = | ) or (s <> | ) + | CaretAfterOperator of mExprBeforeOperator: range + type ShortIdent = string type ShortIdents = ShortIdent[] @@ -1421,7 +1424,7 @@ module ParsedInput = match path with | PartOfParameterList pos precedingArgument args -> Some(CompletionContext.ParameterList args) - | _ -> defaultTraverse expr + | _ -> Some (CompletionContext.CaretAfterOperator id.idRange) | SynExpr.Record(None, None, [], _) -> Some(CompletionContext.RecordField RecordContext.Empty) @@ -1434,6 +1437,13 @@ module ParsedInput = |> List.tryPick (fun pat -> TryGetCompletionContextInPattern true pat None pos) |> Option.orElseWith (fun () -> defaultTraverse expr) + | SynExpr.FromParseError(SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _) + | SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _, _) + | SynExpr.IfThenElse(ifExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _, _)) + | SynExpr.While (whileExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _, _)) + when ident.idText = "op_Equality" || ident.idText = "op_Inequality" -> + Some (CompletionContext.CaretAfterOperator arg.Range) + | _ -> defaultTraverse expr member _.VisitRecordField(path, copyOpt, field) = diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 336e6213a7a..a677f33da89 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -81,6 +81,9 @@ type public CompletionContext = /// Completing a method override (e.g. override this.ToStr|) | MethodOverride of enclosingTypeNameRange: range + /// (s = | ) or (s <> | ) + | CaretAfterOperator of mExprBeforeOperator: range + type public ModuleKind = { IsAutoOpen: bool HasModuleSuffix: bool } From ed3db6087d2eebeeb563d754ce3b6e48cc02b43b Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sat, 25 May 2024 23:42:59 +0800 Subject: [PATCH 02/28] fix bug; format --- src/Compiler/Service/FSharpCheckerResults.fs | 41 +++++++++-------- src/Compiler/Service/ServiceParsedInputOps.fs | 44 ++++++++++++++++--- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 9913cba130a..441aa7bdefb 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -952,7 +952,8 @@ type internal TypeCheckInfo InsertFullName = insertFullName } - let DefaultCompletionItem item = CompletionItem ValueNone ValueNone false item + let DefaultCompletionItem item = + CompletionItem ValueNone ValueNone false item let CompletionItemSuggestedName displayName = { @@ -1649,10 +1650,8 @@ type internal TypeCheckInfo | Some(CompletionContext.CaretAfterOperator m) -> let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () let _, quals = GetExprTypingForPosition(m.End) - let bestQual = - quals - |> Array.tryFind (fun (_, _, _, r) -> posEq m.Start r.Start) - + let bestQual = quals |> Array.tryFind (fun (_, _, _, r) -> posEq m.Start r.Start) + match bestQual with | Some bestQual -> let ty, nenv, ad, m = bestQual @@ -1661,24 +1660,28 @@ type internal TypeCheckInfo ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m) let items = ResolveCompletionsInType ncenv nenv targets m ad true ty + + let items = + items + |> List.filter (function + | Item.Value(valRef) -> typeEquiv g ty valRef.Type + | Item.ILField(iLFieldInfo) -> typeEquiv g ty (iLFieldInfo.FieldType(amap, m)) + | Item.Property(info = pinfo :: _) -> typeEquiv g ty (pinfo.GetPropertyType(amap, m)) + | _ -> false) + let items = items |> List.map ItemWithNoInst let items = items |> RemoveDuplicateItems g - let items = - items - |> RemoveExplicitlySuppressed g - |> List.filter ( - function - | { Item = Item.Value _ } - | { Item = Item.ILField _ } - | { Item = Item.UnionCase _ } - | { Item = Item.Property _ } -> true - | _ -> false) + let items = items |> RemoveExplicitlySuppressed g match declaredItems with - | Some (declaredItems, _, _) -> - Some((items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true)) @ declaredItems, nenv.DisplayEnv, m) - | None -> - Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true), nenv.DisplayEnv, m) + | Some(declaredItems, _, _) -> + Some( + (items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true)) + @ declaredItems, + nenv.DisplayEnv, + m + ) + | None -> Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true), nenv.DisplayEnv, m) | None -> declaredItems // Other completions diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 6c95952577d..4698bb14441 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -1424,7 +1424,7 @@ module ParsedInput = match path with | PartOfParameterList pos precedingArgument args -> Some(CompletionContext.ParameterList args) - | _ -> Some (CompletionContext.CaretAfterOperator id.idRange) + | _ -> Some(CompletionContext.CaretAfterOperator id.idRange) | SynExpr.Record(None, None, [], _) -> Some(CompletionContext.RecordField RecordContext.Empty) @@ -1437,12 +1437,42 @@ module ParsedInput = |> List.tryPick (fun pat -> TryGetCompletionContextInPattern true pat None pos) |> Option.orElseWith (fun () -> defaultTraverse expr) - | SynExpr.FromParseError(SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _) - | SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _, _) - | SynExpr.IfThenElse(ifExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _, _)) - | SynExpr.While (whileExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), arg, _), _, _)) - when ident.idText = "op_Equality" || ident.idText = "op_Inequality" -> - Some (CompletionContext.CaretAfterOperator arg.Range) + | SynExpr.FromParseError(SynExpr.App(ExprAtomicFlag.NonAtomic, + true, + SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), + arg, + _), + _) + | SynExpr.App(ExprAtomicFlag.NonAtomic, + false, + SynExpr.App(ExprAtomicFlag.NonAtomic, + true, + SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), + arg, + _), + _, + _) + | SynExpr.IfThenElse( + ifExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, + false, + SynExpr.App(ExprAtomicFlag.NonAtomic, + true, + SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), + arg, + _), + _, + _)) + | SynExpr.While( + whileExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, + false, + SynExpr.App(ExprAtomicFlag.NonAtomic, + true, + SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), + arg, + _), + _, + _)) when ident.idText = "op_Equality" || ident.idText = "op_Inequality" -> + Some(CompletionContext.CaretAfterOperator arg.Range) | _ -> defaultTraverse expr From 1994e6379cf7d917a93a611e63de48935e251138 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sun, 26 May 2024 00:35:32 +0800 Subject: [PATCH 03/28] try fix --- src/Compiler/Service/ServiceParsedInputOps.fs | 45 +++---------------- src/Compiler/Utilities/range.fs | 2 + src/Compiler/Utilities/range.fsi | 3 ++ 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 4698bb14441..376271ad3d1 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -109,7 +109,7 @@ type CompletionContext = /// Completing a method override (e.g. override this.ToStr|) | MethodOverride of enclosingTypeNameRange: range - /// (s = | ) or (s <> | ) + /// (s =| ) or (s <>| ) | CaretAfterOperator of mExprBeforeOperator: range type ShortIdent = string @@ -1397,7 +1397,7 @@ module ParsedInput = let visitor = { new SyntaxVisitorBase<_>() with member _.VisitExpr(path, _, defaultTraverse, expr) = - + if isAtRangeOp path then match defaultTraverse expr with | None -> Some CompletionContext.RangeOperator // nothing was found - report that we were in the context of range operator @@ -1424,7 +1424,8 @@ module ParsedInput = match path with | PartOfParameterList pos precedingArgument args -> Some(CompletionContext.ParameterList args) - | _ -> Some(CompletionContext.CaretAfterOperator id.idRange) + | _ when rangeAfterPos r.Range pos-> Some(CompletionContext.CaretAfterOperator id.idRange) + | _ -> defaultTraverse expr | SynExpr.Record(None, None, [], _) -> Some(CompletionContext.RecordField RecordContext.Empty) @@ -1437,42 +1438,8 @@ module ParsedInput = |> List.tryPick (fun pat -> TryGetCompletionContextInPattern true pat None pos) |> Option.orElseWith (fun () -> defaultTraverse expr) - | SynExpr.FromParseError(SynExpr.App(ExprAtomicFlag.NonAtomic, - true, - SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), - arg, - _), - _) - | SynExpr.App(ExprAtomicFlag.NonAtomic, - false, - SynExpr.App(ExprAtomicFlag.NonAtomic, - true, - SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), - arg, - _), - _, - _) - | SynExpr.IfThenElse( - ifExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, - false, - SynExpr.App(ExprAtomicFlag.NonAtomic, - true, - SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), - arg, - _), - _, - _)) - | SynExpr.While( - whileExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, - false, - SynExpr.App(ExprAtomicFlag.NonAtomic, - true, - SynExpr.LongIdent(longDotId = SynLongIdent(id = ident :: _)), - arg, - _), - _, - _)) when ident.idText = "op_Equality" || ident.idText = "op_Inequality" -> - Some(CompletionContext.CaretAfterOperator arg.Range) + | Operator "op_Equality" (l, r) | Operator "op_Inequality" (l, r) when rangeAfterPos r.Range pos -> + Some(CompletionContext.CaretAfterOperator l.Range) | _ -> defaultTraverse expr diff --git a/src/Compiler/Utilities/range.fs b/src/Compiler/Utilities/range.fs index 678ab07f452..a26736f3a4e 100755 --- a/src/Compiler/Utilities/range.fs +++ b/src/Compiler/Utilities/range.fs @@ -512,6 +512,8 @@ module Range = let rangeBeforePos (m1: range) p = posGeq p m1.End + let rangeAfterPos (m1: range) p = posGeq m1.Start p + let rangeN fileName line = mkRange fileName (mkPos line 0) (mkPos line 0) diff --git a/src/Compiler/Utilities/range.fsi b/src/Compiler/Utilities/range.fsi index 7b422fcd307..11485c58930 100755 --- a/src/Compiler/Utilities/range.fsi +++ b/src/Compiler/Utilities/range.fsi @@ -236,6 +236,9 @@ module Range = /// Test to see if a range occurs fully before a position val rangeBeforePos: range -> pos -> bool + /// Test to see if a range occurs fully after a position + val rangeAfterPos: range -> pos -> bool + /// Make a dummy range for a file val rangeN: string -> int -> range From 4375a4d3d4810be26303dafd8ce45f0e5e1694f9 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sun, 26 May 2024 01:07:56 +0800 Subject: [PATCH 04/28] fix build --- src/Compiler/Service/ServiceParsedInputOps.fs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 376271ad3d1..c149206833d 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -1424,8 +1424,11 @@ module ParsedInput = match path with | PartOfParameterList pos precedingArgument args -> Some(CompletionContext.ParameterList args) - | _ when rangeAfterPos r.Range pos-> Some(CompletionContext.CaretAfterOperator id.idRange) - | _ -> defaultTraverse expr + | _ -> + match expr with + | Operator "op_Equality" (l, r) when rangeAfterPos r.Range pos -> + Some(CompletionContext.CaretAfterOperator l.Range) + | _ -> defaultTraverse expr | SynExpr.Record(None, None, [], _) -> Some(CompletionContext.RecordField RecordContext.Empty) From 0a204bbf8effc4d99d868c1ddb563407199099a8 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sun, 26 May 2024 15:50:09 +0800 Subject: [PATCH 05/28] improve class member override completion --- src/Compiler/Service/FSharpCheckerResults.fs | 105 ++++++++++++++---- .../Service/ServiceDeclarationLists.fs | 43 +++++-- .../Service/ServiceDeclarationLists.fsi | 8 +- src/Compiler/Service/ServiceParseTreeWalk.fs | 1 + src/Compiler/Service/ServiceParsedInputOps.fs | 26 +++-- .../Service/ServiceParsedInputOps.fsi | 2 +- src/Compiler/Utilities/range.fs | 2 - src/Compiler/Utilities/range.fsi | 3 - 8 files changed, 144 insertions(+), 46 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 441aa7bdefb..2124e352656 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -899,7 +899,7 @@ type internal TypeCheckInfo if p >= 0 then Some p else None /// Build a CompetionItem - let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) insertFullName (item: ItemWithInst) = + let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) insertType (item: ItemWithInst) = let kind = match item.Item with | Item.DelegateCtor _ @@ -949,11 +949,14 @@ type internal TypeCheckInfo IsOwnMember = false Type = ty Unresolved = isUnresolved - InsertFullName = insertFullName + InsertType = insertType } let DefaultCompletionItem item = - CompletionItem ValueNone ValueNone false item + CompletionItem ValueNone ValueNone CompletionInsertType.Default item + + let MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis item = + CompletionItem ValueNone ValueNone (CompletionInsertType.MethodOverride(spacesBeforeOverrideKeyword, hasThis)) item let CompletionItemSuggestedName displayName = { @@ -963,7 +966,7 @@ type internal TypeCheckInfo Kind = CompletionItemKind.SuggestedName IsOwnMember = false Unresolved = None - InsertFullName = false + InsertType = CompletionInsertType.Default } let getItem (x: ItemWithInst) = x.Item @@ -1048,7 +1051,7 @@ 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 GetOverridableMethods pos typeNameRange spacesBeforeOverrideKeyword hasThis = let isMethodOverridable alreadyOverridden (candidate: MethInfo) = not candidate.IsFinal && not ( @@ -1079,11 +1082,38 @@ type internal TypeCheckInfo range0 superTy |> List.filter (isMethodOverridable overriddenMethods) - |> List.groupBy (fun x -> x.DisplayName) - |> List.map (fun (name, overloads) -> - Item.MethodGroup(name, overloads, None) + |> List.map (fun meth -> + let getNameForNoNameArg = + let mutable count = 1 + + fun () -> + let name = $"arg{count}" + count <- count + 1 + name + + let name = meth.DisplayName + + 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 = Option.defaultWith getNameForNoNameArg name + + match tryTcrefOfAppTy g ty with + | ValueSome ty -> $"{name}: {ty}" + | ValueNone -> $"{name}") + |> String.concat ", " + + $"({names})") + |> String.concat " " + + Item.MethodGroup($"{name} {parameters}", [ meth ], None) |> ItemWithNoInst - |> DefaultCompletionItem) + |> MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis) Some(overridableMethods, nenv.DisplayEnv, m) | _ -> None) @@ -1261,7 +1291,12 @@ type internal TypeCheckInfo | NameResResult.Cancel(denv, m) -> Some([], denv, m) | NameResResult.Members(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m)) -> // lookup based on name resolution results successful - Some(items |> List.map (CompletionItem (getType ()) ValueNone false), denv, m) + Some( + items + |> List.map (CompletionItem (getType ()) ValueNone CompletionInsertType.Default), + denv, + m + ) | _ -> match origLongIdentOpt with | None -> None @@ -1302,7 +1337,12 @@ type internal TypeCheckInfo isNil plid -> // lookup based on expression typings successful - Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone false), denv, m) + Some( + items + |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.Default), + denv, + m + ) | ExprTypingsResult.NoneBecauseThereWereTypeErrors, _ -> // There was an error, e.g. we have "." and there is an error determining the type of // In this case, we don't want any of the fallback logic, rather, we want to produce zero results. @@ -1326,17 +1366,32 @@ type internal TypeCheckInfo // First, use unfiltered name resolution items, if they're not empty | NameResResult.Members(items, denv, m), _, _ when not (isNil items) -> // lookup based on name resolution results successful - ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone false), denv, m) + ValueSome( + items + |> List.map (CompletionItem (getType ()) ValueNone CompletionInsertType.Default), + denv, + m + ) // If we have nonempty items from environment that were resolved from a type, then use them... // (that's better than the next case - here we'd return 'int' as a type) | _, FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), _ when not (isNil items) -> // lookup based on name and environment successful - ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone false), denv, m) + ValueSome( + items + |> List.map (CompletionItem (getType ()) ValueNone CompletionInsertType.Default), + denv, + m + ) // Try again with the qualItems | _, _, ExprTypingsResult.Some(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), ty) -> - ValueSome(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone false), denv, m) + ValueSome( + items + |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.Default), + denv, + m + ) | _ -> ValueNone @@ -1365,7 +1420,11 @@ type internal TypeCheckInfo -> globalItemsFiltered |> List.map (fun globalItem -> - CompletionItem (getType ()) (ValueSome globalItem) false (ItemWithNoInst globalItem.Symbol.Item)) + CompletionItem + (getType ()) + (ValueSome globalItem) + CompletionInsertType.Default + (ItemWithNoInst globalItem.Symbol.Item)) |> fun r -> ValueSome(r, denv, m) | _ -> ValueNone | _ -> ValueNone // do not return unresolved items after dot @@ -1582,7 +1641,7 @@ type internal TypeCheckInfo IsOwnMember = false Type = None Unresolved = None - InsertFullName = false + InsertType = CompletionInsertType.Default }) match declaredItems with @@ -1645,7 +1704,8 @@ type internal TypeCheckInfo getDeclaredItemsNotInRangeOpWithAllSymbols () |> Option.bind (FilterRelevantItemsBy getItem2 None IsPatternCandidate) - | Some(CompletionContext.MethodOverride enclosingTypeNameRange) -> GetOverridableMethods pos enclosingTypeNameRange + | Some(CompletionContext.MethodOverride(enclosingTypeNameRange, spacesBeforeOverrideKeyword, hasThis)) -> + GetOverridableMethods pos enclosingTypeNameRange spacesBeforeOverrideKeyword hasThis | Some(CompletionContext.CaretAfterOperator m) -> let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () @@ -1676,12 +1736,19 @@ type internal TypeCheckInfo match declaredItems with | Some(declaredItems, _, _) -> Some( - (items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true)) + (items + |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName)) @ declaredItems, nenv.DisplayEnv, m ) - | None -> Some(items |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone true), nenv.DisplayEnv, m) + | None -> + Some( + items + |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName), + nenv.DisplayEnv, + m + ) | None -> declaredItems // Other completions diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index aec332f261b..ff59e0abde1 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -80,6 +80,12 @@ type UnresolvedSymbol = DisplayName: string Namespace: string[] } +[] +type CompletionInsertType = + | Default + | FullName + | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool + type CompletionItem = { ItemWithInst: ItemWithInst Kind: CompletionItemKind @@ -87,7 +93,7 @@ type CompletionItem = MinorPriority: int Type: TyconRef option Unresolved: UnresolvedSymbol option - InsertFullName: bool } + InsertType: CompletionInsertType } member x.Item = x.ItemWithInst.Item [] @@ -1126,21 +1132,38 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i match u.Namespace with | [||] -> u.DisplayName | ns -> (ns |> String.concat ".") + "." + u.DisplayName - | None -> x.Item.DisplayName) + | None -> + match x.InsertType with + | CompletionInsertType.MethodOverride _ -> + match x.Item with + | Item.MethodGroup (name, _, _) -> name + | _ -> x.Item.DisplayNameCore + | _ -> x.Item.DisplayName + ) |> List.map ( let textInDeclList item = - match item.Unresolved with - | Some u -> u.DisplayName - | None when item.InsertFullName && item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None -> item.Item.DisplayNameCore + match item.Unresolved, item.InsertType with + | Some u, _ -> u.DisplayName + | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" + | None, CompletionInsertType.MethodOverride _ -> + match item.Item with + | Item.MethodGroup (name, _, _) -> name + | _ -> item.Item.DisplayNameCore + | 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 when item.InsertFullName && item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None -> item.Item.DisplayName + match item.Unresolved, item.InsertType with + | Some u, _ -> u.DisplayName + | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" + | None, CompletionInsertType.MethodOverride (spacesBeforeOverrideKeyword, hasThis) -> + match item.Item with + | Item.MethodGroup (name, _, _) -> + let nameWithThis = if hasThis then $"{name} = " else $"this.{name} = " + nameWithThis + System.Environment.NewLine + String.make (spacesBeforeOverrideKeyword + 4) ' ' + $"base.{name}" + | _ -> item.Item.DisplayNameCore + | 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 0bd9588d36b..527f5cf265f 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -77,6 +77,12 @@ type public UnresolvedSymbol = Namespace: string[] } + +[] +type internal CompletionInsertType = + | Default + | FullName + | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool type internal CompletionItem = { @@ -92,7 +98,7 @@ type internal CompletionItem = Unresolved: UnresolvedSymbol option - InsertFullName: bool + InsertType: CompletionInsertType } member Item: Item diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index c395c9422d2..238f72da85e 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -984,6 +984,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 c149206833d..d9063315a60 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -107,7 +107,7 @@ type CompletionContext = | Pattern of context: PatternContext /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of enclosingTypeNameRange: range + | MethodOverride of enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool /// (s =| ) or (s <>| ) | CaretAfterOperator of mExprBeforeOperator: range @@ -1397,7 +1397,7 @@ module ParsedInput = let visitor = { new SyntaxVisitorBase<_>() with member _.VisitExpr(path, _, defaultTraverse, expr) = - + if isAtRangeOp path then match defaultTraverse expr with | None -> Some CompletionContext.RangeOperator // nothing was found - report that we were in the context of range operator @@ -1424,9 +1424,9 @@ module ParsedInput = match path with | PartOfParameterList pos precedingArgument args -> Some(CompletionContext.ParameterList args) - | _ -> + | _ -> match expr with - | Operator "op_Equality" (l, r) when rangeAfterPos r.Range pos -> + | Operator "op_Equality" (l, r) when SyntaxTraversal.rangeContainsPosLeftEdgeInclusive r.Range pos -> Some(CompletionContext.CaretAfterOperator l.Range) | _ -> defaultTraverse expr @@ -1441,7 +1441,8 @@ module ParsedInput = |> List.tryPick (fun pat -> TryGetCompletionContextInPattern true pat None pos) |> Option.orElseWith (fun () -> defaultTraverse expr) - | Operator "op_Equality" (l, r) | Operator "op_Inequality" (l, r) when rangeAfterPos r.Range pos -> + | Operator "op_Equality" (l, r) + | Operator "op_Inequality" (l, r) when SyntaxTraversal.rangeContainsPosLeftEdgeInclusive r.Range pos -> Some(CompletionContext.CaretAfterOperator l.Range) | _ -> defaultTraverse expr @@ -1506,10 +1507,10 @@ module ParsedInput = | SynLeadingKeyword.Override _ -> true | _ -> false - let overrideContext path = + let overrideContext path (mOverride: range) hasThis = match path with | _ :: SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ -> - Some(CompletionContext.MethodOverride enclosingType.idRange) + Some(CompletionContext.MethodOverride(enclosingType.idRange, mOverride.StartColumn, hasThis)) | _ -> Some CompletionContext.Invalid match returnInfo with @@ -1517,20 +1518,25 @@ module ParsedInput = | _ -> match headPat with + // override | + | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword && lineStr.[pos.Column - 1] = ' ' -> + overrideContext path trivia.LeadingKeyword.Range false + // override _.| - | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword -> overrideContext path + | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword -> + overrideContext path trivia.LeadingKeyword.Range true // override this.| | SynPat.Named(ident = SynIdent(ident = selfId)) when isOverride trivia.LeadingKeyword && selfId.idRange.End.IsAdjacentTo pos -> - overrideContext path + overrideContext path trivia.LeadingKeyword.Range true // override this.ToStr| | SynPat.LongIdent(longDotId = SynLongIdent(id = [ _; methodId ])) when isOverride trivia.LeadingKeyword && rangeContainsPos methodId.idRange pos -> - overrideContext path + overrideContext path trivia.LeadingKeyword.Range true | SynPat.LongIdent(longDotId = lidwd; argPats = SynArgPats.Pats pats; range = m) when rangeContainsPos m pos -> if rangeContainsPos lidwd.Range pos then diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index a677f33da89..7e2843a3877 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -79,7 +79,7 @@ type public CompletionContext = | Pattern of context: PatternContext /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of enclosingTypeNameRange: range + | MethodOverride of enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool /// (s = | ) or (s <> | ) | CaretAfterOperator of mExprBeforeOperator: range diff --git a/src/Compiler/Utilities/range.fs b/src/Compiler/Utilities/range.fs index a26736f3a4e..678ab07f452 100755 --- a/src/Compiler/Utilities/range.fs +++ b/src/Compiler/Utilities/range.fs @@ -512,8 +512,6 @@ module Range = let rangeBeforePos (m1: range) p = posGeq p m1.End - let rangeAfterPos (m1: range) p = posGeq m1.Start p - let rangeN fileName line = mkRange fileName (mkPos line 0) (mkPos line 0) diff --git a/src/Compiler/Utilities/range.fsi b/src/Compiler/Utilities/range.fsi index 11485c58930..7b422fcd307 100755 --- a/src/Compiler/Utilities/range.fsi +++ b/src/Compiler/Utilities/range.fsi @@ -236,9 +236,6 @@ module Range = /// Test to see if a range occurs fully before a position val rangeBeforePos: range -> pos -> bool - /// Test to see if a range occurs fully after a position - val rangeAfterPos: range -> pos -> bool - /// Make a dummy range for a file val rangeN: string -> int -> range From a0cccec1444dfc2413314a9d87ede57db34aba02 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sun, 26 May 2024 19:37:13 +0800 Subject: [PATCH 06/28] support override completion for interface implementation and obj expr support static member override completion support get completions from generic base type for interface implementation and obj expr --- src/Compiler/Service/FSharpCheckerResults.fs | 135 ++++++++++-------- .../Service/ServiceDeclarationLists.fs | 15 +- .../Service/ServiceDeclarationLists.fsi | 2 +- src/Compiler/Service/ServiceParseTreeWalk.fs | 5 +- src/Compiler/Service/ServiceParsedInputOps.fs | 44 ++++-- .../Service/ServiceParsedInputOps.fsi | 8 +- src/Compiler/pars.fsy | 19 ++- 7 files changed, 149 insertions(+), 79 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 2124e352656..7ce2bd0c001 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -955,8 +955,8 @@ type internal TypeCheckInfo let DefaultCompletionItem item = CompletionItem ValueNone ValueNone CompletionInsertType.Default item - let MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis item = - CompletionItem ValueNone ValueNone (CompletionInsertType.MethodOverride(spacesBeforeOverrideKeyword, hasThis)) item + let MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis isInterface item = + CompletionItem ValueNone ValueNone (CompletionInsertType.MethodOverride(spacesBeforeOverrideKeyword, hasThis, isInterface)) item let CompletionItemSuggestedName displayName = { @@ -1051,7 +1051,7 @@ type internal TypeCheckInfo |> Option.defaultValue completions /// Gets all methods that a type can override, but has not yet done so. - let GetOverridableMethods pos typeNameRange spacesBeforeOverrideKeyword hasThis = + let GetOverridableMethods pos ctx (typeNameRange: range) spacesBeforeOverrideKeyword hasThis isStatic = let isMethodOverridable alreadyOverridden (candidate: MethInfo) = not candidate.IsFinal && not ( @@ -1061,62 +1061,75 @@ type internal TypeCheckInfo let (nenv, ad), m = GetBestEnvForPos pos - 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 overriddenMethods = - GetImmediateIntrinsicMethInfosOfType (None, ad) g amap typeNameRange ty - |> List.filter (fun x -> x.IsDefiniteFSharpOverride) - - let overridableMethods = - GetIntrinsicMethInfosOfType - infoReader - None - ad - TypeHierarchy.AllowMultiIntfInstantiations.No - FindMemberFlag.PreferOverrides - range0 - superTy - |> List.filter (isMethodOverridable overriddenMethods) - |> List.map (fun meth -> - let getNameForNoNameArg = - let mutable count = 1 - - fun () -> - let name = $"arg{count}" - count <- count + 1 - name - - let name = meth.DisplayName - - 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 = Option.defaultWith getNameForNoNameArg name - - match tryTcrefOfAppTy g ty with - | ValueSome ty -> $"{name}: {ty}" - | ValueNone -> $"{name}") - |> String.concat ", " - - $"({names})") - |> String.concat " " - - Item.MethodGroup($"{name} {parameters}", [ meth ], None) - |> ItemWithNoInst - |> MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis) - - Some(overridableMethods, nenv.DisplayEnv, m) - | _ -> None) + let getOverridableMethods superTy overriddenMethods = + let isInterface = isInterfaceTy g superTy + GetIntrinsicMethInfosOfType + infoReader + None + ad + TypeHierarchy.AllowMultiIntfInstantiations.No + FindMemberFlag.PreferOverrides + range0 + superTy + |> List.filter (isMethodOverridable overriddenMethods) + |> List.filter (fun i -> i.IsInstance <> isStatic) + |> List.map (fun meth -> + let getNameForNoNameArg = + let mutable count = 1 + + fun () -> + let name = $"arg{count}" + count <- count + 1 + name + + let name = meth.DisplayName + + 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 = Option.defaultWith getNameForNoNameArg name + + match tryTcrefOfAppTy g ty with + | ValueSome ty -> $"{name}: {ty}" + | ValueNone -> $"{name}") + |> String.concat ", " + + $"({names})") + |> String.concat " " + + Item.MethodGroup($"{name} {parameters}", [ meth ], None) + |> ItemWithNoInst + |> MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis isInterface) + + if ctx = MethodOverrideCompletionContext.Class then + 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 overriddenMethods = + GetImmediateIntrinsicMethInfosOfType (None, ad) g amap typeNameRange ty + |> List.filter (fun x -> x.IsDefiniteFSharpOverride) + + let overridableMethods = getOverridableMethods superTy overriddenMethods + Some(overridableMethods, nenv.DisplayEnv, m) + | _ -> None) + else + let nameResItems = + GetPreciseItemsFromNameResolution(typeNameRange.End.Line, typeNameRange.End.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes) + match nameResItems with + | NameResResult.Members (ls, _, _) -> + ls + |> List.tryPick (function {Item = Item.Types(_, ty::_)} -> Some ty | _ -> None) + |> Option.map (fun ty -> getOverridableMethods ty [], nenv.DisplayEnv, m) + | _ -> None /// Gets all field identifiers of a union case that can be referred to in a pattern. let GetUnionCaseFields caseIdRange alreadyReferencedFields = @@ -1704,8 +1717,8 @@ type internal TypeCheckInfo getDeclaredItemsNotInRangeOpWithAllSymbols () |> Option.bind (FilterRelevantItemsBy getItem2 None IsPatternCandidate) - | Some(CompletionContext.MethodOverride(enclosingTypeNameRange, spacesBeforeOverrideKeyword, hasThis)) -> - GetOverridableMethods pos enclosingTypeNameRange spacesBeforeOverrideKeyword hasThis + | Some(CompletionContext.MethodOverride(ctx, enclosingTypeNameRange, spacesBeforeOverrideKeyword, hasThis, isStatic)) -> + GetOverridableMethods pos ctx enclosingTypeNameRange spacesBeforeOverrideKeyword hasThis isStatic | Some(CompletionContext.CaretAfterOperator m) -> let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index ff59e0abde1..85bc78d99cc 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -84,7 +84,7 @@ type UnresolvedSymbol = type CompletionInsertType = | Default | FullName - | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool + | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool * isInterface: bool type CompletionItem = { ItemWithInst: ItemWithInst @@ -1157,11 +1157,16 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i match item.Unresolved, item.InsertType with | Some u, _ -> u.DisplayName | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None, CompletionInsertType.MethodOverride (spacesBeforeOverrideKeyword, hasThis) -> + | None, CompletionInsertType.MethodOverride (spacesBeforeOverrideKeyword, hasThis, isInterface) -> match item.Item with - | Item.MethodGroup (name, _, _) -> - let nameWithThis = if hasThis then $"{name} = " else $"this.{name} = " - nameWithThis + System.Environment.NewLine + String.make (spacesBeforeOverrideKeyword + 4) ' ' + $"base.{name}" + | Item.MethodGroup (name, meth :: _, _) -> + let nameWithThis = if not meth.IsInstance || hasThis then $"{name} = " else $"this.{name} = " + let nameWithBase = + if meth.IsInstance then + if isInterface then "raise (System.NotImplementedException())" + else $"base.{name}" + else $"{meth.ApparentEnclosingTyconRef}.{name}" + nameWithThis + System.Environment.NewLine + String.make (spacesBeforeOverrideKeyword + 4) ' ' + nameWithBase | _ -> item.Item.DisplayNameCore | None, _ -> item.Item.DisplayName if not supportsPreferExtsMethodsOverProperty then diff --git a/src/Compiler/Service/ServiceDeclarationLists.fsi b/src/Compiler/Service/ServiceDeclarationLists.fsi index 527f5cf265f..c74af7990d1 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -82,7 +82,7 @@ type public UnresolvedSymbol = type internal CompletionInsertType = | Default | FullName - | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool + | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool * isInterface: bool type internal CompletionItem = { diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 238f72da85e..2e67ae6d137 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -599,9 +599,12 @@ 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 diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index d9063315a60..7a45432e14b 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 + | ObjExpr + [] type CompletionContext = /// Completion context cannot be determined due to errors @@ -105,9 +111,9 @@ type CompletionContext = /// Completing a pattern in a match clause, member/let binding or lambda | Pattern of context: PatternContext - + /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool + | MethodOverride of ctx: MethodOverrideCompletionContext * enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool * isStatic: bool /// (s =| ) or (s <>| ) | CaretAfterOperator of mExprBeforeOperator: range @@ -1507,10 +1513,28 @@ module ParsedInput = | SynLeadingKeyword.Override _ -> true | _ -> false - let overrideContext path (mOverride: range) hasThis = + let isStaticMember leadingKeyword = + match leadingKeyword with + | SynLeadingKeyword.StaticMember _ -> true + | _ -> false + + let overrideContext path (mOverride: range) hasThis isStatic = match path with | _ :: SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ -> - Some(CompletionContext.MethodOverride(enclosingType.idRange, mOverride.StartColumn, hasThis)) + Some(CompletionContext.MethodOverride(MethodOverrideCompletionContext.Class, enclosingType.idRange, mOverride.StartColumn, hasThis, isStatic)) + | SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: _ + | _ :: SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: _ -> + let ty = + match ty with + | SynType.App (typeName = ty) -> ty + | _ -> ty + Some(CompletionContext.MethodOverride(MethodOverrideCompletionContext.Interface, ty.Range, mOverride.StartColumn, hasThis, isStatic)) + | SyntaxNode.SynExpr(SynExpr.ObjExpr(objType = ty)) :: _ -> + let ty = + match ty with + | SynType.App (typeName = ty) -> ty + | _ -> ty + Some(CompletionContext.MethodOverride(MethodOverrideCompletionContext.ObjExpr, ty.Range, mOverride.StartColumn, hasThis, isStatic)) | _ -> Some CompletionContext.Invalid match returnInfo with @@ -1520,23 +1544,27 @@ module ParsedInput = // override | | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword && lineStr.[pos.Column - 1] = ' ' -> - overrideContext path trivia.LeadingKeyword.Range false + overrideContext path trivia.LeadingKeyword.Range false false + + // override | + | SynPat.FromParseError _ when isStaticMember trivia.LeadingKeyword -> + overrideContext path trivia.LeadingKeyword.Range false true // override _.| | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword -> - overrideContext path trivia.LeadingKeyword.Range true + overrideContext path trivia.LeadingKeyword.Range true false // override this.| | SynPat.Named(ident = SynIdent(ident = selfId)) when isOverride trivia.LeadingKeyword && selfId.idRange.End.IsAdjacentTo pos -> - overrideContext path trivia.LeadingKeyword.Range true + overrideContext path trivia.LeadingKeyword.Range true false // override this.ToStr| | SynPat.LongIdent(longDotId = SynLongIdent(id = [ _; methodId ])) when isOverride trivia.LeadingKeyword && rangeContainsPos methodId.idRange pos -> - overrideContext path trivia.LeadingKeyword.Range true + overrideContext path trivia.LeadingKeyword.Range true false | SynPat.LongIdent(longDotId = lidwd; argPats = SynArgPats.Pats pats; range = m) when rangeContainsPos m pos -> if rangeContainsPos lidwd.Range pos then diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 7e2843a3877..47ea5b7e0e3 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 + | ObjExpr + [] type public CompletionContext = /// Completion context cannot be determined due to errors @@ -79,7 +85,7 @@ type public CompletionContext = | Pattern of context: PatternContext /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool + | MethodOverride of ctx: MethodOverrideCompletionContext * enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool * isStatic: bool /// (s = | ) or (s <> | ) | CaretAfterOperator of mExprBeforeOperator: range diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 96058711135..fc120e395c7 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -2375,8 +2375,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 3 + let mEnd = mMember.EndRange + let bindingPat = patFromParseError (SynPat.Wild(mEnd)) + let expr = arbExpr ("classDefnMember1", mEnd) + let mWhole = rhs2 parseState 1 3 + 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 { [] } From 3aa34f8dc89456bdb0d031c2aaa18476df33372b Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Mon, 27 May 2024 14:57:48 +0800 Subject: [PATCH 07/28] fix #14375 support #16562 for C# style extension method --- src/Compiler/Service/FSharpCheckerResults.fs | 138 +++++++++++++++--- .../Service/ServiceAssemblyContent.fs | 8 +- .../Completion/CompletionProvider.fs | 81 +++++----- 3 files changed, 170 insertions(+), 57 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 7ce2bd0c001..94b41e2307b 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -311,7 +311,7 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti /// (Depending on the kind of the items, we may stop processing or continue to find better items) [] type NameResResult = - | Members of (ItemWithInst list * DisplayEnv * range) + | Members of ((ItemWithInst * AssemblySymbol voption) list * DisplayEnv * range) | Cancel of DisplayEnv * range | Empty @@ -325,7 +325,7 @@ type ExprTypingsResult = | NoneBecauseTypecheckIsStaleAndTextChanged | NoneBecauseThereWereTypeErrors | None - | Some of (ItemWithInst list * DisplayEnv * range) * TType + | Some of ((ItemWithInst * AssemblySymbol voption) list * DisplayEnv * range) * TType type Names = string list @@ -443,6 +443,84 @@ type internal TypeCheckInfo items + let getExtMethsOfType m ty getAllSymbols = + /// Checks if the type is used for C# style extension members. + let IsTyconRefUsedForCSharpStyleExtensionMembers g m (tcref: TyconRef) = + // Type must be non-generic and have 'Extension' attribute + isNil(tcref.Typars m) && TyconRefHasAttribute g m g.attrib_ExtensionAttribute tcref + || g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) + + /// A 'plain' method is an extension method not interpreted as an extension method. + let IsMethInfoPlainCSharpStyleExtensionMember g m isEnclExtTy (minfo: MethInfo) = + // Method must be static, have 'Extension' attribute, must not be curried, must have at least one argument + isEnclExtTy && + not minfo.IsInstance && + not minfo.IsExtensionMember && + (match minfo.NumArgs with [x] when x >= 1 -> true | _ -> false) && + AttributeChecking.MethInfoHasAttribute g m g.attrib_ExtensionAttribute minfo + let GetTyconRefForExtensionMembers minfo (deref: Entity) amap m g = + try + let rs = + match metadataOfTycon deref, minfo with + | ILTypeMetadata (TILObjectReprData(scope=scoref)), ILMeth(ilMethInfo=ILMethInfo(ilMethodDef=ilMethod)) -> + match ilMethod.ParameterTypes with + | firstTy :: _ -> + match firstTy with + | ILType.Boxed tspec | ILType.Value tspec -> + let tref = (tspec |> rescopeILTypeSpec scoref).TypeRef + if Import.CanImportILTypeRef amap m tref then + let tcref = tref |> Import.ImportILTypeRef amap m + if isCompiledTupleTyconRef g tcref || tyconRefEq g tcref g.fastFunc_tcr then None + else Some tcref + else None + | _ -> None + | _ -> None + | _ -> + // The results are indexed by the TyconRef of the first 'this' argument, if any. + // So we need to go and crack the type of the 'this' argument. + let thisTy = minfo.GetParamTypes(amap, m, generalizeTypars minfo.FormalMethodTypars).Head.Head + match thisTy with + | AppTy g (tcrefOfTypeExtended, _) when not (isByrefTy g thisTy) -> Some tcrefOfTypeExtended + | _ -> None + Some rs + with RecoverableException e -> // Import of the ILType may fail, if so report the error and skip on + errorRecovery e m + None + + let baseTys = + ty :: infoReader.GetEntireTypeHierarchy(TypeHierarchy.AllowMultiIntfInstantiations.Yes, m, ty) + |> List.choose (fun ty -> match tryTcrefOfAppTy g ty with ValueSome ty' -> Some (ty, ty') | _-> None) + let checkTy ty2 = + baseTys |> List.tryPick (fun (ty, ty') -> if tyconRefEq g ty' ty2 then Some ty else None) + + getAllSymbols() + |> List.collect (fun x -> + match x.Symbol.Item with + | Item.MethodGroup(name, meths, un) when not x.Symbol.IsExplicitlySuppressed -> + let meths = + meths + |> List.choose(fun i -> + let isEnclExtTy = IsTyconRefUsedForCSharpStyleExtensionMembers g m i.DeclaringTyconRef + if IsMethInfoPlainCSharpStyleExtensionMember g m isEnclExtTy i then + let ty2 = GetTyconRefForExtensionMembers i i.DeclaringTyconRef.Deref amap m g + match ty2 with + | Some(Some ty2) -> + match checkTy ty2 with + | Some ty -> + match i with + | ILMeth(_, mi, _) -> + Some(MethInfo.CreateILExtensionMeth(amap, m, ty, i.DeclaringTyconRef, Some 0UL, mi.RawMetadata)) + | _ -> Some i + | _ -> None + | _ -> None + else None + ) + if meths.IsEmpty then [] + else (ItemWithNoInst(Item.MethodGroup(name, meths, un)), ValueSome({ x with UnresolvedSymbol = { x.UnresolvedSymbol with DisplayName = name } })) :: [] + | _ -> [] + ) + let ItemWithNoInstWithNoSymbol (item: ItemWithInst) = item, (ValueNone: AssemblySymbol voption) + // Filter items to show only valid & return Some if there are any let ReturnItemsOfType (items: ItemWithInst list) g denv (m: range) filterCtors = let items = @@ -452,7 +530,7 @@ type internal TypeCheckInfo |> FilterItemsForCtors filterCtors if not (isNil items) then - NameResResult.Members(items, denv, m) + NameResResult.Members(items |> List.map ItemWithNoInstWithNoSymbol, denv, m) else NameResResult.Empty @@ -477,7 +555,7 @@ type internal TypeCheckInfo /// Looks at the exact name resolutions that occurred during type checking /// If 'membersByResidue' is specified, we look for members of the item obtained /// from the name resolution and filter them by the specified residue (?) - let GetPreciseItemsFromNameResolution (line, colAtEndOfNames, membersByResidue, filterCtors, resolveOverloads) = + let GetPreciseItemsFromNameResolution (line, colAtEndOfNames, membersByResidue, filterCtors, resolveOverloads, getAllSymbols) = let endOfNamesPos = mkPos line colAtEndOfNames // Logic below expects the list to be in reverse order of resolution @@ -543,9 +621,21 @@ type internal TypeCheckInfo let targets = ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m) + let globalItems = getExtMethsOfType m ty getAllSymbols let items = ResolveCompletionsInType ncenv nenv targets m ad false ty let items = List.map ItemWithNoInst items - ReturnItemsOfType items g denv m filterCtors + let items = + items + |> RemoveDuplicateItems g + |> RemoveExplicitlySuppressed g + |> FilterItemsForCtors filterCtors + |> List.map ItemWithNoInstWithNoSymbol + let items = items @ globalItems + + if not (isNil items) then + NameResResult.Members(items, denv, m) + else + NameResResult.Empty // No residue, so the items are the full resolution of the name | CNR(_, _, denv, _, _, m) :: _, None -> @@ -709,7 +799,7 @@ type internal TypeCheckInfo /// Looks at the exact expression types at the position to the left of the /// residue then the source when it was typechecked. - let GetPreciseCompletionListFromExprTypings (parseResults: FSharpParseFileResults, endOfExprPos, filterCtors) = + let GetPreciseCompletionListFromExprTypings (parseResults: FSharpParseFileResults, endOfExprPos, filterCtors, getAllSymbols) = let thereWereSomeQuals, quals = GetExprTypingForPosition(endOfExprPos) @@ -748,12 +838,16 @@ type internal TypeCheckInfo let targets = ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m) - + let items = ResolveCompletionsInType ncenv nenv targets m ad false ty let items = items |> List.map ItemWithNoInst let items = items |> RemoveDuplicateItems g let items = items |> RemoveExplicitlySuppressed g let items = items |> FilterItemsForCtors filterCtors + let globalItems = getExtMethsOfType m ty getAllSymbols + let items = + (items |> List.map (fun i -> i, ValueNone)) @ globalItems + |> IPartialEqualityComparer.partialDistinctBy (IPartialEqualityComparer.On (fun (item, _) -> item.Item) (ItemDisplayPartialEquality g)) ExprTypingsResult.Some((items, nenv.DisplayEnv, m), ty) | None -> if textChanged then @@ -970,6 +1064,7 @@ type internal TypeCheckInfo } let getItem (x: ItemWithInst) = x.Item + let getItemTuple2 (x: ItemWithInst, _) = x.Item let getItem2 (x: CompletionItem) = x.Item @@ -1123,11 +1218,11 @@ type internal TypeCheckInfo | _ -> None) else let nameResItems = - GetPreciseItemsFromNameResolution(typeNameRange.End.Line, typeNameRange.End.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes) + GetPreciseItemsFromNameResolution(typeNameRange.End.Line, typeNameRange.End.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes, fun () -> []) match nameResItems with | NameResResult.Members (ls, _, _) -> ls - |> List.tryPick (function {Item = Item.Types(_, ty::_)} -> Some ty | _ -> None) + |> List.tryPick (function ({Item = Item.Types(_, ty::_)}, _) -> Some ty | _ -> None) |> Option.map (fun ty -> getOverridableMethods ty [], nenv.DisplayEnv, m) | _ -> None @@ -1248,7 +1343,7 @@ type internal TypeCheckInfo // This is based on position (i.e. colAtEndOfNamesAndResidue). This is not used if a residueOpt is given. let nameResItems = match residueOpt with - | None -> GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors, resolveOverloads) + | None -> GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors, resolveOverloads, allSymbols) | Some residue -> // Deals with cases when we have spaces between dot and\or identifier, like A . $ // if this is our case - then we need to locate end position of the name skipping whitespaces @@ -1262,7 +1357,7 @@ type internal TypeCheckInfo match FindFirstNonWhitespacePosition lineStr (p - 1) with | Some colAtEndOfNames -> let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based - GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors, resolveOverloads) + GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors, resolveOverloads, allSymbols) | None -> NameResResult.Empty | _ -> NameResResult.Empty @@ -1302,11 +1397,11 @@ type internal TypeCheckInfo match nameResItems with | NameResResult.Cancel(denv, m) -> Some([], denv, m) - | NameResResult.Members(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m)) -> + | NameResResult.Members(FilterRelevantItems getItemTuple2 exactMatchResidueOpt (items, denv, m)) -> // lookup based on name resolution results successful Some( items - |> List.map (CompletionItem (getType ()) ValueNone CompletionInsertType.Default), + |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm CompletionInsertType.Default item), denv, m ) @@ -1334,7 +1429,7 @@ type internal TypeCheckInfo ) match leftOfDot with - | Some(pos, _) -> GetPreciseCompletionListFromExprTypings(parseResults, pos, filterCtors), true + | Some(pos, _) -> GetPreciseCompletionListFromExprTypings(parseResults, pos, filterCtors, allSymbols), true | None -> // Can get here in a case like: if "f xxx yyy" is legal, and we do "f xxx y" // We have no interest in expression typings, those are only useful for dot-completion. We want to fallback @@ -1342,7 +1437,7 @@ type internal TypeCheckInfo ExprTypingsResult.None, false match qualItems, thereIsADotInvolved with - | ExprTypingsResult.Some(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), ty), _ when + | ExprTypingsResult.Some(FilterRelevantItems getItemTuple2 exactMatchResidueOpt (items, denv, m), ty), _ when // Initially we only use the expression typings when looking up, e.g. (expr).Nam or (expr).Name1.Nam // These come through as an empty plid and residue "". Otherwise we try an environment lookup // and then return to the qualItems. This is because the expression typings are a little inaccurate, primarily because @@ -1352,7 +1447,8 @@ type internal TypeCheckInfo // lookup based on expression typings successful Some( items - |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.Default), + |> List.map ( + fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), denv, m ) @@ -1381,7 +1477,7 @@ type internal TypeCheckInfo // lookup based on name resolution results successful ValueSome( items - |> List.map (CompletionItem (getType ()) ValueNone CompletionInsertType.Default), + |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm CompletionInsertType.Default item), denv, m ) @@ -1398,10 +1494,11 @@ type internal TypeCheckInfo ) // Try again with the qualItems - | _, _, ExprTypingsResult.Some(FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), ty) -> + | _, _, ExprTypingsResult.Some(FilterRelevantItems getItemTuple2 exactMatchResidueOpt (items, denv, m), ty) -> ValueSome( items - |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.Default), + |> List.map ( + fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), denv, m ) @@ -1641,6 +1738,9 @@ type internal TypeCheckInfo match results with | NameResResult.Members(items, denv, m) -> + let items = + items + |> List.map fst let filtered = items |> RemoveDuplicateItems g diff --git a/src/Compiler/Service/ServiceAssemblyContent.fs b/src/Compiler/Service/ServiceAssemblyContent.fs index 2b54ed7f229..2a542c49691 100644 --- a/src/Compiler/Service/ServiceAssemblyContent.fs +++ b/src/Compiler/Service/ServiceAssemblyContent.fs @@ -108,14 +108,14 @@ type IAssemblyContentCache = module AssemblyContent = - let UnresolvedSymbol (topRequireQualifiedAccessParent: ShortIdents option) (cleanedIdents: ShortIdents) (fullName: string) = + let UnresolvedSymbol (topRequireQualifiedAccessParent: ShortIdents option) ns (cleanedIdents: ShortIdents) (fullName: string) = let getNamespace (idents: ShortIdents) = if idents.Length > 1 then Some idents[..idents.Length - 2] else None let ns = topRequireQualifiedAccessParent |> Option.bind getNamespace - |> Option.orElseWith (fun () -> getNamespace cleanedIdents) + |> Option.orElse ns |> Option.defaultValue [||] let displayName = @@ -149,7 +149,7 @@ module AssemblyContent = match entity with | FSharpSymbolPatterns.Attribute -> EntityKind.Attribute | _ -> EntityKind.Type - UnresolvedSymbol = UnresolvedSymbol topRequireQualifiedAccessParent cleanIdents fullName + UnresolvedSymbol = UnresolvedSymbol topRequireQualifiedAccessParent ns cleanIdents fullName }) let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = @@ -168,7 +168,7 @@ module AssemblyContent = AutoOpenParent = autoOpenParent Symbol = func Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern - UnresolvedSymbol = UnresolvedSymbol topRequireQualifiedAccessParent cleanedIdents fullName } + UnresolvedSymbol = UnresolvedSymbol topRequireQualifiedAccessParent ns cleanedIdents fullName } [ yield! func.TryGetFullDisplayName() |> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.')) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index a18987ac48c..2c5acb1d2fb 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -235,14 +235,10 @@ type internal FSharpCompletionProvider let glyph = Tokenizer.FSharpGlyphToRoslynGlyph(declarationItem.Glyph, declarationItem.Accessibility) - let namespaceName, filterText = - match declarationItem.NamespaceToOpen, declarationItem.NameInList.Split '.' with - // There is no namespace to open and the item name does not contain dots, so we don't need to pass special FilterText to Roslyn. - | None, [| _ |] -> null, null - | Some namespaceToOpen, idents -> namespaceToOpen, Array.last idents - // Either we have a namespace to open ("DateTime (open System)") or item name contains dots ("Array.map"), or both. - // We are passing last part of long ident as FilterText. - | None, idents -> null, Array.last idents + let namespaceName = + match declarationItem.NamespaceToOpen with + | None -> null + | Some namespaceToOpen -> namespaceToOpen let completionItem = FSharpCommonCompletionItem @@ -251,7 +247,7 @@ type internal FSharpCompletionProvider null, rules = noCommitOnSpaceRules, glyph = Nullable glyph, - filterText = filterText, + filterText = declarationItem.NameInList, inlineDescription = namespaceName ) .AddProperty(FullNamePropName, declarationItem.FullName) @@ -432,42 +428,59 @@ type internal FSharpCompletionProvider match item.Properties.TryGetValue NamespaceToOpenPropName with | false, _ -> return CompletionChange.Create(TextChange(item.Span, nameInCode)) | true, ns -> + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") let! sourceText = document.GetTextAsync(cancellationToken) + let completionInsertRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, item.Span, sourceText) + + let namespaceInserted = + checkFileResults.OpenDeclarations + |> Array.exists (fun i -> + Range.rangeContainsPos i.AppliedScope completionInsertRange.Start + && i.Modules |> List.distinct |> List.exists (fun i -> + i.IsNamespace && + match i.Namespace with + | Some x -> $"{x}.{i.DisplayName}" = ns + | _ -> i.DisplayName = ns + ) + ) - let textWithItemCommitted = - sourceText.WithChanges(TextChange(item.Span, nameInCode)) + if namespaceInserted then + return CompletionChange.Create(TextChange(item.Span, nameInCode)) + else + let textWithItemCommitted = + sourceText.WithChanges(TextChange(item.Span, nameInCode)) - let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) + let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) - let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpCompletionProvider)) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpCompletionProvider)) - let fullNameIdents = - fullName - |> ValueOption.map (fun x -> x.Split '.') - |> ValueOption.defaultValue [||] + let fullNameIdents = + fullName + |> ValueOption.map (fun x -> x.Split '.') + |> ValueOption.defaultValue [||] - let insertionPoint = - if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then - OpenStatementInsertionPoint.TopLevel - else - OpenStatementInsertionPoint.Nearest + let insertionPoint = + if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then + OpenStatementInsertionPoint.TopLevel + else + OpenStatementInsertionPoint.Nearest - let ctx = - ParsedInput.FindNearestPointToInsertOpenDeclaration line.LineNumber parseResults.ParseTree fullNameIdents insertionPoint + let ctx = + ParsedInput.FindNearestPointToInsertOpenDeclaration line.LineNumber parseResults.ParseTree fullNameIdents insertionPoint - let finalSourceText, changedSpanStartPos = - OpenDeclarationHelper.insertOpenDeclaration textWithItemCommitted ctx ns + let finalSourceText, changedSpanStartPos = + OpenDeclarationHelper.insertOpenDeclaration textWithItemCommitted ctx ns - let fullChangingSpan = TextSpan.FromBounds(changedSpanStartPos, item.Span.End) + let fullChangingSpan = TextSpan.FromBounds(changedSpanStartPos, item.Span.End) - let changedSpan = - TextSpan.FromBounds(changedSpanStartPos, item.Span.End + (finalSourceText.Length - sourceText.Length)) + let changedSpan = + TextSpan.FromBounds(changedSpanStartPos, item.Span.End + (finalSourceText.Length - sourceText.Length)) - let changedText = finalSourceText.ToString(changedSpan) + let changedText = finalSourceText.ToString(changedSpan) - return - CompletionChange - .Create(TextChange(fullChangingSpan, changedText)) - .WithNewPosition(Nullable(changedSpan.End)) + return + CompletionChange + .Create(TextChange(fullChangingSpan, changedText)) + .WithNewPosition(Nullable(changedSpan.End)) } |> CancellableTask.start cancellationToken From 2ab006276b04ce737990be01b5d3d12af48ec966 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:25:09 +0800 Subject: [PATCH 08/28] field completion in method parameter list more type completion after colon override completion after `member` in interface or object expreesion interface name completion after `interface` class or interface name completion after `new` of object expression --- src/Compiler/Service/FSharpCheckerResults.fs | 108 ++++++++++---- .../Service/ServiceAssemblyContent.fs | 15 +- src/Compiler/Service/ServiceParsedInputOps.fs | 141 ++++++++++++------ .../Service/ServiceParsedInputOps.fsi | 4 +- .../Completion/CompletionProvider.fs | 22 +-- 5 files changed, 200 insertions(+), 90 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 94b41e2307b..53edf4f3464 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -669,6 +669,24 @@ type internal TypeCheckInfo Some(StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)) | _, _ -> None + let getStaticFieldsOfSameTypeInTheType nenv ad m ty = + let targets = + ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox ncenv.g ncenv.amap m) + + let items = ResolveCompletionsInType ncenv nenv targets m ad true ty + + let items = + items + |> List.filter (function + | Item.Value(valRef) -> typeEquiv g ty valRef.Type + | Item.ILField(iLFieldInfo) -> typeEquiv g ty (iLFieldInfo.FieldType(amap, m)) + | Item.Property(info = pinfo :: _) -> typeEquiv g ty (pinfo.GetPropertyType(amap, m)) + | _ -> false) + + let items = items |> List.map ItemWithNoInst + let items = items |> RemoveDuplicateItems g + items |> RemoveExplicitlySuppressed g + let CollectParameters (methods: MethInfo list) amap m : Item list = methods |> List.collect (fun meth -> @@ -681,12 +699,39 @@ type internal TypeCheckInfo | None -> None) | _ -> []) - let GetNamedParametersAndSettableFields endOfExprPos = + let GetNamedParametersAndSettableFields endOfExprPos paramIdx paramName = let cnrs = GetCapturedNameResolutions endOfExprPos ResolveOverloads.No |> ResizeArray.toList |> List.rev + let getStaticFieldsOfSameTypeOfTheParameter nenv ad m (methods: MethInfo list) (items: Item list) = + match paramName with + | Some name -> + match items |> List.tryFind (fun i -> i.DisplayName = name) with + | Some(Item.OtherName(argType = ty)) -> getStaticFieldsOfSameTypeInTheType nenv ad m ty + | Some(Item.Value(valRef)) -> getStaticFieldsOfSameTypeInTheType nenv ad m valRef.Type + | Some(Item.ILField(iLFieldInfo)) -> getStaticFieldsOfSameTypeInTheType nenv ad m (iLFieldInfo.FieldType(amap, m)) + | Some(Item.Property(info = pinfo :: _)) -> getStaticFieldsOfSameTypeInTheType nenv ad m (pinfo.GetPropertyType(amap, m)) + | _ -> [] + | _ -> + let rec loop i ls = + match ls with + | [] -> None + | x :: t -> + if i = paramIdx then + let (ParamData(ttype = ty)) = x + Some ty + else loop (i + 1) t + methods + |> List.choose (fun meth -> + match meth.GetParamDatas(amap, m, meth.FormalMethodInst) with + | x :: _ -> + loop 0 x + | _ -> None) + |> List.collect (getStaticFieldsOfSameTypeInTheType nenv ad m) + + let result = match cnrs with | CNR(Item.CtorGroup(_, (ctor :: _ as ctors)), _, denv, nenv, ad, m) :: _ -> @@ -702,7 +747,10 @@ type internal TypeCheckInfo let parameters = CollectParameters ctors amap m let items = props @ parameters - Some(denv, m, items) + let p = getStaticFieldsOfSameTypeOfTheParameter nenv ad m ctors items + let items = List.map ItemWithNoInst items + + Some(denv, m, items, p) | CNR(Item.MethodGroup(_, methods, _), _, denv, nenv, ad, m) :: _ -> let props = methods @@ -712,14 +760,21 @@ type internal TypeCheckInfo let parameters = CollectParameters methods amap m let items = props @ parameters - Some(denv, m, items) + let p = getStaticFieldsOfSameTypeOfTheParameter nenv ad m methods items + let items = List.map ItemWithNoInst items + + Some(denv, m, items, p) | _ -> None match result with - | None -> NameResResult.Empty - | Some(denv, m, items) -> - let items = List.map ItemWithNoInst items - ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs + | None -> NameResResult.Empty, [] + | Some(denv, m, items, p) -> + let p = + p + |> RemoveDuplicateItems g + |> RemoveExplicitlySuppressed g + + ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs, p /// finds captured typing for the given position let GetExprTypingForPosition endOfExprPos = @@ -1731,13 +1786,13 @@ type internal TypeCheckInfo | Some(CompletionContext.RecordField(RecordContext.Declaration true)) -> None // Completion at ' SomeMethod( ... ) ' or ' [] ' with named arguments - | Some(CompletionContext.ParameterList(endPos, fields)) -> - let results = GetNamedParametersAndSettableFields endPos + | Some(CompletionContext.ParameterList(endPos, (idx, name, fields))) -> + let results = GetNamedParametersAndSettableFields endPos idx name let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () match results with - | NameResResult.Members(items, denv, m) -> + | NameResResult.Members(items, denv, m), p -> let items = items |> List.map fst @@ -1757,9 +1812,20 @@ type internal TypeCheckInfo InsertType = CompletionInsertType.Default }) + let p = + p + |> List.map (fun i -> + let ty = + match i.Item with + | Item.Value(valRef) -> valRef.Type + | Item.ILField(iLFieldInfo) -> (iLFieldInfo.FieldType(amap, m)) + | Item.Property(info = pinfo :: _) -> (pinfo.GetPropertyType(amap, m)) + | _ -> failwith "not possible" + CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName i) + match declaredItems with - | None -> Some(toCompletionItems (items, denv, m)) - | Some(declItems, declaredDisplayEnv, declaredRange) -> Some(filtered @ declItems, declaredDisplayEnv, declaredRange) + | None -> Some(p @ (items |> List.map DefaultCompletionItem), denv, m) + | Some(declItems, declaredDisplayEnv, declaredRange) -> Some(p @ filtered @ declItems, declaredDisplayEnv, declaredRange) | _ -> declaredItems | Some(CompletionContext.AttributeApplication) -> @@ -1828,23 +1894,7 @@ type internal TypeCheckInfo match bestQual with | Some bestQual -> let ty, nenv, ad, m = bestQual - - let targets = - ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m) - - let items = ResolveCompletionsInType ncenv nenv targets m ad true ty - - let items = - items - |> List.filter (function - | Item.Value(valRef) -> typeEquiv g ty valRef.Type - | Item.ILField(iLFieldInfo) -> typeEquiv g ty (iLFieldInfo.FieldType(amap, m)) - | Item.Property(info = pinfo :: _) -> typeEquiv g ty (pinfo.GetPropertyType(amap, m)) - | _ -> false) - - let items = items |> List.map ItemWithNoInst - let items = items |> RemoveDuplicateItems g - let items = items |> RemoveExplicitlySuppressed g + let items = getStaticFieldsOfSameTypeInTheType nenv ad m ty match declaredItems with | Some(declaredItems, _, _) -> diff --git a/src/Compiler/Service/ServiceAssemblyContent.fs b/src/Compiler/Service/ServiceAssemblyContent.fs index 2a542c49691..c3f99e620c4 100644 --- a/src/Compiler/Service/ServiceAssemblyContent.fs +++ b/src/Compiler/Service/ServiceAssemblyContent.fs @@ -108,12 +108,12 @@ type IAssemblyContentCache = module AssemblyContent = - let UnresolvedSymbol (topRequireQualifiedAccessParent: ShortIdents option) ns (cleanedIdents: ShortIdents) (fullName: string) = + let UnresolvedSymbol (nearestRequireQualifiedAccessParent: ShortIdents option) (cleanedIdents: ShortIdents) (fullName: string) ns = let getNamespace (idents: ShortIdents) = if idents.Length > 1 then Some idents[..idents.Length - 2] else None let ns = - topRequireQualifiedAccessParent + nearestRequireQualifiedAccessParent |> Option.bind getNamespace |> Option.orElse ns |> Option.defaultValue [||] @@ -130,10 +130,12 @@ module AssemblyContent = parent.FormatEntityFullName entity |> Option.map (fun (fullName, cleanIdents) -> let topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix + let nearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix + { FullName = fullName CleanedIdents = cleanIdents Namespace = ns - NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix + NearestRequireQualifiedAccessParent = nearestRequireQualifiedAccessParent TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix Symbol = entity @@ -149,11 +151,12 @@ module AssemblyContent = match entity with | FSharpSymbolPatterns.Attribute -> EntityKind.Attribute | _ -> EntityKind.Type - UnresolvedSymbol = UnresolvedSymbol topRequireQualifiedAccessParent ns cleanIdents fullName + UnresolvedSymbol = UnresolvedSymbol nearestRequireQualifiedAccessParent cleanIdents fullName ns }) let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = let topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix + let nearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix let autoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix membersFunctionsAndValues |> Seq.filter (fun x -> not x.IsInstanceMember && not x.IsPropertyGetterMethod && not x.IsPropertySetterMethod) @@ -163,12 +166,12 @@ module AssemblyContent = { FullName = fullName CleanedIdents = cleanedIdents Namespace = ns - NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix + NearestRequireQualifiedAccessParent = nearestRequireQualifiedAccessParent TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent AutoOpenParent = autoOpenParent Symbol = func Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern - UnresolvedSymbol = UnresolvedSymbol topRequireQualifiedAccessParent ns cleanedIdents fullName } + UnresolvedSymbol = UnresolvedSymbol nearestRequireQualifiedAccessParent cleanedIdents fullName ns } [ yield! func.TryGetFullDisplayName() |> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.')) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 7a45432e14b..1f554e392e9 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -90,8 +90,8 @@ type CompletionContext = | RangeOperator /// Completing named parameters\setters in parameter list of attributes\constructor\method calls - /// end of name ast node * list of properties\parameters that were already set - | ParameterList of pos * HashSet + /// end of name ast node * index of curr paameter * list of properties\parameters that were already set + | ParameterList of pos * (int * string option * HashSet) /// Completing an attribute name, outside of the constructor | AttributeApplication @@ -1085,18 +1085,40 @@ module ParsedInput = | Operator "op_Equality" (SynExpr.Ident id, _) -> Some id | _ -> None - let findSetters argList = + let rangeContainsPosOrIsSpacesBetweenRangeAndPos (lineStr: string) m 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 + rangeContainsPos m pos + // pos is before m + || posLt pos m.Start + || (posGt pos m.End && pos.Line = m.End.Line && loop pos.Column m.End.Column) + + let findSetters (lineStr, pos) argList = match argList with | SynExpr.Paren(SynExpr.Tuple(false, parameters, _, _), _, _, _) -> let setters = HashSet() + let mutable i = 0 + let mutable idx = -1 + let mutable namedParamName = None for p in parameters do + let isCurrParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr p.Range pos + if isCurrParam then idx <- i match p with - | Setter id -> ignore (setters.Add id.idText) + | Setter id -> + if isCurrParam then namedParamName <- Some id.idText + ignore (setters.Add id.idText) | _ -> () + i <- i + 1 - setters - | _ -> emptyStringSet + idx, namedParamName, setters + | SynExpr.Paren(expr = Setter id) -> + let setters = HashSet() + ignore (setters.Add id.idText) + 0, Some id.idText, setters + | _ -> -1, None, emptyStringSet let endOfLastIdent (lid: SynLongIdent) = let last = List.last lid.LongIdent @@ -1112,31 +1134,35 @@ module ParsedInput = | Some m -> m.End | None -> id.idRange.End - let (|NewObjectOrMethodCall|_|) e = + let (|NewObjectOrMethodCall|_|) (lineStr, pos) e = match e with | SynExpr.New(_, SynType.LongIdent typeName, arg, _) -> // new A() - Some(endOfLastIdent typeName, findSetters arg) + Some(endOfLastIdent typeName, findSetters (lineStr, pos) arg) | SynExpr.New(_, SynType.App(StripParenTypes(SynType.LongIdent typeName), _, _, _, mGreaterThan, _, _), arg, _) -> // new A<_>() - Some(endOfClosingTokenOrLastIdent mGreaterThan typeName, findSetters arg) + Some(endOfClosingTokenOrLastIdent mGreaterThan typeName, findSetters (lineStr, pos) arg) | SynExpr.App(_, false, SynExpr.Ident id, arg, _) -> // A() - Some(id.idRange.End, findSetters arg) + Some(id.idRange.End, findSetters (lineStr, pos) arg) | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.Ident id, _, _, _, mGreaterThan, _, _), arg, _) -> // A<_>() - Some(endOfClosingTokenOrIdent mGreaterThan id, findSetters arg) + Some(endOfClosingTokenOrIdent mGreaterThan id, findSetters (lineStr, pos) arg) | SynExpr.App(_, false, SynExpr.LongIdent(_, lid, _, _), arg, _) -> // A.B() - Some(endOfLastIdent lid, findSetters arg) + Some(endOfLastIdent lid, findSetters (lineStr, pos) arg) + + | SynExpr.App(_, false, SynExpr.DotGet(longDotId = lid), arg, _) -> + // A.B() + Some(endOfLastIdent lid, findSetters (lineStr, pos) arg) | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.LongIdent(_, lid, _, _), _, _, _, mGreaterThan, _, _), arg, _) -> // A.B<_>() - Some(endOfClosingTokenOrLastIdent mGreaterThan lid, findSetters arg) + Some(endOfClosingTokenOrLastIdent mGreaterThan lid, findSetters (lineStr, pos) arg) | _ -> None let isOnTheRightOfComma pos (elements: SynExpr list) (commas: range list) current = @@ -1154,11 +1180,12 @@ module ParsedInput = loop elements commas - let (|PartOfParameterList|_|) pos precedingArgument path = + let (|PartOfParameterList|_|) lineStr pos precedingArgument path = match path with - | SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall args) :: _ -> - if Option.isSome precedingArgument then None else Some args - | SyntaxNode.SynExpr(SynExpr.Tuple(false, elements, commas, _)) :: SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall args) :: _ -> + | SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> + //if Option.isSome precedingArgument then None else Some args + Some args + | SyntaxNode.SynExpr(SynExpr.Tuple(false, elements, commas, _)) :: SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> match precedingArgument with | None -> Some args | Some e -> @@ -1399,7 +1426,7 @@ module ParsedInput = /// Try to determine completion context for the given pair (row, columns) let TryGetCompletionContext (pos, parsedInput: ParsedInput, lineStr: string) : CompletionContext option = - + let visitor = { new SyntaxVisitorBase<_>() with member _.VisitExpr(path, _, defaultTraverse, expr) = @@ -1410,29 +1437,31 @@ module ParsedInput = | x -> x // ok, we found something - return it else match expr with + // new A(1, $) + | SynExpr.Paren(expr = SynExpr.Tuple (range = m)) // new A($) - | SynExpr.Const(SynConst.Unit, m) when rangeContainsPos m pos -> + | SynExpr.Const(SynConst.Unit, m) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> match path with - | SyntaxNode.SynExpr(NewObjectOrMethodCall args) :: _ -> Some(CompletionContext.ParameterList args) + | SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> Some(CompletionContext.ParameterList args) | _ -> defaultTraverse expr // new (... A$) | SynExpr.Ident id | SynExpr.LongIdent(longDotId = SynLongIdent([ id ], [], [ Some _ ])) when id.idRange.End = pos -> match path with - | PartOfParameterList pos None args -> Some(CompletionContext.ParameterList args) + | PartOfParameterList lineStr pos None args -> Some(CompletionContext.ParameterList args) | _ -> defaultTraverse expr // new (A$ = 1) // new (A = 1, $) - | Setter id when id.idRange.End = pos || rangeBeforePos expr.Range pos -> + | Setter id when id.idRange.End = pos || rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr expr.Range pos -> let precedingArgument = if id.idRange.End = pos then None else Some expr match path with - | PartOfParameterList pos precedingArgument args -> Some(CompletionContext.ParameterList args) + | PartOfParameterList lineStr pos precedingArgument args -> Some(CompletionContext.ParameterList args) | _ -> match expr with - | Operator "op_Equality" (l, r) when SyntaxTraversal.rangeContainsPosLeftEdgeInclusive r.Range pos -> + | Operator "op_Equality" (l, r) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> Some(CompletionContext.CaretAfterOperator l.Range) | _ -> defaultTraverse expr @@ -1448,9 +1477,25 @@ module ParsedInput = |> Option.orElseWith (fun () -> defaultTraverse expr) | Operator "op_Equality" (l, r) - | Operator "op_Inequality" (l, r) when SyntaxTraversal.rangeContainsPosLeftEdgeInclusive r.Range pos -> + | Operator "op_Inequality" (l, r) + | SynExpr.IfThenElse(ifExpr = Operator "op_Equality" (l, r) | Operator "op_Inequality" (l, r)) + | SynExpr.Set (l, r, _) + when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> Some(CompletionContext.CaretAfterOperator l.Range) + | SynExpr.LongIdentSet (l, r, _) + when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> + Some(CompletionContext.CaretAfterOperator l.Range) + + // let a = 1: | + | SynExpr.Typed(targetType = SynType.FromParseError(range = m)) + // let f() = let a: | + | SynExpr.FromParseError(expr = SynExpr.LetOrUse(bindings = SynBinding(expr = SynExpr.Typed(expr = SynExpr.ArbitraryAfterError(range = m))) :: _)) + when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> + Some CompletionContext.Type + + // { 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) = @@ -1508,19 +1553,25 @@ module ParsedInput = (SynBinding(headPat = headPat; trivia = trivia; returnInfo = returnInfo) as synBinding) ) = - let isOverride leadingKeyword = + let isOverrideOrMember leadingKeyword = match leadingKeyword with - | SynLeadingKeyword.Override _ -> true + | SynLeadingKeyword.Override _ + | SynLeadingKeyword.Member _ -> true | _ -> false let isStaticMember leadingKeyword = match leadingKeyword with | SynLeadingKeyword.StaticMember _ -> true | _ -> false + + let isMember leadingKeyword = + match leadingKeyword with + | SynLeadingKeyword.Member _ -> true + | _ -> false - let overrideContext path (mOverride: range) hasThis isStatic = + let overrideContext path (mOverride: range) hasThis isStatic isMember = match path with - | _ :: SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ -> + | _ :: 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.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: _ -> @@ -1538,33 +1589,33 @@ module ParsedInput = | _ -> 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 - // override | - | SynPat.FromParseError _ when isOverride trivia.LeadingKeyword && lineStr.[pos.Column - 1] = ' ' -> - overrideContext path trivia.LeadingKeyword.Range false false + // static member | + | SynPat.FromParseError _ when isStaticMember trivia.LeadingKeyword -> + overrideContext path trivia.LeadingKeyword.Range false true false // override | - | SynPat.FromParseError _ when isStaticMember trivia.LeadingKeyword -> - overrideContext path trivia.LeadingKeyword.Range false true + | 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 trivia.LeadingKeyword.Range true false + | 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 trivia.LeadingKeyword.Range true false + 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 + overrideContext path trivia.LeadingKeyword.Range true false (isMember trivia.LeadingKeyword) | SynPat.LongIdent(longDotId = lidwd; argPats = SynArgPats.Pats pats; range = m) when rangeContainsPos m pos -> if rangeContainsPos lidwd.Range pos then @@ -1749,9 +1800,15 @@ module ParsedInput = Some CompletionContext.AttributeApplication // [] elif rangeContainsPos att.ArgExpr.Range pos then - Some(CompletionContext.ParameterList(att.TypeName.Range.End, findSetters att.ArgExpr)) + Some(CompletionContext.ParameterList(att.TypeName.Range.End, findSetters (lineStr, pos) 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 47ea5b7e0e3..412bd23ed2e 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -62,8 +62,8 @@ type public CompletionContext = | RangeOperator /// Completing named parameters\setters in parameter list of attributes\constructor\method calls - /// end of name ast node * list of properties\parameters that were already set - | ParameterList of pos * HashSet + /// end of name ast node * index of curr paameter * list of properties\parameters that were already set + | ParameterList of pos * (int * string option * HashSet) /// Completing an attribute name, outside of the constructor | AttributeApplication diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 2c5acb1d2fb..bfad56990f9 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -432,19 +432,19 @@ type internal FSharpCompletionProvider let! sourceText = document.GetTextAsync(cancellationToken) let completionInsertRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, item.Span, sourceText) - let namespaceInserted = + let isNamespaceOrModuleInserted = checkFileResults.OpenDeclarations - |> Array.exists (fun i -> + |> Array.exists (fun i -> Range.rangeContainsPos i.AppliedScope completionInsertRange.Start - && i.Modules |> List.distinct |> List.exists (fun i -> - i.IsNamespace && - match i.Namespace with - | Some x -> $"{x}.{i.DisplayName}" = ns - | _ -> i.DisplayName = ns - ) - ) - - if namespaceInserted then + && i.Modules + |> List.distinct + |> List.exists (fun i -> + (i.IsNamespace || i.IsFSharpModule) + && match i.Namespace with + | Some x -> $"{x}.{i.DisplayName}" = ns + | _ -> i.DisplayName = ns)) + + if isNamespaceOrModuleInserted then return CompletionChange.Create(TextChange(item.Span, nameInCode)) else let textWithItemCommitted = From fc599c1663b7bd145c6ae1f68fe646c3087ca256 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Wed, 5 Jun 2024 02:27:21 +0800 Subject: [PATCH 09/28] sync with main --- .../Service/ServiceAssemblyContent.fs | 4 ++-- .../Completion/CompletionProvider.fs | 21 ------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/Compiler/Service/ServiceAssemblyContent.fs b/src/Compiler/Service/ServiceAssemblyContent.fs index 095ccbb86a6..e2033de5b3b 100644 --- a/src/Compiler/Service/ServiceAssemblyContent.fs +++ b/src/Compiler/Service/ServiceAssemblyContent.fs @@ -149,7 +149,7 @@ module AssemblyContent = { FullName = fullName CleanedIdents = cleanIdents Namespace = ns - NearestRequireQualifiedAccessParent = nearestRequireQualifiedAccessParent + NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix Symbol = entity @@ -180,7 +180,7 @@ module AssemblyContent = { FullName = fullName CleanedIdents = cleanedIdents Namespace = ns - NearestRequireQualifiedAccessParent = nearestRequireQualifiedAccessParent + NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent AutoOpenParent = autoOpenParent Symbol = func diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f0d8f4c2d30..fac20bf6371 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -428,28 +428,7 @@ type internal FSharpCompletionProvider match item.Properties.TryGetValue NamespaceToOpenPropName with | false, _ -> return CompletionChange.Create(TextChange(item.Span, nameInCode)) | true, ns -> - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") let! sourceText = document.GetTextAsync(cancellationToken) - let completionInsertRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, item.Span, sourceText) - - let isNamespaceOrModuleInserted = - checkFileResults.OpenDeclarations - |> Array.exists (fun i -> - Range.rangeContainsPos i.AppliedScope completionInsertRange.Start - && i.Modules - |> List.distinct - |> List.exists (fun i -> - (i.IsNamespace || i.IsFSharpModule) - && match i.Namespace with - | Some x -> $"{x}.{i.DisplayName}" = ns - | _ -> i.DisplayName = ns)) - - if isNamespaceOrModuleInserted then - return CompletionChange.Create(TextChange(item.Span, nameInCode)) - else - let textWithItemCommitted = - sourceText.WithChanges(TextChange(item.Span, nameInCode)) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") let completionInsertRange = From e8351b96ad290e8caa53aee45226cf21eebc9aee Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Wed, 5 Jun 2024 02:27:47 +0800 Subject: [PATCH 10/28] sync with main --- src/Compiler/Service/ServiceAssemblyContent.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compiler/Service/ServiceAssemblyContent.fs b/src/Compiler/Service/ServiceAssemblyContent.fs index e2033de5b3b..f1b3bf2ee16 100644 --- a/src/Compiler/Service/ServiceAssemblyContent.fs +++ b/src/Compiler/Service/ServiceAssemblyContent.fs @@ -170,7 +170,6 @@ module AssemblyContent = let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = let topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix - let nearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix let autoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix membersFunctionsAndValues |> Seq.filter (fun x -> not x.IsInstanceMember && not x.IsPropertyGetterMethod && not x.IsPropertySetterMethod) From 15789945d2a60b88c44d826afc76556cc5aa4f6b Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Thu, 6 Jun 2024 04:25:54 +0800 Subject: [PATCH 11/28] make override completion of property correctly --- src/Compiler/Checking/InfoReader.fs | 47 ++++ src/Compiler/Checking/InfoReader.fsi | 11 + src/Compiler/Service/FSharpCheckerResults.fs | 224 ++++++++++++++---- .../Service/ServiceDeclarationLists.fs | 30 +-- .../Service/ServiceDeclarationLists.fsi | 2 +- 5 files changed, 243 insertions(+), 71 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index f81d89a900a..03447c77e94 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -655,6 +655,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 +943,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.GetIntrinsicPropInfoWithOverridenPropOfType 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 +1002,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 GetIntrinsicPropInfoWithOverridenPropOfType (infoReader: InfoReader) optFilter ad allowMultiIntfInst findFlag m ty = + infoReader.GetIntrinsicPropInfoWithOverridenPropOfType 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..feb4c283d36 100644 --- a/src/Compiler/Checking/InfoReader.fsi +++ b/src/Compiler/Checking/InfoReader.fsi @@ -261,6 +261,17 @@ val GetIntrinsicPropInfosOfType: ty: TType -> PropInfo list +/// Get the flattened list of intrinsic properties in the hierarchy +val GetIntrinsicPropInfoWithOverridenPropOfType: + 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 53edf4f3464..b3adcbc2775 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1104,9 +1104,6 @@ type internal TypeCheckInfo let DefaultCompletionItem item = CompletionItem ValueNone ValueNone CompletionInsertType.Default item - let MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis isInterface item = - CompletionItem ValueNone ValueNone (CompletionInsertType.MethodOverride(spacesBeforeOverrideKeyword, hasThis, isInterface)) item - let CompletionItemSuggestedName displayName = { ItemWithInst = ItemWithNoInst(Item.NewDef(Ident(displayName, range0))) @@ -1206,55 +1203,178 @@ type internal TypeCheckInfo not candidate.IsFinal && not ( alreadyOverridden - |> List.exists (MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate) + |> ResizeArray.exists (MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate) + ) + + let isPropertyOverridable alreadyOverridden (candidate: PropInfo) = + candidate.IsVirtualProperty + && not ( + alreadyOverridden + |> List.exists (PropInfosEquivByNameAndSig EraseNone g amap range0 candidate) ) let (nenv, ad), m = GetBestEnvForPos pos + let denv = nenv.DisplayEnv + + 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 getBase (meth: MethInfo) = + let ty = generalizedTyconRef g meth.DeclaringTyconRef + if typeEquiv g ty superTy then "base" + else $"(this :> {stringOfTy denv ty})" - let getOverridableMethods superTy overriddenMethods = let isInterface = isInterfaceTy g superTy - GetIntrinsicMethInfosOfType - infoReader - None - ad - TypeHierarchy.AllowMultiIntfInstantiations.No - FindMemberFlag.PreferOverrides - range0 - superTy - |> List.filter (isMethodOverridable overriddenMethods) - |> List.filter (fun i -> i.IsInstance <> isStatic) - |> List.map (fun meth -> - let getNameForNoNameArg = - let mutable count = 1 - - fun () -> - let name = $"arg{count}" - count <- count + 1 - name - - let name = meth.DisplayName - - 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 = Option.defaultWith getNameForNoNameArg name - - match tryTcrefOfAppTy g ty with - | ValueSome ty -> $"{name}: {ty}" - | ValueNone -> $"{name}") - |> String.concat ", " - - $"({names})") - |> String.concat " " - - Item.MethodGroup($"{name} {parameters}", [ meth ], None) - |> ItemWithNoInst - |> MethodOverrideCompletionItem spacesBeforeOverrideKeyword hasThis isInterface) + let overridableProps = + GetIntrinsicPropInfoWithOverridenPropOfType + infoReader + None + ad + TypeHierarchy.AllowMultiIntfInstantiations.No + FindMemberFlag.PreferOverrides + range0 + superTy + |> List.choose (fun struct(prop, baseProp) -> + let canPick = + isPropertyOverridable overriddenProperties prop && + prop.IsStatic = isStatic + + 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 + + getterMeth |> ValueOption.iter checkedMethods.Add + setterMeth |> ValueOption.iter checkedMethods.Add + + if not canPick then None else + let getNameForNoNameArg = + let mutable count = 1 + + fun () -> + let name = $"arg{count}" + count <- count + 1 + name + + let parameters = + prop.GetParamNamesAndTypes(amap, m) + |> List.map (fun (ParamNameAndType(name, ty)) -> + let name = + name + |> Option.map _.idText + |> Option.defaultWith getNameForNoNameArg + + $"{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 ($"{getBase meth}.{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 implementBody = checkMethAbstractAndGetImplementBody meth ($"{getBase meth}.{prop.DisplayName}" + (if prop.IsIndexer then $"[({parameters})]" else String.Empty) + " <- value") + let parameters = if prop.IsIndexer then $"({parameters}) " else String.Empty + let setter = $"set {parameters}(value: {retTy})" + setter, $"{setter} = {implementBody}" + | _ -> String.Empty, String.Empty + + let keywordAnd = if getterMeth.IsNone || setterMeth.IsNone then String.Empty else " and " + let name = $"{prop.DisplayName} with {getter}{keywordAnd}{setter}" + + let textInCode = + let this = if hasThis || prop.IsStatic then String.Empty else "this." + let getterWithBody = if String.IsNullOrWhiteSpace getterWithBody then String.Empty else getterWithBody + newlineIndent + this + prop.DisplayName + newlineIndent + "with " + getterWithBody + keywordAnd + setterWithBody + + Item.Property(name, [ prop ], None) + |> ItemWithNoInst + |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) + |> Some) + + let overridableMeths = + GetIntrinsicMethInfosOfType + infoReader + None + ad + TypeHierarchy.AllowMultiIntfInstantiations.No + FindMemberFlag.PreferOverrides + range0 + superTy + |> List.choose (fun meth -> + let canPick = + isMethodOverridable checkedMethods meth && + meth.IsInstance <> isStatic && + (not isInterface || not(tyconRefEq g meth.DeclaringTyconRef g.system_Object_tcref)) + + checkedMethods.Add meth + + if not canPick then None else + + let getNameForNoNameArg = + let mutable count = 1 + + fun () -> + let name = $"arg{count}" + count <- count + 1 + name + + 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 = Option.defaultWith getNameForNoNameArg name + + $"{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 $"{getBase meth}.{meth.DisplayName}{parameters}" + nameWithThis + newlineIndent + implementBody + + Item.MethodGroup(name, [ meth ], None) + |> ItemWithNoInst + |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) + |> Some) + + overridableProps @ overridableMeths if ctx = MethodOverrideCompletionContext.Class then sResolutions.CapturedNameResolutions @@ -1268,8 +1388,12 @@ type internal TypeCheckInfo GetImmediateIntrinsicMethInfosOfType (None, ad) g amap typeNameRange ty |> List.filter (fun x -> x.IsDefiniteFSharpOverride) - let overridableMethods = getOverridableMethods superTy overriddenMethods - Some(overridableMethods, nenv.DisplayEnv, m) + let overriddenProperties = + GetImmediateIntrinsicPropInfosOfType (None, ad) g amap typeNameRange ty + |> List.filter (fun x -> x.IsDefiniteFSharpOverride) + + let overridableMethods = getOverridableMethods superTy overriddenMethods overriddenProperties + Some(overridableMethods, denv, m) | _ -> None) else let nameResItems = @@ -1278,7 +1402,7 @@ type internal TypeCheckInfo | NameResResult.Members (ls, _, _) -> ls |> List.tryPick (function ({Item = Item.Types(_, ty::_)}, _) -> Some ty | _ -> None) - |> Option.map (fun ty -> getOverridableMethods ty [], nenv.DisplayEnv, m) + |> Option.map (fun ty -> getOverridableMethods ty [] [], denv, m) | _ -> None /// Gets all field identifiers of a union case that can be referred to in a pattern. diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index 85bc78d99cc..099aa97adb5 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -84,7 +84,7 @@ type UnresolvedSymbol = type CompletionInsertType = | Default | FullName - | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool * isInterface: bool + | CustomText of textInCode: string type CompletionItem = { ItemWithInst: ItemWithInst @@ -1072,6 +1072,12 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i | _ -> false | _ -> false + static let getTextInListOfCustomTextItem item = + match item with + | Item.MethodGroup (displayName = name) + | Item.Property (name = name) -> name + | _ -> item.DisplayName + member _.Items = declarations member _.IsForType = isForType @@ -1134,10 +1140,7 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i | ns -> (ns |> String.concat ".") + "." + u.DisplayName | None -> match x.InsertType with - | CompletionInsertType.MethodOverride _ -> - match x.Item with - | Item.MethodGroup (name, _, _) -> name - | _ -> x.Item.DisplayNameCore + | CompletionInsertType.CustomText _ -> getTextInListOfCustomTextItem x.Item | _ -> x.Item.DisplayName ) |> List.map ( @@ -1145,10 +1148,7 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i match item.Unresolved, item.InsertType with | Some u, _ -> u.DisplayName | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None, CompletionInsertType.MethodOverride _ -> - match item.Item with - | Item.MethodGroup (name, _, _) -> name - | _ -> item.Item.DisplayNameCore + | None, CompletionInsertType.CustomText _ -> getTextInListOfCustomTextItem item.Item | None, _ -> item.Item.DisplayNameCore let textInCode (item: CompletionItem) = match item.Item with @@ -1157,17 +1157,7 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i match item.Unresolved, item.InsertType with | Some u, _ -> u.DisplayName | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None, CompletionInsertType.MethodOverride (spacesBeforeOverrideKeyword, hasThis, isInterface) -> - match item.Item with - | Item.MethodGroup (name, meth :: _, _) -> - let nameWithThis = if not meth.IsInstance || hasThis then $"{name} = " else $"this.{name} = " - let nameWithBase = - if meth.IsInstance then - if isInterface then "raise (System.NotImplementedException())" - else $"base.{name}" - else $"{meth.ApparentEnclosingTyconRef}.{name}" - nameWithThis + System.Environment.NewLine + String.make (spacesBeforeOverrideKeyword + 4) ' ' + nameWithBase - | _ -> item.Item.DisplayNameCore + | None, CompletionInsertType.CustomText textInCode -> textInCode | None, _ -> item.Item.DisplayName if not supportsPreferExtsMethodsOverProperty then // we don't pay the cost of filtering specific to RFC-1137 diff --git a/src/Compiler/Service/ServiceDeclarationLists.fsi b/src/Compiler/Service/ServiceDeclarationLists.fsi index c74af7990d1..b773637e47c 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -82,7 +82,7 @@ type public UnresolvedSymbol = type internal CompletionInsertType = | Default | FullName - | MethodOverride of spacesBeforeOverrideKeyword: int * hasThis: bool * isInterface: bool + | CustomText of textInCode: string type internal CompletionItem = { From 74967bab9d9f3fb663a95fa291b65d7e332ccf7d Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Thu, 6 Jun 2024 04:34:59 +0800 Subject: [PATCH 12/28] for accessing base method, only write `base` format code --- src/Compiler/Checking/InfoReader.fsi | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 449 +++++++++++------- src/Compiler/Service/ServiceParseTreeWalk.fs | 6 +- src/Compiler/Service/ServiceParsedInputOps.fs | 114 +++-- .../Service/ServiceParsedInputOps.fsi | 7 +- 5 files changed, 372 insertions(+), 206 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fsi b/src/Compiler/Checking/InfoReader.fsi index feb4c283d36..2e6af4fd14d 100644 --- a/src/Compiler/Checking/InfoReader.fsi +++ b/src/Compiler/Checking/InfoReader.fsi @@ -270,7 +270,7 @@ val GetIntrinsicPropInfoWithOverridenPropOfType: findFlag: FindMemberFlag -> m: range -> ty: TType -> - struct(PropInfo * PropInfo voption) list + struct (PropInfo * PropInfo voption) list /// Perform type-directed name resolution of a particular named member in an F# type val TryFindIntrinsicNamedItemOfType: diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index b3adcbc2775..3a85b952a4d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -447,64 +447,88 @@ type internal TypeCheckInfo /// Checks if the type is used for C# style extension members. let IsTyconRefUsedForCSharpStyleExtensionMembers g m (tcref: TyconRef) = // Type must be non-generic and have 'Extension' attribute - isNil(tcref.Typars m) && TyconRefHasAttribute g m g.attrib_ExtensionAttribute tcref + isNil (tcref.Typars m) + && TyconRefHasAttribute g m g.attrib_ExtensionAttribute tcref || g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) - + /// A 'plain' method is an extension method not interpreted as an extension method. let IsMethInfoPlainCSharpStyleExtensionMember g m isEnclExtTy (minfo: MethInfo) = // Method must be static, have 'Extension' attribute, must not be curried, must have at least one argument - isEnclExtTy && - not minfo.IsInstance && - not minfo.IsExtensionMember && - (match minfo.NumArgs with [x] when x >= 1 -> true | _ -> false) && - AttributeChecking.MethInfoHasAttribute g m g.attrib_ExtensionAttribute minfo - let GetTyconRefForExtensionMembers minfo (deref: Entity) amap m g = + isEnclExtTy + && not minfo.IsInstance + && not minfo.IsExtensionMember + && (match minfo.NumArgs with + | [ x ] when x >= 1 -> true + | _ -> false) + && AttributeChecking.MethInfoHasAttribute g m g.attrib_ExtensionAttribute minfo + + let GetTyconRefForExtensionMembers minfo (deref: Entity) amap m g = try let rs = match metadataOfTycon deref, minfo with - | ILTypeMetadata (TILObjectReprData(scope=scoref)), ILMeth(ilMethInfo=ILMethInfo(ilMethodDef=ilMethod)) -> + | ILTypeMetadata(TILObjectReprData(scope = scoref)), ILMeth(ilMethInfo = ILMethInfo(ilMethodDef = ilMethod)) -> match ilMethod.ParameterTypes with | firstTy :: _ -> match firstTy with - | ILType.Boxed tspec | ILType.Value tspec -> + | ILType.Boxed tspec + | ILType.Value tspec -> let tref = (tspec |> rescopeILTypeSpec scoref).TypeRef + if Import.CanImportILTypeRef amap m tref then let tcref = tref |> Import.ImportILTypeRef amap m - if isCompiledTupleTyconRef g tcref || tyconRefEq g tcref g.fastFunc_tcr then None - else Some tcref - else None + + if isCompiledTupleTyconRef g tcref || tyconRefEq g tcref g.fastFunc_tcr then + None + else + Some tcref + else + None | _ -> None | _ -> None | _ -> // The results are indexed by the TyconRef of the first 'this' argument, if any. // So we need to go and crack the type of the 'this' argument. - let thisTy = minfo.GetParamTypes(amap, m, generalizeTypars minfo.FormalMethodTypars).Head.Head + let thisTy = + minfo + .GetParamTypes(amap, m, generalizeTypars minfo.FormalMethodTypars) + .Head.Head + match thisTy with | AppTy g (tcrefOfTypeExtended, _) when not (isByrefTy g thisTy) -> Some tcrefOfTypeExtended | _ -> None + Some rs with RecoverableException e -> // Import of the ILType may fail, if so report the error and skip on errorRecovery e m None - - let baseTys = - ty :: infoReader.GetEntireTypeHierarchy(TypeHierarchy.AllowMultiIntfInstantiations.Yes, m, ty) - |> List.choose (fun ty -> match tryTcrefOfAppTy g ty with ValueSome ty' -> Some (ty, ty') | _-> None) + + let baseTys = + ty + :: infoReader.GetEntireTypeHierarchy(TypeHierarchy.AllowMultiIntfInstantiations.Yes, m, ty) + |> List.choose (fun ty -> + match tryTcrefOfAppTy g ty with + | ValueSome ty' -> Some(ty, ty') + | _ -> None) + let checkTy ty2 = - baseTys |> List.tryPick (fun (ty, ty') -> if tyconRefEq g ty' ty2 then Some ty else None) + baseTys + |> List.tryPick (fun (ty, ty') -> if tyconRefEq g ty' ty2 then Some ty else None) - getAllSymbols() + getAllSymbols () |> List.collect (fun x -> match x.Symbol.Item with | Item.MethodGroup(name, meths, un) when not x.Symbol.IsExplicitlySuppressed -> let meths = meths - |> List.choose(fun i -> - let isEnclExtTy = IsTyconRefUsedForCSharpStyleExtensionMembers g m i.DeclaringTyconRef + |> List.choose (fun i -> + let isEnclExtTy = + IsTyconRefUsedForCSharpStyleExtensionMembers g m i.DeclaringTyconRef + if IsMethInfoPlainCSharpStyleExtensionMember g m isEnclExtTy i then let ty2 = GetTyconRefForExtensionMembers i i.DeclaringTyconRef.Deref amap m g - match ty2 with - | Some(Some ty2) -> + + match ty2 with + | Some(Some ty2) -> match checkTy ty2 with | Some ty -> match i with @@ -513,13 +537,26 @@ type internal TypeCheckInfo | _ -> Some i | _ -> None | _ -> None - else None - ) - if meths.IsEmpty then [] - else (ItemWithNoInst(Item.MethodGroup(name, meths, un)), ValueSome({ x with UnresolvedSymbol = { x.UnresolvedSymbol with DisplayName = name } })) :: [] - | _ -> [] - ) - let ItemWithNoInstWithNoSymbol (item: ItemWithInst) = item, (ValueNone: AssemblySymbol voption) + else + None) + + if meths.IsEmpty then + [] + else + (ItemWithNoInst(Item.MethodGroup(name, meths, un)), + ValueSome( + { x with + UnresolvedSymbol = + { x.UnresolvedSymbol with + DisplayName = name + } + } + )) + :: [] + | _ -> []) + + let ItemWithNoInstWithNoSymbol (item: ItemWithInst) = + item, (ValueNone: AssemblySymbol voption) // Filter items to show only valid & return Some if there are any let ReturnItemsOfType (items: ItemWithInst list) g denv (m: range) filterCtors = @@ -624,12 +661,14 @@ type internal TypeCheckInfo let globalItems = getExtMethsOfType m ty getAllSymbols let items = ResolveCompletionsInType ncenv nenv targets m ad false ty let items = List.map ItemWithNoInst items + let items = items |> RemoveDuplicateItems g |> RemoveExplicitlySuppressed g |> FilterItemsForCtors filterCtors |> List.map ItemWithNoInstWithNoSymbol + let items = items @ globalItems if not (isNil items) then @@ -707,14 +746,14 @@ type internal TypeCheckInfo let getStaticFieldsOfSameTypeOfTheParameter nenv ad m (methods: MethInfo list) (items: Item list) = match paramName with - | Some name -> + | Some name -> match items |> List.tryFind (fun i -> i.DisplayName = name) with | Some(Item.OtherName(argType = ty)) -> getStaticFieldsOfSameTypeInTheType nenv ad m ty | Some(Item.Value(valRef)) -> getStaticFieldsOfSameTypeInTheType nenv ad m valRef.Type | Some(Item.ILField(iLFieldInfo)) -> getStaticFieldsOfSameTypeInTheType nenv ad m (iLFieldInfo.FieldType(amap, m)) | Some(Item.Property(info = pinfo :: _)) -> getStaticFieldsOfSameTypeInTheType nenv ad m (pinfo.GetPropertyType(amap, m)) | _ -> [] - | _ -> + | _ -> let rec loop i ls = match ls with | [] -> None @@ -722,16 +761,16 @@ type internal TypeCheckInfo if i = paramIdx then let (ParamData(ttype = ty)) = x Some ty - else loop (i + 1) t + else + loop (i + 1) t + methods |> List.choose (fun meth -> match meth.GetParamDatas(amap, m, meth.FormalMethodInst) with - | x :: _ -> - loop 0 x + | x :: _ -> loop 0 x | _ -> None) |> List.collect (getStaticFieldsOfSameTypeInTheType nenv ad m) - let result = match cnrs with | CNR(Item.CtorGroup(_, (ctor :: _ as ctors)), _, denv, nenv, ad, m) :: _ -> @@ -769,10 +808,7 @@ type internal TypeCheckInfo match result with | None -> NameResResult.Empty, [] | Some(denv, m, items, p) -> - let p = - p - |> RemoveDuplicateItems g - |> RemoveExplicitlySuppressed g + let p = p |> RemoveDuplicateItems g |> RemoveExplicitlySuppressed g ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs, p @@ -893,16 +929,20 @@ type internal TypeCheckInfo let targets = ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m) - + let items = ResolveCompletionsInType ncenv nenv targets m ad false ty let items = items |> List.map ItemWithNoInst let items = items |> RemoveDuplicateItems g let items = items |> RemoveExplicitlySuppressed g let items = items |> FilterItemsForCtors filterCtors let globalItems = getExtMethsOfType m ty getAllSymbols - let items = + + let items = (items |> List.map (fun i -> i, ValueNone)) @ globalItems - |> IPartialEqualityComparer.partialDistinctBy (IPartialEqualityComparer.On (fun (item, _) -> item.Item) (ItemDisplayPartialEquality g)) + |> IPartialEqualityComparer.partialDistinctBy ( + IPartialEqualityComparer.On (fun (item, _) -> item.Item) (ItemDisplayPartialEquality g) + ) + ExprTypingsResult.Some((items, nenv.DisplayEnv, m), ty) | None -> if textChanged then @@ -1215,17 +1255,22 @@ type internal TypeCheckInfo let (nenv, ad), m = GetBestEnvForPos pos let denv = nenv.DisplayEnv - + 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) ' ' + 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() = + //type AA() = // abstract a: unit -> unit // default _.a() = printfn "A" //type BB() = @@ -1236,12 +1281,8 @@ type internal TypeCheckInfo // override | (* Here should not suggest to override `AA.a` *) let checkedMethods = ResizeArray(overriddenMethods) - let getBase (meth: MethInfo) = - let ty = generalizedTyconRef g meth.DeclaringTyconRef - if typeEquiv g ty superTy then "base" - else $"(this :> {stringOfTy denv ty})" - let isInterface = isInterfaceTy g superTy + let overridableProps = GetIntrinsicPropInfoWithOverridenPropOfType infoReader @@ -1251,72 +1292,104 @@ type internal TypeCheckInfo FindMemberFlag.PreferOverrides range0 superTy - |> List.choose (fun struct(prop, baseProp) -> + |> List.choose (fun struct (prop, baseProp) -> let canPick = - isPropertyOverridable overriddenProperties prop && - prop.IsStatic = isStatic + isPropertyOverridable overriddenProperties prop && prop.IsStatic = isStatic - 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 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 getterMeth |> ValueOption.iter checkedMethods.Add setterMeth |> ValueOption.iter checkedMethods.Add - if not canPick then None else - let getNameForNoNameArg = - let mutable count = 1 - - fun () -> - let name = $"arg{count}" - count <- count + 1 - name + if not canPick then + None + else + let getNameForNoNameArg = + let mutable count = 1 - let parameters = - prop.GetParamNamesAndTypes(amap, m) - |> List.map (fun (ParamNameAndType(name, ty)) -> - let name = + fun () -> + let name = $"arg{count}" + count <- count + 1 name - |> Option.map _.idText - |> Option.defaultWith getNameForNoNameArg - - $"{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 ($"{getBase meth}.{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 implementBody = checkMethAbstractAndGetImplementBody meth ($"{getBase meth}.{prop.DisplayName}" + (if prop.IsIndexer then $"[({parameters})]" else String.Empty) + " <- value") - let parameters = if prop.IsIndexer then $"({parameters}) " else String.Empty - let setter = $"set {parameters}(value: {retTy})" - setter, $"{setter} = {implementBody}" - | _ -> String.Empty, String.Empty - - let keywordAnd = if getterMeth.IsNone || setterMeth.IsNone then String.Empty else " and " - let name = $"{prop.DisplayName} with {getter}{keywordAnd}{setter}" - - let textInCode = - let this = if hasThis || prop.IsStatic then String.Empty else "this." - let getterWithBody = if String.IsNullOrWhiteSpace getterWithBody then String.Empty else getterWithBody + newlineIndent - this + prop.DisplayName + newlineIndent + "with " + getterWithBody + keywordAnd + setterWithBody - - Item.Property(name, [ prop ], None) - |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) - |> Some) + + let parameters = + prop.GetParamNamesAndTypes(amap, m) + |> List.map (fun (ParamNameAndType(name, ty)) -> + let name = name |> Option.map _.idText |> Option.defaultWith getNameForNoNameArg + + $"{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 implementBody = + checkMethAbstractAndGetImplementBody + meth + ($"base.{prop.DisplayName}" + + (if prop.IsIndexer then $"[({parameters})]" else String.Empty) + + " <- value") + + let parameters = if prop.IsIndexer then $"({parameters}) " else String.Empty + let setter = $"set {parameters}(value: {retTy})" + setter, $"{setter} = {implementBody}" + | _ -> String.Empty, String.Empty + + let keywordAnd = + if getterMeth.IsNone || setterMeth.IsNone then + String.Empty + else + " and " + + let name = $"{prop.DisplayName} with {getter}{keywordAnd}{setter}" + + let textInCode = + let this = if hasThis || prop.IsStatic then String.Empty else "this." + + let getterWithBody = + if String.IsNullOrWhiteSpace getterWithBody then + String.Empty + else + getterWithBody + newlineIndent + + this + + prop.DisplayName + + newlineIndent + + "with " + + getterWithBody + + keywordAnd + + setterWithBody + + Item.Property(name, [ prop ], None) + |> ItemWithNoInst + |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) + |> Some) let overridableMeths = GetIntrinsicMethInfosOfType @@ -1327,52 +1400,63 @@ type internal TypeCheckInfo FindMemberFlag.PreferOverrides range0 superTy - |> List.choose (fun meth -> + |> List.choose (fun meth -> let canPick = - isMethodOverridable checkedMethods meth && - meth.IsInstance <> isStatic && - (not isInterface || not(tyconRefEq g meth.DeclaringTyconRef g.system_Object_tcref)) + isMethodOverridable checkedMethods meth + && meth.IsInstance <> isStatic + && (not isInterface + || not (tyconRefEq g meth.DeclaringTyconRef g.system_Object_tcref)) checkedMethods.Add meth - if not canPick then None else - - let getNameForNoNameArg = - let mutable count = 1 - - fun () -> - let name = $"arg{count}" - count <- count + 1 - name - - 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 = Option.defaultWith getNameForNoNameArg name - - $"{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 $"{getBase meth}.{meth.DisplayName}{parameters}" - nameWithThis + newlineIndent + implementBody - - Item.MethodGroup(name, [ meth ], None) - |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) - |> Some) + if not canPick then + None + else + + let getNameForNoNameArg = + let mutable count = 1 + + fun () -> + let name = $"arg{count}" + count <- count + 1 + name + + 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 = Option.defaultWith getNameForNoNameArg name + + $"{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 + + Item.MethodGroup(name, [ meth ], None) + |> ItemWithNoInst + |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) + |> Some) overridableProps @ overridableMeths @@ -1392,16 +1476,28 @@ type internal TypeCheckInfo GetImmediateIntrinsicPropInfosOfType (None, ad) g amap typeNameRange ty |> List.filter (fun x -> x.IsDefiniteFSharpOverride) - let overridableMethods = getOverridableMethods superTy overriddenMethods overriddenProperties + let overridableMethods = + getOverridableMethods superTy overriddenMethods overriddenProperties + Some(overridableMethods, denv, m) | _ -> None) else let nameResItems = - GetPreciseItemsFromNameResolution(typeNameRange.End.Line, typeNameRange.End.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes, fun () -> []) + GetPreciseItemsFromNameResolution( + typeNameRange.End.Line, + typeNameRange.End.Column, + None, + ResolveTypeNamesToTypeRefs, + ResolveOverloads.Yes, + fun () -> [] + ) + match nameResItems with - | NameResResult.Members (ls, _, _) -> - ls - |> List.tryPick (function ({Item = Item.Types(_, ty::_)}, _) -> Some ty | _ -> None) + | NameResResult.Members(ls, _, _) -> + ls + |> List.tryPick (function + | ({ Item = Item.Types(_, ty :: _) }, _) -> Some ty + | _ -> None) |> Option.map (fun ty -> getOverridableMethods ty [] [], denv, m) | _ -> None @@ -1522,7 +1618,8 @@ type internal TypeCheckInfo // This is based on position (i.e. colAtEndOfNamesAndResidue). This is not used if a residueOpt is given. let nameResItems = match residueOpt with - | None -> GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors, resolveOverloads, allSymbols) + | None -> + GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors, resolveOverloads, allSymbols) | Some residue -> // Deals with cases when we have spaces between dot and\or identifier, like A . $ // if this is our case - then we need to locate end position of the name skipping whitespaces @@ -1536,7 +1633,15 @@ type internal TypeCheckInfo match FindFirstNonWhitespacePosition lineStr (p - 1) with | Some colAtEndOfNames -> let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based - GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors, resolveOverloads, allSymbols) + + GetPreciseItemsFromNameResolution( + line, + colAtEndOfNames, + Some(residue), + filterCtors, + resolveOverloads, + allSymbols + ) | None -> NameResResult.Empty | _ -> NameResResult.Empty @@ -1626,8 +1731,7 @@ type internal TypeCheckInfo // lookup based on expression typings successful Some( items - |> List.map ( - fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), + |> List.map (fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), denv, m ) @@ -1676,8 +1780,8 @@ type internal TypeCheckInfo | _, _, ExprTypingsResult.Some(FilterRelevantItems getItemTuple2 exactMatchResidueOpt (items, denv, m), ty) -> ValueSome( items - |> List.map ( - fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), + |> List.map (fun (item, asm) -> + CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), denv, m ) @@ -1917,9 +2021,8 @@ type internal TypeCheckInfo match results with | NameResResult.Members(items, denv, m), p -> - let items = - items - |> List.map fst + let items = items |> List.map fst + let filtered = items |> RemoveDuplicateItems g @@ -1938,18 +2041,20 @@ type internal TypeCheckInfo let p = p - |> List.map (fun i -> + |> List.map (fun i -> let ty = match i.Item with | Item.Value(valRef) -> valRef.Type | Item.ILField(iLFieldInfo) -> (iLFieldInfo.FieldType(amap, m)) | Item.Property(info = pinfo :: _) -> (pinfo.GetPropertyType(amap, m)) | _ -> failwith "not possible" + CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName i) match declaredItems with | None -> Some(p @ (items |> List.map DefaultCompletionItem), denv, m) - | Some(declItems, declaredDisplayEnv, declaredRange) -> Some(p @ filtered @ declItems, declaredDisplayEnv, declaredRange) + | Some(declItems, declaredDisplayEnv, declaredRange) -> + Some(p @ filtered @ declItems, declaredDisplayEnv, declaredRange) | _ -> declaredItems | Some(CompletionContext.AttributeApplication) -> diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 2e67ae6d137..42a461db90d 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -600,9 +600,13 @@ module SyntaxTraversal = for b in binds do yield dive b b.RangeOfBindingWithRhs (traverseSynBinding path) for SynInterfaceImpl(ty, withKeyword, binds, members, range) in ifaces do - let path = SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(ty, withKeyword, Some members, range)) :: path + 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)) ] diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 1f554e392e9..a08824a9bc3 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -111,9 +111,14 @@ type CompletionContext = /// Completing a pattern in a match clause, member/let binding or lambda | Pattern of context: PatternContext - + /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of ctx: MethodOverrideCompletionContext * enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool * isStatic: bool + | MethodOverride of + ctx: MethodOverrideCompletionContext * + enclosingTypeNameRange: range * + spacesBeforeOverrideKeyword: int * + hasThis: bool * + isStatic: bool /// (s =| ) or (s <>| ) | CaretAfterOperator of mExprBeforeOperator: range @@ -1090,6 +1095,7 @@ module ParsedInput = if i >= lineStr.Length || i >= max then true elif Char.IsWhiteSpace lineStr[i] then loop max (i + 1) else false + rangeContainsPos m pos // pos is before m || posLt pos m.Start @@ -1103,18 +1109,25 @@ module ParsedInput = let mutable i = 0 let mutable idx = -1 let mutable namedParamName = None + for p in parameters do let isCurrParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr p.Range pos - if isCurrParam then idx <- i + + if isCurrParam then + idx <- i + match p with - | Setter id -> - if isCurrParam then namedParamName <- Some id.idText + | Setter id -> + if isCurrParam then + namedParamName <- Some id.idText + ignore (setters.Add id.idText) | _ -> () + i <- i + 1 idx, namedParamName, setters - | SynExpr.Paren(expr = Setter id) -> + | SynExpr.Paren(expr = Setter id) -> let setters = HashSet() ignore (setters.Add id.idText) 0, Some id.idText, setters @@ -1185,7 +1198,8 @@ module ParsedInput = | SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> //if Option.isSome precedingArgument then None else Some args Some args - | SyntaxNode.SynExpr(SynExpr.Tuple(false, elements, commas, _)) :: SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> + | SyntaxNode.SynExpr(SynExpr.Tuple(false, elements, commas, _)) :: SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, + pos) args) :: _ -> match precedingArgument with | None -> Some args | Some e -> @@ -1426,7 +1440,7 @@ module ParsedInput = /// Try to determine completion context for the given pair (row, columns) let TryGetCompletionContext (pos, parsedInput: ParsedInput, lineStr: string) : CompletionContext option = - + let visitor = { new SyntaxVisitorBase<_>() with member _.VisitExpr(path, _, defaultTraverse, expr) = @@ -1438,11 +1452,12 @@ module ParsedInput = else match expr with // new A(1, $) - | SynExpr.Paren(expr = SynExpr.Tuple (range = m)) + | SynExpr.Paren(expr = SynExpr.Tuple(range = m)) // new A($) | SynExpr.Const(SynConst.Unit, m) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> match path with - | SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> Some(CompletionContext.ParameterList args) + | SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> + Some(CompletionContext.ParameterList args) | _ -> defaultTraverse expr // new (... A$) @@ -1454,7 +1469,10 @@ module ParsedInput = // new (A$ = 1) // new (A = 1, $) - | Setter id when id.idRange.End = pos || rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr expr.Range pos -> + | Setter id when + id.idRange.End = pos + || rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr expr.Range pos + -> let precedingArgument = if id.idRange.End = pos then None else Some expr match path with @@ -1478,24 +1496,28 @@ module ParsedInput = | Operator "op_Equality" (l, r) | Operator "op_Inequality" (l, r) - | SynExpr.IfThenElse(ifExpr = Operator "op_Equality" (l, r) | Operator "op_Inequality" (l, r)) - | SynExpr.Set (l, r, _) - when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> + | SynExpr.IfThenElse(ifExpr = Operator "op_Equality" (l, r) | Operator "op_Inequality" (l, r)) + | SynExpr.Set(l, r, _) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> Some(CompletionContext.CaretAfterOperator l.Range) - | SynExpr.LongIdentSet (l, r, _) - when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> + | SynExpr.LongIdentSet(l, r, _) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> Some(CompletionContext.CaretAfterOperator l.Range) // let a = 1: | | SynExpr.Typed(targetType = SynType.FromParseError(range = m)) // let f() = let a: | - | SynExpr.FromParseError(expr = SynExpr.LetOrUse(bindings = SynBinding(expr = SynExpr.Typed(expr = SynExpr.ArbitraryAfterError(range = m))) :: _)) - when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> + | SynExpr.FromParseError( + expr = SynExpr.LetOrUse( + bindings = SynBinding(expr = SynExpr.Typed(expr = SynExpr.ArbitraryAfterError(range = m))) :: _)) when + rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos + -> Some CompletionContext.Type // { new | } - | SynExpr.ComputationExpr(expr = SynExpr.ArbitraryAfterError _) when lineStr.Trim().Split(' ') |> Array.contains "new" -> Some(CompletionContext.Inherit(InheritanceContext.Unknown, ([], None))) + | 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) = @@ -1563,7 +1585,7 @@ module ParsedInput = match leadingKeyword with | SynLeadingKeyword.StaticMember _ -> true | _ -> false - + let isMember leadingKeyword = match leadingKeyword with | SynLeadingKeyword.Member _ -> true @@ -1571,25 +1593,54 @@ module ParsedInput = let overrideContext path (mOverride: range) hasThis isStatic isMember = match path with - | _ :: SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo(longId = [ enclosingType ]))) :: _ when not isMember -> - Some(CompletionContext.MethodOverride(MethodOverrideCompletionContext.Class, enclosingType.idRange, mOverride.StartColumn, hasThis, isStatic)) + | _ :: 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.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: _ -> - let ty = + let ty = match ty with - | SynType.App (typeName = ty) -> ty + | SynType.App(typeName = ty) -> ty | _ -> ty - Some(CompletionContext.MethodOverride(MethodOverrideCompletionContext.Interface, ty.Range, mOverride.StartColumn, hasThis, isStatic)) + + Some( + CompletionContext.MethodOverride( + MethodOverrideCompletionContext.Interface, + ty.Range, + mOverride.StartColumn, + hasThis, + isStatic + ) + ) | SyntaxNode.SynExpr(SynExpr.ObjExpr(objType = ty)) :: _ -> - let ty = + let ty = match ty with - | SynType.App (typeName = ty) -> ty + | SynType.App(typeName = ty) -> ty | _ -> ty - Some(CompletionContext.MethodOverride(MethodOverrideCompletionContext.ObjExpr, ty.Range, mOverride.StartColumn, hasThis, isStatic)) + + Some( + CompletionContext.MethodOverride( + MethodOverrideCompletionContext.ObjExpr, + ty.Range, + mOverride.StartColumn, + hasThis, + isStatic + ) + ) | _ -> Some CompletionContext.Invalid match returnInfo with - | Some(SynBindingReturnInfo(range = m)) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> Some CompletionContext.Type + | Some(SynBindingReturnInfo(range = m)) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> + Some CompletionContext.Type | _ -> match headPat with @@ -1613,7 +1664,8 @@ module ParsedInput = // override this.ToStr| | SynPat.LongIdent(longDotId = SynLongIdent(id = [ _; methodId ])) when - isOverrideOrMember trivia.LeadingKeyword && rangeContainsPos methodId.idRange pos + isOverrideOrMember trivia.LeadingKeyword + && rangeContainsPos methodId.idRange pos -> overrideContext path trivia.LeadingKeyword.Range true false (isMember trivia.LeadingKeyword) @@ -1804,7 +1856,7 @@ module ParsedInput = else None) - override _.VisitInterfaceSynMemberDefnType (_, synType: SynType) = + override _.VisitInterfaceSynMemberDefnType(_, synType: SynType) = match synType with | SynType.FromParseError(range = m) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> Some(CompletionContext.Inherit(InheritanceContext.Interface, ([], None))) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 412bd23ed2e..1a12a66c77b 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -85,7 +85,12 @@ type public CompletionContext = | Pattern of context: PatternContext /// Completing a method override (e.g. override this.ToStr|) - | MethodOverride of ctx: MethodOverrideCompletionContext * enclosingTypeNameRange: range * spacesBeforeOverrideKeyword: int * hasThis: bool * isStatic: bool + | MethodOverride of + ctx: MethodOverrideCompletionContext * + enclosingTypeNameRange: range * + spacesBeforeOverrideKeyword: int * + hasThis: bool * + isStatic: bool /// (s = | ) or (s <> | ) | CaretAfterOperator of mExprBeforeOperator: range From f820edc091a7c10b4dd621a4c67780a6f1fcf868 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Thu, 6 Jun 2024 06:23:11 +0800 Subject: [PATCH 13/28] exclude implemented method/prop from interface implement of class --- src/Compiler/Checking/InfoReader.fs | 30 ++++---- src/Compiler/Checking/InfoReader.fsi | 20 ++++++ src/Compiler/Service/FSharpCheckerResults.fs | 72 ++++++++++++------- src/Compiler/Service/ServiceParsedInputOps.fs | 32 +++++++-- .../Service/ServiceParsedInputOps.fsi | 4 +- 5 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 03447c77e94..814cdb623f5 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 = diff --git a/src/Compiler/Checking/InfoReader.fsi b/src/Compiler/Checking/InfoReader.fsi index 2e6af4fd14d..a4d329c33a0 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 diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 3a85b952a4d..df81b005249 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1460,32 +1460,11 @@ type internal TypeCheckInfo overridableProps @ overridableMeths - if ctx = MethodOverrideCompletionContext.Class then - 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 overriddenMethods = - GetImmediateIntrinsicMethInfosOfType (None, ad) g amap typeNameRange ty - |> List.filter (fun x -> x.IsDefiniteFSharpOverride) - - let overriddenProperties = - GetImmediateIntrinsicPropInfosOfType (None, ad) g amap typeNameRange ty - |> List.filter (fun x -> x.IsDefiniteFSharpOverride) - - let overridableMethods = - getOverridableMethods superTy overriddenMethods overriddenProperties - - Some(overridableMethods, denv, m) - | _ -> None) - else + let getTyFromTypeNamePos (endPos: pos) = let nameResItems = GetPreciseItemsFromNameResolution( - typeNameRange.End.Line, - typeNameRange.End.Column, + endPos.Line, + endPos.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes, @@ -1498,9 +1477,52 @@ type internal TypeCheckInfo |> List.tryPick (function | ({ Item = Item.Types(_, ty :: _) }, _) -> Some ty | _ -> None) - |> Option.map (fun ty -> getOverridableMethods ty [] [], denv, m) | _ -> 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 = sResolutions.CapturedNameResolutions diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index a08824a9bc3..1a0f6d71449 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -73,8 +73,8 @@ type PatternContext = [] type MethodOverrideCompletionContext = | Class - | Interface - | ObjExpr + | Interface of mInterfaceName: range + | ObjExpr of mExpr: range [] type CompletionContext = @@ -1605,8 +1605,26 @@ module ParsedInput = isStatic ) ) - | SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: _ - | _ :: SyntaxNode.SynMemberDefn(SynMemberDefn.Interface(interfaceType = ty)) :: _ -> + | 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 @@ -1614,14 +1632,14 @@ module ParsedInput = Some( CompletionContext.MethodOverride( - MethodOverrideCompletionContext.Interface, + MethodOverrideCompletionContext.ObjExpr expr.Range, ty.Range, mOverride.StartColumn, hasThis, isStatic ) ) - | SyntaxNode.SynExpr(SynExpr.ObjExpr(objType = ty)) :: _ -> + | SyntaxNode.SynExpr(SynExpr.ObjExpr(objType = ty)) as expr :: _ -> let ty = match ty with | SynType.App(typeName = ty) -> ty @@ -1629,7 +1647,7 @@ module ParsedInput = Some( CompletionContext.MethodOverride( - MethodOverrideCompletionContext.ObjExpr, + MethodOverrideCompletionContext.ObjExpr expr.Range, ty.Range, mOverride.StartColumn, hasThis, diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 1a12a66c77b..93c0e76bd7e 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -45,8 +45,8 @@ type public PatternContext = [] type MethodOverrideCompletionContext = | Class - | Interface - | ObjExpr + | Interface of mInterfaceName: range + | ObjExpr of mExpr: range [] type public CompletionContext = From a0d28a64a3f13f6f8772a2456842da39f372456f Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Thu, 6 Jun 2024 08:06:07 +0800 Subject: [PATCH 14/28] simplify completionitem construct static thing completion show in short name --- src/Compiler/Service/FSharpCheckerResults.fs | 54 +++++++++++-------- .../Service/ServiceDeclarationLists.fs | 35 ++++-------- .../Service/ServiceDeclarationLists.fsi | 11 ++-- 3 files changed, 46 insertions(+), 54 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index df81b005249..0cfb9f5b7b4 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1088,7 +1088,7 @@ type internal TypeCheckInfo if p >= 0 then Some p else None /// Build a CompetionItem - let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) insertType (item: ItemWithInst) = + let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) insertText displayText (item: ItemWithInst) = let kind = match item.Item with | Item.DelegateCtor _ @@ -1138,11 +1138,12 @@ type internal TypeCheckInfo IsOwnMember = false Type = ty Unresolved = isUnresolved - InsertType = insertType + CustomInsertText = insertText + CustomDisplayText = displayText } let DefaultCompletionItem item = - CompletionItem ValueNone ValueNone CompletionInsertType.Default item + CompletionItem ValueNone ValueNone ValueNone ValueNone item let CompletionItemSuggestedName displayName = { @@ -1152,7 +1153,8 @@ type internal TypeCheckInfo Kind = CompletionItemKind.SuggestedName IsOwnMember = false Unresolved = None - InsertType = CompletionInsertType.Default + CustomInsertText = ValueNone + CustomDisplayText = ValueNone } let getItem (x: ItemWithInst) = x.Item @@ -1388,7 +1390,7 @@ type internal TypeCheckInfo Item.Property(name, [ prop ], None) |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) + |> CompletionItem ValueNone ValueNone (ValueSome textInCode) (ValueSome name) |> Some) let overridableMeths = @@ -1455,7 +1457,7 @@ type internal TypeCheckInfo Item.MethodGroup(name, [ meth ], None) |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone (CompletionInsertType.CustomText textInCode) + |> CompletionItem ValueNone ValueNone (ValueSome textInCode) (ValueSome name) |> Some) overridableProps @ overridableMeths @@ -1707,7 +1709,7 @@ type internal TypeCheckInfo // lookup based on name resolution results successful Some( items - |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm CompletionInsertType.Default item), + |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm ValueNone ValueNone item), denv, m ) @@ -1753,7 +1755,7 @@ type internal TypeCheckInfo // lookup based on expression typings successful Some( items - |> List.map (fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), + |> List.map (fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm ValueNone ValueNone item), denv, m ) @@ -1782,7 +1784,7 @@ type internal TypeCheckInfo // lookup based on name resolution results successful ValueSome( items - |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm CompletionInsertType.Default item), + |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm ValueNone ValueNone item), denv, m ) @@ -1793,7 +1795,7 @@ type internal TypeCheckInfo // lookup based on name and environment successful ValueSome( items - |> List.map (CompletionItem (getType ()) ValueNone CompletionInsertType.Default), + |> List.map (CompletionItem (getType ()) ValueNone ValueNone ValueNone), denv, m ) @@ -1803,7 +1805,7 @@ type internal TypeCheckInfo ValueSome( items |> List.map (fun (item, asm) -> - CompletionItem (tryTcrefOfAppTy g ty) asm CompletionInsertType.Default item), + CompletionItem (tryTcrefOfAppTy g ty) asm ValueNone ValueNone item), denv, m ) @@ -1838,7 +1840,8 @@ type internal TypeCheckInfo CompletionItem (getType ()) (ValueSome globalItem) - CompletionInsertType.Default + ValueNone + ValueNone (ItemWithNoInst globalItem.Symbol.Item)) |> fun r -> ValueSome(r, denv, m) | _ -> ValueNone @@ -2058,7 +2061,8 @@ type internal TypeCheckInfo IsOwnMember = false Type = None Unresolved = None - InsertType = CompletionInsertType.Default + CustomInsertText = ValueNone + CustomDisplayText = ValueNone }) let p = @@ -2071,7 +2075,10 @@ type internal TypeCheckInfo | Item.Property(info = pinfo :: _) -> (pinfo.GetPropertyType(amap, m)) | _ -> failwith "not possible" - CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName i) + let tyName = stringOfTy denv ty + let code = ValueSome $"{tyName}.{i.Item.DisplayName}" + + CompletionItem ValueNone ValueNone code code i) match declaredItems with | None -> Some(p @ (items |> List.map DefaultCompletionItem), denv, m) @@ -2145,22 +2152,27 @@ type internal TypeCheckInfo match bestQual with | Some bestQual -> let ty, nenv, ad, m = bestQual - let items = getStaticFieldsOfSameTypeInTheType nenv ad m ty + let denv = nenv.DisplayEnv + let tyName = stringOfTy denv ty + + let items = + getStaticFieldsOfSameTypeInTheType nenv ad m ty + |> List.map (fun i -> + let code = ValueSome $"{tyName}.{i.Item.DisplayName}" + CompletionItem ValueNone ValueNone code code i) match declaredItems with | Some(declaredItems, _, _) -> Some( - (items - |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName)) + items @ declaredItems, - nenv.DisplayEnv, + denv, m ) | None -> Some( - items - |> List.map (CompletionItem (tryTcrefOfAppTy g ty) ValueNone CompletionInsertType.FullName), - nenv.DisplayEnv, + items, + denv, m ) | None -> declaredItems diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index 099aa97adb5..dd6727a5ba9 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -80,12 +80,6 @@ type UnresolvedSymbol = DisplayName: string Namespace: string[] } -[] -type CompletionInsertType = - | Default - | FullName - | CustomText of textInCode: string - type CompletionItem = { ItemWithInst: ItemWithInst Kind: CompletionItemKind @@ -93,7 +87,8 @@ type CompletionItem = MinorPriority: int Type: TyconRef option Unresolved: UnresolvedSymbol option - InsertType: CompletionInsertType } + CustomInsertText: string voption + CustomDisplayText: string voption } member x.Item = x.ItemWithInst.Item [] @@ -1072,12 +1067,6 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i | _ -> false | _ -> false - static let getTextInListOfCustomTextItem item = - match item with - | Item.MethodGroup (displayName = name) - | Item.Property (name = name) -> name - | _ -> item.DisplayName - member _.Items = declarations member _.IsForType = isForType @@ -1138,26 +1127,22 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i match u.Namespace with | [||] -> u.DisplayName | ns -> (ns |> String.concat ".") + "." + u.DisplayName - | None -> - match x.InsertType with - | CompletionInsertType.CustomText _ -> getTextInListOfCustomTextItem x.Item - | _ -> x.Item.DisplayName + | None when x.CustomDisplayText.IsSome -> x.CustomDisplayText.Value + | None -> x.Item.DisplayName ) |> List.map ( let textInDeclList item = - match item.Unresolved, item.InsertType with - | Some u, _ -> u.DisplayName - | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None, CompletionInsertType.CustomText _ -> getTextInListOfCustomTextItem item.Item - | None, _ -> item.Item.DisplayNameCore + 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, item.InsertType with + match item.Unresolved, item.CustomInsertText with | Some u, _ -> u.DisplayName - | None, CompletionInsertType.FullName when item.Type.IsSome -> $"{item.Type.Value}.{item.Item.DisplayName}" - | None, CompletionInsertType.CustomText textInCode -> textInCode + | None, ValueSome textInCode -> textInCode | None, _ -> item.Item.DisplayName if not supportsPreferExtsMethodsOverProperty then // we don't pay the cost of filtering specific to RFC-1137 diff --git a/src/Compiler/Service/ServiceDeclarationLists.fsi b/src/Compiler/Service/ServiceDeclarationLists.fsi index b773637e47c..3aeaa11112d 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -77,12 +77,6 @@ type public UnresolvedSymbol = Namespace: string[] } - -[] -type internal CompletionInsertType = - | Default - | FullName - | CustomText of textInCode: string type internal CompletionItem = { @@ -97,8 +91,9 @@ type internal CompletionItem = Type: TyconRef option Unresolved: UnresolvedSymbol option - - InsertType: CompletionInsertType + + CustomInsertText: string voption + CustomDisplayText: string voption } member Item: Item From 05894859a0499a86d7757c420febbe8f720cdccd Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 03:28:02 +0800 Subject: [PATCH 15/28] parameter completion on currying function --- src/Compiler/Service/FSharpCheckerResults.fs | 271 ++++++++++-------- src/Compiler/Service/ServiceParsedInputOps.fs | 143 +++++---- .../Service/ServiceParsedInputOps.fsi | 3 +- 3 files changed, 242 insertions(+), 175 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 0cfb9f5b7b4..25012b3e443 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -707,6 +707,64 @@ type internal TypeCheckInfo else Some(StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)) | _, _ -> None + + /// Build a CompetionItem + 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 getStaticFieldsOfSameTypeInTheType nenv ad m ty = let targets = @@ -717,6 +775,7 @@ type internal TypeCheckInfo let items = items |> List.filter (function + | Item.UnionCase _ -> true | Item.Value(valRef) -> typeEquiv g ty valRef.Type | Item.ILField(iLFieldInfo) -> typeEquiv g ty (iLFieldInfo.FieldType(amap, m)) | Item.Property(info = pinfo :: _) -> typeEquiv g ty (pinfo.GetPropertyType(amap, m)) @@ -726,6 +785,15 @@ type internal TypeCheckInfo let items = items |> RemoveDuplicateItems g items |> RemoveExplicitlySuppressed g + let getStaticFieldsOfSameTypeInTheTypeCompletionItem (nenv: NameResolutionEnv) ad m ty = + let tyName = stringOfTy (nenv.DisplayEnv.UseGenericParameterStyle(GenericParameterStyle.Prefix)) ty + getStaticFieldsOfSameTypeInTheType nenv ad m ty + |> List.map (fun i -> + let code = ValueSome $"{tyName}.{i.Item.DisplayName}" + + CompletionItemWithMoreSetting ValueNone ValueNone -2 code code i + ) + let CollectParameters (methods: MethInfo list) amap m : Item list = methods |> List.collect (fun meth -> @@ -738,43 +806,52 @@ type internal TypeCheckInfo | None -> None) | _ -> []) - let GetNamedParametersAndSettableFields endOfExprPos paramIdx paramName = - let cnrs = - GetCapturedNameResolutions endOfExprPos ResolveOverloads.No - |> ResizeArray.toList - |> List.rev + let GetNamedParametersAndSettableFields endOfExprPos paramGroupIdx paramIdx paramName isCurrying = + let paramGroupIdx = if paramGroupIdx = -1 then 0 else paramGroupIdx + let paramIdx = if paramIdx = -1 then 0 else paramIdx + + let getByIdxInParamListList nenv ad m getTy ls = + let rec loopParamList i ls = + match ls with + | [] -> [] + | x :: _ when i = paramIdx -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (getTy x) + | _ :: t -> loopParamList (i + 1) t - let getStaticFieldsOfSameTypeOfTheParameter nenv ad m (methods: MethInfo list) (items: Item list) = + let rec loopParamListList i ls = + match ls with + | [] -> [] + | x :: _ when i = paramGroupIdx -> loopParamList 0 x + | _ :: t -> loopParamListList (i + 1) t + + loopParamListList 0 ls + + let getStaticFieldsOfSameTypeOfTheParameter nenv ad m tinst (methods: MethInfo list) (items: Item list) = match paramName with | Some name -> match items |> List.tryFind (fun i -> i.DisplayName = name) with - | Some(Item.OtherName(argType = ty)) -> getStaticFieldsOfSameTypeInTheType nenv ad m ty - | Some(Item.Value(valRef)) -> getStaticFieldsOfSameTypeInTheType nenv ad m valRef.Type - | Some(Item.ILField(iLFieldInfo)) -> getStaticFieldsOfSameTypeInTheType nenv ad m (iLFieldInfo.FieldType(amap, m)) - | Some(Item.Property(info = pinfo :: _)) -> getStaticFieldsOfSameTypeInTheType nenv ad m (pinfo.GetPropertyType(amap, m)) + | Some(Item.OtherName(argType = ty)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m ty + | Some(Item.Value(valRef)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m valRef.Type + | Some(Item.ILField(iLFieldInfo)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (iLFieldInfo.FieldType(amap, m)) + | Some(Item.Property(info = pinfo :: _)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (pinfo.GetPropertyType(amap, m)) | _ -> [] | _ -> - let rec loop i ls = - match ls with - | [] -> None - | x :: t -> - if i = paramIdx then - let (ParamData(ttype = ty)) = x - Some ty - else - loop (i + 1) t - methods - |> List.choose (fun meth -> - match meth.GetParamDatas(amap, m, meth.FormalMethodInst) with - | x :: _ -> loop 0 x - | _ -> None) - |> List.collect (getStaticFieldsOfSameTypeInTheType nenv ad m) + |> List.map (fun meth -> meth.GetParamDatas(amap, m, meth.FormalMethodInst)) + |> List.collect (getByIdxInParamListList nenv ad m (fun (ParamData(ttype = ty)) -> instType tinst ty)) + + let (|CNR|) (cnr: CapturedNameResolution) = + (cnr.ItemWithInst, cnr.DisplayEnv, cnr.NameResolutionEnv, cnr.AccessorDomain, cnr.Range) + + let cnrs = + GetCapturedNameResolutions endOfExprPos ResolveOverloads.Yes + |> ResizeArray.toList + |> List.rev let result = match cnrs with - | CNR(Item.CtorGroup(_, (ctor :: _ as ctors)), _, denv, nenv, ad, m) :: _ -> + | CNR({Item = Item.CtorGroup(_, (ctor :: _ as ctors)); TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ -> let props = + if isCurrying then [] else ResolveCompletionsInType ncenv nenv @@ -786,12 +863,13 @@ type internal TypeCheckInfo let parameters = CollectParameters ctors amap m let items = props @ parameters - let p = getStaticFieldsOfSameTypeOfTheParameter nenv ad m ctors items + let p = getStaticFieldsOfSameTypeOfTheParameter nenv ad m tinst ctors items let items = List.map ItemWithNoInst items Some(denv, m, items, p) - | CNR(Item.MethodGroup(_, methods, _), _, denv, nenv, ad, m) :: _ -> + | CNR({Item = Item.MethodGroup(_, methods, _); TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ -> let props = + if isCurrying then [] else methods |> List.collect (fun meth -> let retTy = meth.GetFSharpReturnType(amap, m, meth.FormalMethodInst) @@ -799,17 +877,38 @@ type internal TypeCheckInfo let parameters = CollectParameters methods amap m let items = props @ parameters - let p = getStaticFieldsOfSameTypeOfTheParameter nenv ad m methods items + let p = getStaticFieldsOfSameTypeOfTheParameter nenv ad m tinst methods items let items = List.map ItemWithNoInst items Some(denv, m, items, p) + + | CNR({Item = Item.Value vref; TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ when isForallFunctionTy g vref.Type -> + let ty = + match vref.Type with + | TType.TType_forall(bodyTy = ty) + | ty -> instType tinst ty + let p = + stripFunTyN g (paramGroupIdx + 1) ty + |> fst + |> List.map (function ty when isAnyTupleTy g ty -> destAnyTupleTy g ty |> snd | ty -> [ty]) + |> getByIdxInParamListList nenv ad m id + if p.IsEmpty then None else Some(denv, m, [], p) + + | CNR({Item = Item.UnionCase (uci, _); TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ when not uci.UnionCase.IsNullary -> + let p = + uci.UnionCase.RecdFields + |> List.map (fun field -> + match instType tinst field.FormalType with + | ty when isAnyTupleTy g ty -> destAnyTupleTy g ty |> snd + | ty -> [ty]) + |> getByIdxInParamListList nenv ad m id + if p.IsEmpty then None else Some(denv, m, [], p) + | _ -> None match result with | None -> NameResResult.Empty, [] | Some(denv, m, items, p) -> - let p = p |> RemoveDuplicateItems g |> RemoveExplicitlySuppressed g - ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs, p /// finds captured typing for the given position @@ -1087,68 +1186,13 @@ type internal TypeCheckInfo if p >= 0 then Some p else None - /// Build a CompetionItem - let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) 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 = 0 - Kind = kind - IsOwnMember = false - Type = ty - Unresolved = isUnresolved - CustomInsertText = insertText - CustomDisplayText = displayText - } - let DefaultCompletionItem item = - CompletionItem ValueNone ValueNone ValueNone ValueNone item + CompletionItem ValueNone ValueNone item let CompletionItemSuggestedName displayName = { ItemWithInst = ItemWithNoInst(Item.NewDef(Ident(displayName, range0))) - MinorPriority = 0 + MinorPriority = -3 Type = None Kind = CompletionItemKind.SuggestedName IsOwnMember = false @@ -1390,7 +1434,7 @@ type internal TypeCheckInfo Item.Property(name, [ prop ], None) |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone (ValueSome textInCode) (ValueSome name) + |> CompletionItemWithMoreSetting ValueNone ValueNone -1 (ValueSome textInCode) (ValueSome name) |> Some) let overridableMeths = @@ -1457,7 +1501,7 @@ type internal TypeCheckInfo Item.MethodGroup(name, [ meth ], None) |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone (ValueSome textInCode) (ValueSome name) + |> CompletionItemWithMoreSetting ValueNone ValueNone -1 (ValueSome textInCode) (ValueSome name) |> Some) overridableProps @ overridableMeths @@ -1709,7 +1753,7 @@ type internal TypeCheckInfo // lookup based on name resolution results successful Some( items - |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm ValueNone ValueNone item), + |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm item), denv, m ) @@ -1755,7 +1799,7 @@ type internal TypeCheckInfo // lookup based on expression typings successful Some( items - |> List.map (fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm ValueNone ValueNone item), + |> List.map (fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm item), denv, m ) @@ -1784,7 +1828,7 @@ type internal TypeCheckInfo // lookup based on name resolution results successful ValueSome( items - |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm ValueNone ValueNone item), + |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm item), denv, m ) @@ -1795,7 +1839,7 @@ type internal TypeCheckInfo // lookup based on name and environment successful ValueSome( items - |> List.map (CompletionItem (getType ()) ValueNone ValueNone ValueNone), + |> List.map (CompletionItem (getType ()) ValueNone), denv, m ) @@ -1805,7 +1849,7 @@ type internal TypeCheckInfo ValueSome( items |> List.map (fun (item, asm) -> - CompletionItem (tryTcrefOfAppTy g ty) asm ValueNone ValueNone item), + CompletionItem (tryTcrefOfAppTy g ty) asm item), denv, m ) @@ -1840,8 +1884,6 @@ type internal TypeCheckInfo CompletionItem (getType ()) (ValueSome globalItem) - ValueNone - ValueNone (ItemWithNoInst globalItem.Symbol.Item)) |> fun r -> ValueSome(r, denv, m) | _ -> ValueNone @@ -2039,8 +2081,8 @@ type internal TypeCheckInfo | Some(CompletionContext.RecordField(RecordContext.Declaration true)) -> None // Completion at ' SomeMethod( ... ) ' or ' [] ' with named arguments - | Some(CompletionContext.ParameterList(endPos, (idx, name, fields))) -> - let results = GetNamedParametersAndSettableFields endPos idx name + | Some(CompletionContext.ParameterList(endPos, paramGroupIdx, idx, name, fields, isCurrying)) -> + let results = GetNamedParametersAndSettableFields endPos paramGroupIdx idx name isCurrying let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () @@ -2057,7 +2099,7 @@ type internal TypeCheckInfo { ItemWithInst = item Kind = CompletionItemKind.Argument - MinorPriority = 0 + MinorPriority = -1 IsOwnMember = false Type = None Unresolved = None @@ -2065,26 +2107,15 @@ type internal TypeCheckInfo CustomDisplayText = ValueNone }) - let p = - p - |> List.map (fun i -> - let ty = - match i.Item with - | Item.Value(valRef) -> valRef.Type - | Item.ILField(iLFieldInfo) -> (iLFieldInfo.FieldType(amap, m)) - | Item.Property(info = pinfo :: _) -> (pinfo.GetPropertyType(amap, m)) - | _ -> failwith "not possible" - - let tyName = stringOfTy denv ty - let code = ValueSome $"{tyName}.{i.Item.DisplayName}" - - CompletionItem ValueNone ValueNone code code i) - match declaredItems with | None -> Some(p @ (items |> List.map DefaultCompletionItem), denv, m) | Some(declItems, declaredDisplayEnv, declaredRange) -> Some(p @ filtered @ declItems, declaredDisplayEnv, declaredRange) - | _ -> declaredItems + | _, p -> + match declaredItems with + | None -> None + | Some(declItems, declaredDisplayEnv, declaredRange) -> + Some(p @ declItems, declaredDisplayEnv, declaredRange) | Some(CompletionContext.AttributeApplication) -> getDeclaredItemsNotInRangeOpWithAllSymbols () @@ -2153,13 +2184,7 @@ type internal TypeCheckInfo | Some bestQual -> let ty, nenv, ad, m = bestQual let denv = nenv.DisplayEnv - let tyName = stringOfTy denv ty - - let items = - getStaticFieldsOfSameTypeInTheType nenv ad m ty - |> List.map (fun i -> - let code = ValueSome $"{tyName}.{i.Item.DisplayName}" - CompletionItem ValueNone ValueNone code code i) + let items = getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m ty match declaredItems with | Some(declaredItems, _, _) -> diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 1a0f6d71449..27c1d480ddd 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -90,8 +90,7 @@ type CompletionContext = | RangeOperator /// Completing named parameters\setters in parameter list of attributes\constructor\method calls - /// end of name ast node * index of curr paameter * list of properties\parameters that were already set - | ParameterList of pos * (int * string option * HashSet) + | ParameterList of identEndPos: pos * paramGroupIdx: int * paramIdxInGroup: int * paramName: string option * settedParam: HashSet * isCurrying: bool /// Completing an attribute name, outside of the constructor | AttributeApplication @@ -1090,18 +1089,21 @@ module ParsedInput = | Operator "op_Equality" (SynExpr.Ident id, _) -> Some id | _ -> None - let rangeContainsPosOrIsSpacesBetweenRangeAndPos (lineStr: string) m pos = + 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 - || (posGt pos m.End && pos.Line = m.End.Line && loop pos.Column m.End.Column) + || posAfterRangeAndBetweenSpaces lineStr m pos - let findSetters (lineStr, pos) argList = + let findSetters identPos (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup argList = + if currentParamIdxInGroup > -1 then CompletionContext.ParameterList(identPos, currentParamGroup, currentParamIdxInGroup, None, emptyStringSet, isCurrying) else match argList with | SynExpr.Paren(SynExpr.Tuple(false, parameters, _, _), _, _, _) -> let setters = HashSet() @@ -1111,14 +1113,14 @@ module ParsedInput = let mutable namedParamName = None for p in parameters do - let isCurrParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr p.Range pos + let isCurrentParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr p.Range pos - if isCurrParam then + if isCurrentParam then idx <- i match p with - | Setter id -> - if isCurrParam then + | Setter id when not isCurrying -> + if isCurrentParam then namedParamName <- Some id.idText ignore (setters.Add id.idText) @@ -1126,12 +1128,19 @@ module ParsedInput = i <- i + 1 - idx, namedParamName, setters - | SynExpr.Paren(expr = Setter id) -> + CompletionContext.ParameterList(identPos, currentParamGroup, idx, namedParamName, setters, isCurrying) + | SynExpr.Paren(expr = Setter id) when not isCurrying -> let setters = HashSet() ignore (setters.Add id.idText) - 0, Some id.idText, setters - | _ -> -1, None, emptyStringSet + CompletionContext.ParameterList(identPos, currentParamGroup, 0, Some id.idText, setters, isCurrying) + + | SynExpr.Const (SynConst.Unit, range) -> + let isCurrentParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr range pos + if isCurrentParam then + CompletionContext.ParameterList(identPos, currentParamGroup, 0, None, emptyStringSet, isCurrying) + else CompletionContext.ParameterList(identPos, currentParamGroup, -1, None, emptyStringSet, isCurrying) + + | _ -> CompletionContext.ParameterList(identPos, currentParamGroup, -1, None, emptyStringSet, isCurrying) let endOfLastIdent (lid: SynLongIdent) = let last = List.last lid.LongIdent @@ -1142,41 +1151,56 @@ module ParsedInput = | Some m -> m.End | None -> endOfLastIdent lid - let endOfClosingTokenOrIdent (mClosing: range option) (id: Ident) = - match mClosing with - | Some m -> m.End - | None -> id.idRange.End - - let (|NewObjectOrMethodCall|_|) (lineStr, pos) e = - match e with - | SynExpr.New(_, SynType.LongIdent typeName, arg, _) -> - // new A() - Some(endOfLastIdent typeName, findSetters (lineStr, pos) arg) - - | SynExpr.New(_, SynType.App(StripParenTypes(SynType.LongIdent typeName), _, _, _, mGreaterThan, _, _), arg, _) -> - // new A<_>() - Some(endOfClosingTokenOrLastIdent mGreaterThan typeName, findSetters (lineStr, pos) arg) - - | SynExpr.App(_, false, SynExpr.Ident id, arg, _) -> - // A() - Some(id.idRange.End, findSetters (lineStr, pos) arg) + //let endOfClosingTokenOrIdent (mClosing: range option) (id: Ident) = + // match mClosing with + // | Some m -> m.End + // | None -> id.idRange.End + + let checkNewObjectOrMethodCall (lineStr, pos) found e = + let rec loop isCurrying found currentParamGroup currentParamIdxInGroup e = + let currentParamGroup = + if found then currentParamGroup + 1 + else currentParamGroup + match e with + | SynExpr.New(_, SynType.LongIdent typeName, arg, _) -> + // new A() + Some(findSetters (endOfLastIdent typeName) (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup arg) + + | SynExpr.New(_, SynType.App(StripParenTypes(SynType.LongIdent typeName), _, _, _, mGreaterThan, _, _), arg, _) -> + // new A<_>() + Some(findSetters (endOfClosingTokenOrLastIdent mGreaterThan typeName) (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup arg) + + | SynExpr.App(_, false, SynExpr.Ident id, arg, _) + // A() + + | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.Ident id, _, _, _, _, _, _), arg, _) -> + // A<_>() + Some(findSetters (id.idRange.End) (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup arg) + + | SynExpr.App(_, false, SynExpr.LongIdent(_, lid, _, _), arg, _) + | SynExpr.App(_, false, SynExpr.DotGet(longDotId = lid), arg, _) + // A.B() + + | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.LongIdent(_, lid, _, _), _, _, _, _, _, _), arg, _) -> + // A.B<_>() + Some(findSetters (endOfLastIdent lid) (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup arg) + + // f 1 2 + | SynExpr.App(_, false, (SynExpr.App _ as expr), arg, _) -> + let currentParamIdxInGroup, currentParamGroup = + if currentParamGroup = -1 then + match findSetters pos0 (lineStr, pos) true currentParamGroup currentParamIdxInGroup arg with + | CompletionContext.ParameterList (paramIdxInGroup = idx) -> idx, 0 + | _ -> -1, -1 + else currentParamIdxInGroup, currentParamGroup + let found = found || currentParamIdxInGroup > -1 + loop true found currentParamGroup currentParamIdxInGroup expr - | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.Ident id, _, _, _, mGreaterThan, _, _), arg, _) -> - // A<_>() - Some(endOfClosingTokenOrIdent mGreaterThan id, findSetters (lineStr, pos) arg) - - | SynExpr.App(_, false, SynExpr.LongIdent(_, lid, _, _), arg, _) -> - // A.B() - Some(endOfLastIdent lid, findSetters (lineStr, pos) arg) + | _ -> None + loop false found -1 -1 e - | SynExpr.App(_, false, SynExpr.DotGet(longDotId = lid), arg, _) -> - // A.B() - Some(endOfLastIdent lid, findSetters (lineStr, pos) arg) - - | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.LongIdent(_, lid, _, _), _, _, _, mGreaterThan, _, _), arg, _) -> - // A.B<_>() - Some(endOfClosingTokenOrLastIdent mGreaterThan lid, findSetters (lineStr, pos) arg) - | _ -> None + let (|NewObjectOrMethodCall|_|) (lineStr, pos) e = checkNewObjectOrMethodCall (lineStr, pos) false e + let (|NewObjectOrMethodCallFound|_|) (lineStr, pos) e = checkNewObjectOrMethodCall (lineStr, pos) true e let isOnTheRightOfComma pos (elements: SynExpr list) (commas: range list) current = let rec loop elements (commas: range list) = @@ -1457,14 +1481,14 @@ module ParsedInput = | SynExpr.Const(SynConst.Unit, m) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> match path with | SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> - Some(CompletionContext.ParameterList args) + Some(args) | _ -> defaultTraverse expr // new (... A$) | SynExpr.Ident id | SynExpr.LongIdent(longDotId = SynLongIdent([ id ], [], [ Some _ ])) when id.idRange.End = pos -> match path with - | PartOfParameterList lineStr pos None args -> Some(CompletionContext.ParameterList args) + | PartOfParameterList lineStr pos None args -> Some(args) | _ -> defaultTraverse expr // new (A$ = 1) @@ -1476,7 +1500,7 @@ module ParsedInput = let precedingArgument = if id.idRange.End = pos then None else Some expr match path with - | PartOfParameterList lineStr pos precedingArgument args -> Some(CompletionContext.ParameterList args) + | PartOfParameterList lineStr pos precedingArgument args -> Some(args) | _ -> match expr with | Operator "op_Equality" (l, r) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr r.Range pos -> @@ -1518,8 +1542,27 @@ module ParsedInput = lineStr.Trim().Split(' ') |> Array.contains "new" -> Some(CompletionContext.Inherit(InheritanceContext.Unknown, ([], None))) + + | _ when posAfterRangeAndBetweenSpaces lineStr expr.Range pos -> + match expr with + // Some | + | SynExpr.Ident id + | SynExpr.TypeApp(SynExpr.Ident id, _, _, _, _, _, _) -> + Some(CompletionContext.ParameterList(id.idRange.End, 0, 0, None, emptyStringSet, true)) + + | SynExpr.LongIdent(_, lid, _, _) + | SynExpr.DotGet(longDotId = lid) + | SynExpr.TypeApp(SynExpr.LongIdent (longDotId = lid), _, _, _, _, _, _) -> + Some(CompletionContext.ParameterList(endOfLastIdent lid, 0, 0, None, emptyStringSet, true)) + + | NewObjectOrMethodCallFound (lineStr, pos) (CompletionContext.ParameterList(a,b,c,d,e,f)) -> + Some(CompletionContext.ParameterList(a,b + 1,c,d,e,f)) + + | _ -> defaultTraverse expr + | _ -> defaultTraverse expr + member _.VisitRecordField(path, copyOpt, field) = let contextFromTreePath completionPath = // detect records usage in constructor @@ -1870,7 +1913,7 @@ module ParsedInput = Some CompletionContext.AttributeApplication // [] elif rangeContainsPos att.ArgExpr.Range pos then - Some(CompletionContext.ParameterList(att.TypeName.Range.End, findSetters (lineStr, pos) att.ArgExpr)) + Some(findSetters att.TypeName.Range.End (lineStr, pos) false 0 -1 att.ArgExpr) else None) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 93c0e76bd7e..3e3d60a96ae 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -62,8 +62,7 @@ type public CompletionContext = | RangeOperator /// Completing named parameters\setters in parameter list of attributes\constructor\method calls - /// end of name ast node * index of curr paameter * list of properties\parameters that were already set - | ParameterList of pos * (int * string option * HashSet) + | ParameterList of identEndPos: pos * paramGroupIdx: int * paramIdxInGroup: int * paramName: string option * settedParam: HashSet * isCurrying: bool /// Completing an attribute name, outside of the constructor | AttributeApplication From d087238e13e42878e4886d30697d047c2b22276b Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 06:00:17 +0800 Subject: [PATCH 16/28] format code --- src/Compiler/Service/FSharpCheckerResults.fs | 169 ++++++++++-------- src/Compiler/Service/ServiceParsedInputOps.fs | 129 +++++++------ .../Service/ServiceParsedInputOps.fsi | 8 +- 3 files changed, 175 insertions(+), 131 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 25012b3e443..8181d688c33 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -707,9 +707,16 @@ type internal TypeCheckInfo else Some(StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)) | _, _ -> None - + /// Build a CompetionItem - let CompletionItemWithMoreSetting (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) minorPriority insertText displayText (item: ItemWithInst) = + let CompletionItemWithMoreSetting + (ty: TyconRef voption) + (assemblySymbol: AssemblySymbol voption) + minorPriority + insertText + displayText + (item: ItemWithInst) + = let kind = match item.Item with | Item.DelegateCtor _ @@ -762,7 +769,7 @@ type internal TypeCheckInfo CustomInsertText = insertText CustomDisplayText = displayText } - + let CompletionItem (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption) (item: ItemWithInst) = CompletionItemWithMoreSetting ty assemblySymbol 0 ValueNone ValueNone item @@ -786,13 +793,14 @@ type internal TypeCheckInfo items |> RemoveExplicitlySuppressed g let getStaticFieldsOfSameTypeInTheTypeCompletionItem (nenv: NameResolutionEnv) ad m ty = - let tyName = stringOfTy (nenv.DisplayEnv.UseGenericParameterStyle(GenericParameterStyle.Prefix)) ty + let tyName = + stringOfTy (nenv.DisplayEnv.UseGenericParameterStyle(GenericParameterStyle.Prefix)) ty + getStaticFieldsOfSameTypeInTheType nenv ad m ty |> List.map (fun i -> let code = ValueSome $"{tyName}.{i.Item.DisplayName}" - CompletionItemWithMoreSetting ValueNone ValueNone -2 code code i - ) + CompletionItemWithMoreSetting ValueNone ValueNone -2 code code i) let CollectParameters (methods: MethInfo list) amap m : Item list = methods @@ -831,8 +839,10 @@ type internal TypeCheckInfo match items |> List.tryFind (fun i -> i.DisplayName = name) with | Some(Item.OtherName(argType = ty)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m ty | Some(Item.Value(valRef)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m valRef.Type - | Some(Item.ILField(iLFieldInfo)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (iLFieldInfo.FieldType(amap, m)) - | Some(Item.Property(info = pinfo :: _)) -> getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (pinfo.GetPropertyType(amap, m)) + | Some(Item.ILField(iLFieldInfo)) -> + getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (iLFieldInfo.FieldType(amap, m)) + | Some(Item.Property(info = pinfo :: _)) -> + getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m (pinfo.GetPropertyType(amap, m)) | _ -> [] | _ -> methods @@ -849,17 +859,26 @@ type internal TypeCheckInfo let result = match cnrs with - | CNR({Item = Item.CtorGroup(_, (ctor :: _ as ctors)); TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ -> + | CNR({ + Item = Item.CtorGroup(_, (ctor :: _ as ctors)) + TyparInstantiation = tinst + }, + denv, + nenv, + ad, + m) :: _ -> let props = - if isCurrying then [] else - ResolveCompletionsInType - ncenv - nenv - ResolveCompletionTargets.SettablePropertiesAndFields - m - ad - false - ctor.ApparentEnclosingType + if isCurrying then + [] + else + ResolveCompletionsInType + ncenv + nenv + ResolveCompletionTargets.SettablePropertiesAndFields + m + ad + false + ctor.ApparentEnclosingType let parameters = CollectParameters ctors amap m let items = props @ parameters @@ -867,13 +886,22 @@ type internal TypeCheckInfo let items = List.map ItemWithNoInst items Some(denv, m, items, p) - | CNR({Item = Item.MethodGroup(_, methods, _); TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ -> + | CNR({ + Item = Item.MethodGroup(_, methods, _) + TyparInstantiation = tinst + }, + denv, + nenv, + ad, + m) :: _ -> let props = - if isCurrying then [] else - methods - |> List.collect (fun meth -> - let retTy = meth.GetFSharpReturnType(amap, m, meth.FormalMethodInst) - ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false retTy) + if isCurrying then + [] + else + methods + |> List.collect (fun meth -> + let retTy = meth.GetFSharpReturnType(amap, m, meth.FormalMethodInst) + ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false retTy) let parameters = CollectParameters methods amap m let items = props @ parameters @@ -882,34 +910,52 @@ type internal TypeCheckInfo Some(denv, m, items, p) - | CNR({Item = Item.Value vref; TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ when isForallFunctionTy g vref.Type -> - let ty = + | CNR({ + Item = Item.Value vref + TyparInstantiation = tinst + }, + denv, + nenv, + ad, + m) :: _ when isForallFunctionTy g vref.Type -> + let ty = match vref.Type with | TType.TType_forall(bodyTy = ty) | ty -> instType tinst ty + let p = stripFunTyN g (paramGroupIdx + 1) ty |> fst - |> List.map (function ty when isAnyTupleTy g ty -> destAnyTupleTy g ty |> snd | ty -> [ty]) + |> List.map (function + | ty when isAnyTupleTy g ty -> destAnyTupleTy g ty |> snd + | ty -> [ ty ]) |> getByIdxInParamListList nenv ad m id + if p.IsEmpty then None else Some(denv, m, [], p) - | CNR({Item = Item.UnionCase (uci, _); TyparInstantiation = tinst}, denv, nenv, ad, m) :: _ when not uci.UnionCase.IsNullary -> + | CNR({ + Item = Item.UnionCase(uci, _) + TyparInstantiation = tinst + }, + denv, + nenv, + ad, + m) :: _ when not uci.UnionCase.IsNullary -> let p = uci.UnionCase.RecdFields - |> List.map (fun field -> - match instType tinst field.FormalType with - | ty when isAnyTupleTy g ty -> destAnyTupleTy g ty |> snd - | ty -> [ty]) + |> List.map (fun field -> + match instType tinst field.FormalType with + | ty when isAnyTupleTy g ty -> destAnyTupleTy g ty |> snd + | ty -> [ ty ]) |> getByIdxInParamListList nenv ad m id + if p.IsEmpty then None else Some(denv, m, [], p) | _ -> None match result with | None -> NameResResult.Empty, [] - | Some(denv, m, items, p) -> - ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs, p + | Some(denv, m, items, p) -> ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs, p /// finds captured typing for the given position let GetExprTypingForPosition endOfExprPos = @@ -1186,8 +1232,7 @@ type internal TypeCheckInfo if p >= 0 then Some p else None - let DefaultCompletionItem item = - CompletionItem ValueNone ValueNone item + let DefaultCompletionItem item = CompletionItem ValueNone ValueNone item let CompletionItemSuggestedName displayName = { @@ -1751,12 +1796,7 @@ type internal TypeCheckInfo | NameResResult.Cancel(denv, m) -> Some([], denv, m) | NameResResult.Members(FilterRelevantItems getItemTuple2 exactMatchResidueOpt (items, denv, m)) -> // lookup based on name resolution results successful - Some( - items - |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm item), - denv, - m - ) + Some(items |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm item), denv, m) | _ -> match origLongIdentOpt with | None -> None @@ -1826,30 +1866,19 @@ type internal TypeCheckInfo // First, use unfiltered name resolution items, if they're not empty | NameResResult.Members(items, denv, m), _, _ when not (isNil items) -> // lookup based on name resolution results successful - ValueSome( - items - |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm item), - denv, - m - ) + ValueSome(items |> List.map (fun (item, asm) -> CompletionItem (getType ()) asm item), denv, m) // If we have nonempty items from environment that were resolved from a type, then use them... // (that's better than the next case - here we'd return 'int' as a type) | _, FilterRelevantItems getItem exactMatchResidueOpt (items, denv, m), _ when not (isNil items) -> // lookup based on name and environment successful - ValueSome( - items - |> List.map (CompletionItem (getType ()) ValueNone), - denv, - m - ) + ValueSome(items |> List.map (CompletionItem (getType ()) ValueNone), denv, m) // Try again with the qualItems | _, _, ExprTypingsResult.Some(FilterRelevantItems getItemTuple2 exactMatchResidueOpt (items, denv, m), ty) -> ValueSome( items - |> List.map (fun (item, asm) -> - CompletionItem (tryTcrefOfAppTy g ty) asm item), + |> List.map (fun (item, asm) -> CompletionItem (tryTcrefOfAppTy g ty) asm item), denv, m ) @@ -1881,10 +1910,7 @@ type internal TypeCheckInfo -> globalItemsFiltered |> List.map (fun globalItem -> - CompletionItem - (getType ()) - (ValueSome globalItem) - (ItemWithNoInst globalItem.Symbol.Item)) + CompletionItem (getType ()) (ValueSome globalItem) (ItemWithNoInst globalItem.Symbol.Item)) |> fun r -> ValueSome(r, denv, m) | _ -> ValueNone | _ -> ValueNone // do not return unresolved items after dot @@ -2082,7 +2108,8 @@ type internal TypeCheckInfo // Completion at ' SomeMethod( ... ) ' or ' [] ' with named arguments | Some(CompletionContext.ParameterList(endPos, paramGroupIdx, idx, name, fields, isCurrying)) -> - let results = GetNamedParametersAndSettableFields endPos paramGroupIdx idx name isCurrying + let results = + GetNamedParametersAndSettableFields endPos paramGroupIdx idx name isCurrying let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () @@ -2111,11 +2138,10 @@ type internal TypeCheckInfo | None -> Some(p @ (items |> List.map DefaultCompletionItem), denv, m) | Some(declItems, declaredDisplayEnv, declaredRange) -> Some(p @ filtered @ declItems, declaredDisplayEnv, declaredRange) - | _, p -> + | _, p -> match declaredItems with | None -> None - | Some(declItems, declaredDisplayEnv, declaredRange) -> - Some(p @ declItems, declaredDisplayEnv, declaredRange) + | Some(declItems, declaredDisplayEnv, declaredRange) -> Some(p @ declItems, declaredDisplayEnv, declaredRange) | Some(CompletionContext.AttributeApplication) -> getDeclaredItemsNotInRangeOpWithAllSymbols () @@ -2187,19 +2213,8 @@ type internal TypeCheckInfo let items = getStaticFieldsOfSameTypeInTheTypeCompletionItem nenv ad m ty match declaredItems with - | Some(declaredItems, _, _) -> - Some( - items - @ declaredItems, - denv, - m - ) - | None -> - Some( - items, - denv, - m - ) + | Some(declaredItems, _, _) -> Some(items @ declaredItems, denv, m) + | None -> Some(items, denv, m) | None -> declaredItems // Other completions diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 27c1d480ddd..6837c0cbffc 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -90,7 +90,13 @@ type CompletionContext = | RangeOperator /// Completing named parameters\setters in parameter list of attributes\constructor\method calls - | ParameterList of identEndPos: pos * paramGroupIdx: int * paramIdxInGroup: int * paramName: string option * settedParam: HashSet * isCurrying: bool + | ParameterList of + identEndPos: pos * + paramGroupIdx: int * + paramIdxInGroup: int * + paramName: string option * + settedParam: HashSet * + isCurrying: bool /// Completing an attribute name, outside of the constructor | AttributeApplication @@ -1094,6 +1100,7 @@ module ParsedInput = 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 = @@ -1103,44 +1110,49 @@ module ParsedInput = || posAfterRangeAndBetweenSpaces lineStr m pos let findSetters identPos (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup argList = - if currentParamIdxInGroup > -1 then CompletionContext.ParameterList(identPos, currentParamGroup, currentParamIdxInGroup, None, emptyStringSet, isCurrying) else - match argList with - | SynExpr.Paren(SynExpr.Tuple(false, parameters, _, _), _, _, _) -> - let setters = HashSet() + if currentParamIdxInGroup > -1 then + CompletionContext.ParameterList(identPos, currentParamGroup, currentParamIdxInGroup, None, emptyStringSet, isCurrying) + else + match argList with + | SynExpr.Paren(SynExpr.Tuple(false, parameters, _, _), _, _, _) -> + let setters = HashSet() - let mutable i = 0 - let mutable idx = -1 - let mutable namedParamName = None + let mutable i = 0 + let mutable idx = -1 + let mutable namedParamName = None - for p in parameters do - let isCurrentParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr p.Range pos + for p in parameters do + let isCurrentParam = + rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr p.Range pos - if isCurrentParam then - idx <- i - - match p with - | Setter id when not isCurrying -> if isCurrentParam then - namedParamName <- Some id.idText + idx <- i - ignore (setters.Add id.idText) - | _ -> () + match p with + | Setter id when not isCurrying -> + if isCurrentParam then + namedParamName <- Some id.idText - i <- i + 1 + ignore (setters.Add id.idText) + | _ -> () + + i <- i + 1 - CompletionContext.ParameterList(identPos, currentParamGroup, idx, namedParamName, setters, isCurrying) - | SynExpr.Paren(expr = Setter id) when not isCurrying -> - let setters = HashSet() - ignore (setters.Add id.idText) - CompletionContext.ParameterList(identPos, currentParamGroup, 0, Some id.idText, setters, isCurrying) + CompletionContext.ParameterList(identPos, currentParamGroup, idx, namedParamName, setters, isCurrying) + | SynExpr.Paren(expr = Setter id) when not isCurrying -> + let setters = HashSet() + ignore (setters.Add id.idText) + CompletionContext.ParameterList(identPos, currentParamGroup, 0, Some id.idText, setters, isCurrying) - | SynExpr.Const (SynConst.Unit, range) -> - let isCurrentParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr range pos - if isCurrentParam then - CompletionContext.ParameterList(identPos, currentParamGroup, 0, None, emptyStringSet, isCurrying) - else CompletionContext.ParameterList(identPos, currentParamGroup, -1, None, emptyStringSet, isCurrying) + | SynExpr.Const(SynConst.Unit, range) -> + let isCurrentParam = rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr range pos + + if isCurrentParam then + CompletionContext.ParameterList(identPos, currentParamGroup, 0, None, emptyStringSet, isCurrying) + else + CompletionContext.ParameterList(identPos, currentParamGroup, -1, None, emptyStringSet, isCurrying) - | _ -> CompletionContext.ParameterList(identPos, currentParamGroup, -1, None, emptyStringSet, isCurrying) + | _ -> CompletionContext.ParameterList(identPos, currentParamGroup, -1, None, emptyStringSet, isCurrying) let endOfLastIdent (lid: SynLongIdent) = let last = List.last lid.LongIdent @@ -1158,9 +1170,8 @@ module ParsedInput = let checkNewObjectOrMethodCall (lineStr, pos) found e = let rec loop isCurrying found currentParamGroup currentParamIdxInGroup e = - let currentParamGroup = - if found then currentParamGroup + 1 - else currentParamGroup + let currentParamGroup = if found then currentParamGroup + 1 else currentParamGroup + match e with | SynExpr.New(_, SynType.LongIdent typeName, arg, _) -> // new A() @@ -1168,10 +1179,18 @@ module ParsedInput = | SynExpr.New(_, SynType.App(StripParenTypes(SynType.LongIdent typeName), _, _, _, mGreaterThan, _, _), arg, _) -> // new A<_>() - Some(findSetters (endOfClosingTokenOrLastIdent mGreaterThan typeName) (lineStr, pos) isCurrying currentParamGroup currentParamIdxInGroup arg) - - | SynExpr.App(_, false, SynExpr.Ident id, arg, _) - // A() + Some( + findSetters + (endOfClosingTokenOrLastIdent mGreaterThan typeName) + (lineStr, pos) + isCurrying + currentParamGroup + currentParamIdxInGroup + arg + ) + + | SynExpr.App(_, false, SynExpr.Ident id, arg, _) + // A() | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.Ident id, _, _, _, _, _, _), arg, _) -> // A<_>() @@ -1179,7 +1198,7 @@ module ParsedInput = | SynExpr.App(_, false, SynExpr.LongIdent(_, lid, _, _), arg, _) | SynExpr.App(_, false, SynExpr.DotGet(longDotId = lid), arg, _) - // A.B() + // A.B() | SynExpr.App(_, false, SynExpr.TypeApp(SynExpr.LongIdent(_, lid, _, _), _, _, _, _, _, _), arg, _) -> // A.B<_>() @@ -1187,20 +1206,26 @@ module ParsedInput = // f 1 2 | SynExpr.App(_, false, (SynExpr.App _ as expr), arg, _) -> - let currentParamIdxInGroup, currentParamGroup = + let currentParamIdxInGroup, currentParamGroup = if currentParamGroup = -1 then match findSetters pos0 (lineStr, pos) true currentParamGroup currentParamIdxInGroup arg with - | CompletionContext.ParameterList (paramIdxInGroup = idx) -> idx, 0 + | CompletionContext.ParameterList(paramIdxInGroup = idx) -> idx, 0 | _ -> -1, -1 - else currentParamIdxInGroup, currentParamGroup + else + currentParamIdxInGroup, currentParamGroup + let found = found || currentParamIdxInGroup > -1 loop true found currentParamGroup currentParamIdxInGroup expr | _ -> None + loop false found -1 -1 e - let (|NewObjectOrMethodCall|_|) (lineStr, pos) e = checkNewObjectOrMethodCall (lineStr, pos) false e - let (|NewObjectOrMethodCallFound|_|) (lineStr, pos) e = checkNewObjectOrMethodCall (lineStr, pos) true e + let (|NewObjectOrMethodCall|_|) (lineStr, pos) e = + checkNewObjectOrMethodCall (lineStr, pos) false e + + let (|NewObjectOrMethodCallFound|_|) (lineStr, pos) e = + checkNewObjectOrMethodCall (lineStr, pos) true e let isOnTheRightOfComma pos (elements: SynExpr list) (commas: range list) current = let rec loop elements (commas: range list) = @@ -1480,8 +1505,7 @@ module ParsedInput = // new A($) | SynExpr.Const(SynConst.Unit, m) when rangeContainsPosOrIsSpacesBetweenRangeAndPos lineStr m pos -> match path with - | SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> - Some(args) + | SyntaxNode.SynExpr(NewObjectOrMethodCall (lineStr, pos) args) :: _ -> Some(args) | _ -> defaultTraverse expr // new (... A$) @@ -1546,22 +1570,21 @@ module ParsedInput = | _ when posAfterRangeAndBetweenSpaces lineStr expr.Range pos -> match expr with // Some | - | SynExpr.Ident id + | SynExpr.Ident id | SynExpr.TypeApp(SynExpr.Ident id, _, _, _, _, _, _) -> Some(CompletionContext.ParameterList(id.idRange.End, 0, 0, None, emptyStringSet, true)) - | SynExpr.LongIdent(_, lid, _, _) - | SynExpr.DotGet(longDotId = lid) - | SynExpr.TypeApp(SynExpr.LongIdent (longDotId = lid), _, _, _, _, _, _) -> + | SynExpr.LongIdent(_, lid, _, _) + | SynExpr.DotGet(longDotId = lid) + | SynExpr.TypeApp(SynExpr.LongIdent(longDotId = lid), _, _, _, _, _, _) -> Some(CompletionContext.ParameterList(endOfLastIdent lid, 0, 0, None, emptyStringSet, true)) - | NewObjectOrMethodCallFound (lineStr, pos) (CompletionContext.ParameterList(a,b,c,d,e,f)) -> - Some(CompletionContext.ParameterList(a,b + 1,c,d,e,f)) + | NewObjectOrMethodCallFound (lineStr, pos) (CompletionContext.ParameterList(a, b, c, d, e, f)) -> + Some(CompletionContext.ParameterList(a, b + 1, c, d, e, f)) | _ -> defaultTraverse expr - - | _ -> defaultTraverse expr + | _ -> defaultTraverse expr member _.VisitRecordField(path, copyOpt, field) = let contextFromTreePath completionPath = diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 3e3d60a96ae..2175fa3fa54 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -62,7 +62,13 @@ type public CompletionContext = | RangeOperator /// Completing named parameters\setters in parameter list of attributes\constructor\method calls - | ParameterList of identEndPos: pos * paramGroupIdx: int * paramIdxInGroup: int * paramName: string option * settedParam: HashSet * isCurrying: bool + | ParameterList of + identEndPos: pos * + paramGroupIdx: int * + paramIdxInGroup: int * + paramName: string option * + settedParam: HashSet * + isCurrying: bool /// Completing an attribute name, outside of the constructor | AttributeApplication From 38faffafe119d615022a64760d33139b4c9a5f0f Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 06:49:57 +0800 Subject: [PATCH 17/28] fix test --- src/Compiler/Service/FSharpCheckerResults.fs | 22 +++- .../CompletionProviderTests.fs | 100 +++++++++++++----- 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 8181d688c33..5fd517c3e9b 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1384,9 +1384,6 @@ type internal TypeCheckInfo range0 superTy |> List.choose (fun struct (prop, baseProp) -> - let canPick = - isPropertyOverridable overriddenProperties prop && prop.IsStatic = isStatic - let getterMeth = if prop.HasGetter then ValueSome prop.GetterMethod @@ -1399,6 +1396,21 @@ type internal TypeCheckInfo else baseProp |> ValueOption.map _.SetterMethod + let getterOverridable = + getterMeth + |> ValueOption.map (isMethodOverridable checkedMethods) + |> ValueOption.defaultValue false + + let setterOverridable = + setterMeth + |> ValueOption.map (isMethodOverridable checkedMethods) + |> ValueOption.defaultValue false + + let canPick = + isPropertyOverridable overriddenProperties prop + && prop.IsStatic = isStatic + && (getterOverridable || setterOverridable) + getterMeth |> ValueOption.iter checkedMethods.Add setterMeth |> ValueOption.iter checkedMethods.Add @@ -1426,7 +1438,7 @@ type internal TypeCheckInfo let getter, getterWithBody = match getterMeth with - | ValueSome meth -> + | ValueSome meth when getterOverridable -> let implementBody = checkMethAbstractAndGetImplementBody meth @@ -1439,7 +1451,7 @@ type internal TypeCheckInfo let setter, setterWithBody = match setterMeth with - | ValueSome meth -> + | ValueSome meth when setterOverridable -> let implementBody = checkMethAbstractAndGetImplementBody meth diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 551b804bc37..5fe14d2d102 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1853,34 +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" - ] + ["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" + ] + ) ) - VerifyCompletionListExactly( - fileContents, - "override x.", - [ - "Bind" - "BindDelegate" - "Equals" - "Finalize" - "GetHashCode" - "get_ReturnType" - "ToString" - ] + ["override x."; "member this."] + |> 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" + ] + ) ) [] @@ -1912,11 +1927,42 @@ type C () = override A1 () = () override x.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" ]) +type IA = + abstract member A1: unit -> unit + abstract member A1: string -> unit + abstract member A2: unit -> unit + +type TA() = + interface IA with + member this.A1 (arg1: string): unit = () + member thisTA. +""" + + VerifyCompletionListExactly(fileContents, "override _.", [ + "Equals (obj: obj): bool" + "Finalize (): unit" + "GetHashCode (): int" + ]) + VerifyCompletionListExactly(fileContents, "override x.b", [ + "A1 (arg1: 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" + ]) + VerifyCompletionListExactly(fileContents, "member thisTA.", [ + "A1 (): unit" + "A2 (): unit" + ]) [] let ``Completion list for override is empty when the caret is on the self identifier`` () = From 19e678e54eb23ed31c7fe6529c243c2a203dd37e Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 08:28:23 +0800 Subject: [PATCH 18/28] fix indexer generation format code --- src/Compiler/Service/FSharpCheckerResults.fs | 13 +++---------- src/Compiler/Service/ServiceParsedInputOps.fsi | 1 - 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 4f99e5713c3..5ae6e455bce 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1172,8 +1172,7 @@ type internal TypeCheckInfo let implementBody = checkMethAbstractAndGetImplementBody meth - ($"base.{prop.DisplayName}" - + (if prop.IsIndexer then $"[({parameters})]" else "")) + ($"base.{prop.DisplayName}" + (if prop.IsIndexer then $"({parameters})" else "")) let getter = $"get ({parameters}): {retTy}" getter, $"{getter} = {implementBody}" @@ -1186,7 +1185,7 @@ type internal TypeCheckInfo checkMethAbstractAndGetImplementBody meth ($"base.{prop.DisplayName}" - + (if prop.IsIndexer then $"[({parameters})]" else String.Empty) + + (if prop.IsIndexer then $"({parameters})" else String.Empty) + " <- value") let parameters = if prop.IsIndexer then $"({parameters}) " else String.Empty @@ -1295,13 +1294,7 @@ type internal TypeCheckInfo let getTyFromTypeNamePos (endPos: pos) = let nameResItems = - GetPreciseItemsFromNameResolution( - endPos.Line, - endPos.Column, - None, - ResolveTypeNamesToTypeRefs, - ResolveOverloads.Yes - ) + GetPreciseItemsFromNameResolution(endPos.Line, endPos.Column, None, ResolveTypeNamesToTypeRefs, ResolveOverloads.Yes) match nameResItems with | NameResResult.Members(ls, _, _) -> diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 041cc36be04..427ffda43aa 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -92,7 +92,6 @@ type public CompletionContext = hasThis: bool * isStatic: bool - type public ModuleKind = { IsAutoOpen: bool HasModuleSuffix: bool } From 6207dffa3ed9e4a7887b076c0d4fb8056b102ddf Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 08:56:35 +0800 Subject: [PATCH 19/28] update baseline --- ...vice.SurfaceArea.netstandard20.release.bsl | 37 ++++++++++++++++++- .../Expression/Object - Class 06.fs.bsl | 22 ++++++++++- .../Expression/Object - Class 15.fs.bsl | 20 ++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) 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 909e42e2cdc..11251806620 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 @@ -2839,8 +2839,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 @@ -2894,7 +2902,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) @@ -3529,6 +3537,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..b6ba0b08535 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,27 @@ 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,13--4,13), (4,13--4,13)), None, + ArbitraryAfterError ("classDefnMember1", (4,13--4,13)), + (4,5--4,13), NoneAtInvisible, + { LeadingKeyword = Member (4,5--4,11) + InlineKeyword = None + EqualsRange = None }), (4,5--4,13))], [], (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..6b6cf4004b2 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,26 @@ 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 (5,5--5,5), (5,5--5,5)), None, + ArbitraryAfterError ("classDefnMember1", (5,5--5,5)), + (4,5--5,5), NoneAtInvisible, + { LeadingKeyword = Member (4,5--4,11) + InlineKeyword = None + EqualsRange = None }), (4,5--5,5)); + Member (SynBinding (None, Normal, false, false, [], PreXmlDoc ((5,5), FSharp.Compiler.Xml.XmlDocCollector), From 56ab9aa9868429efbb6e9703adcdd869946e2f2a Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 09:03:14 +0800 Subject: [PATCH 20/28] format; release note --- .../.FSharp.Compiler.Service/8.0.400.md | 1 + .../CompletionProviderTests.fs | 66 +++++++++---------- 2 files changed, 34 insertions(+), 33 deletions(-) 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 9033ff771f8..d72fe545084 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -31,3 +31,4 @@ * 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)) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 6ed3399dce5..0516c1c1b9d 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1866,7 +1866,7 @@ let _ = """ // SetIndexBinder inherits from DynamicMetaObjectBinder, but overrides and seals Bind and the ReturnType property - ["override _.a"; "member x."] + [ "override _.a"; "member x." ] |> List.iter (fun i -> VerifyCompletionListExactly( fileContents, @@ -1879,10 +1879,9 @@ let _ = "GetHashCode (): int" "ToString (): string" ] - ) - ) + )) - ["override x."; "member this."] + [ "override x."; "member this." ] |> List.iter (fun i -> VerifyCompletionListExactly( fileContents, @@ -1895,8 +1894,7 @@ let _ = "GetHashCode (): int" "ToString (): string" ] - ) - ) + )) [] let ``Completion list for override does not contain virtual method if it is already overridden in the same type`` () = @@ -1943,33 +1941,35 @@ type TA() = member a. """ - VerifyCompletionListExactly(fileContents, "override _.", [ - "Equals (obj: obj): bool" - "Finalize (): unit" - "GetHashCode (): int" - ]) - VerifyCompletionListExactly(fileContents, "override x.b", [ - "A1 (arg1: 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" - ]) - VerifyCompletionListExactly(fileContents, "member thisTA.", [ - "A1 (): unit" - "A2 (): unit" - ]) - VerifyCompletionListExactly(fileContents, "static member ", [ - "A4 (): unit" - ]) + VerifyCompletionListExactly(fileContents, "override _.", [ "Equals (obj: obj): bool"; "Finalize (): unit"; "GetHashCode (): int" ]) + + VerifyCompletionListExactly( + fileContents, + "override x.b", + [ + "A1 (arg1: 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" + ] + ) + + VerifyCompletionListExactly(fileContents, "member thisTA.", [ "A1 (): unit"; "A2 (): unit" ]) + VerifyCompletionListExactly(fileContents, "static member ", [ "A4 (): unit" ]) [] let ``Completion list for override is empty when the caret is on the self identifier`` () = From 92e0255520c0e66eaf0915aff774eac14470d673 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:22:01 +0800 Subject: [PATCH 21/28] fix test --- .../tests/FSharp.Editor.Tests/CompletionProviderTests.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 0516c1c1b9d..184af2c1c8c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1887,6 +1887,8 @@ let _ = 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" "FallbackSetIndex (target: System.Dynamic.DynamicMetaObject, indexes: System.Dynamic.DynamicMetaObject array, value: System.Dynamic.DynamicMetaObject, errorSuggestion: System.Dynamic.DynamicMetaObject): System.Dynamic.DynamicMetaObject" @@ -1938,7 +1940,7 @@ type TA() = static member A3 (): unit = () static member member this.A1 (arg1: string): unit = () - member a. + member thisTA. """ VerifyCompletionListExactly(fileContents, "override _.", [ "Equals (obj: obj): bool"; "Finalize (): unit"; "GetHashCode (): int" ]) From 8160e50f5d2beafd3b72d63e4c2d62fdb99409f6 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sun, 9 Jun 2024 21:27:13 +0800 Subject: [PATCH 22/28] fix tests --- .../FSharp.Editor.Tests/CompletionProviderTests.fs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 184af2c1c8c..6d71a05ac5c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1891,7 +1891,6 @@ let _ = "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" - "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" @@ -1929,16 +1928,19 @@ type C () = override A1 s = () type IA = + static abstract member A3: unit -> unit + static abstract member B4: unit -> unit + +type IB = abstract member A1: unit -> unit abstract member A1: string -> unit abstract member A2: unit -> unit - static abstract member A3: unit -> unit - static abstract member A4: unit -> unit - + type TA() = interface IA with - static member A3 (): unit = () static member + static member A3 (): unit = () + interface IB with member this.A1 (arg1: string): unit = () member thisTA. """ From 0531a33514302bbd685e22c5bdfb1e15bf031823 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Sun, 9 Jun 2024 22:30:01 +0800 Subject: [PATCH 23/28] static member completion after the method name fix test --- src/Compiler/Service/ServiceParsedInputOps.fs | 7 +++++++ .../tests/FSharp.Editor.Tests/CompletionProviderTests.fs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 14f8a923bd4..3396242c70b 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -1633,6 +1633,13 @@ module ParsedInput = && 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 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 diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 6d71a05ac5c..f0deeaba91b 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1929,7 +1929,7 @@ type C () = type IA = static abstract member A3: unit -> unit - static abstract member B4: unit -> unit + static abstract member A4: unit -> unit type IB = abstract member A1: unit -> unit From d9b539429fc375a9f2507d425e5ad52318c92e45 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Tue, 11 Jun 2024 01:08:44 +0800 Subject: [PATCH 24/28] format --- src/Compiler/Service/ServiceParsedInputOps.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 3396242c70b..0f6f3bc0530 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -1633,11 +1633,10 @@ module ParsedInput = && 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 + isStaticMember trivia.LeadingKeyword && rangeContainsPos methodId.idRange pos -> overrideContext path trivia.LeadingKeyword.Range false true false From 67049f64b466957707406687ee8b19807a342e8f Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Tue, 11 Jun 2024 09:02:00 +0800 Subject: [PATCH 25/28] Fix completion for partial implemented property Fix completion for same signature slot in different type/interface Fix parameter name generation Adjust code --- src/Compiler/Checking/InfoReader.fs | 6 +- src/Compiler/Checking/InfoReader.fsi | 4 +- src/Compiler/Service/FSharpCheckerResults.fs | 304 ++++++++++-------- .../CompletionProviderTests.fs | 65 +++- 4 files changed, 227 insertions(+), 152 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 814cdb623f5..3a36122e89e 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -950,7 +950,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = infoReader.GetIntrinsicPropInfoSetsOfType optFilter ad allowMultiIntfInst findFlag m ty |> List.concat /// Get the flattened list of intrinsic properties in the hierarchy - member infoReader.GetIntrinsicPropInfoWithOverridenPropOfType optFilter ad allowMultiIntfInst findFlag m ty = + 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 @@ -1008,8 +1008,8 @@ 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 GetIntrinsicPropInfoWithOverridenPropOfType (infoReader: InfoReader) optFilter ad allowMultiIntfInst findFlag m ty = - infoReader.GetIntrinsicPropInfoWithOverridenPropOfType 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 a4d329c33a0..6638c1c39e7 100644 --- a/src/Compiler/Checking/InfoReader.fsi +++ b/src/Compiler/Checking/InfoReader.fsi @@ -281,8 +281,8 @@ val GetIntrinsicPropInfosOfType: ty: TType -> PropInfo list -/// Get the flattened list of intrinsic properties in the hierarchy -val GetIntrinsicPropInfoWithOverridenPropOfType: +/// 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 -> diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 5ae6e455bce..310a52a5807 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1060,19 +1060,47 @@ type internal TypeCheckInfo /// Gets all methods that a type can override, but has not yet done so. let GetOverridableMethods pos ctx (typeNameRange: range) spacesBeforeOverrideKeyword hasThis isStatic = - let isMethodOverridable alreadyOverridden (candidate: MethInfo) = + let checkImplementedSlotDeclareType ty (slots: SlotSig list) = + slots |> List.exists (fun (TSlotSig(declaringType = ty2)) -> typeEquiv g ty ty2) + + let isMethodOverridable superTy alreadyOverridden (candidate: MethInfo) = not candidate.IsFinal && not ( alreadyOverridden - |> ResizeArray.exists (MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate) + |> ResizeArray.exists (fun i -> + MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate i + && (checkImplementedSlotDeclareType superTy i.ImplementedSlotSignatures + || tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef)) ) - let isPropertyOverridable alreadyOverridden (candidate: PropInfo) = - candidate.IsVirtualProperty - && not ( - alreadyOverridden - |> List.exists (PropInfosEquivByNameAndSig EraseNone g amap range0 candidate) - ) + 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 + && (checkImplementedSlotDeclareType superTy i.ImplementedSlotSignatures + || tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef)) + |> 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 @@ -1104,8 +1132,85 @@ type internal TypeCheckInfo let isInterface = isInterfaceTy g superTy + // reuse between props and methods + let argNames = HashSet() + let overridableProps = - GetIntrinsicPropInfoWithOverridenPropOfType + 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 @@ -1126,20 +1231,19 @@ type internal TypeCheckInfo else baseProp |> ValueOption.map _.SetterMethod + let isGetterOverridable, isSetterOverridable = + isPropertyOverridable superTy overriddenProperties prop + let isGetterOverridable = - getterMeth - |> ValueOption.map (isMethodOverridable checkedMethods) - |> ValueOption.defaultValue false + isGetterOverridable + && isMethodOptionOverridable superTy checkedMethods getterMeth let isSetterOverridable = - setterMeth - |> ValueOption.map (isMethodOverridable checkedMethods) - |> ValueOption.defaultValue false + isSetterOverridable + && isMethodOptionOverridable superTy checkedMethods setterMeth let canPick = - isPropertyOverridable overriddenProperties prop - && prop.IsStatic = isStatic - && (isGetterOverridable || isSetterOverridable) + prop.IsStatic = isStatic && (isGetterOverridable || isSetterOverridable) getterMeth |> ValueOption.iter checkedMethods.Add setterMeth |> ValueOption.iter checkedMethods.Add @@ -1147,83 +1251,62 @@ type internal TypeCheckInfo if not canPick then None else - let getNameForNoNameArg = - let mutable count = 1 + let getterMeth = if isGetterOverridable then getterMeth else ValueNone + let setterMeth = if isSetterOverridable then setterMeth else ValueNone + let name, textInCode = generatePropertyOverrideBody prop getterMeth setterMeth - fun () -> - let name = $"arg{count}" - count <- count + 1 - name + Item.Property( + name, + [ + prop + if baseProp.IsSome then + baseProp.Value + ], + None + ) + |> ItemWithNoInst + |> CompletionItemWithMoreSetting ValueNone ValueNone -1 (ValueSome textInCode) (ValueSome name) + |> Some) - let parameters = - prop.GetParamNamesAndTypes(amap, m) - |> List.map (fun (ParamNameAndType(name, ty)) -> - let name = name |> Option.map _.idText |> Option.defaultWith getNameForNoNameArg - - $"{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 when isGetterOverridable -> - 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 when isSetterOverridable -> - let implementBody = - checkMethAbstractAndGetImplementBody - meth - ($"base.{prop.DisplayName}" - + (if prop.IsIndexer then $"({parameters})" else String.Empty) - + " <- value") - - let parameters = if prop.IsIndexer then $"({parameters}) " else String.Empty - let setter = $"set {parameters}(value: {retTy})" - setter, $"{setter} = {implementBody}" - | _ -> String.Empty, String.Empty - - let keywordAnd = - if getterMeth.IsNone || setterMeth.IsNone then - String.Empty + 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 - " and " + $"this.{name} = " - let name = $"{prop.DisplayName} with {getter}{keywordAnd}{setter}" + let implementBody = + checkMethAbstractAndGetImplementBody meth $"base.{meth.DisplayName}{parameters}" - let textInCode = - let this = if hasThis || prop.IsStatic then String.Empty else "this." + nameWithThis + newlineIndent + implementBody - let getterWithBody = - if String.IsNullOrWhiteSpace getterWithBody then - String.Empty - else - getterWithBody + newlineIndent - - this - + prop.DisplayName - + newlineIndent - + "with " - + getterWithBody - + keywordAnd - + setterWithBody + name, textInCode - Item.Property(name, [ prop ], None) - |> ItemWithNoInst - |> CompletionItemWithMoreSetting ValueNone ValueNone -1 (ValueSome textInCode) (ValueSome name) - |> Some) - - let overridableMeths = GetIntrinsicMethInfosOfType infoReader None @@ -1234,8 +1317,8 @@ type internal TypeCheckInfo superTy |> List.choose (fun meth -> let canPick = - isMethodOverridable checkedMethods meth - && meth.IsInstance <> isStatic + meth.IsInstance <> isStatic + && isMethodOverridable superTy checkedMethods meth && (not isInterface || not (tyconRefEq g meth.DeclaringTyconRef g.system_Object_tcref)) @@ -1244,46 +1327,7 @@ type internal TypeCheckInfo if not canPick then None else - - let getNameForNoNameArg = - let mutable count = 1 - - fun () -> - let name = $"arg{count}" - count <- count + 1 - name - - 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 = Option.defaultWith getNameForNoNameArg name - - $"{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 + let name, textInCode = generateMethodOverrideBody meth Item.MethodGroup(name, [ meth ], None) |> ItemWithNoInst diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index f0deeaba91b..09c9d6cefaf 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1926,55 +1926,86 @@ type C () = override A1 () = () override x.c override A1 s = () +""" + + 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, "override _.", [ "Equals (obj: obj): bool"; "Finalize (): unit"; "GetHashCode (): int" ]) - VerifyCompletionListExactly( fileContents, - "override x.b", + "static member ", [ - "A1 (arg1: string): unit" - "A2 (): unit" - "Equals (obj: obj): bool" - "Finalize (): unit" - "GetHashCode (): int" - "ToString (): string" + "P1 with get (value: int): int and set (value: int) (value_1: int)" + "P2 with set (value: int)" + "A4 (): unit" ] ) VerifyCompletionListExactly( fileContents, - "override x.c", + "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" - "Equals (obj: obj): bool" - "Finalize (): unit" - "GetHashCode (): int" - "ToString (): string" ] ) - VerifyCompletionListExactly(fileContents, "member thisTA.", [ "A1 (): unit"; "A2 (): unit" ]) - VerifyCompletionListExactly(fileContents, "static member ", [ "A4 (): unit" ]) - [] let ``Completion list for override is empty when the caret is on the self identifier`` () = let fileContents = From 943b0187cc570f54cfb9d888fad1c8d6e18dbc12 Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Tue, 11 Jun 2024 17:39:50 +0800 Subject: [PATCH 26/28] fix _.ImplementedSlotSignatures maybe failed --- src/Compiler/Service/FSharpCheckerResults.fs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 310a52a5807..d74ba14ad30 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1060,8 +1060,10 @@ type internal TypeCheckInfo /// Gets all methods that a type can override, but has not yet done so. let GetOverridableMethods pos ctx (typeNameRange: range) spacesBeforeOverrideKeyword hasThis isStatic = - let checkImplementedSlotDeclareType ty (slots: SlotSig list) = - slots |> List.exists (fun (TSlotSig(declaringType = ty2)) -> typeEquiv g ty ty2) + 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 @@ -1069,8 +1071,8 @@ type internal TypeCheckInfo alreadyOverridden |> ResizeArray.exists (fun i -> MethInfosEquivByNameAndSig EraseNone true g amap range0 candidate i - && (checkImplementedSlotDeclareType superTy i.ImplementedSlotSignatures - || tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef)) + && (tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef + || checkImplementedSlotDeclareType superTy (Option.attempt (fun () -> i.ImplementedSlotSignatures)))) ) let isMethodOptionOverridable superTy alreadyOverridden candidate = @@ -1084,8 +1086,8 @@ type internal TypeCheckInfo alreadyOverridden |> List.filter (fun i -> PropInfosEquivByNameAndSig EraseNone g amap range0 candidate i - && (checkImplementedSlotDeclareType superTy i.ImplementedSlotSignatures - || tyconRefEq g candidate.DeclaringTyconRef i.DeclaringTyconRef)) + && (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) From 48535d369e1f6baa1c1a25515f0ea6ed4032c60c Mon Sep 17 00:00:00 2001 From: ijklam <43789618+Tangent-90@users.noreply.github.com> Date: Wed, 26 Jun 2024 17:44:38 +0800 Subject: [PATCH 27/28] fix parser --- src/Compiler/pars.fsy | 6 +++--- .../data/SyntaxTree/Expression/Object - Class 06.fs.bsl | 9 +++++---- .../data/SyntaxTree/Expression/Object - Class 15.fs.bsl | 9 +++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 5df70673f44..48f616db597 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -2381,11 +2381,11 @@ objectImplementationMember: let flags = Some(memFlagsBuilder SynMemberKind.Member) let xmlDoc = grabXmlDocAtRangeStart (parseState, $1, rangeStart) let trivia = { LeadingKeyword = leadingKeyword; InlineKeyword = None; EqualsRange = None } - let mMember = rhs parseState 3 + let mMember = rhs parseState 2 let mEnd = mMember.EndRange let bindingPat = patFromParseError (SynPat.Wild(mEnd)) - let expr = arbExpr ("classDefnMember1", mEnd) - let mWhole = rhs2 parseState 1 3 + let expr = arbExpr ("objectImplementationMember1", mEnd) + let mWhole = rhs2 parseState 1 2 let binding = mkSynBinding (xmlDoc, bindingPat) 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 b6ba0b08535..1880c3c044f 100644 --- a/tests/service/data/SyntaxTree/Expression/Object - Class 06.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/Object - Class 06.fs.bsl @@ -22,12 +22,13 @@ ImplFile SynValInfo ([[SynArgInfo ([], false, None)]; []], SynArgInfo ([], false, None)), None), - FromParseError (Wild (4,13--4,13), (4,13--4,13)), None, - ArbitraryAfterError ("classDefnMember1", (4,13--4,13)), - (4,5--4,13), NoneAtInvisible, + 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,13))], [], (3,2--3,9), + 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, 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 6b6cf4004b2..ec08bebfe92 100644 --- a/tests/service/data/SyntaxTree/Expression/Object - Class 15.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/Object - Class 15.fs.bsl @@ -22,12 +22,13 @@ ImplFile SynValInfo ([[SynArgInfo ([], false, None)]; []], SynArgInfo ([], false, None)), None), - FromParseError (Wild (5,5--5,5), (5,5--5,5)), None, - ArbitraryAfterError ("classDefnMember1", (5,5--5,5)), - (4,5--5,5), NoneAtInvisible, + 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--5,5)); + EqualsRange = None }), (4,5--4,11)); Member (SynBinding (None, Normal, false, false, [], From f5fe77678e4dc5a6258a2ff7233fe2ccd20eab99 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 27 Jun 2024 13:24:21 +0200 Subject: [PATCH 28/28] fixed typo --- src/Compiler/Service/FSharpCheckerResults.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index d74ba14ad30..cab12da5cdf 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -579,7 +579,7 @@ type internal TypeCheckInfo Some(StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType)) | _, _ -> None - /// Build a CompetionItem + /// Build a CompletionItem let CompletionItemWithMoreSetting (ty: TyconRef voption) (assemblySymbol: AssemblySymbol voption)