From bf890601ebf10710d0c43a8f8d03c741af6c9d90 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 5 Jan 2023 12:00:14 +0100 Subject: [PATCH 01/44] wip --- src/Compiler/Checking/CheckExpressions.fs | 12 +++++++++++- .../FSharpChecker/FindReferences.fs | 13 +++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index ef4930408a0..1b024c46da6 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -4218,7 +4218,17 @@ and TcValSpec (cenv: cenv) env declKind newOk containerInfo memFlagsOpt thisTyOp | _ -> let valSynInfo = AdjustValSynInfoInSignature g declaredTy valSynInfo let prelimValReprInfo = TranslateSynValInfo id.idRange (TcAttributes cenv env) valSynInfo - [ ValSpecResult(altActualParent, None, id, enclosingDeclaredTypars, declaredTypars, declaredTy, prelimValReprInfo, declKind) ], tpenv + + let curriedArgTys, _retTy = stripFunTy g declaredTy + + [ ValSpecResult(altActualParent, None, id, enclosingDeclaredTypars, declaredTypars, declaredTy, prelimValReprInfo, declKind) + for argTy, argInfo in valSynInfo.CurriedArgInfos |> Seq.collect (fun x -> x) |> Seq.zip curriedArgTys do + match argInfo.Ident with + | Some ident -> + ValSpecResult(ParentNone, None, ident, [], [], argTy, (PrelimValReprInfo ([], (argInfo |> TranslateTopArgSynInfo true ident.idRange (TcAttributes cenv env AttributeTargets.Parameter)))), ExpressionBinding) + | None -> () + + ], tpenv //------------------------------------------------------------------------- // Bind types diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index b421de62027..1ecf3b6874f 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -145,6 +145,19 @@ let foo x = x ++ 4""" }) ]) } +[] +let ``We find function parameter in signature file`` () = + SyntheticProject.Create( + { sourceFile "Source" [] with SignatureFile = AutoGenerated }) + .Workflow { + placeCursor "Source" 3 7 "let f x =" ["x"] + findAllReferences (expectToFind [ + "FileSource.fsi", 6, 7, 8 + "FileSource.fs", 3, 6, 7 + "FileSource.fs", 4, 12, 13 + ]) + } + module Attributes = let project() = SyntheticProject.Create( From f11338c4dadf56ce35237842c51a08c98feb5bfc Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 6 Jan 2023 16:35:08 +0100 Subject: [PATCH 02/44] This kinda works --- src/Compiler/Service/FSharpCheckerResults.fs | 11 ++++++++++- .../FSharpChecker/SymbolUse.fs | 6 ++---- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 8 +++++++- .../LanguageService/WorkspaceExtensions.fs | 7 ++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index f2b88684b5c..1dce0f93b8b 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -261,6 +261,16 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti member this.IsPrivateToFile = let isPrivate = match this.Symbol with + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> + + // In case it's a parameter and there is a signature file, then it's not private + + // TODO: Can it be anything else than a parameter? + + // TODO: Is there a way to tell there is a signature file? + + // Since we don't know any better, we have to assume it's not private + false | :? FSharpMemberOrFunctionOrValue as m -> let fileSignatureLocation = m.DeclaringEntity |> Option.bind (fun e -> e.SignatureLocation) @@ -273,7 +283,6 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti let symbolIsNotInSignatureFile = m.SignatureLocation = Some m.DeclarationLocation fileHasSignatureFile && symbolIsNotInSignatureFile - || not m.IsModuleValueOrMember || m.Accessibility.IsPrivate | :? FSharpEntity as m -> m.Accessibility.IsPrivate | :? FSharpGenericParameter -> true diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index c039463d983..e865e06ee76 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -50,7 +50,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.True(symbolUse.IsPrivateToFile)) } - [] + // TODO: Fix this [] let ``Function parameter, no signature file`` () = SyntheticProject.Create(sourceFile "First" []).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> @@ -58,14 +58,12 @@ val f: x: 'a -> TFirstV_1<'a> Assert.True(symbolUse.IsPrivateToFile)) } - /// This is a bug: https://github.com/dotnet/fsharp/issues/14277 [] let ``Function parameter, with signature file`` () = SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - // This should be false, because it's also in the signature file - Assert.True(symbolUse.IsPrivateToFile)) + Assert.False(symbolUse.IsPrivateToFile)) } // [] This is a bug - https://github.com/dotnet/fsharp/issues/14419 diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 3d9c4f9f528..13286e64709 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -344,12 +344,18 @@ module ProjectOperations = failwith $"Found {results.Length} references but expected to find {expected}" let expectToFind expected (foundRanges: range seq) = + let expected = + expected + |> Seq.sortBy (fun (file, _, _, _) -> file) + |> Seq.toArray + let actual = foundRanges |> Seq.map (fun r -> Path.GetFileName(r.FileName), r.StartLine, r.StartColumn, r.EndColumn) |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - Assert.Equal<(string * int * int * int)[]>(expected |> Seq.toArray, actual) + + Assert.Equal<(string * int * int * int)[]>(expected, actual) let rec saveProject (p: SyntheticProject) generateSignatureFiles checker = async { diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index f25746325b2..395c0fcdbd0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -229,7 +229,12 @@ type Project with | Some document when this.IsFastFindReferencesEnabled && document.Project = this -> backgroundTask { let! _, _, _, options = document.GetFSharpCompilationOptionsAsync(userOpName) |> RoslynHelpers.StartAsyncAsTask ct - return options.SourceFiles |> Seq.takeWhile ((<>) document.FilePath) |> Set + let signatureFile = if not (document.FilePath |> isSignatureFile) then $"{document.FilePath}i" else null + return + options.SourceFiles + |> Seq.takeWhile ((<>) document.FilePath) + |> Seq.filter ((<>) signatureFile) + |> Set } | _ -> Task.FromResult Set.empty From 549e42412fccdafcf86baabc4c0853bc4a1e1e74 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 6 Jan 2023 16:37:34 +0100 Subject: [PATCH 03/44] fantomas --- src/Compiler/Service/FSharpCheckerResults.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 1dce0f93b8b..d8c35e08627 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -282,8 +282,7 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti let symbolIsNotInSignatureFile = m.SignatureLocation = Some m.DeclarationLocation - fileHasSignatureFile && symbolIsNotInSignatureFile - || m.Accessibility.IsPrivate + fileHasSignatureFile && symbolIsNotInSignatureFile || m.Accessibility.IsPrivate | :? FSharpEntity as m -> m.Accessibility.IsPrivate | :? FSharpGenericParameter -> true | :? FSharpUnionCase as m -> m.Accessibility.IsPrivate From 158fa7804d6f7cce1b147de071cd9cbfc73273ab Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 16 Jan 2023 12:04:46 +0100 Subject: [PATCH 04/44] still in progress --- src/Compiler/Checking/CheckExpressions.fs | 45 ++++++++++--------- src/Compiler/Checking/NameResolution.fs | 22 ++++----- src/Compiler/Checking/NameResolution.fsi | 4 +- src/Compiler/Checking/SignatureConformance.fs | 6 ++- src/Compiler/Driver/CompilerDiagnostics.fs | 1 + src/Compiler/Interactive/fsi.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 4 +- src/Compiler/Service/ItemKey.fs | 2 +- .../Service/SemanticClassification.fs | 2 +- .../Service/ServiceDeclarationLists.fs | 10 ++--- src/Compiler/Service/service.fs | 2 +- src/Compiler/Symbols/SymbolHelpers.fs | 20 ++++----- src/Compiler/Symbols/Symbols.fs | 14 +++--- src/Compiler/TypedTree/TypedTree.fs | 8 +++- src/Compiler/TypedTree/TypedTree.fsi | 3 ++ src/Compiler/TypedTree/TypedTreeBasics.fs | 8 ++-- src/Compiler/TypedTree/TypedTreeOps.fs | 6 +-- src/Compiler/TypedTree/TypedTreePickle.fs | 2 +- 18 files changed, 88 insertions(+), 73 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index fd74b48f7a8..585c3653400 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -919,7 +919,7 @@ let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, is // Call the attribute checking function let attribs = tcAttributes (optAttrs@attrs) - ({ Attribs = attribs; Name = nm } : ArgReprInfo) + ({ Attribs = attribs; Name = nm; OtherRange = None } : ArgReprInfo) /// Members have an arity inferred from their syntax. This "valSynData" is not quite the same as the arities /// used in the middle and backends of the compiler ("valReprInfo"). @@ -4044,7 +4044,7 @@ and TcPseudoMemberSpec cenv newOk env synTypes tpenv synMemberSig m = else warning(Error(FSComp.SR.tcTraitMayNotUseComplexThings(), m)) - let item = Item.ArgName (Some id, memberConstraintTy, None, id.idRange) + let item = Item.OtherName (Some id, memberConstraintTy, None, None, id.idRange) CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.AccessRights) TTrait(tys, logicalCompiledName, memberFlags, argTys, returnTy, ref None), tpenv @@ -4196,17 +4196,7 @@ and TcValSpec (cenv: cenv) env declKind newOk containerInfo memFlagsOpt thisTyOp | _ -> let valSynInfo = AdjustValSynInfoInSignature g declaredTy valSynInfo let prelimValReprInfo = TranslateSynValInfo id.idRange (TcAttributes cenv env) valSynInfo - - let curriedArgTys, _retTy = stripFunTy g declaredTy - - [ ValSpecResult(altActualParent, None, id, enclosingDeclaredTypars, declaredTypars, declaredTy, prelimValReprInfo, declKind) - for argTy, argInfo in valSynInfo.CurriedArgInfos |> Seq.collect (fun x -> x) |> Seq.zip curriedArgTys do - match argInfo.Ident with - | Some ident -> - ValSpecResult(ParentNone, None, ident, [], [], argTy, (PrelimValReprInfo ([], (argInfo |> TranslateTopArgSynInfo true ident.idRange (TcAttributes cenv env AttributeTargets.Parameter)))), ExpressionBinding) - | None -> () - - ], tpenv + [ ValSpecResult(altActualParent, None, id, enclosingDeclaredTypars, declaredTypars, declaredTy, prelimValReprInfo, declKind) ], tpenv //------------------------------------------------------------------------- // Bind types @@ -4652,7 +4642,7 @@ and TcStaticConstantParameter (cenv: cenv) (env: TcEnv) tpenv kind (StripParenTy let record ttype = match idOpt with | Some id -> - let item = Item.ArgName (Some id, ttype, Some container, id.idRange) + let item = Item.OtherName (Some id, ttype, None, Some container, id.idRange) CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.AccessRights) | _ -> () @@ -8209,7 +8199,7 @@ and TcItemThen (cenv: cenv) (overallTy: OverallTy) env tpenv (tinstEnclosing, it // These items are not expected here - they are only used for reporting symbols from name resolution to language service | Item.ActivePatternCase _ | Item.AnonRecdField _ - | Item.ArgName _ + | Item.OtherName _ | Item.CustomBuilder _ | Item.ModuleOrNamespaces _ | Item.NewDef _ @@ -9094,7 +9084,7 @@ and TcLookupItemThen cenv overallTy env tpenv mObjExpr objExpr objExprTy delayed | Item.NewDef _ | Item.SetterArg _ | Item.CustomBuilder _ - | Item.ArgName _ + | Item.OtherName _ | Item.ActivePatternCase _ -> error (Error (FSComp.SR.tcSyntaxFormUsedOnlyWithRecordLabelsPropertiesAndFields(), mItem)) @@ -9706,7 +9696,7 @@ and TcMethodApplication | Some id -> id.idRange | None -> id.idRange let container = ArgumentContainer.Method finalCalledMethInfo - let item = Item.ArgName (idOpt, assignedArg.CalledArg.CalledArgumentType, Some container, m) + let item = Item.OtherName (idOpt, assignedArg.CalledArg.CalledArgumentType, None, Some container, m) CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, ad)) /// STEP 6. Build the call expression, then adjust for byref-returns, out-parameters-as-tuples, post-hoc property assignments, methods-as-first-class-value, @@ -12027,7 +12017,7 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind (tpenv, valinfos) ||> List.mapFold (fun tpenv valSpecResult -> - let (ValSpecResult (altActualParent, memberInfoOpt, id, enclosingDeclaredTypars, declaredTypars, ty, prelimValReprInfo, declKind)) = valSpecResult + let (ValSpecResult (altActualParent, memberInfoOpt, ident, enclosingDeclaredTypars, declaredTypars, ty, prelimValReprInfo, declKind)) = valSpecResult let inlineFlag = ComputeInlineFlag (memberInfoOpt |> Option.map (fun (PrelimMemberInfo(memberInfo, _, _)) -> memberInfo.MemberFlags)) isInline mutableFlag g attrs m @@ -12038,11 +12028,11 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind let explicitTyparInfo = ExplicitTyparInfo(declaredTypars, declaredTypars, synCanInferTypars) let generalizedTypars = - GeneralizationHelpers.ComputeAndGeneralizeGenericTypars(cenv, denv, id.idRange, + GeneralizationHelpers.ComputeAndGeneralizeGenericTypars(cenv, denv, ident.idRange, emptyFreeTypars, canInferTypars, CanGeneralizeConstrainedTypars, inlineFlag, None, allDeclaredTypars, freeInType, ty, false) - let valscheme1 = PrelimVal1(id, explicitTyparInfo, ty, Some prelimValReprInfo, memberInfoOpt, mutableFlag, inlineFlag, NormalVal, noArgOrRetAttribs, vis, false) + let valscheme1 = PrelimVal1(ident, explicitTyparInfo, ty, Some prelimValReprInfo, memberInfoOpt, mutableFlag, inlineFlag, NormalVal, noArgOrRetAttribs, vis, false) let valscheme2 = GeneralizeVal cenv denv enclosingDeclaredTypars generalizedTypars valscheme1 @@ -12073,6 +12063,21 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind let xmlDoc = xmlDoc.ToXmlDoc(checkXmlDocs, paramNames) let vspec = MakeAndPublishVal cenv env (altActualParent, true, declKind, ValNotInRecScope, valscheme, attrs, xmlDoc, literalValue, false) + let arities = arityOfVal vspec + let numEnclosingTypars = allDeclaredTypars.Length + let _tps, _witnessInfos, curriedArgInfos, _retTy, _ = GetValReprTypeInCompiledForm cenv.g arities numEnclosingTypars vspec.Type vspec.DefinitionRange + + let synArgInfos = synValSig.SynInfo.CurriedArgInfos + let argData = + (synArgInfos, curriedArgInfos) + ||> List.zip + |> Seq.collect (fun x -> x ||> List.zip) + |> Seq.choose (fun (synArgInfo, argInfo) -> synArgInfo.Ident |> Option.map (pair argInfo)) + + for (argTy, argReprInfo), ident in argData do + let item = Item.OtherName (Some ident, argTy, Some argReprInfo, None, ident.idRange) + CallNameResolutionSink cenv.tcSink (ident.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Binding, env.AccessRights) + assert(vspec.InlineInfo = inlineFlag) vspec, tpenv) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index c5ace8603a6..a4264a8da65 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -224,7 +224,7 @@ type Item = /// Represents the resolution of a name to a named argument // - // In the FCS API, Item.ArgName corresponds to FSharpParameter symbols. + // In the FCS API, Item.OtherName corresponds to FSharpParameter symbols. // Not all parameters have names, e.g. for 'g' in this: // // let f (g: int -> int) x = ... @@ -233,7 +233,7 @@ type Item = // based on analyzing the type of g as a function type. // // For these parameters, the identifier will be missing. - | ArgName of ident: Ident option * argType: TType * container: ArgumentContainer option * range: range + | OtherName of ident: Ident option * argType: TType * argInfo: ArgReprInfo option * container: ArgumentContainer option * range: range /// Represents the resolution of a name to a named property setter | SetterArg of Ident * Item @@ -278,8 +278,8 @@ type Item = | Item.TypeVar (nm, _) -> nm | Item.Trait traitInfo -> traitInfo.MemberDisplayNameCore | Item.ModuleOrNamespaces(modref :: _) -> modref.DisplayNameCore - | Item.ArgName (Some id, _, _, _) -> id.idText - | Item.ArgName (None, _, _, _) -> "" + | Item.OtherName (ident = Some id) -> id.idText + | Item.OtherName (ident = None) -> "" | Item.SetterArg (id, _) -> id.idText | Item.CustomOperation (customOpName, _, _) -> customOpName | Item.CustomBuilder (nm, _) -> nm @@ -305,8 +305,8 @@ type Item = | Item.UnqualifiedType(tcref :: _) -> tcref.DisplayName | Item.ModuleOrNamespaces(modref :: _) -> modref.DisplayName | Item.TypeVar (nm, _) -> nm |> ConvertLogicalNameToDisplayName - | Item.ArgName (Some id, _, _, _) -> id.idText |> ConvertValLogicalNameToDisplayName false - | Item.ArgName (None, _, _, _) -> "" + | Item.OtherName (ident = Some id) -> id.idText |> ConvertValLogicalNameToDisplayName false + | Item.OtherName (ident = None) -> "" | _ -> d.DisplayNameCore |> ConvertLogicalNameToDisplayName let valRefHash (vref: ValRef) = @@ -1902,10 +1902,10 @@ let ItemsAreEffectivelyEqual g orig other = | Some vref1, Some vref2 -> valRefDefnEq g vref1 vref2 | _ -> false - | Item.ArgName (Some id1, _, _, m1), Item.ArgName (Some id2, _, _, m2) -> + | Item.OtherName (ident = Some id1; range = m1), Item.OtherName (ident = Some id2; range = m2) -> (id1.idText = id2.idText && equals m1 m2) - | Item.ArgName (Some id, _, _, _), ValUse vref | ValUse vref, Item.ArgName (Some id, _, _, _) -> + | Item.OtherName (ident = Some id), ValUse vref | ValUse vref, Item.OtherName (ident = Some id) -> ((equals id.idRange vref.DefinitionRange || equals id.idRange vref.SigRange) && id.idText = vref.DisplayName) | Item.AnonRecdField(anon1, _, i1, _), Item.AnonRecdField(anon2, _, i2, _) -> anonInfoEquiv anon1 anon2 && i1 = i2 @@ -1947,7 +1947,7 @@ let ItemsAreEffectivelyEqualHash (g: TcGlobals) orig = | ActivePatternCaseUse (_, _, idx)-> hash idx | MethodUse minfo -> minfo.ComputeHashCode() | PropertyUse pinfo -> pinfo.ComputeHashCode() - | Item.ArgName (Some id, _, _, _) -> hash id.idText + | Item.OtherName (ident = Some id) -> hash id.idText | ILFieldUse ilfinfo -> ilfinfo.ComputeHashCode() | UnionCaseUse ucase -> hash ucase.CaseName | RecordFieldUse (name, _) -> hash name @@ -2071,7 +2071,7 @@ type TcResultsSinkImpl(tcGlobals, ?sourceText: ISourceText) = let keyOpt = match item with | Item.Value vref -> Some (endPos, vref.DisplayName) - | Item.ArgName (Some id, _, _, _) -> Some (endPos, id.idText) + | Item.OtherName (ident = Some id) -> Some (endPos, id.idText) | _ -> None match keyOpt with @@ -2239,7 +2239,7 @@ let CheckAllTyparsInferrable amap m item = | Item.CustomOperation _ | Item.CustomBuilder _ | Item.TypeVar _ - | Item.ArgName _ + | Item.OtherName _ | Item.ActivePatternResult _ | Item.Value _ | Item.ActivePatternCase _ diff --git a/src/Compiler/Checking/NameResolution.fsi b/src/Compiler/Checking/NameResolution.fsi index c93881d7712..8fd257e3f6c 100755 --- a/src/Compiler/Checking/NameResolution.fsi +++ b/src/Compiler/Checking/NameResolution.fsi @@ -122,7 +122,7 @@ type Item = /// Represents the resolution of a name to a named argument // - // In the FCS API, Item.ArgName corresponds to FSharpParameter symbols. + // In the FCS API, Item.OtherName corresponds to FSharpParameter symbols. // Not all parameters have names, e.g. for 'g' in this: // // let f (g: int -> int) x = ... @@ -131,7 +131,7 @@ type Item = // based on analyzing the type of g as a function type. // // For these parameters, the identifier will be missing. - | ArgName of ident: Ident option * argType: TType * container: ArgumentContainer option * range: range + | OtherName of ident: Ident option * argType: TType * argInfo: ArgReprInfo option * container: ArgumentContainer option * range: range /// Represents the resolution of a name to a named property setter | SetterArg of Ident * Item diff --git a/src/Compiler/Checking/SignatureConformance.fs b/src/Compiler/Checking/SignatureConformance.fs index 8e10990d11d..8abb7b8f04e 100644 --- a/src/Compiler/Checking/SignatureConformance.fs +++ b/src/Compiler/Checking/SignatureConformance.fs @@ -306,9 +306,11 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = | None -> implVal.Range if sigHasInlineIfLambda && not implHasInlineIfLambda then errorR(Error (FSComp.SR.implMissingInlineIfLambda(), m)) - + + let _sigVal = sigVal implArgInfo.Name <- sigArgInfo.Name - implArgInfo.Attribs <- attribs))) && + implArgInfo.Attribs <- attribs + implArgInfo.OtherRange <- None))) && checkAttribs aenv implRetInfo.Attribs sigRetInfo.Attribs (fun attribs -> implRetInfo.Name <- sigRetInfo.Name diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index 4b78aa9b9f9..48eb3368f61 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -818,6 +818,7 @@ type Exception with { ArgReprInfo.Name = name |> Option.map (fun name -> Ident(name, range.Zero)) ArgReprInfo.Attribs = [] + ArgReprInfo.OtherRange = None }) let argsL, retTyL, genParamTysL = diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 30a55989edf..81b2c5c8c9a 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -1316,7 +1316,7 @@ let internal mkBoundValueTypedImpl tcGlobals m moduleName name ty = let v = Construct.NewVal (name, m, None, ty, ValMutability.Immutable, - false, Some(ValReprInfo([], [], { Attribs = []; Name = None })), vis, ValNotInRecScope, None, NormalVal, [], ValInline.Optional, + false, Some(ValReprInfo([], [], { Attribs = []; Name = None; OtherRange = None })), vis, ValNotInRecScope, None, NormalVal, [], ValInline.Optional, XmlDoc.Empty, true, false, false, false, false, false, None, Parent(TypedTreeBasics.ERefLocal entity)) mty <- ModuleOrNamespaceType(ModuleOrNamespaceKind.ModuleOrType, QueueList.one v, QueueList.empty) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index a47b6b68c72..2cf115d05c3 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -583,7 +583,7 @@ type internal TypeCheckInfo x |> List.choose (fun (ParamData (_isParamArray, _isInArg, _isOutArg, _optArgInfo, _callerInfo, name, _, ty)) -> match name with - | Some id -> Some(Item.ArgName(Some id, ty, Some(ArgumentContainer.Method meth), id.idRange)) + | Some id -> Some(Item.OtherName(Some id, ty, None, Some(ArgumentContainer.Method meth), id.idRange)) | None -> None) | _ -> []) @@ -876,7 +876,7 @@ type internal TypeCheckInfo | Item.NewDef _ | Item.SetterArg _ | Item.CustomBuilder _ - | Item.ArgName _ + | Item.OtherName _ | Item.ActivePatternCase _ -> CompletionItemKind.Other let isUnresolved = diff --git a/src/Compiler/Service/ItemKey.fs b/src/Compiler/Service/ItemKey.fs index 9609ccf9c38..8f72bdc6735 100644 --- a/src/Compiler/Service/ItemKey.fs +++ b/src/Compiler/Service/ItemKey.fs @@ -417,7 +417,7 @@ and [] ItemKeyStoreBuilder() = writeType false ty // We should consider writing ItemKey for each of these - | Item.ArgName _ -> () + | Item.OtherName _ -> () | Item.FakeInterfaceCtor _ -> () | Item.CustomOperation _ -> () | Item.CustomBuilder _ -> () diff --git a/src/Compiler/Service/SemanticClassification.fs b/src/Compiler/Service/SemanticClassification.fs index f1fdc92c0bb..b9576972cfa 100644 --- a/src/Compiler/Service/SemanticClassification.fs +++ b/src/Compiler/Service/SemanticClassification.fs @@ -352,7 +352,7 @@ module TcResolutionsExtensions = | Item.Event _, _, m -> add m SemanticClassificationType.Event - | Item.ArgName _, _, m -> add m SemanticClassificationType.NamedArgument + | Item.OtherName _, _, m -> add m SemanticClassificationType.NamedArgument | Item.SetterArg _, _, m -> add m SemanticClassificationType.NamedArgument diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index 06cf656b078..9af12de2b1c 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -446,7 +446,7 @@ module DeclarationListHelpers = ToolTipElement.Single (layout, FSharpXmlDoc.None) // Named parameters - | Item.ArgName (Some id, argTy, _, _) -> + | Item.OtherName (ident = Some id; argType = argTy) -> let argTy, _ = PrettyTypes.PrettifyType g argTy let layout = wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ @@ -459,7 +459,7 @@ module DeclarationListHelpers = | Item.SetterArg (_, item) -> FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv (ItemWithNoInst item) - | Item.ArgName (None, _, _, _) + | Item.OtherName (ident = None) // TODO: give a decent tooltip for implicit operators that include the resolution of the operator // @@ -844,7 +844,7 @@ module internal DescriptionListsImpl = | Item.NewDef _ | Item.ModuleOrNamespaces _ | Item.ImplicitOp _ - | Item.ArgName _ + | Item.OtherName _ | Item.MethodGroup(_, [], _) | Item.CtorGroup(_,[]) | Item.Property(_,[]) -> @@ -940,7 +940,7 @@ module internal DescriptionListsImpl = | Item.ModuleOrNamespaces(modref :: _) -> if modref.IsNamespace then FSharpGlyph.NameSpace else FSharpGlyph.Module | Item.NewDef _ - | Item.ArgName _ + | Item.OtherName _ | Item.SetterArg _ -> FSharpGlyph.Variable // These empty lists are not expected to occur @@ -984,7 +984,7 @@ module internal DescriptionListsImpl = | Item.CustomBuilder _ | Item.ActivePatternCase _ | Item.AnonRecdField _ - | Item.ArgName _ + | Item.OtherName _ | Item.ImplicitOp _ | Item.ModuleOrNamespaces _ | Item.SetterArg _ diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 9af88d75880..b305d4e5000 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -874,7 +874,7 @@ type BackgroundCompiler if builder.ContainsFile fileName then let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() - + let _fileName = fileName match keyStoreOpt with | None -> return Seq.empty | Some reader -> return reader.FindAll symbol.Item diff --git a/src/Compiler/Symbols/SymbolHelpers.fs b/src/Compiler/Symbols/SymbolHelpers.fs index 5f028e020b6..67161c99f0f 100644 --- a/src/Compiler/Symbols/SymbolHelpers.fs +++ b/src/Compiler/Symbols/SymbolHelpers.fs @@ -109,7 +109,7 @@ module internal SymbolHelpers = | Item.CtorGroup(_, minfos) -> minfos |> List.tryPick (rangeOfMethInfo g preferFlag) | Item.ActivePatternResult(APInfo _, _, _, m) -> Some m | Item.SetterArg (_, item) -> rangeOfItem g preferFlag item - | Item.ArgName (_, _, _, m) -> Some m + | Item.OtherName (range = m) -> Some m | Item.CustomOperation (_, _, implOpt) -> implOpt |> Option.bind (rangeOfMethInfo g preferFlag) | Item.ImplicitOp (_, {contents = Some(TraitConstraintSln.FSMethSln(vref=vref))}) -> Some vref.Range | Item.ImplicitOp _ -> None @@ -168,7 +168,7 @@ module internal SymbolHelpers = |> Option.bind ccuOfValRef |> Option.orElseWith (fun () -> pinfo.DeclaringTyconRef |> computeCcuOfTyconRef)) - | Item.ArgName (_, _, meth, _) -> + | Item.OtherName (container = meth) -> match meth with | None -> None | Some (ArgumentContainer.Method minfo) -> ccuOfMethInfo g minfo @@ -309,7 +309,7 @@ module internal SymbolHelpers = | Item.CtorGroup(_, minfo :: _) -> mkXmlComment (GetXmlDocSigOfMethInfo infoReader m minfo) - | Item.ArgName(_, _, Some argContainer, _) -> + | Item.OtherName(container = Some argContainer) -> match argContainer with | ArgumentContainer.Method minfo -> mkXmlComment (GetXmlDocSigOfMethInfo infoReader m minfo) | ArgumentContainer.Type tcref -> mkXmlComment (GetXmlDocSigOfEntityRef infoReader m tcref) @@ -322,7 +322,7 @@ module internal SymbolHelpers = // These do not have entires in XML doc files | Item.CustomOperation _ - | Item.ArgName _ + | Item.OtherName _ | Item.ActivePatternResult _ | Item.AnonRecdField _ | Item.ImplicitOp _ @@ -393,7 +393,7 @@ module internal SymbolHelpers = // These are never expected to have duplicates in declaration lists etc | Item.ActivePatternResult _ | Item.AnonRecdField _ - | Item.ArgName _ + | Item.OtherName _ | Item.FakeInterfaceCtor _ | Item.ImplicitOp _ | Item.NewDef _ @@ -499,7 +499,7 @@ module internal SymbolHelpers = // These are not expected to occur, see InEqualityRelation and ItemWhereTypIsPreferred | Item.ActivePatternResult _ | Item.AnonRecdField _ - | Item.ArgName _ + | Item.OtherName _ | Item.FakeInterfaceCtor _ | Item.ImplicitOp _ | Item.NewDef _ @@ -578,7 +578,7 @@ module internal SymbolHelpers = let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace) if definiteNamespace then fullDisplayTextOfModRef modref else modref.DisplayName | Item.TypeVar _ - | Item.ArgName _ -> item.DisplayName + | Item.OtherName _ -> item.DisplayName | Item.SetterArg (_, item) -> FullNameOfItem g item | Item.ImplicitOp(id, _) -> id.idText | Item.UnionCaseField (UnionCaseInfo (_, ucref), fieldIndex) -> ucref.FieldByIndex(fieldIndex).DisplayName @@ -678,7 +678,7 @@ module internal SymbolHelpers = else GetXmlCommentForItemAux None infoReader m item - | Item.ArgName (_, _, argContainer, _) -> + | Item.OtherName (container = argContainer) -> let doc = match argContainer with | Some(ArgumentContainer.Method minfo) -> @@ -934,7 +934,7 @@ module internal SymbolHelpers = | Item.MethodGroup(_, [], _) | Item.CustomOperation (_, _, None) // "into" | Item.NewDef _ // "let x$yz = ..." - no keyword - | Item.ArgName _ // no keyword on named parameters + | Item.OtherName _ // no keyword on named parameters | Item.Trait _ | Item.UnionCaseField _ | Item.TypeVar _ @@ -978,7 +978,7 @@ module internal SymbolHelpers = | Item.CustomBuilder _ | Item.ActivePatternCase _ | Item.AnonRecdField _ - | Item.ArgName _ + | Item.OtherName _ | Item.ImplicitOp _ | Item.ModuleOrNamespaces _ | Item.SetterArg _ diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index 480c58f3af6..5100f412b62 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -322,7 +322,7 @@ type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> C | Item.ActivePatternResult (apinfo, ty, n, _) -> FSharpActivePatternCase(cenv, apinfo, ty, n, None, item) :> _ - | Item.ArgName(id, ty, argOwner, m) -> + | Item.OtherName(id, ty, _, argOwner, m) -> FSharpParameter(cenv, id, ty, argOwner, m) :> _ | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(vref=vref)) }) -> @@ -2049,7 +2049,7 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = [ [ for ParamData(isParamArrayArg, isInArg, isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty) in p.GetParamDatas(cenv.amap, range0) do // INCOMPLETENESS: Attribs is empty here, so we can't look at attributes for // either .NET or F# parameters - let argInfo: ArgReprInfo = { Name=nmOpt; Attribs= [] } + let argInfo: ArgReprInfo = { Name=nmOpt; Attribs=[]; OtherRange=None } let m = match nmOpt with | Some v -> v.idRange @@ -2068,7 +2068,7 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = [ for ParamData(isParamArrayArg, isInArg, isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty) in argTys do // INCOMPLETENESS: Attribs is empty here, so we can't look at attributes for // either .NET or F# parameters - let argInfo: ArgReprInfo = { Name=nmOpt; Attribs= [] } + let argInfo: ArgReprInfo = { Name=nmOpt; Attribs=[]; OtherRange=None } let m = match nmOpt with | Some v -> v.idRange @@ -2349,7 +2349,7 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = let nm = String.uncapitalize witnessInfo.MemberName let nm = if used.Contains nm then nm + string i else nm let m = x.DeclarationLocation - let argReprInfo : ArgReprInfo = { Attribs=[]; Name=Some (mkSynId m nm) } + let argReprInfo : ArgReprInfo = { Attribs=[]; Name=Some (mkSynId m nm); OtherRange=None } let p = FSharpParameter(cenv, paramTy, argReprInfo, None, m, false, false, false, false, true) p, (used.Add nm, i + 1)) |> fst @@ -2670,7 +2670,7 @@ type FSharpStaticParameter(cenv, sp: Tainted< TypeProviders.ProvidedParameterInf let paramTy = Import.ImportProvidedType cenv.amap m (sp.PApply((fun x -> x.ParameterType), m)) let nm = sp.PUntaint((fun p -> p.Name), m) let id = mkSynId m nm - Item.ArgName(Some id, paramTy, None, m)), + Item.OtherName(Some id, paramTy, None, None, m)), (fun _ _ _ -> true)) member _.Name = @@ -2708,11 +2708,11 @@ type FSharpStaticParameter(cenv, sp: Tainted< TypeProviders.ProvidedParameterInf type FSharpParameter(cenv, paramTy: TType, topArgInfo: ArgReprInfo, ownerOpt, m: range, isParamArrayArg, isInArg, isOutArg, isOptionalArg, isWitnessArg) = inherit FSharpSymbol(cenv, - (fun () -> Item.ArgName(topArgInfo.Name, paramTy, ownerOpt, m)), + (fun () -> Item.OtherName(topArgInfo.Name, paramTy, Some topArgInfo, ownerOpt, m)), (fun _ _ _ -> true)) new (cenv, idOpt, ty, ownerOpt, m) = - let argInfo: ArgReprInfo = { Name = idOpt; Attribs = [] } + let argInfo: ArgReprInfo = { Name = idOpt; Attribs = []; OtherRange = None } FSharpParameter(cenv, ty, argInfo, ownerOpt, m, false, false, false, false, false) new (cenv, ty, argInfo: ArgReprInfo, m: range) = diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index dfdc9640a0c..1d6f6212c71 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -4597,8 +4597,8 @@ type ValReprInfo = /// Records the "extra information" for an argument compiled as a real /// method argument, specifically the argument name and attributes. [] -type ArgReprInfo = - { +type ArgReprInfo = + { /// The attributes for the argument // MUTABILITY: used when propagating signature attributes into the implementation. mutable Attribs: Attribs @@ -4606,6 +4606,10 @@ type ArgReprInfo = /// The name for the argument at this position, if any // MUTABILITY: used when propagating names of parameters from signature into the implementation. mutable Name: Ident option + + /// The range of the signature/implementation counterpart to this argument, if any + // MUTABILITY: used when propagating ranges from signature into the implementation. + mutable OtherRange: range option } [] diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index b63942d2c8c..56ad409741e 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -3367,6 +3367,9 @@ type ArgReprInfo = /// The name for the argument at this position, if any mutable Name: Syntax.Ident option + + /// The range of the signature/implementation counterpart to this argument, if any + mutable OtherRange: range option } override ToString: unit -> string diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fs b/src/Compiler/TypedTree/TypedTreeBasics.fs index 511a4cc44f2..651eadd0d65 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fs +++ b/src/Compiler/TypedTree/TypedTreeBasics.fs @@ -29,13 +29,13 @@ let getNameOfScopeRef sref = /// Metadata on values (names of arguments etc.) module ValReprInfo = - let unnamedTopArg1: ArgReprInfo = { Attribs=[]; Name=None } + let unnamedTopArg1: ArgReprInfo = { Attribs = []; Name = None; OtherRange = None } let unnamedTopArg = [unnamedTopArg1] let unitArgData: ArgReprInfo list list = [[]] - let unnamedRetVal: ArgReprInfo = { Attribs = []; Name=None } + let unnamedRetVal: ArgReprInfo = { Attribs = []; Name = None; OtherRange = None } let selfMetadata = unnamedTopArg @@ -43,12 +43,12 @@ module ValReprInfo = let IsEmpty info = match info with - | ValReprInfo([], [], { Attribs = []; Name=None }) -> true + | ValReprInfo([], [], { Attribs = []; Name = None; OtherRange = None }) -> true | _ -> false let InferTyparInfo (tps: Typar list) = tps |> List.map (fun tp -> TyparReprInfo(tp.Id, tp.Kind)) - let InferArgReprInfo (v: Val) : ArgReprInfo = { Attribs = []; Name= Some v.Id } + let InferArgReprInfo (v: Val) : ArgReprInfo = { Attribs = []; Name = Some v.Id; OtherRange = None } let InferArgReprInfos (vs: Val list list) = ValReprInfo([], List.mapSquared InferArgReprInfo vs, unnamedRetVal) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index b87db718b3a..25e902df9e4 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -5443,9 +5443,9 @@ let InferValReprInfoOfExpr g allowTypeDirectedDetupling ty partialArgAttribsL re let attribs = if partialAttribs.Length = tys.Length then partialAttribs else tys |> List.map (fun _ -> []) - (ids, attribs) ||> List.map2 (fun id attribs -> { Name = id; Attribs = attribs }: ArgReprInfo )) + (ids, attribs) ||> List.map2 (fun id attribs -> { Name = id; Attribs = attribs; OtherRange = None }: ArgReprInfo )) - let retInfo: ArgReprInfo = { Attribs = retAttribs; Name = None } + let retInfo: ArgReprInfo = { Attribs = retAttribs; Name = None; OtherRange = None } let info = ValReprInfo (ValReprInfo.InferTyparInfo tps, curriedArgInfos, retInfo) if ValReprInfo.IsEmpty info then ValReprInfo.emptyValData else info @@ -5636,7 +5636,7 @@ and remapPossibleForallTyImpl ctxt tmenv ty = remapTypeFull (remapAttribs ctxt tmenv) tmenv ty and remapArgData ctxt tmenv (argInfo: ArgReprInfo) : ArgReprInfo = - { Attribs = remapAttribs ctxt tmenv argInfo.Attribs; Name = argInfo.Name } + { Attribs = remapAttribs ctxt tmenv argInfo.Attribs; Name = argInfo.Name; OtherRange = None } and remapValReprInfo ctxt tmenv (ValReprInfo(tpNames, arginfosl, retInfo)) = ValReprInfo(tpNames, List.mapSquared (remapArgData ctxt tmenv) arginfosl, remapArgData ctxt tmenv retInfo) diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index f19ca1caaa1..cb53852523d 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -1750,7 +1750,7 @@ let u_ArgReprInfo st = let b = u_option u_ident st match a, b with | [], None -> ValReprInfo.unnamedTopArg1 - | _ -> { Attribs = a; Name = b } + | _ -> { Attribs = a; Name = b; OtherRange = None } let u_TyparReprInfo st = let a = u_ident st From 3ed2faa620be7624e96eadc48af1f88ba5f06380 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 16 Jan 2023 16:35:21 +0100 Subject: [PATCH 05/44] this seems to work --- src/Compiler/Checking/CheckExpressions.fs | 12 +++++++++--- src/Compiler/Service/ItemKey.fs | 8 ++++++++ .../FSharpChecker/FindReferences.fs | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 585c3653400..3e92b76f01c 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -12067,11 +12067,17 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind let numEnclosingTypars = allDeclaredTypars.Length let _tps, _witnessInfos, curriedArgInfos, _retTy, _ = GetValReprTypeInCompiledForm cenv.g arities numEnclosingTypars vspec.Type vspec.DefinitionRange + let argInfos = + // Drop "this" argument for instance methods + match vspec.IsInstanceMember, curriedArgInfos with + | true, _::args + | _, args -> args + let synArgInfos = synValSig.SynInfo.CurriedArgInfos let argData = - (synArgInfos, curriedArgInfos) - ||> List.zip - |> Seq.collect (fun x -> x ||> List.zip) + (synArgInfos, argInfos) + ||> Seq.zip + |> Seq.collect (fun x -> x ||> Seq.zip) |> Seq.choose (fun (synArgInfo, argInfo) -> synArgInfo.Ident |> Option.map (pair argInfo)) for (argTy, argReprInfo), ident in argData do diff --git a/src/Compiler/Service/ItemKey.fs b/src/Compiler/Service/ItemKey.fs index 8f72bdc6735..ab0c4744577 100644 --- a/src/Compiler/Service/ItemKey.fs +++ b/src/Compiler/Service/ItemKey.fs @@ -416,6 +416,14 @@ and [] ItemKeyStoreBuilder() = writeString ItemKeyTags.itemDelegateCtor writeType false ty + // Named argument in a signature + | Item.OtherName (ident = Some(ident); argType = ty; argInfo = Some _) -> + writeString ItemKeyTags.itemValue + writeString ident.idText + writeString ItemKeyTags.parameters + writeType false ty + writeChar '%' + // We should consider writing ItemKey for each of these | Item.OtherName _ -> () | Item.FakeInterfaceCtor _ -> () diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 1ecf3b6874f..0e79c8531f7 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -158,6 +158,21 @@ let ``We find function parameter in signature file`` () = ]) } +[] +let ``We find method parameter in signature file`` () = + SyntheticProject.Create( + { sourceFile "Source" [] with + ExtraSource = "type MyClass() = member this.Method(methodParam) = methodParam + 1" + SignatureFile = AutoGenerated }) + .Workflow { + placeCursor "Source" 6 47 "type MyClass() = member this.Method(methodParam) = methodParam + 1" ["methodParam"] + findAllReferences (expectToFind [ + "FileSource.fsi", 14, 17, 28 + "FileSource.fs", 6, 36, 47 + "FileSource.fs", 6, 51, 62 + ]) + } + module Attributes = let project() = SyntheticProject.Create( From 1558207407a31d95ef6b7da92ced050b841bd1f4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 16 Jan 2023 16:36:12 +0100 Subject: [PATCH 06/44] fantomas --- src/Compiler/Checking/NameResolution.fsi | 7 ++++++- src/Compiler/Service/ItemKey.fs | 2 +- src/Compiler/Service/service.fs | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fsi b/src/Compiler/Checking/NameResolution.fsi index 8fd257e3f6c..4dc02c35b54 100755 --- a/src/Compiler/Checking/NameResolution.fsi +++ b/src/Compiler/Checking/NameResolution.fsi @@ -131,7 +131,12 @@ type Item = // based on analyzing the type of g as a function type. // // For these parameters, the identifier will be missing. - | OtherName of ident: Ident option * argType: TType * argInfo: ArgReprInfo option * container: ArgumentContainer option * range: range + | OtherName of + ident: Ident option * + argType: TType * + argInfo: ArgReprInfo option * + container: ArgumentContainer option * + range: range /// Represents the resolution of a name to a named property setter | SetterArg of Ident * Item diff --git a/src/Compiler/Service/ItemKey.fs b/src/Compiler/Service/ItemKey.fs index ab0c4744577..0b3e3198376 100644 --- a/src/Compiler/Service/ItemKey.fs +++ b/src/Compiler/Service/ItemKey.fs @@ -417,7 +417,7 @@ and [] ItemKeyStoreBuilder() = writeType false ty // Named argument in a signature - | Item.OtherName (ident = Some(ident); argType = ty; argInfo = Some _) -> + | Item.OtherName (ident = Some (ident); argType = ty; argInfo = Some _) -> writeString ItemKeyTags.itemValue writeString ident.idText writeString ItemKeyTags.parameters diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index b305d4e5000..7c8b9d0470a 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -875,6 +875,7 @@ type BackgroundCompiler let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() let _fileName = fileName + match keyStoreOpt with | None -> return Seq.empty | Some reader -> return reader.FindAll symbol.Item From f5a9720133709e614554695c9e23ca53d6d6c528 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 17 Jan 2023 14:01:36 +0100 Subject: [PATCH 07/44] setting Val.argInfo otherRange --- src/Compiler/Checking/SignatureConformance.fs | 11 ++++++----- src/Compiler/Service/service.fs | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Checking/SignatureConformance.fs b/src/Compiler/Checking/SignatureConformance.fs index 8abb7b8f04e..0562f597e07 100644 --- a/src/Compiler/Checking/SignatureConformance.fs +++ b/src/Compiler/Checking/SignatureConformance.fs @@ -306,11 +306,12 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = | None -> implVal.Range if sigHasInlineIfLambda && not implHasInlineIfLambda then errorR(Error (FSComp.SR.implMissingInlineIfLambda(), m)) - - let _sigVal = sigVal - implArgInfo.Name <- sigArgInfo.Name - implArgInfo.Attribs <- attribs - implArgInfo.OtherRange <- None))) && + + implArgInfo.OtherRange <- sigArgInfo.Name |> Option.map (fun ident -> ident.idRange) + sigArgInfo.OtherRange <- implArgInfo.Name |> Option.map (fun ident -> ident.idRange) + + implArgInfo.Name <- implArgInfo.Name |> Option.orElse sigArgInfo.Name + implArgInfo.Attribs <- attribs))) && checkAttribs aenv implRetInfo.Attribs sigRetInfo.Attribs (fun attribs -> implRetInfo.Name <- sigRetInfo.Name diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 5187d5ab5e0..de261d0975e 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -883,7 +883,6 @@ type BackgroundCompiler if builder.ContainsFile fileName then let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() - let _fileName = fileName match keyStoreOpt with | None -> return Seq.empty From 9eca897a19aede75f907769f3e0f44f05bc37c3e Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 17 Jan 2023 15:21:26 +0100 Subject: [PATCH 08/44] move publishing arguments into a function --- src/Compiler/Checking/CheckExpressions.fs | 48 ++++++++++++----------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 3e92b76f01c..061a75c24c0 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -11996,6 +11996,27 @@ and TcLetrecBindings overridesOK (cenv: cenv) env tpenv (binds, bindsm, scopem) // Bind specifications of values //------------------------------------------------------------------------- +let private PublishArguments (cenv: cenv) (env: TcEnv) vspec (synValSig: SynValSig) numEnclosingTypars = + let arities = arityOfVal vspec + let _tps, _witnessInfos, curriedArgInfos, _retTy, _ = GetValReprTypeInCompiledForm cenv.g arities numEnclosingTypars vspec.Type vspec.DefinitionRange + + let argInfos = + // Drop "this" argument for instance methods + match vspec.IsInstanceMember, curriedArgInfos with + | true, _::args + | _, args -> args + + let synArgInfos = synValSig.SynInfo.CurriedArgInfos + let argData = + (synArgInfos, argInfos) + ||> Seq.zip + |> Seq.collect (fun x -> x ||> Seq.zip) + |> Seq.choose (fun (synArgInfo, argInfo) -> synArgInfo.Ident |> Option.map (pair argInfo)) + + for (argTy, argReprInfo), ident in argData do + let item = Item.OtherName (Some ident, argTy, Some argReprInfo, None, ident.idRange) + CallNameResolutionSink cenv.tcSink (ident.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Binding, env.AccessRights) + let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind : DeclKind, memFlagsOpt, tpenv, synValSig) = let g = cenv.g @@ -12017,7 +12038,7 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind (tpenv, valinfos) ||> List.mapFold (fun tpenv valSpecResult -> - let (ValSpecResult (altActualParent, memberInfoOpt, ident, enclosingDeclaredTypars, declaredTypars, ty, prelimValReprInfo, declKind)) = valSpecResult + let (ValSpecResult (altActualParent, memberInfoOpt, id, enclosingDeclaredTypars, declaredTypars, ty, prelimValReprInfo, declKind)) = valSpecResult let inlineFlag = ComputeInlineFlag (memberInfoOpt |> Option.map (fun (PrelimMemberInfo(memberInfo, _, _)) -> memberInfo.MemberFlags)) isInline mutableFlag g attrs m @@ -12028,11 +12049,11 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind let explicitTyparInfo = ExplicitTyparInfo(declaredTypars, declaredTypars, synCanInferTypars) let generalizedTypars = - GeneralizationHelpers.ComputeAndGeneralizeGenericTypars(cenv, denv, ident.idRange, + GeneralizationHelpers.ComputeAndGeneralizeGenericTypars(cenv, denv, id.idRange, emptyFreeTypars, canInferTypars, CanGeneralizeConstrainedTypars, inlineFlag, None, allDeclaredTypars, freeInType, ty, false) - let valscheme1 = PrelimVal1(ident, explicitTyparInfo, ty, Some prelimValReprInfo, memberInfoOpt, mutableFlag, inlineFlag, NormalVal, noArgOrRetAttribs, vis, false) + let valscheme1 = PrelimVal1(id, explicitTyparInfo, ty, Some prelimValReprInfo, memberInfoOpt, mutableFlag, inlineFlag, NormalVal, noArgOrRetAttribs, vis, false) let valscheme2 = GeneralizeVal cenv denv enclosingDeclaredTypars generalizedTypars valscheme1 @@ -12063,26 +12084,7 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind let xmlDoc = xmlDoc.ToXmlDoc(checkXmlDocs, paramNames) let vspec = MakeAndPublishVal cenv env (altActualParent, true, declKind, ValNotInRecScope, valscheme, attrs, xmlDoc, literalValue, false) - let arities = arityOfVal vspec - let numEnclosingTypars = allDeclaredTypars.Length - let _tps, _witnessInfos, curriedArgInfos, _retTy, _ = GetValReprTypeInCompiledForm cenv.g arities numEnclosingTypars vspec.Type vspec.DefinitionRange - - let argInfos = - // Drop "this" argument for instance methods - match vspec.IsInstanceMember, curriedArgInfos with - | true, _::args - | _, args -> args - - let synArgInfos = synValSig.SynInfo.CurriedArgInfos - let argData = - (synArgInfos, argInfos) - ||> Seq.zip - |> Seq.collect (fun x -> x ||> Seq.zip) - |> Seq.choose (fun (synArgInfo, argInfo) -> synArgInfo.Ident |> Option.map (pair argInfo)) - - for (argTy, argReprInfo), ident in argData do - let item = Item.OtherName (Some ident, argTy, Some argReprInfo, None, ident.idRange) - CallNameResolutionSink cenv.tcSink (ident.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Binding, env.AccessRights) + PublishArguments cenv env vspec synValSig allDeclaredTypars.Length assert(vspec.InlineInfo = inlineFlag) From 107faa535ed9a4bb9b2ad6fdf5362c530ac12a9c Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 18 Jan 2023 18:58:32 +0100 Subject: [PATCH 09/44] Added arg_repr_info to Val --- src/Compiler/Checking/CheckExpressions.fs | 1 + src/Compiler/Service/FSharpCheckerResults.fs | 12 ++++++++---- src/Compiler/TypedTree/TypedTree.fs | 16 ++++++++++++++++ src/Compiler/TypedTree/TypedTree.fsi | 10 ++++++++++ src/Compiler/TypedTree/TypedTreePickle.fs | 1 + .../FSharpChecker/SymbolUse.fs | 2 +- 6 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 061a75c24c0..22e46cf8e02 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -6138,6 +6138,7 @@ and TcIteratedLambdas (cenv: cenv) isFirst (env: TcEnv) overallTy takenNames tpe | infos :: rest -> if infos.Length = vspecs.Length then (vspecs, infos) ||> List.iter2 (fun v argInfo -> + v.SetArgReprInfoForDisplay (Some argInfo) let inlineIfLambda = HasFSharpAttribute g g.attrib_InlineIfLambdaAttribute argInfo.Attribs if inlineIfLambda then v.SetInlineIfLambda()) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 4ac97c8c8a6..544e2a0eeac 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -263,14 +263,18 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti match this.Symbol with | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> - // In case it's a parameter and there is a signature file, then it's not private + // In case it's a parameter and it's in a signature file, then it's not private // TODO: Can it be anything else than a parameter? - // TODO: Is there a way to tell there is a signature file? + let signatureLocation = + match m.Item with + | Item.Value v -> v.Deref.ArgReprInfoForDisplay + | _ -> None + |> Option.bind (fun a -> a.OtherRange) + + signatureLocation.IsNone || signatureLocation = Some (m.DeclarationLocation) - // Since we don't know any better, we have to assume it's not private - false | :? FSharpMemberOrFunctionOrValue as m -> let fileSignatureLocation = m.DeclaringEntity |> Option.bind (fun e -> e.SignatureLocation) diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index 1d6f6212c71..27e6fdd4d74 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -2537,6 +2537,10 @@ type ValOptionalData = /// that may be compiled as closures (that is are not necessarily compiled as top-level methods). mutable val_repr_info_for_display: ValReprInfo option + /// Records the "extra information" for parameters in implementation files if we've been able to correlate + /// them with lambda arguments. + mutable arg_repr_info_for_display: ArgReprInfo option + /// How visible is this? /// MUTABILITY: for unpickle linkage mutable val_access: Accessibility @@ -2597,6 +2601,7 @@ type Val = val_defn = None val_repr_info = None val_repr_info_for_display = None + arg_repr_info_for_display = None val_access = TAccess [] val_xmldoc = XmlDoc.Empty val_member_info = None @@ -2666,6 +2671,11 @@ type Val = | Some optData -> optData.val_repr_info_for_display | _ -> None + member x.ArgReprInfoForDisplay: ArgReprInfo option = + match x.val_opt_data with + | Some optData -> optData.arg_repr_info_for_display + | _ -> None + member x.Id = ident(x.LogicalName, x.Range) /// Is this represented as a "top level" static binding (i.e. a static field, static member, @@ -3053,6 +3063,11 @@ type Val = | Some optData -> optData.val_repr_info_for_display <- info | _ -> x.val_opt_data <- Some { Val.NewEmptyValOptData() with val_repr_info_for_display = info } + member x.SetArgReprInfoForDisplay info = + match x.val_opt_data with + | Some optData -> optData.arg_repr_info_for_display <- info + | _ -> x.val_opt_data <- Some { Val.NewEmptyValOptData() with arg_repr_info_for_display = info } + member x.SetType ty = x.val_type <- ty member x.SetOtherRange m = @@ -3111,6 +3126,7 @@ type Val = val_const = tg.val_const val_defn = tg.val_defn val_repr_info_for_display = tg.val_repr_info_for_display + arg_repr_info_for_display = tg.arg_repr_info_for_display val_repr_info = tg.val_repr_info val_access = tg.val_access val_xmldoc = tg.val_xmldoc diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index 56ad409741e..054419b6034 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -1809,6 +1809,10 @@ type ValOptionalData = /// that may be compiled as closures (that is are not necessarily compiled as top-level methods). mutable val_repr_info_for_display: ValReprInfo option + /// Records the "extra information" for parameters in implementation files if we've been able to correlate + /// them with lambda arguments. + mutable arg_repr_info_for_display: ArgReprInfo option + /// How visible is this? /// MUTABILITY: for unpickle linkage mutable val_access: Accessibility @@ -1920,6 +1924,8 @@ type Val = member SetValReprInfoForDisplay: info: ValReprInfo option -> unit + member SetArgReprInfoForDisplay: info: ArgReprInfo option -> unit + override ToString: unit -> string /// How visible is this value, function or member? @@ -2178,6 +2184,10 @@ type Val = /// that may be compiled as closures (that is are not necessarily compiled as top-level methods). member ValReprInfoForDisplay: ValReprInfo option + /// Records the "extra information" for parameters in implementation files if we've been able to correlate + /// them with lambda arguments. + member ArgReprInfoForDisplay: ArgReprInfo option + /// Get the declared documentation for the value member XmlDoc: XmlDoc diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index cb53852523d..dd11a9dd6b3 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -2259,6 +2259,7 @@ and u_ValData st = val_defn = None val_repr_info = x10 val_repr_info_for_display = None + arg_repr_info_for_display = None val_const = x14 val_access = x13 val_xmldoc = defaultArg x15 XmlDoc.Empty diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index e865e06ee76..48ecf10b39d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -50,7 +50,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.True(symbolUse.IsPrivateToFile)) } - // TODO: Fix this [] + [] let ``Function parameter, no signature file`` () = SyntheticProject.Create(sourceFile "First" []).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> From fb811dd8d52a861a8cb048f05c760c54bdf29c18 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 19 Jan 2023 01:54:51 +0100 Subject: [PATCH 10/44] fantomas --- 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 544e2a0eeac..54cd3e98022 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -273,7 +273,7 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti | _ -> None |> Option.bind (fun a -> a.OtherRange) - signatureLocation.IsNone || signatureLocation = Some (m.DeclarationLocation) + signatureLocation.IsNone || signatureLocation = Some(m.DeclarationLocation) | :? FSharpMemberOrFunctionOrValue as m -> let fileSignatureLocation = From 2dea4c6a18256ceb7a0552853e0cbb8063609f1a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 20 Jan 2023 10:23:05 +0100 Subject: [PATCH 11/44] test --- .../FSharpChecker/FindReferences.fs | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 1e9eb9ad672..7c9be845432 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -151,33 +151,58 @@ let foo x = x ++ 4""" }) ]) } -[] -let ``We find function parameter in signature file`` () = - SyntheticProject.Create( - { sourceFile "Source" [] with SignatureFile = AutoGenerated }) - .Workflow { - placeCursor "Source" 3 7 "let f x =" ["x"] - findAllReferences (expectToFind [ - "FileSource.fsi", 6, 7, 8 - "FileSource.fs", 3, 6, 7 - "FileSource.fs", 4, 12, 13 - ]) - } +module Parameters = -[] -let ``We find method parameter in signature file`` () = - SyntheticProject.Create( - { sourceFile "Source" [] with - ExtraSource = "type MyClass() = member this.Method(methodParam) = methodParam + 1" - SignatureFile = AutoGenerated }) - .Workflow { - placeCursor "Source" 6 47 "type MyClass() = member this.Method(methodParam) = methodParam + 1" ["methodParam"] - findAllReferences (expectToFind [ - "FileSource.fsi", 14, 17, 28 - "FileSource.fs", 6, 36, 47 - "FileSource.fs", 6, 51, 62 - ]) - } + [] + let ``We find function parameter in signature file`` () = + SyntheticProject.Create( + { sourceFile "Source" [] with SignatureFile = AutoGenerated }) + .Workflow { + placeCursor "Source" 3 7 "let f x =" ["x"] + findAllReferences (expectToFind [ + "FileSource.fsi", 6, 7, 8 + "FileSource.fs", 3, 6, 7 + "FileSource.fs", 4, 12, 13 + ]) + } + + [] + let ``We find method parameter in signature file`` () = + SyntheticProject.Create( + { sourceFile "Source" [] with + ExtraSource = "type MyClass() = member this.Method(methodParam) = methodParam + 1" + SignatureFile = AutoGenerated }) + .Workflow { + placeCursor "Source" 6 47 "type MyClass() = member this.Method(methodParam) = methodParam + 1" ["methodParam"] + findAllReferences (expectToFind [ + "FileSource.fsi", 14, 17, 28 + "FileSource.fs", 6, 36, 47 + "FileSource.fs", 6, 51, 62 + ]) + } + + [] + let ``We only find the correct parameter`` () = + let source = """ +let myFunc1 param = param + 1 +let myFunc2 param = param + 2 +""" + let signature = """ +val myFunc1: param: int -> int +val myFunc2: param: int -> int +""" + SyntheticProject.Create("TupleParameterTest", + { sourceFile "Source" [] with + ExtraSource = source + SignatureFile = Custom $"module TupleParameterTest.ModuleFirst\n{signature}" }) + .Workflow { + placeCursor "Source" 7 17 "let myFunc1 param = param + 1" ["param"] + findAllReferences (expectToFind [ + "FileSource.fsi", 3, 13, 18 + "FileSource.fs", 7, 12, 17 + "FileSource.fs", 7, 20, 25 + ]) + } module Attributes = From 9a7af41648c3bbc4a77930db806987a4f4ab4157 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 8 Feb 2023 18:40:00 +0100 Subject: [PATCH 12/44] write signature location for parameters in itemkey --- src/Compiler/Service/ItemKey.fs | 5 +++++ src/Compiler/TypedTree/TypedTreeOps.fs | 2 +- .../FSharpChecker/FindReferences.fs | 3 ++- .../FSharpChecker/SymbolUse.fs | 1 - tests/FSharp.Test.Utilities/ProjectGeneration.fs | 11 +++++++++-- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Service/ItemKey.fs b/src/Compiler/Service/ItemKey.fs index 0b3e3198376..e5037bfa7a4 100644 --- a/src/Compiler/Service/ItemKey.fs +++ b/src/Compiler/Service/ItemKey.fs @@ -294,6 +294,10 @@ and [] ItemKeyStoreBuilder() = writeString ItemKeyTags.parameters writeType false vref.Type + match vref.Deref.ArgReprInfoForDisplay with + | Some ({ OtherRange = Some (r)}) -> writeRange r + | _ -> () + match vref.TryDeclaringEntity with | ParentNone -> writeChar '%' | Parent eref -> writeEntityRef eref @@ -422,6 +426,7 @@ and [] ItemKeyStoreBuilder() = writeString ident.idText writeString ItemKeyTags.parameters writeType false ty + writeRange ident.idRange writeChar '%' // We should consider writing ItemKey for each of these diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 4702eb94429..78e94156234 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -5636,7 +5636,7 @@ and remapPossibleForallTyImpl ctxt tmenv ty = remapTypeFull (remapAttribs ctxt tmenv) tmenv ty and remapArgData ctxt tmenv (argInfo: ArgReprInfo) : ArgReprInfo = - { Attribs = remapAttribs ctxt tmenv argInfo.Attribs; Name = argInfo.Name; OtherRange = None } + { Attribs = remapAttribs ctxt tmenv argInfo.Attribs; Name = argInfo.Name; OtherRange = argInfo.OtherRange } and remapValReprInfo ctxt tmenv (ValReprInfo(tpNames, arginfosl, retInfo)) = ValReprInfo(tpNames, List.mapSquared (remapArgData ctxt tmenv) arginfosl, remapArgData ctxt tmenv retInfo) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 7c9be845432..4828842aef6 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -194,8 +194,9 @@ val myFunc2: param: int -> int SyntheticProject.Create("TupleParameterTest", { sourceFile "Source" [] with ExtraSource = source - SignatureFile = Custom $"module TupleParameterTest.ModuleFirst\n{signature}" }) + SignatureFile = Custom signature }) .Workflow { + checkFile "Source" expectOk placeCursor "Source" 7 17 "let myFunc1 param = param + 1" ["param"] findAllReferences (expectToFind [ "FileSource.fsi", 3, 13, 18 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index 48ecf10b39d..a8dcdda56c2 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -35,7 +35,6 @@ module IsPrivateToFile = let ``Function definition not in signature file`` () = let projectName = "IsPrivateToFileTest1" let signature = $""" -module {projectName}.ModuleFirst type TFirstV_1<'a> = | TFirst of 'a val f: x: 'a -> TFirstV_1<'a> // no f2 here diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 82777419d93..46af3839f79 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -175,13 +175,18 @@ type SyntheticProject = module Internal = - let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = + let renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = seq { if project.RecursiveNamespace then $"namespace rec {project.Name}" $"module {f.ModuleName}" else $"module %s{project.Name}.{f.ModuleName}" + } |> String.concat Environment.NewLine + + let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + renderNamespaceModule project f for p in project.DependsOn do $"open {p.Name}" @@ -396,7 +401,9 @@ module ProjectOperations = let! results = checkFile file.Id project checker let signature = getSignature results writeFileIfChanged signatureFileName signature - | Custom signature -> writeFileIfChanged signatureFileName signature + | Custom signature -> + let signatureContent = $"{renderNamespaceModule p file}\n{signature}" + writeFileIfChanged signatureFileName signatureContent | _ -> () writeFileIfChanged (p.ProjectDir ++ $"{p.Name}.fsproj") (renderFsProj p) From e2647a52cc05fbf09284273b97ff83542210e9d1 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 8 Feb 2023 18:41:18 +0100 Subject: [PATCH 13/44] fantomas --- src/Compiler/Service/ItemKey.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Service/ItemKey.fs b/src/Compiler/Service/ItemKey.fs index e5037bfa7a4..c5c8038d796 100644 --- a/src/Compiler/Service/ItemKey.fs +++ b/src/Compiler/Service/ItemKey.fs @@ -295,7 +295,7 @@ and [] ItemKeyStoreBuilder() = writeType false vref.Type match vref.Deref.ArgReprInfoForDisplay with - | Some ({ OtherRange = Some (r)}) -> writeRange r + | Some ({ OtherRange = Some (r) }) -> writeRange r | _ -> () match vref.TryDeclaringEntity with From c068774213763e28619ed36e88eed8dd1af37157 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 15 Feb 2023 15:07:49 +0100 Subject: [PATCH 14/44] Added Symbol.IsPrivateToFileAndSignatureFile for optimized finding of parameter references --- src/Compiler/Service/FSharpCheckerResults.fs | 33 ++++++++---- src/Compiler/Service/FSharpCheckerResults.fsi | 4 ++ src/Compiler/TypedTree/TypedTree.fs | 5 +- .../FSharpChecker/SymbolUse.fs | 15 ++++-- .../src/FSharp.Editor/Common/Pervasive.fs | 7 +++ .../InlineRename/InlineRenameService.fs | 4 +- .../LanguageService/SymbolHelpers.fs | 54 ++++++++++++------- .../FSharp.Editor/LanguageService/Symbols.fs | 15 +++--- .../Navigation/FindUsagesService.fs | 6 +-- 9 files changed, 97 insertions(+), 46 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index e018e97b37a..d9b9cb5b26f 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -258,23 +258,35 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti member _.Range = range - member this.IsPrivateToFile = - let isPrivate = + member this.IsPrivateToFileAndSignatureFile = + + let couldBeParameter, declarationLocation = match this.Symbol with + | :? FSharpParameter as p -> + true, Some p.DeclarationLocation | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> + true, Some m.DeclarationLocation + | _ -> + false, None - // In case it's a parameter and it's in a signature file, then it's not private + let thisIsSignature = SourceFileImpl.IsSignatureFile this.Range.FileName - // TODO: Can it be anything else than a parameter? + let signatureLocation = + match this.Symbol.Item with + | Item.Value v -> v.Deref.ArgReprInfoForDisplay + | _ -> None + |> Option.bind (fun a -> a.OtherRange) - let signatureLocation = - match m.Item with - | Item.Value v -> v.Deref.ArgReprInfoForDisplay - | _ -> None - |> Option.bind (fun a -> a.OtherRange) + couldBeParameter && (thisIsSignature || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) - signatureLocation.IsNone || signatureLocation = Some(m.DeclarationLocation) + member this.IsPrivateToFile = + let isPrivate = + match this.Symbol with + | _ when this.IsPrivateToFileAndSignatureFile -> false + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> + // local binding or parameter + true | :? FSharpMemberOrFunctionOrValue as m -> let fileSignatureLocation = m.DeclaringEntity |> Option.bind (fun e -> e.SignatureLocation) @@ -286,6 +298,7 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti fileHasSignatureFile && not m.HasSignatureFile || m.Accessibility.IsPrivate | :? FSharpEntity as m -> m.Accessibility.IsPrivate + | :? FSharpParameter -> true | :? FSharpGenericParameter -> true | :? FSharpUnionCase as m -> m.Accessibility.IsPrivate | :? FSharpField as m -> m.Accessibility.IsPrivate diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 444807b0117..a958a47ce2e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -180,6 +180,10 @@ type public FSharpSymbolUse = /// The range of text representing the reference to the symbol member Range: range + /// Indicates if the FSharpSymbolUse is private to the implementation & signature file. + /// This is true for function and method parameters. + member IsPrivateToFileAndSignatureFile: bool + /// Indicates if the FSharpSymbolUse is declared as private member IsPrivateToFile: bool diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index 2b2e3c6075e..8fbe59aeddf 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -1891,7 +1891,7 @@ type ExceptionInfo = override x.ToString() = sprintf "%+A" x -/// Represents the contents of of a module of namespace +/// Represents the contents of a module or namespace [] type ModuleOrNamespaceType(kind: ModuleOrNamespaceKind, vals: QueueList, entities: QueueList) = @@ -2622,8 +2622,9 @@ type Val = | _ -> x.val_range /// Range of the definition (signature) of the value, used by Visual Studio - member x.SigRange = + member x.SigRange = match x.val_opt_data with + | Some { arg_repr_info_for_display = Some { OtherRange = Some m } } -> m | Some { val_other_range = Some(m, false) } -> m | _ -> x.val_range diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index a8dcdda56c2..25363c8a24e 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -16,7 +16,8 @@ module IsPrivateToFile = project.Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 6, "let f2 x = x + 1", ["f2"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - Assert.False(symbolUse.IsPrivateToFile)) + Assert.False(symbolUse.IsPrivateToFile) + Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] @@ -28,7 +29,8 @@ module IsPrivateToFile = project.Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 6, "let f2 x = x + 1", ["f2"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - Assert.False(symbolUse.IsPrivateToFile)) + Assert.False(symbolUse.IsPrivateToFile) + Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] @@ -46,7 +48,8 @@ val f: x: 'a -> TFirstV_1<'a> project.Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 6, "let f2 x = x + 1", ["f2"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - Assert.True(symbolUse.IsPrivateToFile)) + Assert.True(symbolUse.IsPrivateToFile) + Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] @@ -54,7 +57,8 @@ val f: x: 'a -> TFirstV_1<'a> SyntheticProject.Create(sourceFile "First" []).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - Assert.True(symbolUse.IsPrivateToFile)) + Assert.True(symbolUse.IsPrivateToFile) + Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] @@ -62,7 +66,8 @@ val f: x: 'a -> TFirstV_1<'a> SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - Assert.False(symbolUse.IsPrivateToFile)) + Assert.False(symbolUse.IsPrivateToFile) + Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } // [] This is a bug - https://github.com/dotnet/fsharp/issues/14419 diff --git a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs index 66fb92e6d04..d093c81bbc4 100644 --- a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs +++ b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs @@ -9,6 +9,13 @@ open System.Diagnostics let isSignatureFile (filePath:string) = String.Equals (Path.GetExtension filePath, ".fsi", StringComparison.OrdinalIgnoreCase) +/// Returns the corresponding signature file path for given implementation file path or vice versa +let getOtherFile (filePath: string) = + if isSignatureFile filePath then + Path.ChangeExtension(filePath, ".fs") + else + Path.ChangeExtension(filePath, ".fsi") + /// Checks if the file paht ends with '.fsx' or '.fsscript' let isScriptFile (filePath:string) = let ext = Path.GetExtension filePath diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 669c3ed8683..7c82f962190 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -65,7 +65,7 @@ type internal InlineRenameInfo triggerSpan: TextSpan, lexerSymbol: LexerSymbol, symbolUse: FSharpSymbolUse, - declLoc: SymbolDeclarationLocation, + declLoc: SymbolScope, checkFileResults: FSharpCheckFileResults ) = @@ -77,7 +77,7 @@ type internal InlineRenameInfo | _ -> document.GetTextAsync(cancellationToken).Result let symbolUses ct = - SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document.Project.Solution, ct) + SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document, ct) override _.CanRename = true override _.LocalizedErrorMessage = null diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index f9202636eea..50ab7d36433 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -42,29 +42,47 @@ module internal SymbolHelpers = Task.Run(fun () -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct))) |> Task.WhenAll - let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolDeclarationLocation, checkFileResults: FSharpCheckFileResults, solution: Solution, ct: CancellationToken) = - async { - let toDict (symbolUseRanges: range seq) = - let groups = - symbolUseRanges - |> Seq.collect (fun symbolUse -> - solution.GetDocumentIdsWithFilePath(symbolUse.FileName) |> Seq.map (fun id -> id, symbolUse)) - |> Seq.groupBy fst - groups.ToImmutableDictionary( - (fun (id, _) -> id), - fun (_, xs) -> xs |> Seq.map snd |> Seq.toArray) + let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolScope, checkFileResults: FSharpCheckFileResults, document: Document, ct: CancellationToken) = + let solution = document.Project.Solution + let toDict (symbolUseRanges: range seq) = + let groups = + symbolUseRanges + |> Seq.collect (fun symbolUse -> + solution.GetDocumentIdsWithFilePath(symbolUse.FileName) |> Seq.map (fun id -> id, symbolUse)) + |> Seq.groupBy fst + groups.ToImmutableDictionary( + (fun (id, _) -> id), + fun (_, xs) -> xs |> Seq.map snd |> Seq.toArray) + async { match declLoc with - | SymbolDeclarationLocation.CurrentDocument -> - let! ct = Async.CancellationToken + | SymbolScope.CurrentDocument -> let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbol, ct) return toDict (symbolUses |> Seq.map (fun symbolUse -> symbolUse.Range)) - | SymbolDeclarationLocation.Projects (projects, isInternalToProject) -> + + | SymbolScope.SignatureAndImplementation -> + let otherFile = getOtherFile document.FilePath + let! otherFileCheckResults = + match solution.TryGetDocumentFromPath otherFile with + | Some doc -> + async { + let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("getSymbolUsesInSolution") + return [checkFileResults] + } + | None -> async.Return [] + + return + checkFileResults :: otherFileCheckResults + |> Seq.collect (fun checkFileResults -> checkFileResults.GetUsesOfSymbolInFile(symbol, ct)) + |> Seq.map (fun symbolUse -> symbolUse.Range) + |> toDict + + | SymbolScope.Projects (projects, isInternalToProject) -> let symbolUseRanges = ConcurrentBag() - + let projects = if isInternalToProject then projects - else + else [ for project in projects do yield project yield! project.GetDependentProjects() ] @@ -75,7 +93,7 @@ module internal SymbolHelpers = async { symbolUseRanges.Add symbolUseRange } do! getSymbolUsesInProjects (symbol, projects, onFound, ct) |> Async.AwaitTask - + // Distinct these down because each TFM will produce a new 'project'. // Unless guarded by a #if define, symbols with the same range will be added N times let symbolUseRanges = symbolUseRanges |> Seq.distinct @@ -116,7 +134,7 @@ module internal SymbolHelpers = Func<_,_>(fun (cancellationToken: CancellationToken) -> async { let! symbolUsesByDocumentId = - getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document.Project.Solution, cancellationToken) + getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document, cancellationToken) let mutable solution = document.Project.Solution diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs b/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs index 5f5d7fae794..91da05c8f1c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs @@ -8,8 +8,9 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Symbols [] -type SymbolDeclarationLocation = +type SymbolScope = | CurrentDocument + | SignatureAndImplementation | Projects of Project list * isLocalForProject: bool [] @@ -31,9 +32,11 @@ type FSharpSymbol with type FSharpSymbolUse with - member this.GetDeclarationLocation (currentDocument: Document) : SymbolDeclarationLocation option = + member this.GetDeclarationLocation (currentDocument: Document) : SymbolScope option = if this.IsPrivateToFile then - Some SymbolDeclarationLocation.CurrentDocument + Some SymbolScope.CurrentDocument + elif this.IsPrivateToFileAndSignatureFile then + Some SymbolScope.SignatureAndImplementation else let isSymbolLocalForProject = this.Symbol.IsInternalToProject @@ -47,12 +50,12 @@ type FSharpSymbolUse with let filePath = Path.GetFullPathSafe loc.FileName let isScript = isScriptFile filePath if isScript && filePath = currentDocument.FilePath then - Some SymbolDeclarationLocation.CurrentDocument + Some SymbolScope.CurrentDocument elif isScript then // The standalone script might include other files via '#load' // These files appear in project options and the standalone file // should be treated as an individual project - Some (SymbolDeclarationLocation.Projects ([currentDocument.Project], isSymbolLocalForProject)) + Some (SymbolScope.Projects ([currentDocument.Project], isSymbolLocalForProject)) else let projects = currentDocument.Project.Solution.GetDocumentIdsWithFilePath(filePath) @@ -62,7 +65,7 @@ type FSharpSymbolUse with |> Seq.toList match projects with | [] -> None - | projects -> Some (SymbolDeclarationLocation.Projects (projects, isSymbolLocalForProject)) + | projects -> Some (SymbolScope.Projects (projects, isSymbolLocalForProject)) | None -> None type FSharpEntity with diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 702ed777426..a997ebd0b1e 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -101,7 +101,7 @@ type internal FSharpFindUsagesService try do! context.OnReferenceFoundAsync(referenceItem) |> Async.AwaitTask with | _ -> () } match symbolUse.GetDeclarationLocation document with - | Some SymbolDeclarationLocation.CurrentDocument -> + | Some SymbolScope.CurrentDocument -> let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) for symbolUse in symbolUses do match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with @@ -112,12 +112,12 @@ type internal FSharpFindUsagesService | scope -> let projectsToCheck = match scope with - | Some (SymbolDeclarationLocation.Projects (declProjects, false)) -> + | Some (SymbolScope.Projects (declProjects, false)) -> [ for declProject in declProjects do yield declProject yield! declProject.GetDependentProjects() ] |> List.distinct - | Some (SymbolDeclarationLocation.Projects (declProjects, true)) -> declProjects + | Some (SymbolScope.Projects (declProjects, true)) -> declProjects // The symbol is declared in .NET framework, an external assembly or in a C# project within the solution. // In order to find all its usages we have to check all F# projects. | _ -> Seq.toList document.Project.Solution.Projects From 6aa5b021cb76deb3698726fcf23be5f606716457 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Feb 2023 15:22:18 +0100 Subject: [PATCH 15/44] fantomas --- src/Compiler/Service/FSharpCheckerResults.fs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index d9b9cb5b26f..b58e004632c 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -262,12 +262,9 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti let couldBeParameter, declarationLocation = match this.Symbol with - | :? FSharpParameter as p -> - true, Some p.DeclarationLocation - | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> - true, Some m.DeclarationLocation - | _ -> - false, None + | :? FSharpParameter as p -> true, Some p.DeclarationLocation + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation + | _ -> false, None let thisIsSignature = SourceFileImpl.IsSignatureFile this.Range.FileName @@ -277,14 +274,16 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti | _ -> None |> Option.bind (fun a -> a.OtherRange) - couldBeParameter && (thisIsSignature || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) + couldBeParameter + && (thisIsSignature + || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) member this.IsPrivateToFile = let isPrivate = match this.Symbol with | _ when this.IsPrivateToFileAndSignatureFile -> false - | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> // local binding or parameter true | :? FSharpMemberOrFunctionOrValue as m -> From 735eb0163024f3be3277dbdcef7d9516f3727acc Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Feb 2023 15:27:42 +0100 Subject: [PATCH 16/44] Disable method parameter test --- .../FSharpChecker/FindReferences.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 4828842aef6..ab1f902556b 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -166,8 +166,9 @@ module Parameters = ]) } + /// This is a bug: https://github.com/dotnet/fsharp/issues/14753 [] - let ``We find method parameter in signature file`` () = + let ``We DON'T find method parameter in signature file`` () = SyntheticProject.Create( { sourceFile "Source" [] with ExtraSource = "type MyClass() = member this.Method(methodParam) = methodParam + 1" @@ -175,7 +176,7 @@ module Parameters = .Workflow { placeCursor "Source" 6 47 "type MyClass() = member this.Method(methodParam) = methodParam + 1" ["methodParam"] findAllReferences (expectToFind [ - "FileSource.fsi", 14, 17, 28 + // "FileSource.fsi", 14, 17, 28 <-- this should also be found "FileSource.fs", 6, 36, 47 "FileSource.fs", 6, 51, 62 ]) From 65610819eff474bbd84d8d06e6b1ce0b082bbc9c Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Feb 2023 15:46:13 +0100 Subject: [PATCH 17/44] Update surface area test --- .../FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index fe96ceca8ca..41a0d62e0f1 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2178,6 +2178,7 @@ FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromPattern FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromType FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromUse FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsPrivateToFile +FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsPrivateToFileAndSignatureFile FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromAttribute() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromComputationExpression() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromDefinition() @@ -2187,6 +2188,7 @@ FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromPattern() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromType() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromUse() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsPrivateToFile() +FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsPrivateToFileAndSignatureFile() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpDisplayContext DisplayContext FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpDisplayContext get_DisplayContext() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpSymbol Symbol From 72b8a5d86a23ffd21e2afca5cbb46d6573872871 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Feb 2023 16:26:24 +0100 Subject: [PATCH 18/44] Update surface area test --- ...Sharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl | 2 ++ 1 file changed, 2 insertions(+) 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 fe96ceca8ca..41a0d62e0f1 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 @@ -2178,6 +2178,7 @@ FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromPattern FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromType FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromUse FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsPrivateToFile +FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsPrivateToFileAndSignatureFile FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromAttribute() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromComputationExpression() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromDefinition() @@ -2187,6 +2188,7 @@ FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromPattern() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromType() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsFromUse() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsPrivateToFile() +FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean get_IsPrivateToFileAndSignatureFile() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpDisplayContext DisplayContext FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpDisplayContext get_DisplayContext() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: FSharp.Compiler.Symbols.FSharpSymbol Symbol From 3ad2fd4a3832cb7f05919532bfdf356f54ac53e0 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Feb 2023 19:28:27 +0100 Subject: [PATCH 19/44] Path for SymbolScope.SignatureAndImplementation in Find All References --- .../Navigation/FindUsagesService.fs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index a997ebd0b1e..b68d409fa5d 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -99,8 +99,9 @@ type internal FSharpFindUsagesService let referenceItem = FSharpSourceReferenceItem(definitionItem, FSharpDocumentSpan(doc, textSpan)) // REVIEW: OnReferenceFoundAsync is throwing inside Roslyn, putting a try/with so find-all refs doesn't fail. try do! context.OnReferenceFoundAsync(referenceItem) |> Async.AwaitTask with | _ -> () } - + match symbolUse.GetDeclarationLocation document with + | Some SymbolScope.CurrentDocument -> let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) for symbolUse in symbolUses do @@ -109,6 +110,32 @@ type internal FSharpFindUsagesService do! onFound document textSpan symbolUse.Range |> liftAsync | _ -> () + + | Some SymbolScope.SignatureAndImplementation -> + let otherFile = getOtherFile document.FilePath + let! otherFileCheckResults = + match document.Project.Solution.TryGetDocumentFromPath otherFile with + | Some doc -> + async { + let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("findReferencedSymbolsAsync") + let! sourceText = doc.GetTextAsync(context.CancellationToken) |> Async.AwaitTask + return [checkFileResults, sourceText, doc] + } + | None -> async.Return [] + |> liftAsync + + let symbolUses = + (checkFileResults, sourceText, document) :: otherFileCheckResults + |> Seq.collect (fun (checkFileResults, sourceText, doc) -> + checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) + |> Seq.choose (fun symbolUse -> + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with + | Some textSpan -> Some (doc, textSpan, symbolUse.Range) + | None -> None)) + + for document, textSpan, range in symbolUses do + do! onFound document textSpan range |> liftAsync + | scope -> let projectsToCheck = match scope with From 2e062cd51bf40d06b32e3b90e9e3155db6ed6792 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Feb 2023 11:16:19 +0100 Subject: [PATCH 20/44] Declaration location -> symbol scope --- .../src/FSharp.Editor/InlineRename/InlineRenameService.fs | 4 ++-- .../src/FSharp.Editor/LanguageService/SymbolHelpers.fs | 8 ++++---- .../src/FSharp.Editor/LanguageService/Symbols.fs | 2 +- .../src/FSharp.Editor/Navigation/FindUsagesService.fs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 7c82f962190..601dad43c47 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -143,12 +143,12 @@ type internal InlineRenameService let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(InlineRenameService)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) - let! declLoc = symbolUse.GetDeclarationLocation(document) + let! symbolScope = symbolUse.GetSymbolScope(document) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) let triggerSpan = Tokenizer.fixupSpan(sourceText, span) - return InlineRenameInfo(document, triggerSpan, symbol, symbolUse, declLoc, checkFileResults) :> FSharpInlineRenameInfo + return InlineRenameInfo(document, triggerSpan, symbol, symbolUse, symbolScope, checkFileResults) :> FSharpInlineRenameInfo } override _.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 50ab7d36433..c046ab574de 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -42,7 +42,7 @@ module internal SymbolHelpers = Task.Run(fun () -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct))) |> Task.WhenAll - let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolScope, checkFileResults: FSharpCheckFileResults, document: Document, ct: CancellationToken) = + let getSymbolUsesInSolution (symbol: FSharpSymbol, symbolScope: SymbolScope, checkFileResults: FSharpCheckFileResults, document: Document, ct: CancellationToken) = let solution = document.Project.Solution let toDict (symbolUseRanges: range seq) = let groups = @@ -55,7 +55,7 @@ module internal SymbolHelpers = fun (_, xs) -> xs |> Seq.map snd |> Seq.toArray) async { - match declLoc with + match symbolScope with | SymbolScope.CurrentDocument -> let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbol, ct) return toDict (symbolUses |> Seq.map (fun symbolUse -> symbolUse.Range)) @@ -127,14 +127,14 @@ module internal SymbolHelpers = let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) - let! declLoc = symbolUse.GetDeclarationLocation(document) + let! symbolScope = symbolUse.GetSymbolScope(document) let newText = textChanger originalText // defer finding all symbol uses throughout the solution return Func<_,_>(fun (cancellationToken: CancellationToken) -> async { let! symbolUsesByDocumentId = - getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document, cancellationToken) + getSymbolUsesInSolution(symbolUse.Symbol, symbolScope, checkFileResults, document, cancellationToken) let mutable solution = document.Project.Solution diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs b/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs index 91da05c8f1c..84dbd946060 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs @@ -32,7 +32,7 @@ type FSharpSymbol with type FSharpSymbolUse with - member this.GetDeclarationLocation (currentDocument: Document) : SymbolScope option = + member this.GetSymbolScope (currentDocument: Document) : SymbolScope option = if this.IsPrivateToFile then Some SymbolScope.CurrentDocument elif this.IsPrivateToFileAndSignatureFile then diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index b68d409fa5d..9381556ee57 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -100,7 +100,7 @@ type internal FSharpFindUsagesService // REVIEW: OnReferenceFoundAsync is throwing inside Roslyn, putting a try/with so find-all refs doesn't fail. try do! context.OnReferenceFoundAsync(referenceItem) |> Async.AwaitTask with | _ -> () } - match symbolUse.GetDeclarationLocation document with + match symbolUse.GetSymbolScope document with | Some SymbolScope.CurrentDocument -> let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) From dfeeee25888bc54a978139258f9c79d9bea1c9ab Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Feb 2023 14:41:10 +0100 Subject: [PATCH 21/44] Unified finding references and rename code --- .../InlineRename/InlineRenameService.fs | 14 +-- .../LanguageService/SymbolHelpers.fs | 110 +++++++++--------- .../LanguageService/WorkspaceExtensions.fs | 12 +- .../Navigation/FindUsagesService.fs | 94 ++++----------- 4 files changed, 91 insertions(+), 139 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 601dad43c47..f8244e74433 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -65,7 +65,6 @@ type internal InlineRenameInfo triggerSpan: TextSpan, lexerSymbol: LexerSymbol, symbolUse: FSharpSymbolUse, - declLoc: SymbolScope, checkFileResults: FSharpCheckFileResults ) = @@ -76,8 +75,8 @@ type internal InlineRenameInfo | true, text -> text | _ -> document.GetTextAsync(cancellationToken).Result - let symbolUses ct = - SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document, ct) + let symbolUses = + SymbolHelpers.getSymbolUsesInSolution(symbolUse, checkFileResults, document) override _.CanRename = true override _.LocalizedErrorMessage = null @@ -103,19 +102,19 @@ type internal InlineRenameInfo override _.FindRenameLocationsAsync(_, _, cancellationToken) = async { - let! symbolUsesByDocumentId = symbolUses cancellationToken + let! symbolUsesByDocumentId = symbolUses let! locations = symbolUsesByDocumentId |> Seq.map (fun (KeyValue(documentId, symbolUses)) -> async { let document = document.Project.Solution.GetDocument(documentId) let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return + return [| for symbolUse in symbolUses do match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with | Some span -> let textSpan = Tokenizer.fixupSpan(sourceText, span) - yield FSharpInlineRenameLocation(document, textSpan) + yield FSharpInlineRenameLocation(document, textSpan) | None -> () |] }) |> Async.Parallel @@ -143,12 +142,11 @@ type internal InlineRenameService let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(InlineRenameService)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) - let! symbolScope = symbolUse.GetSymbolScope(document) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) let triggerSpan = Tokenizer.fixupSpan(sourceText, span) - return InlineRenameInfo(document, triggerSpan, symbol, symbolUse, symbolScope, checkFileResults) :> FSharpInlineRenameInfo + return InlineRenameInfo(document, triggerSpan, symbol, symbolUse, checkFileResults) :> FSharpInlineRenameInfo } override _.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index c046ab574de..cd8192ba880 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -36,70 +36,77 @@ module internal SymbolHelpers = return symbolUses } - let getSymbolUsesInProjects (symbol: FSharpSymbol, projects: Project list, onFound: Document -> TextSpan -> range -> Async, ct: CancellationToken) = + let getSymbolUsesInProjects (symbol: FSharpSymbol, projects: Project list, onFound: Document -> range -> Async, ct: CancellationToken) = projects |> Seq.map (fun project -> Task.Run(fun () -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct))) |> Task.WhenAll - let getSymbolUsesInSolution (symbol: FSharpSymbol, symbolScope: SymbolScope, checkFileResults: FSharpCheckFileResults, document: Document, ct: CancellationToken) = - let solution = document.Project.Solution - let toDict (symbolUseRanges: range seq) = - let groups = - symbolUseRanges - |> Seq.collect (fun symbolUse -> - solution.GetDocumentIdsWithFilePath(symbolUse.FileName) |> Seq.map (fun id -> id, symbolUse)) - |> Seq.groupBy fst - groups.ToImmutableDictionary( - (fun (id, _) -> id), - fun (_, xs) -> xs |> Seq.map snd |> Seq.toArray) - + let findSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) onFound = async { - match symbolScope with - | SymbolScope.CurrentDocument -> - let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbol, ct) - return toDict (symbolUses |> Seq.map (fun symbolUse -> symbolUse.Range)) + match symbolUse.GetSymbolScope currentDocument with + + | Some SymbolScope.CurrentDocument -> + let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) + for symbolUse in symbolUses do + do! onFound currentDocument symbolUse.Range - | SymbolScope.SignatureAndImplementation -> - let otherFile = getOtherFile document.FilePath + | Some SymbolScope.SignatureAndImplementation -> + let otherFile = getOtherFile currentDocument.FilePath let! otherFileCheckResults = - match solution.TryGetDocumentFromPath otherFile with + match currentDocument.Project.Solution.TryGetDocumentFromPath otherFile with | Some doc -> async { - let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("getSymbolUsesInSolution") - return [checkFileResults] + let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("findReferencedSymbolsAsync") + return [checkFileResults, doc] } | None -> async.Return [] - return - checkFileResults :: otherFileCheckResults - |> Seq.collect (fun checkFileResults -> checkFileResults.GetUsesOfSymbolInFile(symbol, ct)) - |> Seq.map (fun symbolUse -> symbolUse.Range) - |> toDict - - | SymbolScope.Projects (projects, isInternalToProject) -> - let symbolUseRanges = ConcurrentBag() - - let projects = - if isInternalToProject then projects - else - [ for project in projects do - yield project - yield! project.GetDependentProjects() ] - |> List.distinctBy (fun x -> x.Id) - - let onFound = - fun _ _ symbolUseRange -> - async { symbolUseRanges.Add symbolUseRange } - - do! getSymbolUsesInProjects (symbol, projects, onFound, ct) |> Async.AwaitTask - - // Distinct these down because each TFM will produce a new 'project'. - // Unless guarded by a #if define, symbols with the same range will be added N times - let symbolUseRanges = symbolUseRanges |> Seq.distinct - return toDict symbolUseRanges + let symbolUses = + (checkFileResults, currentDocument) :: otherFileCheckResults + |> Seq.collect (fun (checkFileResults, doc) -> + checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) + |> Seq.map (fun symbolUse -> (doc, symbolUse.Range))) + + for document, range in symbolUses do + do! onFound document range + + | scope -> + let projectsToCheck = + match scope with + | Some (SymbolScope.Projects (scopeProjects, false)) -> + [ for scopeProject in scopeProjects do + yield scopeProject + yield! scopeProject.GetDependentProjects() ] + |> List.distinct + | Some (SymbolScope.Projects (scopeProjects, true)) -> scopeProjects + // The symbol is declared in .NET framework, an external assembly or in a C# project within the solution. + // In order to find all its usages we have to check all F# projects. + | _ -> Seq.toList currentDocument.Project.Solution.Projects + let! ct = Async.CancellationToken + do! getSymbolUsesInProjects (symbolUse.Symbol, projectsToCheck, onFound, ct) |> Async.AwaitTask } - + + let getSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) = + async { + let symbolUses = ConcurrentBag() + let onFound = + fun document range -> + async { symbolUses.Add (document, range) } + + do! findSymbolUses symbolUse currentDocument checkFileResults onFound + + return symbolUses |> seq + } + + let getSymbolUsesInSolution (symbolUse: FSharpSymbolUse, checkFileResults: FSharpCheckFileResults, document: Document) = + async { + let! symbolUses = getSymbolUses symbolUse document checkFileResults + let symbolUsesWithDocumentId = symbolUses |> Seq.map (fun (doc, range) -> doc.Id, range) + let usesByDocumentId = symbolUsesWithDocumentId |> Seq.groupBy fst + return usesByDocumentId.ToImmutableDictionary(fst, snd >> Seq.map snd >> Seq.toArray) + } + type OriginalText = string // Note, this function is broken and shouldn't be used because the source text ranges to replace are applied sequentially, @@ -127,14 +134,13 @@ module internal SymbolHelpers = let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) - let! symbolScope = symbolUse.GetSymbolScope(document) let newText = textChanger originalText // defer finding all symbol uses throughout the solution return Func<_,_>(fun (cancellationToken: CancellationToken) -> async { let! symbolUsesByDocumentId = - getSymbolUsesInSolution(symbolUse.Symbol, symbolScope, checkFileResults, document, cancellationToken) + getSymbolUsesInSolution(symbolUse, checkFileResults, document) let mutable solution = document.Project.Solution diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 6388c2ac6f9..e04e386f8f9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -187,14 +187,8 @@ type Document with let! symbolUses = checker.FindBackgroundReferencesInFile(this.FilePath, projectOptions, symbol, canInvalidateProject = false, fastCheck = this.Project.IsFastFindReferencesEnabled) - let! ct = Async.CancellationToken - let! sourceText = this.GetTextAsync ct |> Async.AwaitTask for symbolUse in symbolUses do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with - | Some textSpan -> - do! onFound textSpan symbolUse - | _ -> - () + do! onFound symbolUse } /// Try to find a F# lexer/token symbol of the given F# document and position. @@ -234,12 +228,12 @@ type Project with do! documents |> Seq.map (fun doc -> Task.Run(fun () -> - doc.FindFSharpReferencesAsync(symbol, (fun textSpan range -> onFound doc textSpan range), userOpName) + doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) |> RoslynHelpers.StartAsyncUnitAsTask ct)) |> Task.WhenAll else for doc in documents do - do! doc.FindFSharpReferencesAsync(symbol, (fun textSpan range -> onFound doc textSpan range), userOpName) + do! doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) |> RoslynHelpers.StartAsyncAsTask ct } diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 9381556ee57..10a9d0222aa 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -80,76 +80,30 @@ type internal FSharpFindUsagesService if isExternal then do! context.OnDefinitionFoundAsync(externalDefinitionItem) |> Async.AwaitTask |> liftAsync - let onFound = - fun (doc: Document) (textSpan: TextSpan) (symbolUse: range) -> - async { - match declarationRange with - | Some declRange when Range.equals declRange symbolUse -> () - | _ -> - if allReferences then - let definitionItem = - if isExternal then - externalDefinitionItem - else - definitionItems - |> List.tryFind (fun (_, projectId) -> doc.Project.Id = projectId) - |> Option.map (fun (definitionItem, _) -> definitionItem) - |> Option.defaultValue externalDefinitionItem - - let referenceItem = FSharpSourceReferenceItem(definitionItem, FSharpDocumentSpan(doc, textSpan)) - // REVIEW: OnReferenceFoundAsync is throwing inside Roslyn, putting a try/with so find-all refs doesn't fail. - try do! context.OnReferenceFoundAsync(referenceItem) |> Async.AwaitTask with | _ -> () } - - match symbolUse.GetSymbolScope document with - - | Some SymbolScope.CurrentDocument -> - let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) - for symbolUse in symbolUses do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with - | Some textSpan -> - do! onFound document textSpan symbolUse.Range |> liftAsync - | _ -> - () - - | Some SymbolScope.SignatureAndImplementation -> - let otherFile = getOtherFile document.FilePath - let! otherFileCheckResults = - match document.Project.Solution.TryGetDocumentFromPath otherFile with - | Some doc -> - async { - let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("findReferencedSymbolsAsync") - let! sourceText = doc.GetTextAsync(context.CancellationToken) |> Async.AwaitTask - return [checkFileResults, sourceText, doc] - } - | None -> async.Return [] - |> liftAsync - - let symbolUses = - (checkFileResults, sourceText, document) :: otherFileCheckResults - |> Seq.collect (fun (checkFileResults, sourceText, doc) -> - checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) - |> Seq.choose (fun symbolUse -> - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with - | Some textSpan -> Some (doc, textSpan, symbolUse.Range) - | None -> None)) - - for document, textSpan, range in symbolUses do - do! onFound document textSpan range |> liftAsync - - | scope -> - let projectsToCheck = - match scope with - | Some (SymbolScope.Projects (declProjects, false)) -> - [ for declProject in declProjects do - yield declProject - yield! declProject.GetDependentProjects() ] - |> List.distinct - | Some (SymbolScope.Projects (declProjects, true)) -> declProjects - // The symbol is declared in .NET framework, an external assembly or in a C# project within the solution. - // In order to find all its usages we have to check all F# projects. - | _ -> Seq.toList document.Project.Solution.Projects - let! ct = Async.CancellationToken |> liftAsync - do! SymbolHelpers.getSymbolUsesInProjects (symbolUse.Symbol, projectsToCheck, onFound, ct) |> Async.AwaitTask |> liftAsync + let onFound (doc: Document) (symbolUse: range) = + async { + let! sourceText = doc.GetTextAsync(context.CancellationToken) |> Async.AwaitTask + match declarationRange, RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with + | Some declRange, _ when Range.equals declRange symbolUse -> () + | _, None -> () + | _, Some textSpan -> + if allReferences then + let definitionItem = + if isExternal then + externalDefinitionItem + else + definitionItems + |> List.tryFind (fun (_, projectId) -> doc.Project.Id = projectId) + |> Option.map (fun (definitionItem, _) -> definitionItem) + |> Option.defaultValue externalDefinitionItem + + let referenceItem = FSharpSourceReferenceItem(definitionItem, FSharpDocumentSpan(doc, textSpan)) + // REVIEW: OnReferenceFoundAsync is throwing inside Roslyn, putting a try/with so find-all refs doesn't fail. + try do! context.OnReferenceFoundAsync(referenceItem) |> Async.AwaitTask with | _ -> () + } + + do! SymbolHelpers.findSymbolUses symbolUse document checkFileResults onFound |> liftAsync + } |> Async.Ignore interface IFSharpFindUsagesService with From d9daedbdb20de029e3f8afba22d7c23c8d330cf9 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 17 Feb 2023 16:59:39 +0100 Subject: [PATCH 22/44] tests WIP --- .../ProjectGeneration.fs | 139 ++++++++++-------- .../FSharp.Editor.Tests.fsproj | 1 + .../FindReferencesTests.fs | 37 +++++ .../Helpers/RoslynHelpers.fs | 26 +++- .../tests/UnitTests/FindReferencesTests.fs | 69 +++++++++ .../UnitTests/VisualFSharp.UnitTests.fsproj | 2 + 6 files changed, 212 insertions(+), 62 deletions(-) create mode 100644 vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs create mode 100644 vsintegration/tests/UnitTests/FindReferencesTests.fs diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 89ad4fcbce2..8c19b695541 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -170,91 +170,103 @@ type SyntheticProject = OptionsCache[cacheKey] + member this.GetAllProjects() = + [ this + for p in this.DependsOn do + yield! p.GetAllProjects() ] + member this.GetAllFiles() = [ for f in this.SourceFiles do this, f for p in this.DependsOn do yield! p.GetAllFiles() ] -module Internal = - let renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = - seq { - if project.RecursiveNamespace then - $"namespace rec {project.Name}" - $"module {f.ModuleName}" - else - $"module %s{project.Name}.{f.ModuleName}" - } |> String.concat Environment.NewLine +let getFilePath p (f: SyntheticSourceFile) = p.ProjectDir ++ f.FileName +let getSignatureFilePath p (f: SyntheticSourceFile) = p.ProjectDir ++ f.SignatureFileName - let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = - seq { - renderNamespaceModule project f - for p in project.DependsOn do - $"open {p.Name}" +type SyntheticProject with + member this.GetFilePath fileId = this.Find fileId |> getFilePath this + member this.GetSignatureFilePath fileId = this.Find fileId |> getSignatureFilePath this - $"type {f.TypeName}<'a> = T{f.Id} of 'a" - $"let {f.FunctionName} x =" +let private renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + if project.RecursiveNamespace then + $"namespace rec {project.Name}" + $"module {f.ModuleName}" + else + $"module %s{project.Name}.{f.ModuleName}" + } |> String.concat Environment.NewLine - for dep in f.DependsOn do - $" Module{dep}.{defaultFunctionName} x," +let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + renderNamespaceModule project f - $" T{f.Id} x" + for p in project.DependsOn do + $"open {p.Name}" - $"let f2 x = x + {f.InternalVersion}" + $"type {f.TypeName}<'a> = T{f.Id} of 'a" - f.ExtraSource + $"let {f.FunctionName} x =" - if f.HasErrors then - "let wrong = 1 + 'a'" + for dep in f.DependsOn do + $" Module{dep}.{defaultFunctionName} x," - if f.EntryPoint then - "[]" - "let main _ =" - " f 1 |> ignore" - " printfn \"Hello World!\"" - " 0" - } - |> String.concat Environment.NewLine + $" T{f.Id} x" - let renderFsProj (p: SyntheticProject) = - seq { - """ - + $"let f2 x = x + {f.InternalVersion}" - - Exe - net7.0 - + f.ExtraSource - - """ + if f.HasErrors then + "let wrong = 1 + 'a'" - for f in p.SourceFiles do - if f.HasSignatureFile then - $"" + if f.EntryPoint then + "[]" + "let main _ =" + " f 1 |> ignore" + " printfn \"Hello World!\"" + " 0" + } + |> String.concat Environment.NewLine - $"" +let private renderFsProj (p: SyntheticProject) = + seq { + """ + - """ - - - """ - } - |> String.concat Environment.NewLine + + Exe + net7.0 + - let writeFileIfChanged path content = - if not (File.Exists path) || File.ReadAllText(path) <> content then - File.WriteAllText(path, content) + + """ - let writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = - let fileName = p.ProjectDir ++ f.FileName - let content = renderSourceFile p f - writeFileIfChanged fileName content + for f in p.SourceFiles do + if f.HasSignatureFile then + $"" + + $"" + + """ + + + """ + } + |> String.concat Environment.NewLine + +let private writeFileIfChanged path content = + if not (File.Exists path) || File.ReadAllText(path) <> content then + File.WriteAllText(path, content) + +let private writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = + let fileName = getFilePath p f + let content = renderSourceFile p f + writeFileIfChanged fileName content -open Internal [] module ProjectOperations = @@ -303,7 +315,7 @@ module ProjectOperations = let checkFile fileId (project: SyntheticProject) (checker: FSharpChecker) = let file = project.Find fileId let contents = renderSourceFile project file - let absFileName = project.ProjectDir ++ file.FileName + let absFileName = getFilePath project file checker.ParseAndCheckFileInProject( absFileName, @@ -621,7 +633,7 @@ type ProjectWorkflowBuilder failwith $"Please place cursor at a valid location via {nameof this.PlaceCursor} first") let file = ctx.Project.Find fileId - let absFileName = ctx.Project.ProjectDir ++ file.FileName + let absFileName = getFilePath ctx.Project file let! results = checker.FindBackgroundReferencesInFile(absFileName, options, symbolUse.Symbol, fastCheck = true) @@ -733,3 +745,8 @@ type SyntheticProject with member this.WorkflowWith checker = ProjectWorkflowBuilder(this, checker = checker) + + /// Saves project to disk and checks it with default options. Returns the FSharpChecker that was created + member this.SaveAndCheck() = + this.Workflow.Yield() |> Async.RunSynchronously |> ignore + this.Workflow.Checker \ No newline at end of file diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index 7c45d65bbce..a966b604896 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -26,6 +26,7 @@ + diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs new file mode 100644 index 00000000000..1fafbbbd32e --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs @@ -0,0 +1,37 @@ +module FSharp.Editor.Tests.FindReferencesTests + +open Xunit +open FSharp.Test.ProjectGeneration +open FSharp.Editor.Tests.Helpers + +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.FindUsages + + +[] +let ``Find references to a document-local symbol`` () = + + let project = SyntheticProject.Create( + sourceFile "First" [] |> addSignatureFile, + sourceFile "Second" []) + + let solution, checker = RoslynTestHelpers.CreateSolution project + + let _projectDir = project.ProjectDir + + ignore solution + ignore checker + + + //let context = + // { new IFSharpFindUsagesContext + // with member _.x = () + // } + + () + + +let ``Find references to a implementation + signature local symbol`` () = () + +let ``Find references to a symbol in project`` () = () + +let ``Find references to a symbol in multiple projects`` () = () diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs index 331a812ddd7..074530246ef 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -16,6 +16,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis.Host.Mef open FSharp.Compiler.CodeAnalysis open System.Threading +open FSharp.Test.ProjectGeneration [] module MefHelpers = @@ -284,4 +285,27 @@ type RoslynTestHelpers private () = static member GetSingleDocument(solution: Solution) = let project = solution.Projects |> Seq.exactlyOne let document = project.Documents |> Seq.exactlyOne - document \ No newline at end of file + document + + static member CreateSolution (syntheticProject: SyntheticProject) = + + let checker = syntheticProject.SaveAndCheck() + + assert (syntheticProject.DependsOn = []) // multi-project not supported yet + + let projId = ProjectId.CreateNewId() + + let docInfos = + [ for project, file in syntheticProject.GetAllFiles() do + let filePath = getFilePath project file + RoslynTestHelpers.CreateDocumentInfo projId filePath (File.ReadAllText filePath) + if file.HasSignatureFile then + let sigFilePath = getSignatureFilePath project file + RoslynTestHelpers.CreateDocumentInfo projId sigFilePath (File.ReadAllText sigFilePath) ] + + let projInfo = RoslynTestHelpers.CreateProjectInfo projId syntheticProject.ProjectFileName docInfos + let solution = RoslynTestHelpers.CreateSolution [projInfo] + + syntheticProject.GetProjectOptions checker |> RoslynTestHelpers.SetProjectOptions projId solution + + solution, checker \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/FindReferencesTests.fs b/vsintegration/tests/UnitTests/FindReferencesTests.fs new file mode 100644 index 00000000000..edd0d3600d1 --- /dev/null +++ b/vsintegration/tests/UnitTests/FindReferencesTests.fs @@ -0,0 +1,69 @@ +module FSharp.Editor.Tests.FindReferencesTests + +open NUnit.Framework + +open FSharp.Test.ProjectGeneration +open FSharp.Editor.Tests.Helpers + +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.FindUsages +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.FindUsages + +open Microsoft.VisualStudio.FSharp.Editor + +open System.Threading.Tasks +open System.Threading +open System.IO + + +let getPositionOf (subString: string) (filePath) = + filePath + |> File.ReadAllText + |> fun source -> source.IndexOf subString + + +[] +let ``Find references to a document-local symbol`` () = + + let project = SyntheticProject.Create( + sourceFile "First" [] |> addSignatureFile, + sourceFile "Second" []) + + let solution, checker = RoslynTestHelpers.CreateSolution project + + let _projectDir = project.ProjectDir + + ignore solution + ignore checker + + let context = + { new IFSharpFindUsagesContext with + + member _.OnDefinitionFoundAsync (definition: FSharpDefinitionItem) = Task.CompletedTask + + member _.OnReferenceFoundAsync (reference: FSharpSourceReferenceItem) = Task.CompletedTask + + member _.ReportMessageAsync (message: string) = Task.CompletedTask + + member _.ReportProgressAsync (current: int, maximum: int) = Task.CompletedTask + + member _.SetSearchTitleAsync (title: string) = Task.CompletedTask + + member _.CancellationToken = CancellationToken.None + } + + let findUsagesService = FSharpFindUsagesService() :> IFSharpFindUsagesService + + let documentPath = project.GetFilePath "Second" + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (failwith "Document not found") + + //findService.FindReferencesAsync( + + () + () + + +let ``Find references to a implementation + signature local symbol`` () = () + +let ``Find references to a symbol in project`` () = () + +let ``Find references to a symbol in multiple projects`` () = () diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 1ee9d2b4a4a..c1ef95a23ed 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -37,6 +37,7 @@ + @@ -73,6 +74,7 @@ + From babba26c872480fb4b48a1b1dd6532643ac82448 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 21 Feb 2023 18:12:31 +0100 Subject: [PATCH 23/44] working tests --- .../FSharpChecker/CommonWorkflows.fs | 4 +- .../ProjectGeneration.fs | 16 ++-- .../FSharp.Editor.Tests.fsproj | 1 - .../FindReferencesTests.fs | 37 -------- .../Helpers/RoslynHelpers.fs | 21 ++++- .../tests/UnitTests/FindReferencesTests.fs | 92 +++++++++++++------ 6 files changed, 91 insertions(+), 80 deletions(-) delete mode 100644 vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 6b5775f23e7..80369e1532a 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -1,4 +1,5 @@ -module FSharp.Compiler.ComponentTests.FSharpChecker.CommonWorkflows + +module FSharp.Compiler.ComponentTests.FSharpChecker.CommonWorkflows open System open System.IO @@ -6,7 +7,6 @@ open System.IO open Xunit open FSharp.Test.ProjectGeneration -open FSharp.Test.ProjectGeneration.Internal open FSharp.Compiler.Text open FSharp.Compiler.CodeAnalysis diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 8c19b695541..5b2a42ef65a 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -342,7 +342,7 @@ module ProjectOperations = if checkResult.Diagnostics.Length > 0 then failwith $"Expected no errors, but there were some: \n%A{checkResult.Diagnostics}" - let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = + let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = let checkResult = getTypeCheckResult parseAndCheckResults let errors = checkResult.Diagnostics|> Array.filter (fun d -> d.Severity = FSharpDiagnosticSeverity.Error) if errors.Length > 0 then @@ -351,10 +351,10 @@ module ProjectOperations = let warnings = checkResult.Diagnostics|> Array.filter (fun d -> d.Severity = FSharpDiagnosticSeverity.Warning) match warnings |> Array.tryExactlyOne with | None -> failwith $"Expected 1 warning, but got {warnings.Length} instead: \n%A{warnings}" - | Some w -> + | Some w -> if w.Message.Contains warningSubString then () - else + else failwith $"Expected 1 warning with substring '{warningSubString}' but got %A{w}" let expectErrors parseAndCheckResults _ = @@ -385,16 +385,16 @@ module ProjectOperations = let expectToFind expected (foundRanges: range seq) = let expected = - expected - |> Seq.sortBy (fun (file, _, _, _) -> file) + expected + |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - + let actual = foundRanges |> Seq.map (fun r -> Path.GetFileName(r.FileName), r.StartLine, r.StartColumn, r.EndColumn) |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - + Assert.Equal<(string * int * int * int)[]>(expected, actual) let rec saveProject (p: SyntheticProject) generateSignatureFiles checker = @@ -731,7 +731,7 @@ type ProjectWorkflowBuilder exn $"Compilation failed with exit code {exitCode}" |> raise return ctx } - + /// Execute a set of operations on a given synthetic project. /// The project is saved to disk and type checked at the start. let projectWorkflow project = ProjectWorkflowBuilder project diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index a966b604896..7c45d65bbce 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -26,7 +26,6 @@ - diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs deleted file mode 100644 index 1fafbbbd32e..00000000000 --- a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs +++ /dev/null @@ -1,37 +0,0 @@ -module FSharp.Editor.Tests.FindReferencesTests - -open Xunit -open FSharp.Test.ProjectGeneration -open FSharp.Editor.Tests.Helpers - -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.FindUsages - - -[] -let ``Find references to a document-local symbol`` () = - - let project = SyntheticProject.Create( - sourceFile "First" [] |> addSignatureFile, - sourceFile "Second" []) - - let solution, checker = RoslynTestHelpers.CreateSolution project - - let _projectDir = project.ProjectDir - - ignore solution - ignore checker - - - //let context = - // { new IFSharpFindUsagesContext - // with member _.x = () - // } - - () - - -let ``Find references to a implementation + signature local symbol`` () = () - -let ``Find references to a symbol in project`` () = () - -let ``Find references to a symbol in multiple projects`` () = () diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs index 074530246ef..b04081a373a 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -17,6 +17,7 @@ open Microsoft.CodeAnalysis.Host.Mef open FSharp.Compiler.CodeAnalysis open System.Threading open FSharp.Test.ProjectGeneration +open Microsoft.CodeAnalysis [] module MefHelpers = @@ -220,7 +221,7 @@ type RoslynTestHelpers private () = Stamp = None } - static member private GetSourceCodeKind filePath = + static member private GetSourceCodeKind filePath = let extension = Path.GetExtension(filePath) match extension with | ".fsx" -> SourceCodeKind.Script @@ -233,7 +234,7 @@ type RoslynTestHelpers private () = let id = SolutionId.CreateNewId() let versionStamp = VersionStamp.Create(DateTime.UtcNow) let slnPath = "test.sln" - + let solutionInfo = SolutionInfo.Create(id, versionStamp, slnPath, projects) let solution = workspace.AddSolution(solutionInfo) solution @@ -256,7 +257,7 @@ type RoslynTestHelpers private () = documents = documents, filePath = filePath) - static member SetProjectOptions projId (solution: Solution) (options: FSharpProjectOptions) = + static member SetProjectOptions projId (solution: Solution) (options: FSharpProjectOptions) = solution .Workspace .Services @@ -276,7 +277,7 @@ type RoslynTestHelpers private () = let projInfo = RoslynTestHelpers.CreateProjectInfo projId projFilePath [docInfo] let solution = RoslynTestHelpers.CreateSolution [projInfo] - options + options |> Option.defaultValue RoslynTestHelpers.DefaultProjectOptions |> RoslynTestHelpers.SetProjectOptions projId solution @@ -304,8 +305,18 @@ type RoslynTestHelpers private () = RoslynTestHelpers.CreateDocumentInfo projId sigFilePath (File.ReadAllText sigFilePath) ] let projInfo = RoslynTestHelpers.CreateProjectInfo projId syntheticProject.ProjectFileName docInfos + + let options = syntheticProject.GetProjectOptions checker + + let metadataReferences = + options.OtherOptions + |> Seq.filter (fun x -> x.StartsWith("-r:")) + |> Seq.map (fun x -> x.Substring(3) |> MetadataReference.CreateFromFile :> MetadataReference) + + let projInfo = projInfo.WithMetadataReferences metadataReferences + let solution = RoslynTestHelpers.CreateSolution [projInfo] - syntheticProject.GetProjectOptions checker |> RoslynTestHelpers.SetProjectOptions projId solution + options |> RoslynTestHelpers.SetProjectOptions projId solution solution, checker \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/FindReferencesTests.fs b/vsintegration/tests/UnitTests/FindReferencesTests.fs index edd0d3600d1..bc5a863b5ba 100644 --- a/vsintegration/tests/UnitTests/FindReferencesTests.fs +++ b/vsintegration/tests/UnitTests/FindReferencesTests.fs @@ -13,6 +13,7 @@ open Microsoft.VisualStudio.FSharp.Editor open System.Threading.Tasks open System.Threading open System.IO +open System.Collections.Concurrent let getPositionOf (subString: string) (filePath) = @@ -21,49 +22,86 @@ let getPositionOf (subString: string) (filePath) = |> fun source -> source.IndexOf subString -[] -let ``Find references to a document-local symbol`` () = +module FindReferences = let project = SyntheticProject.Create( - sourceFile "First" [] |> addSignatureFile, - sourceFile "Second" []) - + { sourceFile "First" [] with + SignatureFile = AutoGenerated + ExtraSource = "let someFunc funcParam = funcParam * 1\n" + + "let sharedFunc funcParam = funcParam * 2\n" }, + { sourceFile "Second" [] with + ExtraSource = "let someFunc funcParam = funcParam * 1" }, + { sourceFile "Third" ["First"] with + ExtraSource = "let someFunc x = ModuleFirst.sharedFunc x + 10" } + ) + let solution, checker = RoslynTestHelpers.CreateSolution project - + let _projectDir = project.ProjectDir - ignore solution - ignore checker + let findUsagesService = FSharpFindUsagesService() :> IFSharpFindUsagesService + + let getContext () = + let foundDefinitions = ConcurrentBag() + let foundReferences = ConcurrentBag() + let context = + { new IFSharpFindUsagesContext with - let context = - { new IFSharpFindUsagesContext with + member _.OnDefinitionFoundAsync (definition: FSharpDefinitionItem) = + foundDefinitions.Add definition + Task.CompletedTask - member _.OnDefinitionFoundAsync (definition: FSharpDefinitionItem) = Task.CompletedTask + member _.OnReferenceFoundAsync (reference: FSharpSourceReferenceItem) = + foundReferences.Add reference + Task.CompletedTask - member _.OnReferenceFoundAsync (reference: FSharpSourceReferenceItem) = Task.CompletedTask + member _.ReportMessageAsync _ = Task.CompletedTask + member _.ReportProgressAsync (_,_) = Task.CompletedTask + member _.SetSearchTitleAsync _ = Task.CompletedTask - member _.ReportMessageAsync (message: string) = Task.CompletedTask + member _.CancellationToken = CancellationToken.None + } + context, foundDefinitions, foundReferences - member _.ReportProgressAsync (current: int, maximum: int) = Task.CompletedTask + [] + let ``Find references to a document-local symbol`` () = - member _.SetSearchTitleAsync (title: string) = Task.CompletedTask + let context, foundDefinitions, foundReferences = getContext() - member _.CancellationToken = CancellationToken.None - } + let documentPath = project.GetFilePath "Second" - let findUsagesService = FSharpFindUsagesService() :> IFSharpFindUsagesService + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + + findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() + + if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" + if foundReferences.Count <> 1 then failwith $"Expected 1 reference but found {foundReferences.Count}" + + [] + let ``Find references to a implementation + signature local symbol`` () = + + let context, foundDefinitions, foundReferences = getContext() + + let documentPath = project.GetFilePath "First" + + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + + findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() - let documentPath = project.GetFilePath "Second" - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (failwith "Document not found") - - //findService.FindReferencesAsync( + if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" + if foundReferences.Count <> 2 // One in signature file, one in function body + then failwith $"Expected 2 references but found {foundReferences.Count}" - () - () + [] + let ``Find references to a symbol in project`` () = + let context, foundDefinitions, foundReferences = getContext() + let documentPath = project.GetFilePath "First" -let ``Find references to a implementation + signature local symbol`` () = () + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") -let ``Find references to a symbol in project`` () = () + findUsagesService.FindReferencesAsync(document, getPositionOf "sharedFunc" documentPath, context).Wait() -let ``Find references to a symbol in multiple projects`` () = () + if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" + if foundReferences.Count <> 2 // One in signature file, one in Third file + then failwith $"Expected 2 references but found {foundReferences.Count}" From 5fae95ac2a4e98a54478aeb89c22e243bbbeaabe Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 22 Feb 2023 11:35:42 +0100 Subject: [PATCH 24/44] Revert the tests, have to wait for Roslyn update to put them into FSharp.Editor.Tests --- .../FSharpChecker/CommonWorkflows.fs | 4 +- .../ProjectGeneration.fs | 155 ++++++++---------- .../Helpers/RoslynHelpers.fs | 45 +---- .../tests/UnitTests/FindReferencesTests.fs | 107 ------------ .../UnitTests/VisualFSharp.UnitTests.fsproj | 2 - 5 files changed, 76 insertions(+), 237 deletions(-) delete mode 100644 vsintegration/tests/UnitTests/FindReferencesTests.fs diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 80369e1532a..6b5775f23e7 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -1,5 +1,4 @@ - -module FSharp.Compiler.ComponentTests.FSharpChecker.CommonWorkflows +module FSharp.Compiler.ComponentTests.FSharpChecker.CommonWorkflows open System open System.IO @@ -7,6 +6,7 @@ open System.IO open Xunit open FSharp.Test.ProjectGeneration +open FSharp.Test.ProjectGeneration.Internal open FSharp.Compiler.Text open FSharp.Compiler.CodeAnalysis diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 5b2a42ef65a..89ad4fcbce2 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -170,103 +170,91 @@ type SyntheticProject = OptionsCache[cacheKey] - member this.GetAllProjects() = - [ this - for p in this.DependsOn do - yield! p.GetAllProjects() ] - member this.GetAllFiles() = [ for f in this.SourceFiles do this, f for p in this.DependsOn do yield! p.GetAllFiles() ] +module Internal = -let getFilePath p (f: SyntheticSourceFile) = p.ProjectDir ++ f.FileName -let getSignatureFilePath p (f: SyntheticSourceFile) = p.ProjectDir ++ f.SignatureFileName - - -type SyntheticProject with - member this.GetFilePath fileId = this.Find fileId |> getFilePath this - member this.GetSignatureFilePath fileId = this.Find fileId |> getSignatureFilePath this - - -let private renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = - seq { - if project.RecursiveNamespace then - $"namespace rec {project.Name}" - $"module {f.ModuleName}" - else - $"module %s{project.Name}.{f.ModuleName}" - } |> String.concat Environment.NewLine + let renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + if project.RecursiveNamespace then + $"namespace rec {project.Name}" + $"module {f.ModuleName}" + else + $"module %s{project.Name}.{f.ModuleName}" + } |> String.concat Environment.NewLine -let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = - seq { - renderNamespaceModule project f + let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + renderNamespaceModule project f - for p in project.DependsOn do - $"open {p.Name}" + for p in project.DependsOn do + $"open {p.Name}" - $"type {f.TypeName}<'a> = T{f.Id} of 'a" + $"type {f.TypeName}<'a> = T{f.Id} of 'a" - $"let {f.FunctionName} x =" + $"let {f.FunctionName} x =" - for dep in f.DependsOn do - $" Module{dep}.{defaultFunctionName} x," + for dep in f.DependsOn do + $" Module{dep}.{defaultFunctionName} x," - $" T{f.Id} x" + $" T{f.Id} x" - $"let f2 x = x + {f.InternalVersion}" + $"let f2 x = x + {f.InternalVersion}" - f.ExtraSource + f.ExtraSource - if f.HasErrors then - "let wrong = 1 + 'a'" + if f.HasErrors then + "let wrong = 1 + 'a'" - if f.EntryPoint then - "[]" - "let main _ =" - " f 1 |> ignore" - " printfn \"Hello World!\"" - " 0" - } - |> String.concat Environment.NewLine + if f.EntryPoint then + "[]" + "let main _ =" + " f 1 |> ignore" + " printfn \"Hello World!\"" + " 0" + } + |> String.concat Environment.NewLine -let private renderFsProj (p: SyntheticProject) = - seq { - """ - + let renderFsProj (p: SyntheticProject) = + seq { + """ + - - Exe - net7.0 - + + Exe + net7.0 + - - """ + + """ - for f in p.SourceFiles do - if f.HasSignatureFile then - $"" + for f in p.SourceFiles do + if f.HasSignatureFile then + $"" - $"" + $"" - """ - - - """ - } - |> String.concat Environment.NewLine + """ + + + """ + } + |> String.concat Environment.NewLine -let private writeFileIfChanged path content = - if not (File.Exists path) || File.ReadAllText(path) <> content then - File.WriteAllText(path, content) + let writeFileIfChanged path content = + if not (File.Exists path) || File.ReadAllText(path) <> content then + File.WriteAllText(path, content) -let private writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = - let fileName = getFilePath p f - let content = renderSourceFile p f - writeFileIfChanged fileName content + let writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = + let fileName = p.ProjectDir ++ f.FileName + let content = renderSourceFile p f + writeFileIfChanged fileName content +open Internal [] module ProjectOperations = @@ -315,7 +303,7 @@ module ProjectOperations = let checkFile fileId (project: SyntheticProject) (checker: FSharpChecker) = let file = project.Find fileId let contents = renderSourceFile project file - let absFileName = getFilePath project file + let absFileName = project.ProjectDir ++ file.FileName checker.ParseAndCheckFileInProject( absFileName, @@ -342,7 +330,7 @@ module ProjectOperations = if checkResult.Diagnostics.Length > 0 then failwith $"Expected no errors, but there were some: \n%A{checkResult.Diagnostics}" - let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = + let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = let checkResult = getTypeCheckResult parseAndCheckResults let errors = checkResult.Diagnostics|> Array.filter (fun d -> d.Severity = FSharpDiagnosticSeverity.Error) if errors.Length > 0 then @@ -351,10 +339,10 @@ module ProjectOperations = let warnings = checkResult.Diagnostics|> Array.filter (fun d -> d.Severity = FSharpDiagnosticSeverity.Warning) match warnings |> Array.tryExactlyOne with | None -> failwith $"Expected 1 warning, but got {warnings.Length} instead: \n%A{warnings}" - | Some w -> + | Some w -> if w.Message.Contains warningSubString then () - else + else failwith $"Expected 1 warning with substring '{warningSubString}' but got %A{w}" let expectErrors parseAndCheckResults _ = @@ -385,16 +373,16 @@ module ProjectOperations = let expectToFind expected (foundRanges: range seq) = let expected = - expected - |> Seq.sortBy (fun (file, _, _, _) -> file) + expected + |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - + let actual = foundRanges |> Seq.map (fun r -> Path.GetFileName(r.FileName), r.StartLine, r.StartColumn, r.EndColumn) |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - + Assert.Equal<(string * int * int * int)[]>(expected, actual) let rec saveProject (p: SyntheticProject) generateSignatureFiles checker = @@ -633,7 +621,7 @@ type ProjectWorkflowBuilder failwith $"Please place cursor at a valid location via {nameof this.PlaceCursor} first") let file = ctx.Project.Find fileId - let absFileName = getFilePath ctx.Project file + let absFileName = ctx.Project.ProjectDir ++ file.FileName let! results = checker.FindBackgroundReferencesInFile(absFileName, options, symbolUse.Symbol, fastCheck = true) @@ -731,7 +719,7 @@ type ProjectWorkflowBuilder exn $"Compilation failed with exit code {exitCode}" |> raise return ctx } - + /// Execute a set of operations on a given synthetic project. /// The project is saved to disk and type checked at the start. let projectWorkflow project = ProjectWorkflowBuilder project @@ -745,8 +733,3 @@ type SyntheticProject with member this.WorkflowWith checker = ProjectWorkflowBuilder(this, checker = checker) - - /// Saves project to disk and checks it with default options. Returns the FSharpChecker that was created - member this.SaveAndCheck() = - this.Workflow.Yield() |> Async.RunSynchronously |> ignore - this.Workflow.Checker \ No newline at end of file diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs index b04081a373a..331a812ddd7 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -16,8 +16,6 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis.Host.Mef open FSharp.Compiler.CodeAnalysis open System.Threading -open FSharp.Test.ProjectGeneration -open Microsoft.CodeAnalysis [] module MefHelpers = @@ -221,7 +219,7 @@ type RoslynTestHelpers private () = Stamp = None } - static member private GetSourceCodeKind filePath = + static member private GetSourceCodeKind filePath = let extension = Path.GetExtension(filePath) match extension with | ".fsx" -> SourceCodeKind.Script @@ -234,7 +232,7 @@ type RoslynTestHelpers private () = let id = SolutionId.CreateNewId() let versionStamp = VersionStamp.Create(DateTime.UtcNow) let slnPath = "test.sln" - + let solutionInfo = SolutionInfo.Create(id, versionStamp, slnPath, projects) let solution = workspace.AddSolution(solutionInfo) solution @@ -257,7 +255,7 @@ type RoslynTestHelpers private () = documents = documents, filePath = filePath) - static member SetProjectOptions projId (solution: Solution) (options: FSharpProjectOptions) = + static member SetProjectOptions projId (solution: Solution) (options: FSharpProjectOptions) = solution .Workspace .Services @@ -277,7 +275,7 @@ type RoslynTestHelpers private () = let projInfo = RoslynTestHelpers.CreateProjectInfo projId projFilePath [docInfo] let solution = RoslynTestHelpers.CreateSolution [projInfo] - options + options |> Option.defaultValue RoslynTestHelpers.DefaultProjectOptions |> RoslynTestHelpers.SetProjectOptions projId solution @@ -286,37 +284,4 @@ type RoslynTestHelpers private () = static member GetSingleDocument(solution: Solution) = let project = solution.Projects |> Seq.exactlyOne let document = project.Documents |> Seq.exactlyOne - document - - static member CreateSolution (syntheticProject: SyntheticProject) = - - let checker = syntheticProject.SaveAndCheck() - - assert (syntheticProject.DependsOn = []) // multi-project not supported yet - - let projId = ProjectId.CreateNewId() - - let docInfos = - [ for project, file in syntheticProject.GetAllFiles() do - let filePath = getFilePath project file - RoslynTestHelpers.CreateDocumentInfo projId filePath (File.ReadAllText filePath) - if file.HasSignatureFile then - let sigFilePath = getSignatureFilePath project file - RoslynTestHelpers.CreateDocumentInfo projId sigFilePath (File.ReadAllText sigFilePath) ] - - let projInfo = RoslynTestHelpers.CreateProjectInfo projId syntheticProject.ProjectFileName docInfos - - let options = syntheticProject.GetProjectOptions checker - - let metadataReferences = - options.OtherOptions - |> Seq.filter (fun x -> x.StartsWith("-r:")) - |> Seq.map (fun x -> x.Substring(3) |> MetadataReference.CreateFromFile :> MetadataReference) - - let projInfo = projInfo.WithMetadataReferences metadataReferences - - let solution = RoslynTestHelpers.CreateSolution [projInfo] - - options |> RoslynTestHelpers.SetProjectOptions projId solution - - solution, checker \ No newline at end of file + document \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/FindReferencesTests.fs b/vsintegration/tests/UnitTests/FindReferencesTests.fs deleted file mode 100644 index bc5a863b5ba..00000000000 --- a/vsintegration/tests/UnitTests/FindReferencesTests.fs +++ /dev/null @@ -1,107 +0,0 @@ -module FSharp.Editor.Tests.FindReferencesTests - -open NUnit.Framework - -open FSharp.Test.ProjectGeneration -open FSharp.Editor.Tests.Helpers - -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.FindUsages -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.FindUsages - -open Microsoft.VisualStudio.FSharp.Editor - -open System.Threading.Tasks -open System.Threading -open System.IO -open System.Collections.Concurrent - - -let getPositionOf (subString: string) (filePath) = - filePath - |> File.ReadAllText - |> fun source -> source.IndexOf subString - - -module FindReferences = - - let project = SyntheticProject.Create( - { sourceFile "First" [] with - SignatureFile = AutoGenerated - ExtraSource = "let someFunc funcParam = funcParam * 1\n" + - "let sharedFunc funcParam = funcParam * 2\n" }, - { sourceFile "Second" [] with - ExtraSource = "let someFunc funcParam = funcParam * 1" }, - { sourceFile "Third" ["First"] with - ExtraSource = "let someFunc x = ModuleFirst.sharedFunc x + 10" } - ) - - let solution, checker = RoslynTestHelpers.CreateSolution project - - let _projectDir = project.ProjectDir - - let findUsagesService = FSharpFindUsagesService() :> IFSharpFindUsagesService - - let getContext () = - let foundDefinitions = ConcurrentBag() - let foundReferences = ConcurrentBag() - let context = - { new IFSharpFindUsagesContext with - - member _.OnDefinitionFoundAsync (definition: FSharpDefinitionItem) = - foundDefinitions.Add definition - Task.CompletedTask - - member _.OnReferenceFoundAsync (reference: FSharpSourceReferenceItem) = - foundReferences.Add reference - Task.CompletedTask - - member _.ReportMessageAsync _ = Task.CompletedTask - member _.ReportProgressAsync (_,_) = Task.CompletedTask - member _.SetSearchTitleAsync _ = Task.CompletedTask - - member _.CancellationToken = CancellationToken.None - } - context, foundDefinitions, foundReferences - - [] - let ``Find references to a document-local symbol`` () = - - let context, foundDefinitions, foundReferences = getContext() - - let documentPath = project.GetFilePath "Second" - - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") - - findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() - - if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 1 then failwith $"Expected 1 reference but found {foundReferences.Count}" - - [] - let ``Find references to a implementation + signature local symbol`` () = - - let context, foundDefinitions, foundReferences = getContext() - - let documentPath = project.GetFilePath "First" - - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") - - findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() - - if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 2 // One in signature file, one in function body - then failwith $"Expected 2 references but found {foundReferences.Count}" - - [] - let ``Find references to a symbol in project`` () = - let context, foundDefinitions, foundReferences = getContext() - - let documentPath = project.GetFilePath "First" - - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") - - findUsagesService.FindReferencesAsync(document, getPositionOf "sharedFunc" documentPath, context).Wait() - - if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 2 // One in signature file, one in Third file - then failwith $"Expected 2 references but found {foundReferences.Count}" diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index c1ef95a23ed..1ee9d2b4a4a 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -37,7 +37,6 @@ - @@ -74,7 +73,6 @@ - From 4ce721f18c61e652807c8f585a84798ff10563ef Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 28 Feb 2023 13:07:39 +0100 Subject: [PATCH 25/44] Added tests back --- .../FSharpChecker/CommonWorkflows.fs | 1 - .../FSharpChecker/FindReferences.fs | 5 +- .../ProjectGeneration.fs | 155 ++++++++++-------- .../FSharp.Editor.Tests.fsproj | 1 + .../FindReferencesTests.fs | 105 ++++++++++++ .../Helpers/RoslynHelpers.fs | 35 ++++ 6 files changed, 229 insertions(+), 73 deletions(-) create mode 100644 vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 6b5775f23e7..428c76732f6 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -6,7 +6,6 @@ open System.IO open Xunit open FSharp.Test.ProjectGeneration -open FSharp.Test.ProjectGeneration.Internal open FSharp.Compiler.Text open FSharp.Compiler.CodeAnalysis diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 45bceb92280..d8f5abf1838 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -255,12 +255,11 @@ let foo x = 5""" }) [] let ``We find a type that has been aliased`` () = - let project = SyntheticProject.Create("TypeAliasTest", + let project = SyntheticProject.Create( { sourceFile "First" [] with ExtraSource = "type MyInt = int32\n" + "let myNum = 7" - SignatureFile = Custom ("module TypeAliasTest.ModuleFirst\n" + - "type MyInt = int32\n" + + SignatureFile = Custom ("type MyInt = int32\n" + "val myNum: MyInt") }, { sourceFile "Second" [] with ExtraSource = "let goo x = ModuleFirst.myNum + x"}) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index ac2187230d8..63465f35ffb 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -170,91 +170,103 @@ type SyntheticProject = OptionsCache[cacheKey] + member this.GetAllProjects() = + [ this + for p in this.DependsOn do + yield! p.GetAllProjects() ] + member this.GetAllFiles() = [ for f in this.SourceFiles do this, f for p in this.DependsOn do yield! p.GetAllFiles() ] -module Internal = - let renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = - seq { - if project.RecursiveNamespace then - $"namespace rec {project.Name}" - $"module {f.ModuleName}" - else - $"module %s{project.Name}.{f.ModuleName}" - } |> String.concat Environment.NewLine +let getFilePath p (f: SyntheticSourceFile) = p.ProjectDir ++ f.FileName +let getSignatureFilePath p (f: SyntheticSourceFile) = p.ProjectDir ++ f.SignatureFileName - let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = - seq { - renderNamespaceModule project f - for p in project.DependsOn do - $"open {p.Name}" +type SyntheticProject with + member this.GetFilePath fileId = this.Find fileId |> getFilePath this + member this.GetSignatureFilePath fileId = this.Find fileId |> getSignatureFilePath this + - $"type {f.TypeName}<'a> = T{f.Id} of 'a" +let private renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + if project.RecursiveNamespace then + $"namespace rec {project.Name}" + $"module {f.ModuleName}" + else + $"module %s{project.Name}.{f.ModuleName}" + } |> String.concat Environment.NewLine - $"let {f.FunctionName} x =" +let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = + seq { + renderNamespaceModule project f - for dep in f.DependsOn do - $" Module{dep}.{defaultFunctionName} x," + for p in project.DependsOn do + $"open {p.Name}" - $" T{f.Id} x" + $"type {f.TypeName}<'a> = T{f.Id} of 'a" - $"let f2 x = x + {f.InternalVersion}" + $"let {f.FunctionName} x =" - f.ExtraSource + for dep in f.DependsOn do + $" Module{dep}.{defaultFunctionName} x," - if f.HasErrors then - "let wrong = 1 + 'a'" + $" T{f.Id} x" - if f.EntryPoint then - "[]" - "let main _ =" - " f 1 |> ignore" - " printfn \"Hello World!\"" - " 0" - } - |> String.concat Environment.NewLine + $"let f2 x = x + {f.InternalVersion}" - let renderFsProj (p: SyntheticProject) = - seq { - """ - + f.ExtraSource - - Exe - net7.0 - + if f.HasErrors then + "let wrong = 1 + 'a'" - - """ + if f.EntryPoint then + "[]" + "let main _ =" + " f 1 |> ignore" + " printfn \"Hello World!\"" + " 0" + } + |> String.concat Environment.NewLine - for f in p.SourceFiles do - if f.HasSignatureFile then - $"" +let private renderFsProj (p: SyntheticProject) = + seq { + """ + - $"" + + Exe + net7.0 + - """ - - - """ - } - |> String.concat Environment.NewLine + + """ - let writeFileIfChanged path content = - if not (File.Exists path) || File.ReadAllText(path) <> content then - File.WriteAllText(path, content) + for f in p.SourceFiles do + if f.HasSignatureFile then + $"" - let writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = - let fileName = p.ProjectDir ++ f.FileName - let content = renderSourceFile p f - writeFileIfChanged fileName content + $"" + + """ + + + """ + } + |> String.concat Environment.NewLine + +let private writeFileIfChanged path content = + if not (File.Exists path) || File.ReadAllText(path) <> content then + File.WriteAllText(path, content) + +let private writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = + let fileName = getFilePath p f + let content = renderSourceFile p f + writeFileIfChanged fileName content -open Internal [] module ProjectOperations = @@ -303,7 +315,7 @@ module ProjectOperations = let checkFile fileId (project: SyntheticProject) (checker: FSharpChecker) = let file = project.Find fileId let contents = renderSourceFile project file - let absFileName = project.ProjectDir ++ file.FileName + let absFileName = getFilePath project file checker.ParseAndCheckFileInProject( absFileName, @@ -330,7 +342,7 @@ module ProjectOperations = if checkResult.Diagnostics.Length > 0 then failwith $"Expected no errors, but there were some: \n%A{checkResult.Diagnostics}" - let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = + let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = let checkResult = getTypeCheckResult parseAndCheckResults let errors = checkResult.Diagnostics|> Array.filter (fun d -> d.Severity = FSharpDiagnosticSeverity.Error) if errors.Length > 0 then @@ -339,10 +351,10 @@ module ProjectOperations = let warnings = checkResult.Diagnostics|> Array.filter (fun d -> d.Severity = FSharpDiagnosticSeverity.Warning) match warnings |> Array.tryExactlyOne with | None -> failwith $"Expected 1 warning, but got {warnings.Length} instead: \n%A{warnings}" - | Some w -> + | Some w -> if w.Message.Contains warningSubString then () - else + else failwith $"Expected 1 warning with substring '{warningSubString}' but got %A{w}" let expectErrors parseAndCheckResults _ = @@ -373,16 +385,16 @@ module ProjectOperations = let expectToFind expected (foundRanges: range seq) = let expected = - expected - |> Seq.sortBy (fun (file, _, _, _) -> file) + expected + |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - + let actual = foundRanges |> Seq.map (fun r -> Path.GetFileName(r.FileName), r.StartLine, r.StartColumn, r.EndColumn) |> Seq.sortBy (fun (file, _, _, _) -> file) |> Seq.toArray - + Assert.Equal<(string * int * int * int)[]>(expected, actual) let rec saveProject (p: SyntheticProject) generateSignatureFiles checker = @@ -648,7 +660,7 @@ type ProjectWorkflowBuilder failwith $"Please place cursor at a valid location via placeCursor first") let file = ctx.Project.Find fileId - let absFileName = ctx.Project.ProjectDir ++ file.FileName + let absFileName = getFilePath ctx.Project file let! results = checker.FindBackgroundReferencesInFile(absFileName, options, symbolUse.Symbol, fastCheck = true) @@ -746,7 +758,7 @@ type ProjectWorkflowBuilder exn $"Compilation failed with exit code {exitCode}" |> raise return ctx } - + /// Execute a set of operations on a given synthetic project. /// The project is saved to disk and type checked at the start. let projectWorkflow project = ProjectWorkflowBuilder project @@ -760,3 +772,8 @@ type SyntheticProject with member this.WorkflowWith checker = ProjectWorkflowBuilder(this, checker = checker) + + /// Saves project to disk and checks it with default options. Returns the FSharpChecker that was created + member this.SaveAndCheck() = + this.Workflow.Yield() |> Async.RunSynchronously |> ignore + this.Workflow.Checker \ No newline at end of file diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index 7c45d65bbce..4c860f8a256 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -25,6 +25,7 @@ + diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs new file mode 100644 index 00000000000..feb41cd8342 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs @@ -0,0 +1,105 @@ +module FSharp.Editor.Tests.FindReferencesTests + +open System.Threading.Tasks +open System.Threading +open System.IO +open System.Collections.Concurrent + +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.FindUsages +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.FindUsages +open Microsoft.VisualStudio.FSharp.Editor + +open Xunit + +open FSharp.Test.ProjectGeneration +open FSharp.Editor.Tests.Helpers + + +let getPositionOf (subString: string) (filePath) = + filePath + |> File.ReadAllText + |> fun source -> source.IndexOf subString + + +module FindReferences = + + let project = SyntheticProject.Create( + { sourceFile "First" [] with + SignatureFile = AutoGenerated + ExtraSource = "let someFunc funcParam = funcParam * 1\n" + + "let sharedFunc funcParam = funcParam * 2\n" }, + { sourceFile "Second" [] with + ExtraSource = "let someFunc funcParam = funcParam * 1" }, + { sourceFile "Third" ["First"] with + ExtraSource = "let someFunc x = ModuleFirst.sharedFunc x + 10" } + ) + + let solution, checker = RoslynTestHelpers.CreateSolution project + + let findUsagesService = FSharpFindUsagesService() :> IFSharpFindUsagesService + + let getContext () = + let foundDefinitions = ConcurrentBag() + let foundReferences = ConcurrentBag() + let context = + { new IFSharpFindUsagesContext with + + member _.OnDefinitionFoundAsync (definition: FSharpDefinitionItem) = + foundDefinitions.Add definition + Task.CompletedTask + + member _.OnReferenceFoundAsync (reference: FSharpSourceReferenceItem) = + foundReferences.Add reference + Task.CompletedTask + + member _.ReportMessageAsync _ = Task.CompletedTask + member _.ReportProgressAsync (_,_) = Task.CompletedTask + member _.SetSearchTitleAsync _ = Task.CompletedTask + member _.CancellationToken = CancellationToken.None + } + context, foundDefinitions, foundReferences + + [] + let ``Find references to a document-local symbol`` () = + + let context, foundDefinitions, foundReferences = getContext() + + let documentPath = project.GetFilePath "Second" + + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + + findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() + + // We cannot easily inspect what exactly was found here, but that should be verified + // in FSharp.Compiler.ComponentTests.FSharpChecker.FindReferences + if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" + if foundReferences.Count <> 1 then failwith $"Expected 1 reference but found {foundReferences.Count}" + + [] + let ``Find references to an implementation + signature symbol`` () = + + let context, foundDefinitions, foundReferences = getContext() + + let documentPath = project.GetFilePath "First" + + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + + findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() + + if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" + if foundReferences.Count <> 2 // One in signature file, one in function body + then failwith $"Expected 2 references but found {foundReferences.Count}" + + [] + let ``Find references to a symbol in project`` () = + let context, foundDefinitions, foundReferences = getContext() + + let documentPath = project.GetFilePath "First" + + let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + + findUsagesService.FindReferencesAsync(document, getPositionOf "sharedFunc" documentPath, context).Wait() + + if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" + if foundReferences.Count <> 2 // One in signature file, one in Third file + then failwith $"Expected 2 references but found {foundReferences.Count}" diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs index 9efa3ecc055..981b54cd17b 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -16,6 +16,8 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis.Host.Mef open FSharp.Compiler.CodeAnalysis open System.Threading +open FSharp.Test.ProjectGeneration +open Microsoft.CodeAnalysis [] module MefHelpers = @@ -289,3 +291,36 @@ type RoslynTestHelpers private () = let project = solution.Projects |> Seq.exactlyOne let document = project.Documents |> Seq.exactlyOne document + + static member CreateSolution (syntheticProject: SyntheticProject) = + + let checker = syntheticProject.SaveAndCheck() + + assert (syntheticProject.DependsOn = []) // multi-project not supported yet + + let projId = ProjectId.CreateNewId() + + let docInfos = + [ for project, file in syntheticProject.GetAllFiles() do + let filePath = getFilePath project file + RoslynTestHelpers.CreateDocumentInfo projId filePath (File.ReadAllText filePath) + if file.HasSignatureFile then + let sigFilePath = getSignatureFilePath project file + RoslynTestHelpers.CreateDocumentInfo projId sigFilePath (File.ReadAllText sigFilePath) ] + + let projInfo = RoslynTestHelpers.CreateProjectInfo projId syntheticProject.ProjectFileName docInfos + + let options = syntheticProject.GetProjectOptions checker + + let metadataReferences = + options.OtherOptions + |> Seq.filter (fun x -> x.StartsWith("-r:")) + |> Seq.map (fun x -> x.Substring(3) |> MetadataReference.CreateFromFile :> MetadataReference) + + let projInfo = projInfo.WithMetadataReferences metadataReferences + + let solution = RoslynTestHelpers.CreateSolution [projInfo] + + options |> RoslynTestHelpers.SetProjectOptions projId solution + + solution, checker \ No newline at end of file From 59b368e1ce27aa89dcdf6612a6b5b28cdc6d17b8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 28 Feb 2023 13:18:22 +0100 Subject: [PATCH 26/44] fantomas --- .../FindReferencesTests.fs | 96 ++++++++++++------- .../Helpers/RoslynHelpers.fs | 24 +++-- 2 files changed, 75 insertions(+), 45 deletions(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs index feb41cd8342..50c9d7fbf7c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs @@ -14,25 +14,26 @@ open Xunit open FSharp.Test.ProjectGeneration open FSharp.Editor.Tests.Helpers - let getPositionOf (subString: string) (filePath) = - filePath - |> File.ReadAllText - |> fun source -> source.IndexOf subString - + filePath |> File.ReadAllText |> (fun source -> source.IndexOf subString) module FindReferences = - let project = SyntheticProject.Create( - { sourceFile "First" [] with - SignatureFile = AutoGenerated - ExtraSource = "let someFunc funcParam = funcParam * 1\n" + - "let sharedFunc funcParam = funcParam * 2\n" }, - { sourceFile "Second" [] with - ExtraSource = "let someFunc funcParam = funcParam * 1" }, - { sourceFile "Third" ["First"] with - ExtraSource = "let someFunc x = ModuleFirst.sharedFunc x + 10" } - ) + let project = + SyntheticProject.Create( + { sourceFile "First" [] with + SignatureFile = AutoGenerated + ExtraSource = + "let someFunc funcParam = funcParam * 1\n" + + "let sharedFunc funcParam = funcParam * 2\n" + }, + { sourceFile "Second" [] with + ExtraSource = "let someFunc funcParam = funcParam * 1" + }, + { sourceFile "Third" [ "First" ] with + ExtraSource = "let someFunc x = ModuleFirst.sharedFunc x + 10" + } + ) let solution, checker = RoslynTestHelpers.CreateSolution project @@ -41,65 +42,90 @@ module FindReferences = let getContext () = let foundDefinitions = ConcurrentBag() let foundReferences = ConcurrentBag() + let context = { new IFSharpFindUsagesContext with - member _.OnDefinitionFoundAsync (definition: FSharpDefinitionItem) = + member _.OnDefinitionFoundAsync(definition: FSharpDefinitionItem) = foundDefinitions.Add definition Task.CompletedTask - member _.OnReferenceFoundAsync (reference: FSharpSourceReferenceItem) = + member _.OnReferenceFoundAsync(reference: FSharpSourceReferenceItem) = foundReferences.Add reference Task.CompletedTask member _.ReportMessageAsync _ = Task.CompletedTask - member _.ReportProgressAsync (_,_) = Task.CompletedTask + member _.ReportProgressAsync(_, _) = Task.CompletedTask member _.SetSearchTitleAsync _ = Task.CompletedTask member _.CancellationToken = CancellationToken.None } + context, foundDefinitions, foundReferences [] let ``Find references to a document-local symbol`` () = - let context, foundDefinitions, foundReferences = getContext() + let context, foundDefinitions, foundReferences = getContext () let documentPath = project.GetFilePath "Second" - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + let document = + solution.TryGetDocumentFromPath documentPath + |> Option.defaultWith (fun _ -> failwith "Document not found") - findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() + findUsagesService + .FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context) + .Wait() // We cannot easily inspect what exactly was found here, but that should be verified // in FSharp.Compiler.ComponentTests.FSharpChecker.FindReferences - if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 1 then failwith $"Expected 1 reference but found {foundReferences.Count}" + if foundDefinitions.Count <> 1 then + failwith $"Expected 1 definition but found {foundDefinitions.Count}" + + if foundReferences.Count <> 1 then + failwith $"Expected 1 reference but found {foundReferences.Count}" [] let ``Find references to an implementation + signature symbol`` () = - let context, foundDefinitions, foundReferences = getContext() + let context, foundDefinitions, foundReferences = getContext () let documentPath = project.GetFilePath "First" - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + let document = + solution.TryGetDocumentFromPath documentPath + |> Option.defaultWith (fun _ -> failwith "Document not found") - findUsagesService.FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context).Wait() + findUsagesService + .FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context) + .Wait() - if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 2 // One in signature file, one in function body - then failwith $"Expected 2 references but found {foundReferences.Count}" + if foundDefinitions.Count <> 1 then + failwith $"Expected 1 definition but found {foundDefinitions.Count}" + + if + foundReferences.Count <> 2 // One in signature file, one in function body + then + failwith $"Expected 2 references but found {foundReferences.Count}" [] let ``Find references to a symbol in project`` () = - let context, foundDefinitions, foundReferences = getContext() + let context, foundDefinitions, foundReferences = getContext () let documentPath = project.GetFilePath "First" - let document = solution.TryGetDocumentFromPath documentPath |> Option.defaultWith (fun _ -> failwith "Document not found") + let document = + solution.TryGetDocumentFromPath documentPath + |> Option.defaultWith (fun _ -> failwith "Document not found") + + findUsagesService + .FindReferencesAsync(document, getPositionOf "sharedFunc" documentPath, context) + .Wait() - findUsagesService.FindReferencesAsync(document, getPositionOf "sharedFunc" documentPath, context).Wait() + if foundDefinitions.Count <> 1 then + failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 2 // One in signature file, one in Third file - then failwith $"Expected 2 references but found {foundReferences.Count}" + if + foundReferences.Count <> 2 // One in signature file, one in Third file + then + failwith $"Expected 2 references but found {foundReferences.Count}" diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs index 981b54cd17b..9cecb8e8f9f 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -292,7 +292,7 @@ type RoslynTestHelpers private () = let document = project.Documents |> Seq.exactlyOne document - static member CreateSolution (syntheticProject: SyntheticProject) = + static member CreateSolution(syntheticProject: SyntheticProject) = let checker = syntheticProject.SaveAndCheck() @@ -301,14 +301,18 @@ type RoslynTestHelpers private () = let projId = ProjectId.CreateNewId() let docInfos = - [ for project, file in syntheticProject.GetAllFiles() do - let filePath = getFilePath project file - RoslynTestHelpers.CreateDocumentInfo projId filePath (File.ReadAllText filePath) - if file.HasSignatureFile then - let sigFilePath = getSignatureFilePath project file - RoslynTestHelpers.CreateDocumentInfo projId sigFilePath (File.ReadAllText sigFilePath) ] + [ + for project, file in syntheticProject.GetAllFiles() do + let filePath = getFilePath project file + RoslynTestHelpers.CreateDocumentInfo projId filePath (File.ReadAllText filePath) - let projInfo = RoslynTestHelpers.CreateProjectInfo projId syntheticProject.ProjectFileName docInfos + if file.HasSignatureFile then + let sigFilePath = getSignatureFilePath project file + RoslynTestHelpers.CreateDocumentInfo projId sigFilePath (File.ReadAllText sigFilePath) + ] + + let projInfo = + RoslynTestHelpers.CreateProjectInfo projId syntheticProject.ProjectFileName docInfos let options = syntheticProject.GetProjectOptions checker @@ -319,8 +323,8 @@ type RoslynTestHelpers private () = let projInfo = projInfo.WithMetadataReferences metadataReferences - let solution = RoslynTestHelpers.CreateSolution [projInfo] + let solution = RoslynTestHelpers.CreateSolution [ projInfo ] options |> RoslynTestHelpers.SetProjectOptions projId solution - solution, checker \ No newline at end of file + solution, checker From 06f768b26bc5b3d0e64df998000912c6d274e9e9 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 6 Mar 2023 11:41:43 +0100 Subject: [PATCH 27/44] split suspicious test --- .../FSharpChecker/SymbolUse.fs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index 25363c8a24e..d0c998f091b 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -62,11 +62,18 @@ val f: x: 'a -> TFirstV_1<'a> } [] - let ``Function parameter, with signature file`` () = + let ``Function parameter, with signature file, part 1`` () = + SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { + checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> + let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + Assert.False(symbolUse.IsPrivateToFile)) + } + + [] + let ``Function parameter, with signature file, part 2`` () = SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - Assert.False(symbolUse.IsPrivateToFile) Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } From 79883349dfbcf69ece80cb660367609dfb8fbf1b Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 6 Mar 2023 13:18:13 +0100 Subject: [PATCH 28/44] build fix --- eng/Build.ps1 | 2 +- eng/common/tools.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 075d8cdf5ed..4e7ee1c58e7 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -445,7 +445,7 @@ function EnablePreviewSdks() { } $vsId = $vsInfo.instanceId - $vsMajorVersion = $vsInfo.installationVersion.Split('.')[0] + $vsMajorVersion = 17 # $vsInfo.installationVersion.Split('.')[0] $instanceDir = Join-Path ${env:USERPROFILE} "AppData\Local\Microsoft\VisualStudio\$vsMajorVersion.0_$vsId" Create-Directory $instanceDir diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index d4dbae993c5..f7bd94f7445 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -400,7 +400,7 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = $vsInfo = LocateVisualStudio $vsRequirements if ($vsInfo -ne $null) { $vsInstallDir = $vsInfo.installationPath - $vsMajorVersion = $vsInfo.installationVersion.Split('.')[0] + $vsMajorVersion = 17 #$vsInfo.installationVersion.Split('.')[0] InitializeVisualStudioEnvironmentVariables $vsInstallDir $vsMajorVersion } else { From 71c911c4a52cb9f060c37d38b72f0f22f5c88703 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 6 Mar 2023 16:56:23 +0100 Subject: [PATCH 29/44] test diagnostic --- .../FSharpChecker/SymbolUse.fs | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index e9619857bfc..5aa1d262b9c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -6,6 +6,9 @@ open Xunit open FSharp.Test.ProjectGeneration open FSharp.Compiler.Symbols +open FSharp.Compiler.EditorServices +open FSharp.Compiler.NameResolution + module IsPrivateToFile = @@ -79,42 +82,40 @@ val f: x: 'a -> TFirstV_1<'a> Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } -open FSharp.Compiler.EditorServices -open FSharp.Compiler.NameResolution - [] let ``Function parameter, with signature file, part 3`` () = - SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - let couldBeParameter, declarationLocation = - match symbolUse.Symbol with - | :? FSharpParameter as p -> true, Some p.DeclarationLocation - | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation - | _ -> false, None + for attempt in 1 .. 20 do + SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { + checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> + let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") - let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName + let couldBeParameter, declarationLocation = + match symbolUse.Symbol with + | :? FSharpParameter as p -> true, Some p.DeclarationLocation + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation + | _ -> false, None - let argReprInfo = - match symbolUse.Symbol.Item with - | Item.Value v -> v.Deref.ArgReprInfoForDisplay - | _ -> None + let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName - let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) + let argReprInfo = + match symbolUse.Symbol.Item with + | Item.Value v -> v.Deref.ArgReprInfoForDisplay + | _ -> None - let diagnostics = $"couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" + let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) - let result = - couldBeParameter - && (thisIsSignature - || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) - - if not result then - failwith diagnostics) - } + let diagnostics = $"#{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" + let result = + couldBeParameter + && (thisIsSignature + || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) + if not result then + failwith diagnostics) + } + |> ignore // [] This is a bug - https://github.com/dotnet/fsharp/issues/14419 let ``Private function, with signature file`` () = From 04af54c4655eccaa3d1baa6b73d2ec4714d5f972 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 15 Mar 2023 16:55:08 +0100 Subject: [PATCH 30/44] post-merge updates --- .../FSharpChecker/FindReferences.fs | 4 ++-- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 11 ++++++++--- .../tests/FSharp.Editor.Tests/FindReferencesTests.fs | 6 ++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 6b3025eeb44..883280ad93c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -306,7 +306,7 @@ let x = MyType() """ SyntheticProject.Create( { sourceFile "Program" [] with - SignatureFile = Custom "module Moo" + SignatureFile = Custom "" Source = source } ).Workflow { placeCursor "Program" "MyType" @@ -335,7 +335,7 @@ let x = MyType() """ let project = SyntheticProject.Create( { sourceFile "Program" [] with - SignatureFile = Custom "module Moo" + SignatureFile = Custom "" Source = source } ) ProjectWorkflowBuilder(project, useGetSource = true, useChangeNotifications = true) { diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 772e8448043..578a39aa17d 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -238,6 +238,11 @@ let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = } |> String.concat Environment.NewLine +let renderCustomSignatureFile (project: SyntheticProject) (f: SyntheticSourceFile) = + match f.SignatureFile with + | Custom signature -> $"{renderNamespaceModule project f}\n{signature}" + | _ -> failwith $"File {f.FileName} does not have a custom signature file." + let private renderFsProj (p: SyntheticProject) = seq { """ @@ -422,8 +427,8 @@ module ProjectOperations = let! results = checkFile file.Id project checker let signature = getSignature results writeFileIfChanged signatureFileName signature - | Custom signature -> - let signatureContent = $"{renderNamespaceModule p file}\n{signature}" + | Custom _ -> + let signatureContent = renderCustomSignatureFile p file writeFileIfChanged signatureFileName signatureContent | _ -> () @@ -541,7 +546,7 @@ type ProjectWorkflowBuilder let source = latestProject.FindByPath implFilePath match source.SignatureFile with | No -> failwith $"{implFilePath} does not have a signature file" - | Custom s -> s + | Custom _ -> renderCustomSignatureFile latestProject source | AutoGenerated -> failwith "AutoGenerated signatures not yet supported for getSource workflow" else filePath diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs index 50c9d7fbf7c..076de4fc3d8 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs @@ -103,8 +103,7 @@ module FindReferences = if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if - foundReferences.Count <> 2 // One in signature file, one in function body + if foundReferences.Count <> 2 // One in signature file, one in function body then failwith $"Expected 2 references but found {foundReferences.Count}" @@ -125,7 +124,6 @@ module FindReferences = if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if - foundReferences.Count <> 2 // One in signature file, one in Third file + if foundReferences.Count <> 2 // One in signature file, one in Third file then failwith $"Expected 2 references but found {foundReferences.Count}" From 7e6ae9632cd84e0551429f5940d041cc93b1f5b5 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 15 Mar 2023 17:16:58 +0100 Subject: [PATCH 31/44] ok fantomas... --- .../tests/FSharp.Editor.Tests/FindReferencesTests.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs index 076de4fc3d8..50c9d7fbf7c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs @@ -103,7 +103,8 @@ module FindReferences = if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 2 // One in signature file, one in function body + if + foundReferences.Count <> 2 // One in signature file, one in function body then failwith $"Expected 2 references but found {foundReferences.Count}" @@ -124,6 +125,7 @@ module FindReferences = if foundDefinitions.Count <> 1 then failwith $"Expected 1 definition but found {foundDefinitions.Count}" - if foundReferences.Count <> 2 // One in signature file, one in Third file + if + foundReferences.Count <> 2 // One in signature file, one in Third file then failwith $"Expected 2 references but found {foundReferences.Count}" From 5d06f7c613cd0ae3be716332f89210f7a2f6c831 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 16 Mar 2023 15:21:05 +0100 Subject: [PATCH 32/44] Skip flaky tests --- .../FSharpChecker/SymbolUse.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index 5aa1d262b9c..dba06740d42 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -66,7 +66,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } - [] + [] let ``Function parameter, with signature file, part 1`` () = SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> @@ -74,7 +74,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.False(symbolUse.IsPrivateToFile)) } - [] + [] let ``Function parameter, with signature file, part 2`` () = SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> @@ -82,7 +82,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } - [] + [] let ``Function parameter, with signature file, part 3`` () = for attempt in 1 .. 20 do From c23ecc0ae956cc459175891b88a3f584241c6b87 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 21 Mar 2023 13:03:03 +0100 Subject: [PATCH 33/44] experiment --- src/Compiler/Checking/CheckExpressions.fs | 22 +++++++++++-- .../FSharpChecker/FindReferences.fs | 31 ++++++++++++++----- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 5a9316020bf..56351207dae 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -44,6 +44,8 @@ open FSharp.Compiler.TypeRelations #if !NO_TYPEPROVIDERS open FSharp.Compiler.TypeProviders +open System.Collections.Concurrent + #endif //------------------------------------------------------------------------- @@ -912,6 +914,8 @@ let AdjustValSynInfoInSignature g ty (SynValInfo(argsData, retData) as sigMD) = | _ -> sigMD +let argDataCache = new ConcurrentDictionary<(string * range), ArgReprInfo>() + let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) = // Synthesize an artificial "OptionalArgument" attribute for the parameter let optAttrs = @@ -921,7 +925,7 @@ let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, is Target=None AppliesToGetterAndSetter=false Range=m} : SynAttribute) ] - else + else [] if isArg && not (isNil attrs) && Option.isNone nm then @@ -932,7 +936,21 @@ let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, is // Call the attribute checking function let attribs = tcAttributes (optAttrs@attrs) - ({ Attribs = attribs; Name = nm; OtherRange = None } : ArgReprInfo) + + let key = nm |> Option.map (fun id -> (id.idText, id.idRange)) + + let argInfo = + key + |> Option.map argDataCache.TryGetValue + |> Option.bind (fun (found, info) -> if found then Some info else None) + |> Option.defaultValue ({ Attribs = attribs; Name = nm; OtherRange = None }: ArgReprInfo) + + match key with + | Some k -> argDataCache.[k] <- argInfo + | None -> () + + argInfo + //({ Attribs = attribs; Name = nm; OtherRange = None } : ArgReprInfo) /// Members have an arity inferred from their syntax. This "valSynData" is not quite the same as the arities /// used in the middle and backends of the compiler ("valReprInfo"). diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 883280ad93c..530cb3c322b 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -155,31 +155,46 @@ let foo x = x ++ 4""" }) module Parameters = [] - let ``We find function parameter in signature file`` () = + let ``We find function parameter in impl file`` () = SyntheticProject.Create( - { sourceFile "Source" [] with SignatureFile = AutoGenerated }) + sourceFile "Source" [] ) .Workflow { placeCursor "Source" 3 7 "let f x =" ["x"] findAllReferences (expectToFind [ - "FileSource.fsi", 6, 7, 8 + //"FileSource.fsi", 6, 7, 8 "FileSource.fs", 3, 6, 7 "FileSource.fs", 4, 12, 13 ]) } + [] + let ``We find function parameter in signature file`` () = + let source = """let f param = param + 1""" + let signature = """val f: param:int -> int""" + SyntheticProject.Create( + { sourceFile "Source" [] with Source = source; SignatureFile = Custom signature }) + .Workflow { + placeCursor "Source" "param" + findAllReferences (expectToFind [ + "FileSource.fsi", 2, 7, 12 + "FileSource.fs", 2, 6, 11 + "FileSource.fs", 2, 14, 19 + ]) + } + /// This is a bug: https://github.com/dotnet/fsharp/issues/14753 [] let ``We DON'T find method parameter in signature file`` () = SyntheticProject.Create( { sourceFile "Source" [] with - ExtraSource = "type MyClass() = member this.Method(methodParam) = methodParam + 1" + Source = "type MyClass() = member this.Method(methodParam) = methodParam + 1" SignatureFile = AutoGenerated }) .Workflow { - placeCursor "Source" 6 47 "type MyClass() = member this.Method(methodParam) = methodParam + 1" ["methodParam"] + placeCursor "Source" "methodParam" findAllReferences (expectToFind [ - // "FileSource.fsi", 14, 17, 28 <-- this should also be found - "FileSource.fs", 6, 36, 47 - "FileSource.fs", 6, 51, 62 + "FileSource.fsi", 8, 17, 28 // <-- this should also be found + "FileSource.fs", 2, 36, 47 + "FileSource.fs", 2, 51, 62 ]) } From e33dba453a6b200cadfd0fbb80c7cf63d3fa268d Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 21 Mar 2023 13:53:28 +0100 Subject: [PATCH 34/44] argInfo cache in TcEnv --- src/Compiler/Checking/CheckBasics.fs | 2 ++ src/Compiler/Checking/CheckBasics.fsi | 5 ++++ src/Compiler/Checking/CheckDeclarations.fs | 9 +++--- src/Compiler/Checking/CheckExpressions.fs | 29 ++++++++++--------- src/Compiler/Checking/CheckExpressions.fsi | 1 + .../Checking/CheckIncrementalClasses.fs | 4 +-- .../FSharpChecker/FindReferences.fs | 18 ++---------- .../FSharpChecker/SymbolUse.fs | 6 ++-- 8 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/Compiler/Checking/CheckBasics.fs b/src/Compiler/Checking/CheckBasics.fs index 6df98110001..6df7eebe889 100644 --- a/src/Compiler/Checking/CheckBasics.fs +++ b/src/Compiler/Checking/CheckBasics.fs @@ -242,6 +242,8 @@ type TcEnv = // Do we lay down an implicit debug point? eIsControlFlow: bool + + eArgInfoCache: Dictionary } member tenv.DisplayEnv = tenv.eNameResEnv.DisplayEnv diff --git a/src/Compiler/Checking/CheckBasics.fsi b/src/Compiler/Checking/CheckBasics.fsi index 6081eab8ef6..924b8380561 100644 --- a/src/Compiler/Checking/CheckBasics.fsi +++ b/src/Compiler/Checking/CheckBasics.fsi @@ -127,6 +127,11 @@ type TcEnv = eLambdaArgInfos: ArgReprInfo list list eIsControlFlow: bool + + /// A cache for ArgReprInfos which get created multiple times for the same values + /// Since they need to be later mutated with updates from signature files this should make sure + /// we're always dealing with the same instance and the updates don't get lost + eArgInfoCache: Dictionary } member DisplayEnv: DisplayEnv diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 7ee34cb5cd8..a02cadbfa41 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -531,7 +531,7 @@ module TcRecdUnionAndEnumDeclarations = | SynUnionCaseKind.FullType (ty, arity) -> let tyR, _ = TcTypeAndRecover cenv NoNewTypars CheckCxs ItemOccurence.UseInType WarnOnIWSAM.Yes env tpenv ty - let curriedArgTys, recordTy = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m + let curriedArgTys, recordTy = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo env m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m if curriedArgTys.Length > 1 then errorR(Error(FSComp.SR.tcIllegalFormForExplicitTypeDeclaration(), m)) @@ -2454,7 +2454,7 @@ module EstablishTypeDefinitionCores = | SynUnionCaseKind.FullType (ty, arity) -> let tyR, _ = TcTypeAndRecover cenv NoNewTypars NoCheckCxs ItemOccurence.UseInType WarnOnIWSAM.Yes env tpenv ty - let curriedArgTys, _ = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m + let curriedArgTys, _ = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo env m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m if curriedArgTys.Length > 1 then errorR(Error(FSComp.SR.tcIllegalFormForExplicitTypeDeclaration(), m)) @@ -3455,7 +3455,7 @@ module EstablishTypeDefinitionCores = noFieldsCheck userFields primaryConstructorInDelegateCheck(implicitCtorSynPats) let tyR, _ = TcTypeAndRecover cenv NoNewTypars CheckCxs ItemOccurence.UseInType WarnOnIWSAM.Yes envinner tpenv ty - let _, _, curriedArgInfos, returnTy, _ = GetValReprTypeInCompiledForm g (arity |> TranslateSynValInfo m (TcAttributes cenv envinner) |> TranslatePartialValReprInfo []) 0 tyR m + let _, _, curriedArgInfos, returnTy, _ = GetValReprTypeInCompiledForm g (arity |> TranslateSynValInfo envinner m (TcAttributes cenv envinner) |> TranslatePartialValReprInfo []) 0 tyR m if curriedArgInfos.Length < 1 then error(Error(FSComp.SR.tcInvalidDelegateSpecification(), m)) if curriedArgInfos.Length > 1 then error(Error(FSComp.SR.tcDelegatesCannotBeCurried(), m)) let ttps = thisTyconRef.Typars m @@ -5224,7 +5224,8 @@ let emptyTcEnv g = eCtorInfo = None eCallerMemberName = None eLambdaArgInfos = [] - eIsControlFlow = false } + eIsControlFlow = false + eArgInfoCache = Dictionary<_,_>() } let CreateInitialTcEnv(g, amap, scopem, assemblyName, ccus) = (emptyTcEnv g, ccus) ||> List.collectFold (fun env (ccu, autoOpens, internalsVisible) -> diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 56351207dae..b92e109dcbb 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -914,9 +914,8 @@ let AdjustValSynInfoInSignature g ty (SynValInfo(argsData, retData) as sigMD) = | _ -> sigMD -let argDataCache = new ConcurrentDictionary<(string * range), ArgReprInfo>() -let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) = +let TranslateTopArgSynInfo (env: TcEnv) isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) = // Synthesize an artificial "OptionalArgument" attribute for the parameter let optAttrs = if isOpt then @@ -937,20 +936,22 @@ let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, is // Call the attribute checking function let attribs = tcAttributes (optAttrs@attrs) - let key = nm |> Option.map (fun id -> (id.idText, id.idRange)) + let key = nm |> Option.map (fun id -> id.idText, id.idRange) let argInfo = key - |> Option.map argDataCache.TryGetValue + |> Option.map env.eArgInfoCache.TryGetValue |> Option.bind (fun (found, info) -> if found then Some info else None) |> Option.defaultValue ({ Attribs = attribs; Name = nm; OtherRange = None }: ArgReprInfo) match key with - | Some k -> argDataCache.[k] <- argInfo + | Some k -> env.eArgInfoCache.[k] <- argInfo | None -> () + // Set freshly computed attribs in case they are different in the cache + argInfo.Attribs <- attribs + argInfo - //({ Attribs = attribs; Name = nm; OtherRange = None } : ArgReprInfo) /// Members have an arity inferred from their syntax. This "valSynData" is not quite the same as the arities /// used in the middle and backends of the compiler ("valReprInfo"). @@ -958,9 +959,9 @@ let TranslateTopArgSynInfo isArg m tcAttributes (SynArgInfo(Attributes attrs, is /// Hence remove all "zeros" from arity and replace them with 1 here. /// Note we currently use the compiled form for choosing unique names, to distinguish overloads because this must match up /// between signature and implementation, and the signature just has "unit". -let TranslateSynValInfo m tcAttributes (SynValInfo(argsData, retData)) = - PrelimValReprInfo (argsData |> List.mapSquared (TranslateTopArgSynInfo true m (tcAttributes AttributeTargets.Parameter)), - retData |> TranslateTopArgSynInfo false m (tcAttributes AttributeTargets.ReturnValue)) +let TranslateSynValInfo env m tcAttributes (SynValInfo(argsData, retData)) = + PrelimValReprInfo (argsData |> List.mapSquared (TranslateTopArgSynInfo env true m (tcAttributes AttributeTargets.Parameter)), + retData |> TranslateTopArgSynInfo env false m (tcAttributes AttributeTargets.ReturnValue)) let TranslatePartialValReprInfo tps (PrelimValReprInfo (argsData, retData)) = ValReprInfo(ValReprInfo.InferTyparInfo tps, argsData, retData) @@ -4170,7 +4171,7 @@ and TcValSpec (cenv: cenv) env declKind newOk containerInfo memFlagsOpt thisTyOp let reallyGenerateOneMember(id: Ident, valSynInfo, tyR, memberFlags) = let PrelimValReprInfo(argsData, _) as prelimValReprInfo = - TranslateSynValInfo id.idRange (TcAttributes cenv env) valSynInfo + TranslateSynValInfo env id.idRange (TcAttributes cenv env) valSynInfo // Fold in the optional argument information @@ -4226,7 +4227,7 @@ and TcValSpec (cenv: cenv) env declKind newOk containerInfo memFlagsOpt thisTyOp yield! generateOneMember({memberFlags with MemberKind=SynMemberKind.PropertySet}) ], tpenv | _ -> let valSynInfo = AdjustValSynInfoInSignature g declaredTy valSynInfo - let prelimValReprInfo = TranslateSynValInfo id.idRange (TcAttributes cenv env) valSynInfo + let prelimValReprInfo = TranslateSynValInfo env id.idRange (TcAttributes cenv env) valSynInfo [ ValSpecResult(altActualParent, None, id, enclosingDeclaredTypars, declaredTypars, declaredTy, prelimValReprInfo, declKind) ], tpenv //------------------------------------------------------------------------- @@ -6840,7 +6841,7 @@ and ComputeObjectExprOverrides (cenv: cenv) (env: TcEnv) tpenv impls = // Convert the syntactic info to actual info let overrides = (overrides, bindNameAndSynInfoPairs) ||> List.map2 (fun (id: Ident, memberFlags, ty, bindingAttribs, bindingBody) (_, valSynData) -> - let partialValInfo = TranslateSynValInfo id.idRange (TcAttributes cenv env) valSynData + let partialValInfo = TranslateSynValInfo env id.idRange (TcAttributes cenv env) valSynData let tps, _ = tryDestForallTy g ty let valInfo = TranslatePartialValReprInfo tps partialValInfo DispatchSlotChecking.GetObjectExprOverrideInfo g cenv.amap (implTy, id, memberFlags, ty, valInfo, bindingAttribs, bindingBody)) @@ -10417,7 +10418,7 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt // Use the syntactic arity if we're defining a function let (SynValData(_, valSynInfo, _)) = valSynData - let prelimValReprInfo = TranslateSynValInfo mBinding (TcAttributes cenv env) valSynInfo + let prelimValReprInfo = TranslateSynValInfo env mBinding (TcAttributes cenv env) valSynInfo // Check the pattern of the l.h.s. of the binding let tcPatPhase2, (TcPatLinearEnv (tpenv, nameToPrelimValSchemeMap, _)) = @@ -11494,7 +11495,7 @@ and AnalyzeAndMakeAndPublishRecursiveValue // NOTE: The type scheme here is normally not 'complete'!!!! The type is more or less just a type variable at this point. // NOTE: top arity, type and typars get fixed-up after inference let prelimTyscheme = GeneralizedType(enclosingDeclaredTypars@declaredTypars, ty) - let prelimValReprInfo = TranslateSynValInfo mBinding (TcAttributes cenv envinner) valSynInfo + let prelimValReprInfo = TranslateSynValInfo envinner mBinding (TcAttributes cenv envinner) valSynInfo let valReprInfo, valReprInfoForDisplay = UseSyntacticValReprInfo declKind prelimTyscheme prelimValReprInfo let hasDeclaredTypars = not (List.isEmpty declaredTypars) let prelimValScheme = ValScheme(bindingId, prelimTyscheme, valReprInfo, valReprInfoForDisplay, memberInfoOpt, false, inlineFlag, NormalVal, vis, false, false, false, hasDeclaredTypars) diff --git a/src/Compiler/Checking/CheckExpressions.fsi b/src/Compiler/Checking/CheckExpressions.fsi index f3e9ad559d4..370247394da 100644 --- a/src/Compiler/Checking/CheckExpressions.fsi +++ b/src/Compiler/Checking/CheckExpressions.fsi @@ -852,6 +852,7 @@ val TcValSpec: /// giving the names and attributes relevant to arguments and return, but before type /// parameters have been fully inferred via generalization. val TranslateSynValInfo: + env: TcEnv -> range -> tcAttributes: (AttributeTargets -> SynAttribute list -> Attrib list) -> synValInfo: SynValInfo -> diff --git a/src/Compiler/Checking/CheckIncrementalClasses.fs b/src/Compiler/Checking/CheckIncrementalClasses.fs index 7ace6fe83b3..353149db7b7 100644 --- a/src/Compiler/Checking/CheckIncrementalClasses.fs +++ b/src/Compiler/Checking/CheckIncrementalClasses.fs @@ -130,7 +130,7 @@ let TcImplicitCtorLhs_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, attr CheckForNonAbstractInterface g ModuleOrMemberBinding tcref memberFlags false id.idRange let memberInfo = MakeMemberDataAndMangledNameForMemberVal(g, tcref, false, attribs, [], memberFlags, valSynData, id, false) - let prelimValReprInfo = TranslateSynValInfo m (TcAttributes cenv env) valSynData + let prelimValReprInfo = TranslateSynValInfo env m (TcAttributes cenv env) valSynData let prelimTyschemeG = GeneralizedType(copyOfTyconTypars, ctorTy) let isComplete = ComputeIsComplete copyOfTyconTypars [] ctorTy let varReprInfo = InferGenericArityFromTyScheme prelimTyschemeG prelimValReprInfo @@ -154,7 +154,7 @@ let TcImplicitCtorLhs_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, attr let id = ident ("cctor", m) CheckForNonAbstractInterface g ModuleOrMemberBinding tcref ClassCtorMemberFlags false id.idRange let memberInfo = MakeMemberDataAndMangledNameForMemberVal(g, tcref, false, [], [], ClassCtorMemberFlags, valSynData, id, false) - let prelimValReprInfo = TranslateSynValInfo m (TcAttributes cenv env) valSynData + let prelimValReprInfo = TranslateSynValInfo env m (TcAttributes cenv env) valSynData let prelimTyschemeG = GeneralizedType(copyOfTyconTypars, cctorTy) let valReprInfo = InferGenericArityFromTyScheme prelimTyschemeG prelimValReprInfo let cctorValScheme = ValScheme(id, prelimTyschemeG, Some valReprInfo, None, Some memberInfo, false, ValInline.Never, NormalVal, Some (SynAccess.Private Range.Zero), false, true, false, false) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index 530cb3c322b..a357921333c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -154,19 +154,6 @@ let foo x = x ++ 4""" }) module Parameters = - [] - let ``We find function parameter in impl file`` () = - SyntheticProject.Create( - sourceFile "Source" [] ) - .Workflow { - placeCursor "Source" 3 7 "let f x =" ["x"] - findAllReferences (expectToFind [ - //"FileSource.fsi", 6, 7, 8 - "FileSource.fs", 3, 6, 7 - "FileSource.fs", 4, 12, 13 - ]) - } - [] let ``We find function parameter in signature file`` () = let source = """let f param = param + 1""" @@ -182,9 +169,8 @@ module Parameters = ]) } - /// This is a bug: https://github.com/dotnet/fsharp/issues/14753 [] - let ``We DON'T find method parameter in signature file`` () = + let ``We find method parameter in signature file`` () = SyntheticProject.Create( { sourceFile "Source" [] with Source = "type MyClass() = member this.Method(methodParam) = methodParam + 1" @@ -192,7 +178,7 @@ module Parameters = .Workflow { placeCursor "Source" "methodParam" findAllReferences (expectToFind [ - "FileSource.fsi", 8, 17, 28 // <-- this should also be found + "FileSource.fsi", 8, 17, 28 "FileSource.fs", 2, 36, 47 "FileSource.fs", 2, 51, 62 ]) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index dba06740d42..5aa1d262b9c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -66,7 +66,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } - [] + [] let ``Function parameter, with signature file, part 1`` () = SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> @@ -74,7 +74,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.False(symbolUse.IsPrivateToFile)) } - [] + [] let ``Function parameter, with signature file, part 2`` () = SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> @@ -82,7 +82,7 @@ val f: x: 'a -> TFirstV_1<'a> Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } - [] + [] let ``Function parameter, with signature file, part 3`` () = for attempt in 1 .. 20 do From 77d453135c9df281cd471380c69c32193136a431 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 12 Apr 2023 17:42:55 +0200 Subject: [PATCH 35/44] Moved argInfoCache to cenv --- src/Compiler/Checking/CheckBasics.fs | 5 +++-- src/Compiler/Checking/CheckBasics.fsi | 10 ++++----- src/Compiler/Checking/CheckDeclarations.fs | 9 ++++---- src/Compiler/Checking/CheckExpressions.fs | 22 +++++++++---------- src/Compiler/Checking/CheckExpressions.fsi | 2 +- .../Checking/CheckIncrementalClasses.fs | 4 ++-- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Compiler/Checking/CheckBasics.fs b/src/Compiler/Checking/CheckBasics.fs index 6df7eebe889..30f45c25028 100644 --- a/src/Compiler/Checking/CheckBasics.fs +++ b/src/Compiler/Checking/CheckBasics.fs @@ -242,8 +242,6 @@ type TcEnv = // Do we lay down an implicit debug point? eIsControlFlow: bool - - eArgInfoCache: Dictionary } member tenv.DisplayEnv = tenv.eNameResEnv.DisplayEnv @@ -315,6 +313,8 @@ type TcFileState = diagnosticOptions: FSharpDiagnosticOptions + argInfoCache: Dictionary<(string * range), ArgReprInfo> + // forward call TcPat: WarnOnUpperFlag -> TcFileState -> TcEnv -> PrelimValReprInfo option -> TcPatValFlags -> TcPatLinearEnv -> TType -> SynPat -> (TcPatPhase2Input -> Pattern) * TcPatLinearEnv @@ -364,6 +364,7 @@ type TcFileState = conditionalDefines = conditionalDefines isInternalTestSpanStackReferring = isInternalTestSpanStackReferring diagnosticOptions = diagnosticOptions + argInfoCache = Dictionary() TcPat = tcPat TcSimplePats = tcSimplePats TcSequenceExpressionEntry = tcSequenceExpressionEntry diff --git a/src/Compiler/Checking/CheckBasics.fsi b/src/Compiler/Checking/CheckBasics.fsi index 924b8380561..e30b7e4daf5 100644 --- a/src/Compiler/Checking/CheckBasics.fsi +++ b/src/Compiler/Checking/CheckBasics.fsi @@ -127,11 +127,6 @@ type TcEnv = eLambdaArgInfos: ArgReprInfo list list eIsControlFlow: bool - - /// A cache for ArgReprInfos which get created multiple times for the same values - /// Since they need to be later mutated with updates from signature files this should make sure - /// we're always dealing with the same instance and the updates don't get lost - eArgInfoCache: Dictionary } member DisplayEnv: DisplayEnv @@ -268,6 +263,11 @@ type TcFileState = diagnosticOptions: FSharpDiagnosticOptions + /// A cache for ArgReprInfos which get created multiple times for the same values + /// Since they need to be later mutated with updates from signature files this should make sure + /// we're always dealing with the same instance and the updates don't get lost + argInfoCache: Dictionary<(string * range), ArgReprInfo> + // forward call TcPat: WarnOnUpperFlag -> TcFileState diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index ddbc6ee374b..d00a177b181 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -531,7 +531,7 @@ module TcRecdUnionAndEnumDeclarations = | SynUnionCaseKind.FullType (ty, arity) -> let tyR, _ = TcTypeAndRecover cenv NoNewTypars CheckCxs ItemOccurence.UseInType WarnOnIWSAM.Yes env tpenv ty - let curriedArgTys, recordTy = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo env m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m + let curriedArgTys, recordTy = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo cenv m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m if curriedArgTys.Length > 1 then errorR(Error(FSComp.SR.tcIllegalFormForExplicitTypeDeclaration(), m)) @@ -2454,7 +2454,7 @@ module EstablishTypeDefinitionCores = | SynUnionCaseKind.FullType (ty, arity) -> let tyR, _ = TcTypeAndRecover cenv NoNewTypars NoCheckCxs ItemOccurence.UseInType WarnOnIWSAM.Yes env tpenv ty - let curriedArgTys, _ = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo env m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m + let curriedArgTys, _ = GetTopTauTypeInFSharpForm g (arity |> TranslateSynValInfo cenv m (TcAttributes cenv env) |> TranslatePartialValReprInfo []).ArgInfos tyR m if curriedArgTys.Length > 1 then errorR(Error(FSComp.SR.tcIllegalFormForExplicitTypeDeclaration(), m)) @@ -3455,7 +3455,7 @@ module EstablishTypeDefinitionCores = noFieldsCheck userFields primaryConstructorInDelegateCheck(implicitCtorSynPats) let tyR, _ = TcTypeAndRecover cenv NoNewTypars CheckCxs ItemOccurence.UseInType WarnOnIWSAM.Yes envinner tpenv ty - let _, _, curriedArgInfos, returnTy, _ = GetValReprTypeInCompiledForm g (arity |> TranslateSynValInfo envinner m (TcAttributes cenv envinner) |> TranslatePartialValReprInfo []) 0 tyR m + let _, _, curriedArgInfos, returnTy, _ = GetValReprTypeInCompiledForm g (arity |> TranslateSynValInfo cenv m (TcAttributes cenv envinner) |> TranslatePartialValReprInfo []) 0 tyR m if curriedArgInfos.Length < 1 then error(Error(FSComp.SR.tcInvalidDelegateSpecification(), m)) if curriedArgInfos.Length > 1 then error(Error(FSComp.SR.tcDelegatesCannotBeCurried(), m)) let ttps = thisTyconRef.Typars m @@ -5224,8 +5224,7 @@ let emptyTcEnv g = eCtorInfo = None eCallerMemberName = None eLambdaArgInfos = [] - eIsControlFlow = false - eArgInfoCache = Dictionary<_,_>() } + eIsControlFlow = false } let CreateInitialTcEnv(g, amap, scopem, assemblyName, ccus) = (emptyTcEnv g, ccus) ||> List.collectFold (fun env (ccu, autoOpens, internalsVisible) -> diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 17a3b2d2e86..b7f96e591a5 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -915,7 +915,7 @@ let AdjustValSynInfoInSignature g ty (SynValInfo(argsData, retData) as sigMD) = sigMD -let TranslateTopArgSynInfo (env: TcEnv) isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) = +let TranslateTopArgSynInfo (cenv: cenv) isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) = // Synthesize an artificial "OptionalArgument" attribute for the parameter let optAttrs = if isOpt then @@ -940,12 +940,12 @@ let TranslateTopArgSynInfo (env: TcEnv) isArg m tcAttributes (SynArgInfo(Attribu let argInfo = key - |> Option.map env.eArgInfoCache.TryGetValue + |> Option.map cenv.argInfoCache.TryGetValue |> Option.bind (fun (found, info) -> if found then Some info else None) |> Option.defaultValue ({ Attribs = attribs; Name = nm; OtherRange = None }: ArgReprInfo) match key with - | Some k -> env.eArgInfoCache.[k] <- argInfo + | Some k -> cenv.argInfoCache.[k] <- argInfo | None -> () // Set freshly computed attribs in case they are different in the cache @@ -959,9 +959,9 @@ let TranslateTopArgSynInfo (env: TcEnv) isArg m tcAttributes (SynArgInfo(Attribu /// Hence remove all "zeros" from arity and replace them with 1 here. /// Note we currently use the compiled form for choosing unique names, to distinguish overloads because this must match up /// between signature and implementation, and the signature just has "unit". -let TranslateSynValInfo env m tcAttributes (SynValInfo(argsData, retData)) = - PrelimValReprInfo (argsData |> List.mapSquared (TranslateTopArgSynInfo env true m (tcAttributes AttributeTargets.Parameter)), - retData |> TranslateTopArgSynInfo env false m (tcAttributes AttributeTargets.ReturnValue)) +let TranslateSynValInfo (cenv: cenv) m tcAttributes (SynValInfo(argsData, retData)) = + PrelimValReprInfo (argsData |> List.mapSquared (TranslateTopArgSynInfo cenv true m (tcAttributes AttributeTargets.Parameter)), + retData |> TranslateTopArgSynInfo cenv false m (tcAttributes AttributeTargets.ReturnValue)) let TranslatePartialValReprInfo tps (PrelimValReprInfo (argsData, retData)) = ValReprInfo(ValReprInfo.InferTyparInfo tps, argsData, retData) @@ -4171,7 +4171,7 @@ and TcValSpec (cenv: cenv) env declKind newOk containerInfo memFlagsOpt thisTyOp let reallyGenerateOneMember(id: Ident, valSynInfo, tyR, memberFlags) = let PrelimValReprInfo(argsData, _) as prelimValReprInfo = - TranslateSynValInfo env id.idRange (TcAttributes cenv env) valSynInfo + TranslateSynValInfo cenv id.idRange (TcAttributes cenv env) valSynInfo // Fold in the optional argument information @@ -4227,7 +4227,7 @@ and TcValSpec (cenv: cenv) env declKind newOk containerInfo memFlagsOpt thisTyOp yield! generateOneMember({memberFlags with MemberKind=SynMemberKind.PropertySet}) ], tpenv | _ -> let valSynInfo = AdjustValSynInfoInSignature g declaredTy valSynInfo - let prelimValReprInfo = TranslateSynValInfo env id.idRange (TcAttributes cenv env) valSynInfo + let prelimValReprInfo = TranslateSynValInfo cenv id.idRange (TcAttributes cenv env) valSynInfo [ ValSpecResult(altActualParent, None, id, enclosingDeclaredTypars, declaredTypars, declaredTy, prelimValReprInfo, declKind) ], tpenv //------------------------------------------------------------------------- @@ -6848,7 +6848,7 @@ and ComputeObjectExprOverrides (cenv: cenv) (env: TcEnv) tpenv impls = // Convert the syntactic info to actual info let overrides = (overrides, bindNameAndSynInfoPairs) ||> List.map2 (fun (id: Ident, memberFlags, ty, bindingAttribs, bindingBody) (_, valSynData) -> - let partialValInfo = TranslateSynValInfo env id.idRange (TcAttributes cenv env) valSynData + let partialValInfo = TranslateSynValInfo cenv id.idRange (TcAttributes cenv env) valSynData let tps, _ = tryDestForallTy g ty let valInfo = TranslatePartialValReprInfo tps partialValInfo DispatchSlotChecking.GetObjectExprOverrideInfo g cenv.amap (implTy, id, memberFlags, ty, valInfo, bindingAttribs, bindingBody)) @@ -10425,7 +10425,7 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt // Use the syntactic arity if we're defining a function let (SynValData(_, valSynInfo, _)) = valSynData - let prelimValReprInfo = TranslateSynValInfo env mBinding (TcAttributes cenv env) valSynInfo + let prelimValReprInfo = TranslateSynValInfo cenv mBinding (TcAttributes cenv env) valSynInfo // Check the pattern of the l.h.s. of the binding let tcPatPhase2, (TcPatLinearEnv (tpenv, nameToPrelimValSchemeMap, _)) = @@ -11502,7 +11502,7 @@ and AnalyzeAndMakeAndPublishRecursiveValue // NOTE: The type scheme here is normally not 'complete'!!!! The type is more or less just a type variable at this point. // NOTE: top arity, type and typars get fixed-up after inference let prelimTyscheme = GeneralizedType(enclosingDeclaredTypars@declaredTypars, ty) - let prelimValReprInfo = TranslateSynValInfo envinner mBinding (TcAttributes cenv envinner) valSynInfo + let prelimValReprInfo = TranslateSynValInfo cenv mBinding (TcAttributes cenv envinner) valSynInfo let valReprInfo, valReprInfoForDisplay = UseSyntacticValReprInfo declKind prelimTyscheme prelimValReprInfo let hasDeclaredTypars = not (List.isEmpty declaredTypars) let prelimValScheme = ValScheme(bindingId, prelimTyscheme, valReprInfo, valReprInfoForDisplay, memberInfoOpt, false, inlineFlag, NormalVal, vis, false, false, false, hasDeclaredTypars) diff --git a/src/Compiler/Checking/CheckExpressions.fsi b/src/Compiler/Checking/CheckExpressions.fsi index 370247394da..0d02f07a223 100644 --- a/src/Compiler/Checking/CheckExpressions.fsi +++ b/src/Compiler/Checking/CheckExpressions.fsi @@ -852,7 +852,7 @@ val TcValSpec: /// giving the names and attributes relevant to arguments and return, but before type /// parameters have been fully inferred via generalization. val TranslateSynValInfo: - env: TcEnv -> + cenv: TcFileState -> range -> tcAttributes: (AttributeTargets -> SynAttribute list -> Attrib list) -> synValInfo: SynValInfo -> diff --git a/src/Compiler/Checking/CheckIncrementalClasses.fs b/src/Compiler/Checking/CheckIncrementalClasses.fs index 353149db7b7..9e6570056c1 100644 --- a/src/Compiler/Checking/CheckIncrementalClasses.fs +++ b/src/Compiler/Checking/CheckIncrementalClasses.fs @@ -130,7 +130,7 @@ let TcImplicitCtorLhs_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, attr CheckForNonAbstractInterface g ModuleOrMemberBinding tcref memberFlags false id.idRange let memberInfo = MakeMemberDataAndMangledNameForMemberVal(g, tcref, false, attribs, [], memberFlags, valSynData, id, false) - let prelimValReprInfo = TranslateSynValInfo env m (TcAttributes cenv env) valSynData + let prelimValReprInfo = TranslateSynValInfo cenv m (TcAttributes cenv env) valSynData let prelimTyschemeG = GeneralizedType(copyOfTyconTypars, ctorTy) let isComplete = ComputeIsComplete copyOfTyconTypars [] ctorTy let varReprInfo = InferGenericArityFromTyScheme prelimTyschemeG prelimValReprInfo @@ -154,7 +154,7 @@ let TcImplicitCtorLhs_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, attr let id = ident ("cctor", m) CheckForNonAbstractInterface g ModuleOrMemberBinding tcref ClassCtorMemberFlags false id.idRange let memberInfo = MakeMemberDataAndMangledNameForMemberVal(g, tcref, false, [], [], ClassCtorMemberFlags, valSynData, id, false) - let prelimValReprInfo = TranslateSynValInfo env m (TcAttributes cenv env) valSynData + let prelimValReprInfo = TranslateSynValInfo cenv m (TcAttributes cenv env) valSynData let prelimTyschemeG = GeneralizedType(copyOfTyconTypars, cctorTy) let valReprInfo = InferGenericArityFromTyScheme prelimTyschemeG prelimValReprInfo let cctorValScheme = ValScheme(id, prelimTyschemeG, Some valReprInfo, None, Some memberInfo, false, ValInline.Never, NormalVal, Some (SynAccess.Private Range.Zero), false, true, false, false) From c1937462ac46ba501d279a6f36e4ce8c86511933 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 12 Apr 2023 17:54:58 +0200 Subject: [PATCH 36/44] Removed unused open --- src/Compiler/Checking/CheckExpressions.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index b7f96e591a5..2dd69c2e7ca 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -44,8 +44,6 @@ open FSharp.Compiler.TypeRelations #if !NO_TYPEPROVIDERS open FSharp.Compiler.TypeProviders -open System.Collections.Concurrent - #endif //------------------------------------------------------------------------- From 74d2aa2aac5d61065aac0b75487d2a9ba2829a25 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 13 Apr 2023 12:49:48 +0200 Subject: [PATCH 37/44] make argInfoCache a ConcurrentDictionary --- src/Compiler/Checking/CheckBasics.fs | 6 ++++-- src/Compiler/Checking/CheckBasics.fsi | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/CheckBasics.fs b/src/Compiler/Checking/CheckBasics.fs index 30f45c25028..42e7208e834 100644 --- a/src/Compiler/Checking/CheckBasics.fs +++ b/src/Compiler/Checking/CheckBasics.fs @@ -24,6 +24,8 @@ open FSharp.Compiler.TypedTreeOps #if !NO_TYPEPROVIDERS open FSharp.Compiler.TypeProviders +open System.Collections.Concurrent + #endif #if DEBUG @@ -313,7 +315,7 @@ type TcFileState = diagnosticOptions: FSharpDiagnosticOptions - argInfoCache: Dictionary<(string * range), ArgReprInfo> + argInfoCache: ConcurrentDictionary<(string * range), ArgReprInfo> // forward call TcPat: WarnOnUpperFlag -> TcFileState -> TcEnv -> PrelimValReprInfo option -> TcPatValFlags -> TcPatLinearEnv -> TType -> SynPat -> (TcPatPhase2Input -> Pattern) * TcPatLinearEnv @@ -364,7 +366,7 @@ type TcFileState = conditionalDefines = conditionalDefines isInternalTestSpanStackReferring = isInternalTestSpanStackReferring diagnosticOptions = diagnosticOptions - argInfoCache = Dictionary() + argInfoCache = ConcurrentDictionary() TcPat = tcPat TcSimplePats = tcSimplePats TcSequenceExpressionEntry = tcSequenceExpressionEntry diff --git a/src/Compiler/Checking/CheckBasics.fsi b/src/Compiler/Checking/CheckBasics.fsi index e30b7e4daf5..24c4fe0a42e 100644 --- a/src/Compiler/Checking/CheckBasics.fsi +++ b/src/Compiler/Checking/CheckBasics.fsi @@ -2,6 +2,7 @@ module internal FSharp.Compiler.CheckBasics +open System.Collections.Concurrent open System.Collections.Generic open FSharp.Compiler.Diagnostics open Internal.Utilities.Library @@ -266,7 +267,7 @@ type TcFileState = /// A cache for ArgReprInfos which get created multiple times for the same values /// Since they need to be later mutated with updates from signature files this should make sure /// we're always dealing with the same instance and the updates don't get lost - argInfoCache: Dictionary<(string * range), ArgReprInfo> + argInfoCache: ConcurrentDictionary<(string * range), ArgReprInfo> // forward call TcPat: WarnOnUpperFlag From 72c986e5dd1903644ed25a91cd3e3aecfad16cec Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 13 Apr 2023 12:56:42 +0200 Subject: [PATCH 38/44] fix import --- src/Compiler/Checking/CheckBasics.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compiler/Checking/CheckBasics.fs b/src/Compiler/Checking/CheckBasics.fs index 42e7208e834..bbfa5557b2d 100644 --- a/src/Compiler/Checking/CheckBasics.fs +++ b/src/Compiler/Checking/CheckBasics.fs @@ -2,6 +2,7 @@ module internal FSharp.Compiler.CheckBasics +open System.Collections.Concurrent open System.Collections.Generic open FSharp.Compiler.Diagnostics @@ -24,8 +25,6 @@ open FSharp.Compiler.TypedTreeOps #if !NO_TYPEPROVIDERS open FSharp.Compiler.TypeProviders -open System.Collections.Concurrent - #endif #if DEBUG From 5c93b3fa4e00a4f59cab3939bc508063f1b1833f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Apr 2023 14:14:46 +0200 Subject: [PATCH 39/44] test refactoring, clear cache experiment --- src/Compiler/Checking/CheckExpressions.fs | 5 +- .../FSharpChecker/SymbolUse.fs | 93 +++++++++++++------ .../ProjectGeneration.fs | 35 ++++++- 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 2dd69c2e7ca..367b7d81292 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -939,7 +939,10 @@ let TranslateTopArgSynInfo (cenv: cenv) isArg m tcAttributes (SynArgInfo(Attribu let argInfo = key |> Option.map cenv.argInfoCache.TryGetValue - |> Option.bind (fun (found, info) -> if found then Some info else None) + |> Option.bind (fun (found, info) -> + if found then + Some info + else None) |> Option.defaultValue ({ Attribs = attribs; Name = nm; OtherRange = None }: ArgReprInfo) match key with diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index 96f0787f756..62d4058f1e8 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -12,15 +12,21 @@ open FSharp.Compiler.NameResolution module IsPrivateToFile = + let functionParameter = "param" + let source = $""" + let f x = x + 1 + let f2 {functionParameter} = {functionParameter} + 1 + """ + let testFile = { sourceFile "Test" [] with Source = source } + [] let ``Function definition in signature file`` () = let project = SyntheticProject.Create( - sourceFile "First" [] |> addSignatureFile, - sourceFile "Second" ["First"]) + testFile |> addSignatureFile, + sourceFile "Second" [testFile.Id]) project.Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 6, "let f2 x = x + 1", ["f2"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + checkSymbolUse testFile.Id "f2" (fun symbolUse -> Assert.False(symbolUse.IsPrivateToFile) Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } @@ -28,56 +34,47 @@ module IsPrivateToFile = [] let ``Function definition, no signature file`` () = let project = SyntheticProject.Create( - sourceFile "First" [], - sourceFile "Second" ["First"]) + testFile, + sourceFile "Second" [testFile.Id]) project.Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 6, "let f2 x = x + 1", ["f2"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + checkSymbolUse testFile.Id "f2" (fun symbolUse -> Assert.False(symbolUse.IsPrivateToFile) Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] let ``Function definition not in signature file`` () = - let signature = $""" -type TFirstV_1<'a> = | TFirst of 'a -val f: x: 'a -> TFirstV_1<'a> -// no f2 here -""" + let signature = "val f: x: int -> int" let project = SyntheticProject.Create( - { sourceFile "First" [] with SignatureFile = Custom signature }, - sourceFile "Second" ["First"]) + { testFile with SignatureFile = Custom signature }, + sourceFile "Second" [testFile.Id]) project.Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 6, "let f2 x = x + 1", ["f2"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + checkSymbolUse testFile.Id "f2" (fun symbolUse -> Assert.True(symbolUse.IsPrivateToFile) Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] let ``Function parameter, no signature file`` () = - SyntheticProject.Create(sourceFile "First" []).Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + SyntheticProject.Create(testFile).Workflow { + checkSymbolUse testFile.Id functionParameter (fun symbolUse -> Assert.True(symbolUse.IsPrivateToFile) Assert.False(symbolUse.IsPrivateToFileAndSignatureFile)) } [] let ``Function parameter, with signature file, part 1`` () = - SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + SyntheticProject.Create(testFile |> addSignatureFile).Workflow { + checkSymbolUse testFile.Id functionParameter (fun symbolUse -> Assert.False(symbolUse.IsPrivateToFile)) } [] let ``Function parameter, with signature file, part 2`` () = - SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + SyntheticProject.Create(testFile |> addSignatureFile).Workflow { + checkSymbolUse testFile.Id functionParameter (fun symbolUse -> Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } @@ -85,9 +82,45 @@ val f: x: 'a -> TFirstV_1<'a> let ``Function parameter, with signature file, part 3`` () = for attempt in 1 .. 20 do - SyntheticProject.Create(sourceFile "First" [] |> addSignatureFile).Workflow { - checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) -> - let symbolUse = typeCheckResult.GetSymbolUseAtLocation(5, 8, "let f2 x = x + 1", ["x"]) |> Option.defaultWith (fun () -> failwith "no symbol use found") + SyntheticProject.Create(testFile |> addSignatureFile).Workflow { + checkSymbolUse testFile.Id functionParameter (fun symbolUse -> + + let couldBeParameter, declarationLocation = + match symbolUse.Symbol with + | :? FSharpParameter as p -> true, Some p.DeclarationLocation + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation + | _ -> false, None + + let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName + + let argReprInfo = + match symbolUse.Symbol.Item with + | Item.Value v -> v.Deref.ArgReprInfoForDisplay + | _ -> None + + let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) + + let diagnostics = $"Attempt: #{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" + + let result = + couldBeParameter + && (thisIsSignature + || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) + + if not result then + failwith diagnostics) + } + |> ignore + + [] + let ``Function parameter, with signature file, clear cache`` () = + + for attempt in 1 .. 20 do + SyntheticProject.Create(testFile |> addSignatureFile).Workflow { + + clearCache + + checkSymbolUse testFile.Id functionParameter (fun symbolUse -> let couldBeParameter, declarationLocation = match symbolUse.Symbol with @@ -104,7 +137,7 @@ val f: x: 'a -> TFirstV_1<'a> let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) - let diagnostics = $"#{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" + let diagnostics = $"Attempt: #{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" let result = couldBeParameter diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 578a39aa17d..ab8d2f17403 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -700,19 +700,33 @@ type ProjectWorkflowBuilder return { ctx with Cursor = su } } - /// Find a symbol by finding the first occurrence of the symbol name in the file - [] - member this.PlaceCursor(workflow: Async, fileId, symbolName: string) = + member this.FindSymbolUse(ctx: WorkflowContext, fileId, symbolName: string) = async { - let! ctx = workflow let file = ctx.Project.Find fileId let fileName = ctx.Project.ProjectDir ++ file.FileName let source = renderSourceFile ctx.Project file let options= ctx.Project.GetProjectOptions checker - let! su = getSymbolUse fileName source symbolName options checker + return! getSymbolUse fileName source symbolName options checker + } + + /// Find a symbol by finding the first occurrence of the symbol name in the file + [] + member this.PlaceCursor(workflow: Async, fileId, symbolName: string) = + async { + let! ctx = workflow + let! su = this.FindSymbolUse(ctx, fileId, symbolName) return { ctx with Cursor = Some su } } + [] + member this.CheckSymbolUse(workflow: Async, fileId, symbolName: string, check) = + async { + let! ctx = workflow + let! su = this.FindSymbolUse(ctx, fileId, symbolName) + check su + return ctx + } + /// Find all references within a single file, results are provided to the 'processResults' function [] member this.FindAllReferencesInFile(workflow: Async, fileId: string, processResults) = @@ -776,6 +790,17 @@ type ProjectWorkflowBuilder return ctx } + /// Clear checker caches. + [] + member this.ClearCache(workflow: Async) = + async { + let! ctx = workflow + let options = [for p in ctx.Project.GetAllProjects() -> p.GetProjectOptions checker] + checker.ClearCache(options) + checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + return ctx + } + /// Find all references to a module defined in a given file. /// These should only be found in files that depend on this file. /// From bcbab6eb8fe60657db2719e4719332cbba1e14b3 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Apr 2023 15:54:17 +0200 Subject: [PATCH 40/44] another experiment --- .../FSharpChecker/SymbolUse.fs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index 62d4058f1e8..c2fa219a77c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -149,6 +149,45 @@ module IsPrivateToFile = } |> ignore + [] + let ``Function parameter, with signature file, don't auto-generate signature`` () = + + let signature = $""" + val f: x: int -> int + val f2: {functionParameter}: int -> int + """ + for attempt in 1 .. 20 do + SyntheticProject.Create({ testFile with SignatureFile = Custom signature }).Workflow { + + checkSymbolUse testFile.Id functionParameter (fun symbolUse -> + + let couldBeParameter, declarationLocation = + match symbolUse.Symbol with + | :? FSharpParameter as p -> true, Some p.DeclarationLocation + | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation + | _ -> false, None + + let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName + + let argReprInfo = + match symbolUse.Symbol.Item with + | Item.Value v -> v.Deref.ArgReprInfoForDisplay + | _ -> None + + let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) + + let diagnostics = $"Attempt: #{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" + + let result = + couldBeParameter + && (thisIsSignature + || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) + + if not result then + failwith diagnostics) + } + |> ignore + // [] This is a bug - https://github.com/dotnet/fsharp/issues/14419 let ``Private function, with signature file`` () = SyntheticProject.Create( From 18a5da64a73ba8342f1fb2d7616ed3550429f125 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Apr 2023 16:48:56 +0200 Subject: [PATCH 41/44] test adjustment --- .../FSharpChecker/FindReferences.fs | 3 + .../FSharpChecker/SymbolUse.fs | 91 +++---------------- 2 files changed, 14 insertions(+), 80 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index a802a1f630a..a17d83d7212 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -176,6 +176,9 @@ module Parameters = Source = "type MyClass() = member this.Method(methodParam) = methodParam + 1" SignatureFile = AutoGenerated }) .Workflow { + // Some race condition probably triggered by auto-generating signatures makes this + // flaky in CI compressed metadata builds. Clearing the cache before we start fixes it ¯\_(ツ)_/¯ + clearCache placeCursor "Source" "methodParam" findAllReferences (expectToFind [ "FileSource.fsi", 8, 17, 28 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index c2fa219a77c..a61ef8df232 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -19,10 +19,17 @@ module IsPrivateToFile = """ let testFile = { sourceFile "Test" [] with Source = source } + let signature = $""" + let f: x:int -> int + let f2: {functionParameter}: int -> int + """ + + let testFileWithSignature = { testFile with SignatureFile = Custom signature } + [] let ``Function definition in signature file`` () = let project = SyntheticProject.Create( - testFile |> addSignatureFile, + testFileWithSignature, sourceFile "Second" [testFile.Id]) project.Workflow { @@ -66,14 +73,14 @@ module IsPrivateToFile = [] let ``Function parameter, with signature file, part 1`` () = - SyntheticProject.Create(testFile |> addSignatureFile).Workflow { + SyntheticProject.Create(testFileWithSignature).Workflow { checkSymbolUse testFile.Id functionParameter (fun symbolUse -> Assert.False(symbolUse.IsPrivateToFile)) } [] let ``Function parameter, with signature file, part 2`` () = - SyntheticProject.Create(testFile |> addSignatureFile).Workflow { + SyntheticProject.Create(testFileWithSignature).Workflow { checkSymbolUse testFile.Id functionParameter (fun symbolUse -> Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } @@ -82,83 +89,7 @@ module IsPrivateToFile = let ``Function parameter, with signature file, part 3`` () = for attempt in 1 .. 20 do - SyntheticProject.Create(testFile |> addSignatureFile).Workflow { - checkSymbolUse testFile.Id functionParameter (fun symbolUse -> - - let couldBeParameter, declarationLocation = - match symbolUse.Symbol with - | :? FSharpParameter as p -> true, Some p.DeclarationLocation - | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation - | _ -> false, None - - let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName - - let argReprInfo = - match symbolUse.Symbol.Item with - | Item.Value v -> v.Deref.ArgReprInfoForDisplay - | _ -> None - - let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) - - let diagnostics = $"Attempt: #{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" - - let result = - couldBeParameter - && (thisIsSignature - || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) - - if not result then - failwith diagnostics) - } - |> ignore - - [] - let ``Function parameter, with signature file, clear cache`` () = - - for attempt in 1 .. 20 do - SyntheticProject.Create(testFile |> addSignatureFile).Workflow { - - clearCache - - checkSymbolUse testFile.Id functionParameter (fun symbolUse -> - - let couldBeParameter, declarationLocation = - match symbolUse.Symbol with - | :? FSharpParameter as p -> true, Some p.DeclarationLocation - | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation - | _ -> false, None - - let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName - - let argReprInfo = - match symbolUse.Symbol.Item with - | Item.Value v -> v.Deref.ArgReprInfoForDisplay - | _ -> None - - let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) - - let diagnostics = $"Attempt: #{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" - - let result = - couldBeParameter - && (thisIsSignature - || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) - - if not result then - failwith diagnostics) - } - |> ignore - - [] - let ``Function parameter, with signature file, don't auto-generate signature`` () = - - let signature = $""" - val f: x: int -> int - val f2: {functionParameter}: int -> int - """ - for attempt in 1 .. 20 do - SyntheticProject.Create({ testFile with SignatureFile = Custom signature }).Workflow { - + SyntheticProject.Create(testFileWithSignature).Workflow { checkSymbolUse testFile.Id functionParameter (fun symbolUse -> let couldBeParameter, declarationLocation = From b0b17e3b8c1bdbd05840d8d079808f0ab988d513 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Apr 2023 16:52:41 +0200 Subject: [PATCH 42/44] fix signature --- .../FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index a61ef8df232..9a68336965d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -20,8 +20,8 @@ module IsPrivateToFile = let testFile = { sourceFile "Test" [] with Source = source } let signature = $""" - let f: x:int -> int - let f2: {functionParameter}: int -> int + val f: x:int -> int + val f2: {functionParameter}: int -> int """ let testFileWithSignature = { testFile with SignatureFile = Custom signature } From d4d6d4ad31050353ef33512b1c2b2144a2aa884c Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Apr 2023 17:46:17 +0200 Subject: [PATCH 43/44] fix getSymbolUse --- src/Compiler/Service/FSharpCheckerResults.fs | 6 +----- .../ProjectGeneration.fs | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 9bdac47d924..c0dcd3f2962 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -269,11 +269,7 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti let thisIsSignature = SourceFileImpl.IsSignatureFile this.Range.FileName - let signatureLocation = - match this.Symbol.Item with - | Item.Value v -> v.Deref.ArgReprInfoForDisplay - | _ -> None - |> Option.bind (fun a -> a.OtherRange) + let signatureLocation = this.Symbol.SignatureLocation couldBeParameter && (thisIsSignature diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index ab8d2f17403..23b2315efee 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -440,10 +440,17 @@ module Helpers = let getSymbolUse fileName (source: string) (symbolName: string) options (checker: FSharpChecker) = async { - let index = source.IndexOf symbolName - let line = source |> Seq.take index |> Seq.where ((=) '\n') |> Seq.length - let fullLine = source.Split '\n' |> Array.item line - let colAtEndOfNames = fullLine.IndexOf symbolName + symbolName.Length + let lines = source.Split '\n' |> Seq.skip 1 // module definition + let lineNumber, fullLine, colAtEndOfNames = + lines + |> Seq.mapi (fun lineNumber line -> + let index = line.IndexOf symbolName + if index >= 0 then + let colAtEndOfNames = line.IndexOf symbolName + symbolName.Length + Some (lineNumber + 2, line, colAtEndOfNames) + else None) + |> Seq.tryPick id + |> Option.defaultValue (-1, "", -1) let! results = checker.ParseAndCheckFileInProject( fileName, 0, SourceText.ofString source, options) @@ -451,10 +458,10 @@ module Helpers = let typeCheckResults = getTypeCheckResult results let symbolUse = - typeCheckResults.GetSymbolUseAtLocation(line + 1, colAtEndOfNames, fullLine, [symbolName]) + typeCheckResults.GetSymbolUseAtLocation(lineNumber, colAtEndOfNames, fullLine, [symbolName]) return symbolUse |> Option.defaultWith (fun () -> - failwith $"No symbol found in {fileName} at {line}:{colAtEndOfNames}\nFile contents:\n\n{source}\n") + failwith $"No symbol found in {fileName} at {lineNumber}:{colAtEndOfNames}\nFile contents:\n\n{source}\n") } let singleFileChecker source = From 24c0c895a6d44aad392c7a3298b02f599e170d31 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Apr 2023 19:01:57 +0200 Subject: [PATCH 44/44] remove debugging test --- .../FSharpChecker/SymbolUse.fs | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs index 9a68336965d..720dc0b1271 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/SymbolUse.fs @@ -85,40 +85,6 @@ module IsPrivateToFile = Assert.True(symbolUse.IsPrivateToFileAndSignatureFile)) } - [] - let ``Function parameter, with signature file, part 3`` () = - - for attempt in 1 .. 20 do - SyntheticProject.Create(testFileWithSignature).Workflow { - checkSymbolUse testFile.Id functionParameter (fun symbolUse -> - - let couldBeParameter, declarationLocation = - match symbolUse.Symbol with - | :? FSharpParameter as p -> true, Some p.DeclarationLocation - | :? FSharpMemberOrFunctionOrValue as m when not m.IsModuleValueOrMember -> true, Some m.DeclarationLocation - | _ -> false, None - - let thisIsSignature = SourceFileImpl.IsSignatureFile symbolUse.Range.FileName - - let argReprInfo = - match symbolUse.Symbol.Item with - | Item.Value v -> v.Deref.ArgReprInfoForDisplay - | _ -> None - - let signatureLocation = argReprInfo |> Option.bind (fun a -> a.OtherRange) - - let diagnostics = $"Attempt: #{attempt} couldBeParameter: {couldBeParameter} \n declarationLocation: {declarationLocation} \n thisIsSignature: {thisIsSignature} \n signatureLocation: {signatureLocation} \n argReprInfo: {argReprInfo}" - - let result = - couldBeParameter - && (thisIsSignature - || (signatureLocation.IsSome && signatureLocation <> declarationLocation)) - - if not result then - failwith diagnostics) - } - |> ignore - // [] This is a bug - https://github.com/dotnet/fsharp/issues/14419 let ``Private function, with signature file`` () = SyntheticProject.Create(