diff --git a/INTERNAL.md b/INTERNAL.md index 4993301cb36..ec64caa69de 100644 --- a/INTERNAL.md +++ b/INTERNAL.md @@ -66,8 +66,13 @@ Update the `insertTargetBranch` value at the bottom of `azure-pipelines.yml` in ### When VS `main` is open for insertions for preview releases of VS: 1. Create a new `release/dev*` branch (e.g., `release/dev17.4`) and initially set its HEAD commit to that of the previous release (e.g., `release/dev17.3` in this case). -2. Set the new branch to receive auto-merges from `main`, and also set the old release branch to flow into the new one. [This PR](https://github.com/dotnet/roslyn-tools/pull/1245/files) is a good example of what to do when a new `release/dev17.4` branch is created that should receive merges from both `main` and the previous release branch, `release/dev17.3`. -3. Set the packages from the new branch to flow into the correct package feeds via the `darc` tool. To do this: + ```console + git checkout -b release/dev17.4 + git reset --hard upstream/release/dev17.3 + git push --set-upstream upstream release/dev17.4 + ``` +3. Set the new branch to receive auto-merges from `main`, and also set the old release branch to flow into the new one. [This PR](https://github.com/dotnet/roslyn-tools/pull/1245/files) is a good example of what to do when a new `release/dev17.4` branch is created that should receive merges from both `main` and the previous release branch, `release/dev17.3`. +4. Set the packages from the new branch to flow into the correct package feeds via the `darc` tool. To do this: 1. Ensure the latest `darc` tool is installed by running `eng/common/darc-init.ps1`. 2. (only needed once) Run the command `darc authenticate`. A text file will be opened with instructions on how to populate access tokens. 3. Check the current package/channel subscriptions by running `darc get-default-channels --source-repo fsharp`. For this example, notice that the latest subscription shows the F# branch `release/dev17.3` is getting added to the `VS 17.3` channel. diff --git a/a b/a deleted file mode 100644 index 6ff3473cd9a..00000000000 Binary files a/a and /dev/null differ diff --git a/eng/Versions.props b/eng/Versions.props index 713404c6cd6..8c6c2c85e14 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -97,12 +97,12 @@ 6.0.0 4.5.0 - 4.4.0-3.22470.1 - 17.4.196-preview - 17.4.0-preview-3-32916-145 - 17.4.342-pre - 17.4.23-alpha - 17.4.0 + 4.5.0-1.22520.13 + 17.5.49-preview + 17.5.0-preview-1-33020-520 + 17.5.202-pre-g89e17c9f72 + 17.4.27 + 17.4.0-preview-22469-04 $(RoslynVersion) $(RoslynVersion) @@ -116,7 +116,7 @@ $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) - 17.4.0-preview-3-32916-053 + 17.5.0-preview-1-33019-447 $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) @@ -133,8 +133,8 @@ $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) - 17.4.0-preview-3-32916-053 - 17.4.0-preview-3-32916-053 + 17.5.0-preview-1-33019-447 + 17.5.0-preview-1-33019-447 $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) $(MicrosoftVisualStudioShellPackagesVersion) @@ -171,7 +171,7 @@ 2.3.6152103 17.1.4054 - 17.4.7-alpha + 17.5.9-alpha-g84529e7115 17.0.0 17.0.64 9.0.30729 @@ -204,8 +204,8 @@ 3.11.0 2.1.80 1.0.0-beta2-dev3 - 2.13.23-alpha - 2.9.87-alpha + 2.14.6-alpha + 2.9.112 2.4.1 2.4.2 5.10.3 diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 4f74ce59c61..a501f74edc0 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -1655,6 +1655,19 @@ module MutRecBindingChecking = defnsEs, envMutRec +let private ReportErrorOnStaticClass (synMembers: SynMemberDefn list) = + for mem in synMembers do + match mem with + | SynMemberDefn.ImplicitCtor(ctorArgs = SynSimplePats.SimplePats(pats = pats)) when (not pats.IsEmpty) -> + for pat in pats do + errorR(Error(FSComp.SR.chkConstructorWithArgumentsOnStaticClasses(), pat.Range)) + + | SynMemberDefn.Member(SynBinding(valData = SynValData(memberFlags = Some memberFlags)), m) when memberFlags.MemberKind = SynMemberKind.Constructor -> + errorR(Error(FSComp.SR.chkAdditionalConstructorOnStaticClasses(), m)) + | SynMemberDefn.Member(SynBinding(valData = SynValData(memberFlags = Some memberFlags)), m) when memberFlags.MemberKind = SynMemberKind.Member && memberFlags.IsInstance -> + errorR(Error(FSComp.SR.chkInstanceMemberOnStaticClasses(), m)); + | _ -> () + /// Check and generalize the interface implementations, members, 'let' definitions in a mutually recursive group of definitions. let TcMutRecDefns_Phase2 (cenv: cenv) envInitial mBinds scopem mutRecNSInfo (envMutRec: TcEnv) (mutRecDefns: MutRecDefnsPhase2Data) isMutRec = let g = cenv.g @@ -1755,7 +1768,13 @@ let TcMutRecDefns_Phase2 (cenv: cenv) envInitial mBinds scopem mutRecNSInfo (env let binds: MutRecDefnsPhase2Info = (envMutRec, mutRecDefns) ||> MutRecShapes.mapTyconsWithEnv (fun envForDecls tyconData -> - let (MutRecDefnsPhase2DataForTycon(tyconOpt, _, declKind, tcref, _, _, declaredTyconTypars, _, _, _, fixupFinalAttrs)) = tyconData + let (MutRecDefnsPhase2DataForTycon(tyconOpt, _x, declKind, tcref, _, _, declaredTyconTypars, synMembers, _, _, fixupFinalAttrs)) = tyconData + + // If a tye uses both [] and [] attributes it means it is a static class. + let isStaticClass = HasFSharpAttribute cenv.g cenv.g.attrib_SealedAttribute tcref.Attribs && HasFSharpAttribute cenv.g cenv.g.attrib_AbstractClassAttribute tcref.Attribs + if isStaticClass && cenv.g.langVersion.SupportsFeature(LanguageFeature.ErrorReportingOnStaticClasses) then + ReportErrorOnStaticClass synMembers + let envForDecls = // This allows to implement protected interface methods if it's a DIM. // Does not need to be hidden behind a lang version as it needs to be possible to @@ -4030,6 +4049,7 @@ module TcDeclarations = let rec private SplitTyconDefn (SynTypeDefn(typeInfo=synTyconInfo;typeRepr=trepr; members=extraMembers)) = let extraMembers = desugarGetSetMembers extraMembers let implements1 = List.choose (function SynMemberDefn.Interface (interfaceType=ty) -> Some(ty, ty.Range) | _ -> None) extraMembers + match trepr with | SynTypeDefnRepr.ObjectModel(kind, cspec, m) -> let cspec = desugarGetSetMembers cspec @@ -4047,7 +4067,7 @@ module TcDeclarations = let members = let membersIncludingAutoProps = cspec |> List.filter (fun memb -> - match memb with + match memb with | SynMemberDefn.Interface _ | SynMemberDefn.Member _ | SynMemberDefn.GetSetMember _ @@ -4837,7 +4857,7 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem let moduleEntity = Construct.NewModuleOrNamespace (Some env.eCompPath) vis id xmlDoc modAttrs (MaybeLazy.Strict moduleTy) // Now typecheck. - let! moduleContents, topAttrsNew, envAtEnd = TcModuleOrNamespaceElements cenv (Parent (mkLocalModuleRef moduleEntity)) endm envForModule xml None [] moduleDefs + let! moduleContents, topAttrsNew, envAtEnd = TcModuleOrNamespaceElements cenv (Parent (mkLocalModuleRef moduleEntity)) endm envForModule xml None [] moduleDefs // Get the inferred type of the decls and record it in the modul. moduleEntity.entity_modul_type <- MaybeLazy.Strict moduleTyAcc.Value @@ -4924,8 +4944,7 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem let! moduleContents, topAttrs, envAtEnd = TcModuleOrNamespaceElements cenv parent endm envNS xml mutRecNSInfo [] defs - MutRecBindingChecking.TcMutRecDefns_UpdateNSContents nsInfo - + MutRecBindingChecking.TcMutRecDefns_UpdateNSContents nsInfo let env, openDecls = if isNil enclosingNamespacePath then envAtEnd, [] diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 4f05ce235c0..03ab28bfc97 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1560,6 +1560,7 @@ featureCSharpExtensionAttributeNotRequired,"Allow implicit Extension attribute o featureErrorForNonVirtualMembersOverrides,"Raises errors for non-virtual members overrides" featureWarningWhenInliningMethodImplNoInlineMarkedFunction,"Raises warnings when 'let inline ... =' is used together with [] attribute. Function is not getting inlined." featureArithmeticInLiterals,"Allow arithmetic and logical operations in literals" +featureErrorReportingOnStaticClasses,"Error reporting on static classes" 3353,fsiInvalidDirective,"Invalid directive '#%s %s'" 3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." 3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." @@ -1663,5 +1664,8 @@ reprStateMachineInvalidForm,"The state machine has an unexpected form" 3548,matchNotAllowedForUnionCaseWithNoData,"Pattern discard is not allowed for union case that takes no data." 3549,tcSynTypeOrInvalidInDeclaration,"SynType.Or is not permitted in this declaration" 3550,chkDuplicatedMethodParameter,"Duplicate parameter. The parameter '%s' has been used more that once in this method." +featureEscapeBracesInFormattableString,"Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString" 3551,buildDuplicateFile,"The source file '%s' (at position %d/%d) already appeared in the compilation list (at position %d/%d). Please verify that it is included only once in the project file." -featureEscapeBracesInFormattableString,"Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString" \ No newline at end of file +3552,chkConstructorWithArgumentsOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Constructor with arguments is not allowed." +3553,chkAdditionalConstructorOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Additional constructor is not allowed." +3554,chkInstanceMemberOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Instance members are not allowed." diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 7dfedf0be08..e7654bea422 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -60,7 +60,8 @@ type LanguageFeature = | WarningWhenInliningMethodImplNoInlineMarkedFunction | EscapeDotnetFormattableStrings | ArithmeticInLiterals - + | ErrorReportingOnStaticClasses + /// LanguageVersion management type LanguageVersion(versionText) = @@ -136,6 +137,7 @@ type LanguageVersion(versionText) = LanguageFeature.WarningWhenInliningMethodImplNoInlineMarkedFunction, previewVersion LanguageFeature.EscapeDotnetFormattableStrings, previewVersion LanguageFeature.ArithmeticInLiterals, previewVersion + LanguageFeature.ErrorReportingOnStaticClasses, previewVersion ] @@ -249,6 +251,7 @@ type LanguageVersion(versionText) = | LanguageFeature.WarningWhenInliningMethodImplNoInlineMarkedFunction -> FSComp.SR.featureWarningWhenInliningMethodImplNoInlineMarkedFunction () | LanguageFeature.EscapeDotnetFormattableStrings -> FSComp.SR.featureEscapeBracesInFormattableString () | LanguageFeature.ArithmeticInLiterals -> FSComp.SR.featureArithmeticInLiterals () + | LanguageFeature.ErrorReportingOnStaticClasses -> FSComp.SR.featureErrorReportingOnStaticClasses () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index ac1ae354599..d070a8b4e36 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -50,6 +50,7 @@ type LanguageFeature = | WarningWhenInliningMethodImplNoInlineMarkedFunction | EscapeDotnetFormattableStrings | ArithmeticInLiterals + | ErrorReportingOnStaticClasses /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index 499654b029a..480c58f3af6 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -1777,7 +1777,7 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = if isUnresolved() then false else match fsharpInfo() with | None -> false - | Some v -> + | Some v -> v.IsCompilerGenerated member _.InlineAnnotation = diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 7a3e67fb959..6edc8b7cd46 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -22,6 +22,16 @@ Soubor {0} má nerozpoznanou příponu. Zdrojové soubory musí mít příponu .fs, .fsi, .fsx nebo .fsscript. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Duplicitní parametr Parametr {0} byl v této metodě použit vícekrát. @@ -62,6 +72,11 @@ Použití incr z knihovny F# je zastaralé. Více informací: https://aka.ms/fsharp-refcell-ops. Změňte prosím například incr cell na cell.Value <- cell.Value + 1. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. Atribut AssemblyKeyNameAttribute je zastaralý. Použijte místo něj AssemblyKeyFileAttribute. @@ -197,6 +212,11 @@ chyba při zastaralém přístupu konstruktoru s atributem RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Uvede složené závorky před voláním formattableStringFactory.Create, pokud je interpolovaný řetězcový literál zadán jako FormattableString. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 119f725123e..0e49da8710e 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -22,6 +22,16 @@ Die Dateierweiterung von „{0}“ wurde nicht erkannt. Quelldateien müssen die Erweiterung .fs, .fsi, .fsx oder .fsscript haben + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Doppelter Parameter. Der Parameter „{0}“ wurde in dieser Methode mehrmals verwendet. @@ -62,6 +72,11 @@ Die Verwendung von "incr" aus der F#-Bibliothek ist veraltet. Siehe https://aka.ms/fsharp-refcell-ops. Ändern Sie z. B. "incr cell" in "cell.Value <- cell.Value + 1". + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. "AssemblyKeyNameAttribute" gilt als veraltet. Verwenden Sie stattdessen "AssemblyKeyFileAttribute". @@ -197,6 +212,11 @@ Beim veralteten Zugriff auf das Konstrukt mit dem RequireQualifiedAccess-Attribut wird ein Fehler ausgegeben. + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Escapezeichen mit geschweiften Klammern, bevor FormattableStringFactory.Create aufgerufen wird, wenn ein interpoliertes Zeichenfolgenliteral als FormattableString eingegeben wird. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 83cea28c308..10b58cd8a70 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -22,6 +22,16 @@ No se reconoce la extensión de archivo de '{0}'. Los archivos de código fuente deben tener las extensiones .fs, .fsi, .fsx o .fsscript + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Parámetro duplicado. El parámetro '{0}' se ha usado más una vez en este método. @@ -62,6 +72,11 @@ El uso de "incr" de la biblioteca de F# está en desuso. Vea https://aka.ms/fsharp-refcell-ops. Por ejemplo, cambie "incr cell" a "cell.Value <- cell.Value + 1". + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. El elemento "AssemblyKeyNameAttribute" está en desuso. Use "AssemblyKeyFileAttribute" en su lugar. @@ -197,6 +212,11 @@ error en el acceso en desuso de la construcción con el atributo RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Crea un escape de llaves antes de llamar a FormattableStringFactory.Create cuando el literal de cadena interpolado se escribe como FormattableString. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 66593466ce3..f59cfd66661 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -22,6 +22,16 @@ L'extension de fichier de '{0}' n'est pas reconnue. Les fichiers sources doivent avoir l'extension .fs, .fsi, .fsx, ou .fsscript. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Paramètre dupliqué. Le paramètre « {0} » a été utilisé une fois de plus dans cette méthode. @@ -62,6 +72,11 @@ L’utilisation de « incr » à partir de la bibliothèque F# est déconseillée. Voir https://aka.ms/fsharp-refcell-ops. Par exemple, veuillez remplacer « incr cell » par « cell.Value <- cell.Value + 1 ». + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. 'AssemblyKeyNameAttribute' a été déprécié. Utilisez 'AssemblyKeyFileAttribute' à la place. @@ -197,6 +212,11 @@ donner une erreur sur l’accès déconseillé de la construction avec l’attribut RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Échappe les accolades avant d’appeler FormattableStringFactory.Create lorsque le littéral de chaîne interpolé est tapé en tant que FormattableString. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index f2dacca560e..5a1908c4e75 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -22,6 +22,16 @@ Estensione di file di '{0}' non riconosciuta. I file di origine devono avere estensione .fs, .fsi, .fsx or .fsscript + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Parametro duplicato. Il parametro '{0}' è stato utilizzato più volte in questo metodo. @@ -62,6 +72,11 @@ L'uso di 'incr' dalla libreria F # è deprecato. Vedere https://aka.ms/fsharp-refcell-ops. Ad esempio, modificare 'incr cell' in 'cell.Value <- cell.Value + 1'. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. L'attributo 'AssemblyKeyNameAttribute' è deprecato. In alternativa, usare 'AssemblyKeyFileAttribute'. @@ -197,6 +212,11 @@ errore durante l'accesso deprecato del costrutto con l'attributo RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Trasferisce le parentesi graffe prima di chiamare FormattableStringFactory.Create quando il valore letterale stringa interpolato viene digitato come FormattableString diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 3b905ddd7b5..f9e8af34870 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -22,6 +22,16 @@ '{0}' のファイル拡張子は認識されません。ソース ファイルの拡張子は .fs、.fsi、.fsx、または .fsscript にする必要があります。 + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. パラメーターが重複しています。パラメーター '{0}' は、このメソッドで 1 回以上使用されています。 @@ -62,6 +72,11 @@ F# ライブラリからの 'incr' の使用は非推奨です。https://aka.ms/fsharp-refcell-ops を参照してください。たとえば、'incr cell' を 'cell.Value <- cell.Value +1' に変更してください。 + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. 'AssemblyKeyNameAttribute' は非推奨になりました。代わりに 'AssemblyKeyFileAttribute' を使用してください。 @@ -197,6 +212,11 @@ RequireQualifiedAccess 属性を持つコンストラクトの非推奨アクセスでエラーが発生しました + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString 挿入文字列リテラルが FormattableString として型指定されている場合は、FormattableStringFactory.Create を呼び出す前に波かっこをエスケープします diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 49aa7ab67a7..a150456cb49 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -22,6 +22,16 @@ '{0}'의 파일 확장명을 인식할 수 없습니다. 원본 파일의 확장명은 .fs, .fsi, .fsx 또는 .fsscript여야 합니다. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. 매개 변수가 중복되었습니다. 이 메소드에서 매개 변수 '{0}'이(가) 두 번 이상 사용되었습니다. @@ -62,6 +72,11 @@ F# 라이브러리의 'incr' 사용은 더 이상 사용되지 않습니다. https://aka.ms/fsharp-refcell-ops를 참조하세요. 예를 들어 'incr cell'을 'cell.Value <- cell.Value + 1'로 변경하세요. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. 'AssemblyKeyNameAttribute'는 사용되지 않습니다. 대신 'AssemblyKeyFileAttribute'를 사용하세요. @@ -197,6 +212,11 @@ RequireQualifiedAccess 특성을 사용하여 사용되지 않는 구문 액세스에 대한 오류 제공 + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString 보간된 문자열 리터럴이 FormattableString으로 형식화된 경우 FormattableStringFactory.Create를 호출하기 전에 중괄호를 이스케이프합니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index ae7efedd77f..986518d0ee9 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -22,6 +22,16 @@ Rozszerzenie pliku "{0}" nie zostało rozpoznane. Pliki źródłowe muszą mieć rozszerzenie .fs, .fsi, .fsx lub .fsscript + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Zduplikowany parametr. Parametr „{0}” został użyty więcej niż raz w tej metodzie. @@ -62,6 +72,11 @@ Użycie elementu „incr” z biblioteki języka F# jest przestarzałe. Sprawdź stronę https://aka.ms/fsharp-refcell-ops. Na przykład zmień wyrażenie „incr cell” na „cell.Value <- cell.Value + 1”. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. Element „AssemblyKeyNameAttribute” jest przestarzały. Zamiast niego użyj elementu „AssemblyKeyFileAttribute”. @@ -197,6 +212,11 @@ wskazywanie błędu w przypadku przestarzałego dostępu do konstrukcji z atrybutem RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Uniknie nawiasów klamrowych przed wywołaniem metody FormattableStringFactory.Create, gdy interpolowany literał ciągu jest wpisywany jako FormattableString diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 1ab71ae01e6..e4221b5ed1c 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -22,6 +22,16 @@ A extensão do arquivo de '{0}' não foi reconhecida. Os arquivos de origem devem ter a extensão .fs, .fsi, .fsx or .fsscript + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Parâmetro duplicado. O parâmetro '{0}' foi usado mais de uma vez neste método. @@ -62,6 +72,11 @@ O uso de 'incr' da biblioteca F# foi preterido. Consulte https://aka.ms/fsharp-refcell-ops. Por exemplo, altere a célula 'incr cell' para 'cell.Value <- cell.Value + 1'. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. O 'AssemblyKeyNameAttribute' foi preterido. Use o 'AssemblyKeyFileAttribute'. @@ -197,6 +212,11 @@ fornecer erro no acesso preterido do constructo com o atributo RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Sai das chaves antes de chamar FormattableStringFactory.Create quando o literal de cadeia de caracteres é digitado como FormattableString diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index a2448d6bc73..1ae14bbb5c1 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -22,6 +22,16 @@ Расширение файла "{0}" не распознано. Исходные файлы должны иметь расширения FS, FSI, FSX или FSSCRIPT + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Повторяющийся параметр. Параметр "{0}" использовался в этом методе несколько раз. @@ -62,6 +72,11 @@ Использование "incr" из библиотеки F# является нерекомендуемым. См. https://aka.ms/fsharp-refcell-ops. Например, замените "incr cell" на "cell.Value <- cell.Value + 1". + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. Атрибут "AssemblyKeyNameAttribute" является устаревшим. Используйте вместо него атрибут "AssemblyKeyFileAttribute". @@ -197,6 +212,11 @@ выдать ошибку при устаревшем доступе к конструкции с атрибутом RequireQualifiedAccess + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString Экранирует фигурные скобки перед вызовом FormattableStringFactory.Create, когда интерполированный строковый литерал введен как FormattableString diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 09a78a6bf3d..bf35b586795 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -22,6 +22,16 @@ '{0}' kaynak dosyasının dosya uzantısı tanınmadı. Kaynak dosyaların uzantısı .fs, .fsi, .fsx veya .fsscript olmalıdır. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Yinelenen parametre. '{0}' parametresi bu metotta bir kereden fazla kullanıldı. @@ -62,6 +72,11 @@ F# kitaplığından gelen 'incr' kullanımı kullanım dışı. https://aka.ms/fsharp-refcell-ops’a bakın. Örneğin, lütfen 'incr cell' ifadesini 'cell.Value <- cell.Value + 1' olarak değiştirin. + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. 'AssemblyKeyNameAttribute' kullanım dışı bırakıldı. Bunun yerine 'AssemblyKeyFileAttribute' kullanın. @@ -197,6 +212,11 @@ RequireQualifiedAccess özniteliğine sahip yapının kullanım dışı erişiminde hata + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString İçe eklenmiş dize sabit değerinin türü FormattableString olduğunda FormattableStringFactory.Create çağrılmadan önce küme ayraçlarını atlar diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 6e13f1f91d3..84880217177 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -22,6 +22,16 @@ 无法识别“{0}”的文件扩展名。源文件必须具有扩展名 .fs、.fsi、.fsx 或 .fsscript + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. 参数重复。此方法中多次使用了参数“{0}”。 @@ -62,6 +72,11 @@ 已弃用 F# 库中的“incr”。请参阅 https://aka.ms/fsharp-refcell-ops。 例如,请将“incr cell”更改为“cell.Value <- cell.Value + 1”。 + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. "AssemblyKeyNameAttribute" 已被弃用。请改为使用 "AssemblyKeyFileAttribute"。 @@ -197,6 +212,11 @@ 对具有 RequireQualifiedAccess 属性的构造进行弃用的访问时出错 + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString 当内插字符串文本键入为 FormattableString 时,在调用 FormattableStringFactory.Create 之前转义大括号 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index ae37757d6d4..dd29ebfd8c3 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -22,6 +22,16 @@ 無法辨識 '{0}' 的副檔名。來源檔案的副檔名必須是 .fs、.fsi、.fsx 或 .fsscript。 + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Additional constructor is not allowed. + + + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Constructor with arguments is not allowed. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. 重複的參數。參數 '{0}' 在此方法中使用多次。 @@ -62,6 +72,11 @@ 透過 F# 程式庫使用 'incr' 的方式已淘汰。請參閱 https://aka.ms/fsharp-refcell-ops。舉例來說,請將 'incr cell' 變更為 'cell.Value <- cell.Value + 1'。 + + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance members are not allowed. + + The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead. 'AssemblyKeyNameAttribute' 已淘汰。請改用 'AssemblyKeyFileAttribute'。 @@ -197,6 +212,11 @@ 對具有 RequireQualifiedAccess 屬性的建構的已取代存取發出錯誤 + + Error reporting on static classes + Error reporting on static classes + + Escapes curly braces before calling FormattableStringFactory.Create when interpolated string literal is typed as FormattableString 當差補字串常值輸入為 FormattableString 時,在呼叫 FormattableStringFactory.Create 之前先逸出大括弧 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 27713b6a4a3..3a00acd4d02 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -177,6 +177,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/StaticClassTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/StaticClassTests.fs new file mode 100644 index 00000000000..b6a2f32e2f1 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Language/StaticClassTests.fs @@ -0,0 +1,255 @@ +namespace FSharp.Compiler.ComponentTests.Language + +open Xunit +open FSharp.Test.Compiler + +module StaticClassTests = + + [] + let ``Sealed and AbstractClass on a type in lang version70`` () = + Fsx """ +[] +type T = class end + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type in lang preview`` () = + Fsx """ +[] +type T = class end + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type with constructor in lang preview`` () = + Fsx """ +[] +type T() = class end + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type with constructor in lang version70`` () = + Fsx """ +[] +type T() = class end + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type with constructor with arguments in lang preview`` () = + Fsx """ +[] +type T(x: int) = class end + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3552, Line 3, Col 8, Line 3, Col 14, "If a type uses both [] and [] attributes, it means it is static. Constructor with arguments is not allowed.") + ] + + [] + let ``Sealed and AbstractClass on a type with constructor with arguments in lang version70`` () = + Fsx """ +[] +type T(x: int) = class end + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``When Sealed and AbstractClass on a type with additional constructors in lang preview`` () = + Fsx """ +[] +type T = + new () = {} + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3553, Line 4, Col 5, Line 4, Col 16, "If a type uses both [] and [] attributes, it means it is static. Additional constructor is not allowed.") + ] + + [] + let ``When Sealed and AbstractClass on a type with additional constructors in lang version70`` () = + Fsx """ +[] +type T = + new () = {} + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``When Sealed and AbstractClass on a type with a primary(parameters) and additional constructor in lang preview`` () = + Fsx """ +[] +type T(x: int) = + new () = T(42) + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3552, Line 3, Col 8, Line 3, Col 14, "If a type uses both [] and [] attributes, it means it is static. Constructor with arguments is not allowed.") + (Error 3553, Line 4, Col 5, Line 4, Col 19, "If a type uses both [] and [] attributes, it means it is static. Additional constructor is not allowed.") + ] + + [] + let ``When Sealed and AbstractClass on a type with explicit fields and constructor in lang version70`` () = + Fsx """ +[] +type B = + val F : int + val mutable G : int + new () = { F = 3; G = 3 } + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + [] + let ``When Sealed and AbstractClass on a generic type with constructor in lang version70`` () = + Fsx """ +[] +type ListDebugView<'T>(l: 'T list) = class end + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``When Sealed and AbstractClass on a generic type with constructor in lang preview`` () = + Fsx """ +[] +type ListDebugView<'T>(l: 'T list) = class end + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3552, Line 3, Col 24, Line 3, Col 34, "If a type uses both [] and [] attributes, it means it is static. Constructor with arguments is not allowed.") + ] + + [] + let ``When Sealed and AbstractClass on a type with explicit fields and constructor in lang preview`` () = + Fsx """ +[] +type B = + val F : int + val mutable G : int + new () = { F = 3; G = 3 } + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3553, Line 6, Col 5, Line 6, Col 30, "If a type uses both [] and [] attributes, it means it is static. Additional constructor is not allowed.") + ] + + [] + [] + [] + let ``Mutually recursive type definition that using custom attributes``(langVersion) = + let code = """ + module Test + + open System.Diagnostics + + [] + [>)>] + [] + [] + type MyCustomList<'T> = + | Empty + | NonEmpty of Head: 'T * Tail: MyCustomList<'T> + + and MyImbaAlias<'T> = MyCustomList<'T> + + //------------------------------------------------------------------------- + // List (debug view) + //------------------------------------------------------------------------- + + and + MyCustomListDebugView<'T>(l: MyCustomList<'T>) = + let asList = + let rec toList ml = + match ml with + | Empty -> [] + | NonEmpty (head,tail) -> head :: (toList tail) + toList l + + [] + member x.Items = asList |> List.toArray + + [] + member x._FullList = asList |> List.toArray + + """ + Fs code + |> withLangVersion langVersion + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type with instance members in lang version70`` () = + Fsx """ +[] +type T() = + member this.M() = () + static member X = 1 + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type with instance members in lang preview`` () = + Fsx """ +[] +type T() = + member this.M() = () + static member X = 1 + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3554, Line 4, Col 5, Line 4, Col 25, "If a type uses both [] and [] attributes, it means it is static. Instance members are not allowed.") + ] + + [] + let ``Sealed and AbstractClass on a type with static members in lang version70`` () = + Fsx """ +[] +type T() = + static member M() = () + static member X = T.M() + """ + |> withLangVersion70 + |> compile + |> shouldSucceed + + [] + let ``Sealed and AbstractClass on a type with static members in lang preview`` () = + Fsx """ +[] +type T() = + static member M() = () + static member X = T.M() + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs b/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs index 6ac4e754c1f..0a74deb77cd 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs @@ -94,7 +94,7 @@ module internal MetadataAsSource = [] [); Composition.Shared>] -type internal FSharpMetadataAsSourceService() = +type FSharpMetadataAsSourceService() = let serviceProvider = ServiceProvider.GlobalProvider let projs = System.Collections.Concurrent.ConcurrentDictionary() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index f25746325b2..b2fb1792cf5 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -91,7 +91,7 @@ module private CheckerExtensions = } [] -module private ProjectCache = +module internal ProjectCache = /// This is a cache to maintain FSharpParsingOptions and FSharpProjectOptions per Roslyn Project. /// The Roslyn Project is held weakly meaning when it is cleaned up by the GC, the FSharParsingOptions and FSharpProjectOptions will be cleaned up by the GC. @@ -99,9 +99,8 @@ module private ProjectCache = let Projects = ConditionalWeakTable() type Solution with - /// Get the instance of IFSharpWorkspaceService. - member private this.GetFSharpWorkspaceService() = + member internal this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() type Document with @@ -247,3 +246,20 @@ type Project with do! doc.FindFSharpReferencesAsync(symbol, (fun textSpan range -> onFound doc textSpan range), userOpName) |> RoslynHelpers.StartAsyncAsTask ct } + + member this.GetFSharpCompilationOptionsAsync(ct: CancellationToken) = + backgroundTask { + if this.IsFSharp then + match ProjectCache.Projects.TryGetValue(this) with + | true, result -> return result + | _ -> + let service = this.Solution.GetFSharpWorkspaceService() + let projectOptionsManager = service.FSharpProjectOptionsManager + match! projectOptionsManager.TryGetOptionsByProject(this, ct) with + | None -> return raise(OperationCanceledException("FSharp project options not found.")) + | Some(parsingOptions, projectOptions) -> + let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) + return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_,_>.CreateValueCallback(fun _ -> result)) + else + return raise(OperationCanceledException("Project is not a FSharp project.")) + } diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 4bdfec0bf11..bb84e395b8d 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -16,15 +16,18 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation open Microsoft.VisualStudio +open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Shell.Interop +open Microsoft.VisualStudio.LanguageServices -open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Text open FSharp.Compiler.Text.Range open FSharp.Compiler.Symbols open FSharp.Compiler.Tokenization +open System.Composition +open System.Text.RegularExpressions module private Symbol = @@ -90,7 +93,7 @@ module private ExternalSymbol = |> Option.map (fun args -> upcast methsym, FindDeclExternalSymbol.Constructor(fullTypeName, args)) ) |> List.ofSeq - + (symbol, FindDeclExternalSymbol.Type fullTypeName) :: constructors | :? IMethodSymbol as methsym -> @@ -111,14 +114,14 @@ module private ExternalSymbol = | _ -> [] // TODO: Uncomment code when VS has a fix for updating the status bar. -type internal StatusBar(statusBar: IVsStatusbar) = +type StatusBar(statusBar: IVsStatusbar) = let mutable _searchIcon = int16 Microsoft.VisualStudio.Shell.Interop.Constants.SBAI_Find :> obj let _clear() = // unfreeze the statusbar - statusBar.FreezeOutput 0 |> ignore + statusBar.FreezeOutput 0 |> ignore statusBar.Clear() |> ignore - + member _.Message(_msg: string) = () //let _, frozen = statusBar.IsFrozen() @@ -137,11 +140,11 @@ type internal StatusBar(statusBar: IVsStatusbar) = // | 0, currentText when currentText <> msg -> () // | _ -> clear() //}|> Async.Start - + member _.Clear() = () //clear() /// Animated magnifying glass that displays on the status bar while a symbol search is in progress. - member _.Animate() : IDisposable = + member _.Animate() : IDisposable = //statusBar.Animation (1, &searchIcon) |> ignore { new IDisposable with member _.Dispose() = () } //statusBar.Animation(0, &searchIcon) |> ignore } @@ -155,19 +158,18 @@ type internal FSharpGoToDefinitionResult = | ExternalAssembly of FSharpSymbolUse * MetadataReference seq type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = - - /// Use an origin document to provide the solution & workspace used to + /// Use an origin document to provide the solution & workspace used to /// find the corresponding textSpan and INavigableItem for the range - let rangeToNavigableItem (range: range, document: Document) = + let rangeToNavigableItem (range: range, document: Document) = async { let fileName = try System.IO.Path.GetFullPath range.FileName with _ -> range.FileName let refDocumentIds = document.Project.Solution.GetDocumentIdsWithFilePath fileName - if not refDocumentIds.IsEmpty then + if not refDocumentIds.IsEmpty then let refDocumentId = refDocumentIds.First() let refDocument = document.Project.Solution.GetDocument refDocumentId let! cancellationToken = Async.CancellationToken let! refSourceText = refDocument.GetTextAsync(cancellationToken) |> Async.AwaitTask - match RoslynHelpers.TryFSharpRangeToTextSpan (refSourceText, range) with + match RoslynHelpers.TryFSharpRangeToTextSpan (refSourceText, range) with | None -> return None | Some refTextSpan -> return Some (FSharpGoToDefinitionNavigableItem (refDocument, refTextSpan)) else return None @@ -182,7 +184,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() + let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() let idRange = lexerSymbol.Ident.idRange let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync(nameof(GoToDefinition)) |> liftAsync @@ -191,14 +193,14 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = // if the tooltip was spawned in an implementation file and we have a range targeting // a signature file, try to find the corresponding implementation file and target the // desired symbol - if isSignatureFile fsSymbolUse.FileName && preferSignature = false then + if isSignatureFile fsSymbolUse.FileName && preferSignature = false then let fsfilePath = Path.ChangeExtension (originRange.FileName,"fs") if not (File.Exists fsfilePath) then return! None else let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () let! _, checkFileResults = implDoc.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol - let! implSymbol = symbolUses |> Array.tryHead + let! implSymbol = symbolUses |> Array.tryHead let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, implSymbol.Range) return FSharpGoToDefinitionNavigableItem (implDoc, implTextSpan) else @@ -206,10 +208,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = return! rangeToNavigableItem (fsSymbolUse.Range, targetDocument) } - /// if the symbol is defined in the given file, return its declaration location, otherwise use the targetSymbol to find the first + /// if the symbol is defined in the given file, return its declaration location, otherwise use the targetSymbol to find the first /// instance of its presence in the provided source file. The first case is needed to return proper declaration location for /// recursive type definitions, where the first its usage may not be the declaration. - member _.FindSymbolDeclarationInDocument(targetSymbolUse: FSharpSymbolUse, document: Document) = + member _.FindSymbolDeclarationInDocument(targetSymbolUse: FSharpSymbolUse, document: Document) = asyncMaybe { let filePath = document.FilePath match targetSymbolUse.Symbol.DeclarationLocation with @@ -217,7 +219,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | _ -> let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("FindSymbolDeclarationInDocument") |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile targetSymbolUse.Symbol - let! implSymbol = symbolUses |> Array.tryHead + let! implSymbol = symbolUses |> Array.tryHead return implSymbol.Range } @@ -229,10 +231,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let textLinePos = sourceText.Lines.GetLinePosition position let textLineString = textLine.ToString() let fcsTextLineNumber = Line.fromZ textLinePos.Line - let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - + let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() + let preferSignature = isSignatureFile originDocument.FilePath - + let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) let idRange = lexerSymbol.Ident.idRange @@ -262,10 +264,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let! location = symbol.Locations |> Seq.tryHead return (FSharpGoToDefinitionResult.NavigableItem(FSharpGoToDefinitionNavigableItem(project.GetDocument(location.SourceTree), location.SourceSpan)), idRange) | _ -> - let metadataReferences = originDocument.Project.MetadataReferences + let metadataReferences = originDocument.Project.MetadataReferences return (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) - | FindDeclResult.DeclFound targetRange -> + | FindDeclResult.DeclFound targetRange -> // If the file is not associated with a document, it's considered external. if not (originDocument.Project.Solution.ContainsDocumentWithFilePath(targetRange.FileName)) then let metadataReferences = originDocument.Project.MetadataReferences @@ -279,7 +281,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let implFilePath = Path.ChangeExtension (originDocument.FilePath,"fs") if not (File.Exists implFilePath) then return! None else let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) let! implSourceText = implDocument.GetTextAsync(cancellationToken) |> liftTaskAsync let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) @@ -288,7 +290,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = else // jump from implementation to the corresponding signature let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLineString, lexerSymbol.FullIsland, true) match declarations with - | FindDeclResult.DeclFound targetRange -> + | FindDeclResult.DeclFound targetRange -> let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) |> liftTaskAsync let! sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sigSourceText, targetRange) @@ -296,28 +298,28 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) | _ -> return! None - // when the target range is different follow the navigation convention of + // when the target range is different follow the navigation convention of // - gotoDefn origin = signature , gotoDefn destination = signature - // - gotoDefn origin = implementation, gotoDefn destination = implementation + // - gotoDefn origin = implementation, gotoDefn destination = implementation else let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) |> liftTaskAsync let! sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sigSourceText, targetRange) // if the gotodef call originated from a signature and the returned target is a signature, navigate there - if isSignatureFile targetRange.FileName && preferSignature then + if isSignatureFile targetRange.FileName && preferSignature then let navItem = FSharpGoToDefinitionNavigableItem (sigDocument, sigTextSpan) return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) else // we need to get an FSharpSymbol from the targetRange found in the signature // that symbol will be used to find the destination in the corresponding implementation file let implFilePath = // Bugfix: apparently sigDocument not always is a signature file - if isSignatureFile sigDocument.FilePath then Path.ChangeExtension (sigDocument.FilePath, "fs") + if isSignatureFile sigDocument.FilePath then Path.ChangeExtension (sigDocument.FilePath, "fs") else sigDocument.FilePath let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - - let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) - + + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) + let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) let navItem = FSharpGoToDefinitionNavigableItem (implDocument, implTextSpan) @@ -326,7 +328,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = return! None } - /// find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition + /// find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition member this.FindDeclarationOfSymbolAtRange(targetDocument: Document, symbolRange: range, targetSource: SourceText) = this.FindSymbolHelper(targetDocument, symbolRange, targetSource, preferSignature=true) @@ -341,7 +343,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = >> Array.toSeq) |> RoslynHelpers.StartAsyncAsTask cancellationToken - /// Construct a task that will return a navigation target for the implementation definition of the symbol + /// Construct a task that will return a navigation target for the implementation definition of the symbol /// at the provided position in the document. member this.FindDefinitionTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = this.FindDefinitionAtPosition(originDocument, position, cancellationToken) @@ -355,7 +357,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let navigationService = workspace.Services.GetService() let navigationSucceeded = navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) - if not navigationSucceeded then + if not navigationSucceeded then statusBar.TempMessage (SR.CannotNavigateUnknown()) member _.NavigateToItem(navigableItem: FSharpNavigableItem, statusBar: StatusBar, cancellationToken: CancellationToken) = @@ -368,13 +370,13 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = // Prefer open documents in the preview tab. let result = navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) - - if result then + + if result then statusBar.Clear() - else + else statusBar.TempMessage (SR.CannotNavigateUnknown()) - /// Find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition + /// Find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition member this.NavigateToSymbolDeclarationAsync(targetDocument: Document, targetSourceText: SourceText, symbolRange: range, statusBar: StatusBar, cancellationToken: CancellationToken) = asyncMaybe { let! item = this.FindDeclarationOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) @@ -406,10 +408,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let result = match textOpt with | Some (text, fileName) -> - let tmpProjInfo, tmpDocInfo = + let tmpProjInfo, tmpDocInfo = MetadataAsSource.generateTemporaryDocument( - AssemblyIdentity(targetSymbolUse.Symbol.Assembly.QualifiedName), - fileName, + AssemblyIdentity(targetSymbolUse.Symbol.Assembly.QualifiedName), + fileName, metadataReferences) let tmpShownDocOpt = metadataAsSource.ShowDocument(tmpProjInfo, tmpDocInfo.FilePath, SourceText.From(text.ToString())) match tmpShownDocOpt with @@ -427,7 +429,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = ty1.GenericArguments.Count = ty2.GenericArguments.Count && (ty1.GenericArguments, ty2.GenericArguments) ||> Seq.forall2 areTypesEqual - ) + ) if generic then true else @@ -436,7 +438,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = namesEqual && accessPathsEqual // This tries to find the best possible location of the target symbol's location in the metadata source. - // We really should rely on symbol equality within FCS instead of doing it here, + // We really should rely on symbol equality within FCS instead of doing it here, // but the generated metadata as source isn't perfect for symbol equality. checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) |> Seq.tryFind (fun x -> @@ -470,7 +472,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | Some span -> span | _ -> TextSpan() - return span + return span } let span = @@ -478,7 +480,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | Some span -> span | _ -> TextSpan() - let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) + let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) this.NavigateToItem(navItem, statusBar, cancellationToken) true | _ -> @@ -486,9 +488,9 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | _ -> false - if result then + if result then statusBar.Clear() - else + else statusBar.TempMessage (SR.CannotNavigateUnknown()) type internal QuickInfo = @@ -526,7 +528,7 @@ module internal FSharpQuickInfo = let! extLexerSymbol = extDocument.TryFindFSharpLexerSymbolAsync(extSpan.Start, SymbolLookupKind.Greedy, true, true, userOpName) let! _, extCheckFileResults = extDocument.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync - let extQuickInfoText = + let extQuickInfoText = extCheckFileResults.GetToolTip (declRange.StartLine, extLexerSymbol.Ident.idRange.EndColumn, extLineText, extLexerSymbol.FullIsland, FSharpTokenTag.IDENT) @@ -558,7 +560,7 @@ module internal FSharpQuickInfo = let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true, userOpName) let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let! sourceText = document.GetTextAsync cancellationToken - let idRange = lexerSymbol.Ident.idRange + let idRange = lexerSymbol.Ident.idRange let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() @@ -584,13 +586,13 @@ module internal FSharpQuickInfo = SymbolKind = lexerSymbol.Kind } } - match lexerSymbol.Kind with + match lexerSymbol.Kind with | LexerSymbolKind.Keyword | LexerSymbolKind.String -> let! targetQuickInfo = getTargetSymbolQuickInfo (None, FSharpTokenTag.STRING) return lexerSymbol.Range, None, Some targetQuickInfo - - | _ -> + + | _ -> let! symbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) // if the target is in a signature file, adjusting the quick info is unnecessary @@ -606,7 +608,7 @@ module internal FSharpQuickInfo = let! targetQuickInfo = getTargetSymbolQuickInfo (Some symbolUse.Symbol, FSharpTokenTag.IDENT) let! result = - match findSigDeclarationResult with + match findSigDeclarationResult with | FindDeclResult.DeclFound declRange when isSignatureFile declRange.FileName -> asyncMaybe { let! sigQuickInfo = getQuickInfoFromRange(document, declRange, cancellationToken) @@ -666,11 +668,11 @@ type internal FSharpNavigation // To ensure proper navigation decsions, we need to check the type of document the navigation call // is originating from and the target we're provided by default: - // - signature files (.fsi) should navigate to other signature files + // - signature files (.fsi) should navigate to other signature files // - implementation files (.fs) should navigate to other implementation files let (|Signature|Implementation|) filepath = if isSignatureFile filepath then Signature else Implementation - + match initialDoc.FilePath, targetPath with | Signature, Signature | Implementation, Implementation -> @@ -679,7 +681,7 @@ type internal FSharpNavigation // Adjust the target from signature to implementation. | Implementation, Signature -> return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range, statusBar, cancellationToken) - + // Adjust the target from implmentation to signature. | Signature, Implementation -> return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, range, statusBar, cancellationToken) @@ -699,10 +701,10 @@ type internal FSharpNavigation ) |> Task.FromResult - member this.TryGoToDefinition(position, cancellationToken) = + member _.TryGoToDefinition(position, cancellationToken) = let gtd = GoToDefinition(metadataAsSource) let gtdTask = gtd.FindDefinitionTask(initialDoc, position, cancellationToken) - + // Wrap this in a try/with as if the user clicks "Cancel" on the thread dialog, we'll be cancelled. // Task.Wait throws an exception if the task is cancelled, so be sure to catch it. try @@ -718,12 +720,244 @@ type internal FSharpNavigation gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken, statusBar) // 'true' means do it, like Sheev Palpatine would want us to. true - else + else statusBar.TempMessage (SR.CannotDetermineSymbol()) false - with exc -> + with exc -> statusBar.TempMessage(String.Format(SR.NavigateToFailed(), Exception.flattenMessage exc)) - + // Don't show the dialog box as it's most likely that the user cancelled. // Don't make them click twice. - true \ No newline at end of file + true + +[] +type internal SymbolMemberType = + Event | Property | Method | Constructor | Other + static member FromString(s: string) = + match s with + | "E" -> Event + | "P" -> Property + | "CTOR" -> Constructor // That one is "artificial one", so we distinguish constructors. + | "M" -> Method + | _ -> Other + +type internal SymbolPath = { EntityPath: string list; MemberOrValName: string; GenericParameters: int } + +[] +type internal DocCommentId = + | Member of SymbolPath * SymbolMemberType: SymbolMemberType + | Field of SymbolPath + | Type of EntityPath: string list + | None + +type FSharpNavigableLocation(statusBar: StatusBar, metadataAsSource: FSharpMetadataAsSourceService, symbolRange: range, project: Project) = + interface IFSharpNavigableLocation with + member _.NavigateToAsync(_options: FSharpNavigationOptions2, cancellationToken: CancellationToken) : Task = + asyncMaybe { + let targetPath = symbolRange.FileName + let! targetDoc = project.Solution.TryGetDocumentFromFSharpRange (symbolRange, project.Id) + let! targetSource = targetDoc.GetTextAsync(cancellationToken) + let gtd = GoToDefinition(metadataAsSource) + + let (|Signature|Implementation|) filepath = + if isSignatureFile filepath then Signature else Implementation + + match targetPath with + | Signature -> + return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange, statusBar, cancellationToken) + | Implementation -> + return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange, statusBar, cancellationToken) + } + |> Async.map (fun a -> a.IsSome) + |> RoslynHelpers.StartAsyncAsTask cancellationToken + +[)>] +[)>] +type FSharpCrossLanguageSymbolNavigationService() = + let componentModel = Package.GetGlobalService(typeof) :?> ComponentModelHost.IComponentModel + let workspace = componentModel.GetService() + let statusBar = StatusBar(ServiceProvider.GlobalProvider.GetService()) + let metadataAsSource = componentModel.DefaultExportProvider.GetExport().Value + + let tryFindFieldByName (name: string) (e: FSharpEntity) = + let fields = + e.FSharpFields + |> Seq.filter ( + fun x -> + x.DisplayName = name + && not x.IsCompilerGenerated) + |> Seq.map (fun e -> e.DeclarationLocation) + + if fields.Count() <= 0 && (e.IsFSharpUnion || e.IsFSharpRecord) then + Seq.singleton e.DeclarationLocation + else + fields + + let tryFindValByNameAndType (name: string) (symbolMemberType: SymbolMemberType) (genericParametersCount: int) (e: FSharpEntity) (entities: FSharpMemberOrFunctionOrValue seq) = + + let defaultFilter (e: FSharpMemberOrFunctionOrValue) = + (e.DisplayName = name || e.CompiledName = name) && + e.GenericParameters.Count = genericParametersCount + + let isProperty (e: FSharpMemberOrFunctionOrValue) = defaultFilter e && e.IsProperty + let isConstructor (e: FSharpMemberOrFunctionOrValue) = defaultFilter e && e.IsConstructor + + let getLocation (e: FSharpMemberOrFunctionOrValue) = e.DeclarationLocation + + let filteredEntities: range seq = + match symbolMemberType with + | SymbolMemberType.Other + | SymbolMemberType.Method -> + entities + |> Seq.filter defaultFilter + |> Seq.map getLocation + // F# record-specific logic, if navigating to the record's ctor, then navigate to record declaration. + // If we navigating to F# record property, we first check if it's "custom" property, if it's one of the record fields, we search for it in the fields. + | SymbolMemberType.Constructor when e.IsFSharpRecord -> + Seq.singleton e.DeclarationLocation + | SymbolMemberType.Property when e.IsFSharpRecord -> + let properties = + entities + |> Seq.filter isProperty + |> Seq.map getLocation + let fields = tryFindFieldByName name e + Seq.append properties fields + | SymbolMemberType.Constructor -> + entities + |> Seq.filter isConstructor + |> Seq.map getLocation + // When navigating to property for the record, it will be in members bag for custom ones, but will be in the fields in fields. + | SymbolMemberType.Event // Events are just properties` + | SymbolMemberType.Property -> + entities + |> Seq.filter isProperty + |> Seq.map getLocation + + filteredEntities + + let tryFindVal (name: string) (documentCommentId: string) (symbolMemberType: SymbolMemberType) (genericParametersCount: int) (e: FSharpEntity) = + let entities = e.TryGetMembersFunctionsAndValues() + + // First, try and find entity by exact xml signature, return if found, + // otherwise, just try and match by parsed name and number of arguments. + + let entitiesByXmlSig = + entities + |> Seq.filter (fun e -> e.XmlDocSig = documentCommentId) + |> Seq.map (fun e -> e.DeclarationLocation) + + if Seq.isEmpty entitiesByXmlSig then + tryFindValByNameAndType name symbolMemberType genericParametersCount e entities + else + entitiesByXmlSig + + static member internal DocCommentIdToPath (docId:string) = + // The groups are following: + // 1 - type (see below). + // 2 - Path - a dotted path to a symbol. + // 3 - parameters, opetional, only for methods and properties. + // 4 - return type, optional, only for methods. + let docCommentIdRx = Regex(@"^(?\w):(?[\w\d#`.]+)(?\(.+\))?(?:~([\w\d.]+))?$", RegexOptions.Compiled) + + // Parse generic args out of the function name + let fnGenericArgsRx = Regex(@"^(?.+)``(?\d+)$", RegexOptions.Compiled) + // docCommentId is in the following format: + // + // "T:" prefix for types + // "T:N.X.Nested" - type + // "T:N.X.D" - delegate + // + // "M:" prefix is for methods + // "M:N.X.#ctor" - constructor + // "M:N.X.#ctor(System.Int32)" - constructor with one parameter + // "M:N.X.f" - method with unit parameter + // "M:N.X.bb(System.String,System.Int32@)" - method with two parameters + // "M:N.X.gg(System.Int16[],System.Int32[0:,0:])" - method with two parameters, 1d and 2d array + // "M:N.X.op_Addition(N.X,N.X)" - operator + // "M:N.X.op_Explicit(N.X)~System.Int32" - operator with return type + // "M:N.GenericMethod.WithNestedType``1(N.GenericType{``0}.NestedType)" - generic type with one parameter + // "M:N.GenericMethod.WithIntOfNestedType``1(N.GenericType{System.Int32}.NestedType)" - generic type with one parameter + // "M:N.X.N#IX{N#KVP{System#String,System#Int32}}#IXA(N.KVP{System.String,System.Int32})" - explicit interface implementation + // + // "E:" prefix for events + // + // "E:N.X.d". + // + // "F:" prefix for fields + // "F:N.X.q" - field + // + // "P:" prefix for properties + // "P:N.X.prop" - property with getter and setter + + let m = docCommentIdRx.Match(docId) + let t = m.Groups["kind"].Value + match m.Success, t with + | true, ("M" | "P" | "E") -> + // TODO: Probably, there's less janky way of dealing with those. + let parts = m.Groups["entity"].Value.Split('.') + let entityPath = parts[..(parts.Length - 2)] |> List.ofArray + let memberOrVal = parts[parts.Length - 1] + + // Try and parse generic params count from the name (e.g. NameOfTheFunction``1, where ``1 is amount of type parameters) + let genericM = fnGenericArgsRx.Match(memberOrVal) + let (memberOrVal, genericParametersCount) = + if genericM.Success then + (genericM.Groups["entity"].Value, int genericM.Groups["typars"].Value) + else + memberOrVal, 0 + + // A hack/fixup for the constructor name (#ctor in doccommentid and ``.ctor`` in F#) + if memberOrVal = "#ctor" then + DocCommentId.Member ({ EntityPath = entityPath; MemberOrValName = "``.ctor``"; GenericParameters = 0 },SymbolMemberType.Constructor) + else + DocCommentId.Member ({ EntityPath = entityPath; MemberOrValName = memberOrVal; GenericParameters = genericParametersCount }, (SymbolMemberType.FromString t)) + | true, "T" -> + let entityPath = m.Groups["entity"].Value.Split('.') |> List.ofArray + DocCommentId.Type entityPath + | true, "F" -> + let parts = m.Groups["entity"].Value.Split('.') + let entityPath = parts[..(parts.Length - 2)] |> List.ofArray + let memberOrVal = parts[parts.Length - 1] + DocCommentId.Field { EntityPath = entityPath; MemberOrValName = memberOrVal; GenericParameters = 0 } + | _ -> DocCommentId.None + + interface IFSharpCrossLanguageSymbolNavigationService with + member _.TryGetNavigableLocationAsync(assemblyName: string, documentationCommentId: string, cancellationToken: CancellationToken) : Task = + let path = FSharpCrossLanguageSymbolNavigationService.DocCommentIdToPath documentationCommentId + backgroundTask { + let projects = workspace.CurrentSolution.Projects |> Seq.filter (fun p -> p.IsFSharp && p.AssemblyName = assemblyName) + + let mutable locations = Seq.empty + + for project in projects do + let! checker, _, _, options = project.GetFSharpCompilationOptionsAsync(cancellationToken) + let! result = checker.ParseAndCheckProject(options) + + match path with + | DocCommentId.Member ({ EntityPath = entityPath; MemberOrValName = memberOrVal; GenericParameters = genericParametersCount }, memberType) -> + let entity = result.AssemblySignature.FindEntityByPath (entityPath) + entity |> Option.iter (fun e -> + locations <- e |> tryFindVal memberOrVal documentationCommentId memberType genericParametersCount + |> Seq.map (fun m -> (m, project)) + |> Seq.append locations) + | DocCommentId.Field { EntityPath = entityPath; MemberOrValName = memberOrVal } -> + let entity = result.AssemblySignature.FindEntityByPath (entityPath) + entity |> Option.iter (fun e -> + locations <- e |> tryFindFieldByName memberOrVal + |> Seq.map (fun m -> (m, project)) + |> Seq.append locations) + | DocCommentId.Type entityPath -> + let entity = result.AssemblySignature.FindEntityByPath (entityPath) + entity |> Option.iter (fun e -> + locations <- Seq.append locations [e.DeclarationLocation, project]) + | DocCommentId.None -> () + + // TODO: Figure out the way of giving the user choice where to navigate, if there are more than one result + // For now, we only take 1st one, since it's usually going to be only one result (given we process names correctly). + // More results can theoretically be returned in case of method overloads, or when we have both signature and implementation files. + if locations.Count() >= 1 then + let (location, project) = locations.First() + return FSharpNavigableLocation(statusBar, metadataAsSource, location, project) :> IFSharpNavigableLocation + else + return Unchecked.defaultof<_> // returning null here, so Roslyn can fallback to default source-as-metadata implementation. + } diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs index 6df54bce6a7..64d03d619c0 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs @@ -147,7 +147,7 @@ type Song() as x = Assert.IsEmpty(result) [] - let ``Hints are shown for lambdas`` () = + let ``Hints are shown within lambdas`` () = let code = """ let iamboring() = @@ -162,7 +162,7 @@ let iamboring() = Assert.AreEqual(expected, actual) [] - let ``Hints are shown for lambdas with tuples`` () = + let ``Hints are shown within lambdas with tuples`` () = let code = """ let zip4 (l1: 'a list) (l2: 'b list) (l3: 'c list) (l4: 'd list) = @@ -184,6 +184,19 @@ let zip4 (l1: 'a list) (l2: 'b list) (l3: 'c list) (l4: 'd list) = CollectionAssert.AreEquivalent(expected, actual) + [] + let ``Hints are not shown for lambda return types`` () = + let code = + """ +let func = fun () -> 3 +""" + + let document = getFsDocument code + + let result = getTypeHints document + + Assert.IsEmpty(result) + [] let ``Hints are not shown for unfinished expressions`` () = let code = @@ -248,7 +261,6 @@ type Number<'T when IAddition<'T>>(value: 'T) = CollectionAssert.AreEquivalent(expected, actual) - [] let ``Hints are not shown when type is specified`` () = let code = @@ -264,4 +276,4 @@ type MyType() = let result = getTypeHints document - Assert.IsEmpty(result) \ No newline at end of file + Assert.IsEmpty(result) diff --git a/vsintegration/tests/UnitTests/DocCommentIdParserTests.fs b/vsintegration/tests/UnitTests/DocCommentIdParserTests.fs new file mode 100644 index 00000000000..b9cc39f66dc --- /dev/null +++ b/vsintegration/tests/UnitTests/DocCommentIdParserTests.fs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +[] +module Tests.ServiceAnalysis.DocCommentIdParser + +open NUnit.Framework +open Microsoft.VisualStudio.FSharp.Editor + + + + +[] +let ``Test DocCommentId parser``() = + let testData = dict [ + "T:N.X.Nested", DocCommentId.Type ["N"; "X"; "Nested"]; + "M:N.X.#ctor", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "``.ctor``"; GenericParameters = 0 }, SymbolMemberType.Constructor); + "M:N.X.#ctor(System.Int32)", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "``.ctor``"; GenericParameters = 0 }, SymbolMemberType.Constructor); + "M:N.X.f", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "f"; GenericParameters = 0 }, SymbolMemberType.Method); + "M:N.X.bb(System.String,System.Int32@)", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "bb"; GenericParameters = 0 }, SymbolMemberType.Method); + "M:N.X.gg(System.Int16[],System.Int32[0:,0:])", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "gg"; GenericParameters = 0 }, SymbolMemberType.Method); + "M:N.X.op_Addition(N.X,N.X)", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "op_Addition"; GenericParameters = 0 }, SymbolMemberType.Method); + "M:N.X.op_Explicit(N.X)~System.Int32", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "op_Explicit"; GenericParameters = 0 }, SymbolMemberType.Method); + "M:N.GenericMethod.WithNestedType``1(N.GenericType{``0}.NestedType)", DocCommentId.Member ({ EntityPath = ["N"; "GenericMethod"]; MemberOrValName = "WithNestedType"; GenericParameters = 1 }, SymbolMemberType.Method); + "M:N.GenericMethod.WithIntOfNestedType``1(N.GenericType{System.Int32}.NestedType)", DocCommentId.Member ({ EntityPath = ["N"; "GenericMethod"]; MemberOrValName = "WithIntOfNestedType"; GenericParameters = 1 }, SymbolMemberType.Method); + "E:N.X.d", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "d"; GenericParameters = 0 }, SymbolMemberType.Event); + "F:N.X.q", DocCommentId.Field { EntityPath = ["N"; "X"]; MemberOrValName = "q"; GenericParameters = 0 }; + "P:N.X.prop", DocCommentId.Member ({ EntityPath = ["N"; "X"]; MemberOrValName = "prop"; GenericParameters = 0 }, SymbolMemberType.Property); + ] + + let mutable res = "" + for pair in testData do + let docId = pair.Key + let expected = pair.Value + let actual = FSharpCrossLanguageSymbolNavigationService.DocCommentIdToPath(docId) + if actual <> expected then + res <- res + $"DocumentId: {docId}; Expected = %A{expected} = Actual = %A{actual}\n" + + if res <> "" then + failwith res + () \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 8bfcdc25ff1..1ee9d2b4a4a 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -57,6 +57,9 @@ + + CompilerService\DocCommentIdParserTests.fs + CompilerService\UnusedOpensTests.fs