diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
index 56f71544b5..83a1b23aa9 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
@@ -14,6 +14,7 @@
* Allow calling method with both Optional and ParamArray. ([#PR 16688](https://github.com/dotnet/fsharp/pull/16688), [suggestions #1120](https://github.com/fsharp/fslang-suggestions/issues/1120))
* Fix release inline optimization, which leads to MethodAccessException if used with `assembly:InternalsVisibleTo`` attribute. ([Issue #16105](https://github.com/dotnet/fsharp/issues/16105), ([PR #16737](https://github.com/dotnet/fsharp/pull/16737))
* Enforce AttributeTargets on let values and functions. ([PR #16692](https://github.com/dotnet/fsharp/pull/16692))
+* Enforce AttributeTargets on union case declarations. ([PR #16764](https://github.com/dotnet/fsharp/pull/16764))
### Added
diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md
index f39dd17256..a4dd48f213 100644
--- a/docs/release-notes/.Language/preview.md
+++ b/docs/release-notes/.Language/preview.md
@@ -9,6 +9,7 @@
* Allow extension methods without type attribute work for types from imported assemblies. ([PR #16368](https://github.com/dotnet/fsharp/pull/16368))
* Enforce AttributeTargets on let values and functions. ([PR #16692](https://github.com/dotnet/fsharp/pull/16692))
+* Enforce AttributeTargets on union case declarations. ([PR #16764](https://github.com/dotnet/fsharp/pull/16764))
### Changed
diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs
index 5bb8d4081a..c82a5edf6f 100644
--- a/src/Compiler/Checking/CheckDeclarations.fs
+++ b/src/Compiler/Checking/CheckDeclarations.fs
@@ -511,7 +511,14 @@ module TcRecdUnionAndEnumDeclarations =
let TcUnionCaseDecl (cenv: cenv) env parent thisTy thisTyInst tpenv hasRQAAttribute (SynUnionCase(Attributes synAttrs, SynIdent(id, _), args, xmldoc, vis, m, _)) =
let g = cenv.g
- let attrs = TcAttributes cenv env AttributeTargets.UnionCaseDecl synAttrs // the attributes of a union case decl get attached to the generated "static factory" method
+ let attrs =
+ // The attributes of a union case decl get attached to the generated "static factory" method
+ // Enforce that the union-cases can only be targeted by attributes with AttributeTargets.Method
+ if g.langVersion.SupportsFeature(LanguageFeature.EnforceAttributeTargetsUnionCaseDeclarations) then
+ TcAttributes cenv env AttributeTargets.Method synAttrs
+ else
+ TcAttributes cenv env AttributeTargets.UnionCaseDecl synAttrs
+
let vis, _ = ComputeAccessAndCompPath g env None m vis None parent
let vis = CombineReprAccess parent vis
diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs
index 11ecd5216f..f7e048223b 100644
--- a/src/Compiler/Checking/CheckExpressions.fs
+++ b/src/Compiler/Checking/CheckExpressions.fs
@@ -10875,17 +10875,31 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt
errorR(Error(FSComp.SR.tcLiteralCannotHaveGenericParameters(), mBinding))
if g.langVersion.SupportsFeature(LanguageFeature.EnforceAttributeTargetsOnFunctions) && memberFlagsOpt.IsNone && not attrs.IsEmpty then
- let rhsIsFunction = isFunTy g overallPatTy
- let lhsIsFunction = isFunTy g overallExprTy
- let attrTgt =
- match rhsIsFunction, lhsIsFunction with
- | false, false when declaredTypars.IsEmpty -> AttributeTargets.Field ||| AttributeTargets.Property ||| AttributeTargets.ReturnValue
- | _, _ -> AttributeTargets.Method ||| AttributeTargets.ReturnValue
-
- TcAttributesWithPossibleTargets false cenv env attrTgt attrs |> ignore
+ TcAttributeTargetsOnLetBindings cenv env attrs overallPatTy overallExprTy (not declaredTypars.IsEmpty)
CheckedBindingInfo(inlineFlag, valAttribs, xmlDoc, tcPatPhase2, explicitTyparInfo, nameToPrelimValSchemeMap, rhsExprChecked, argAndRetAttribs, overallPatTy, mBinding, debugPoint, isCompGen, literalValue, isFixed), tpenv
+// Note:
+// - Let bound values can only have attributes that uses AttributeTargets.Field ||| AttributeTargets.Property ||| AttributeTargets.ReturnValue
+// - Let function bindings can only have attributes that uses AttributeTargets.Method ||| AttributeTargets.ReturnValue
+and TcAttributeTargetsOnLetBindings (cenv: cenv) env attrs overallPatTy overallExprTy areTyparsDeclared =
+ let attrTgt =
+ if
+ // It's a type function:
+ // let x<'a> = …
+ areTyparsDeclared
+ // It's a regular function-valued binding:
+ // let f x = …
+ // let f = fun x -> …
+ || isFunTy cenv.g overallPatTy
+ || isFunTy cenv.g overallExprTy
+ then
+ AttributeTargets.ReturnValue ||| AttributeTargets.Method
+ else
+ AttributeTargets.ReturnValue ||| AttributeTargets.Field ||| AttributeTargets.Property
+
+ TcAttributes cenv env attrTgt attrs |> ignore
+
and TcLiteral (cenv: cenv) overallTy env tpenv (attrs, synLiteralValExpr) =
let g = cenv.g
diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt
index cdd483858e..52a5a18482 100644
--- a/src/Compiler/FSComp.txt
+++ b/src/Compiler/FSComp.txt
@@ -1595,6 +1595,7 @@ featureChkTailCallAttrOnNonRec,"Raises warnings if the 'TailCall' attribute is u
featureUnionIsPropertiesVisible,"Union case test properties"
featureBooleanReturningAndReturnTypeDirectedPartialActivePattern,"Boolean-returning and return-type-directed partial active patterns"
featureEnforceAttributeTargetsOnFunctions,"Enforce AttributeTargets on functions"
+featureEnforceAttributeTargetsUnionCaseDeclarations,"Enforce AttributeTargets on union case declarations"
featureLowerInterpolatedStringToConcat,"Optimizes interpolated strings in certain cases, by lowering to concatenation"
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."
diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs
index 6e2d91bc6c..dd7133224e 100644
--- a/src/Compiler/Facilities/LanguageFeatures.fs
+++ b/src/Compiler/Facilities/LanguageFeatures.fs
@@ -86,6 +86,7 @@ type LanguageFeature =
| WarningWhenTailCallAttrOnNonRec
| BooleanReturningAndReturnTypeDirectedPartialActivePattern
| EnforceAttributeTargetsOnFunctions
+ | EnforceAttributeTargetsUnionCaseDeclarations
| LowerInterpolatedStringToConcat
/// LanguageVersion management
@@ -200,6 +201,7 @@ type LanguageVersion(versionText) =
LanguageFeature.UnionIsPropertiesVisible, previewVersion
LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern, previewVersion
LanguageFeature.EnforceAttributeTargetsOnFunctions, previewVersion
+ LanguageFeature.EnforceAttributeTargetsUnionCaseDeclarations, previewVersion
LanguageFeature.LowerInterpolatedStringToConcat, previewVersion
]
@@ -345,6 +347,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern ->
FSComp.SR.featureBooleanReturningAndReturnTypeDirectedPartialActivePattern ()
| LanguageFeature.EnforceAttributeTargetsOnFunctions -> FSComp.SR.featureEnforceAttributeTargetsOnFunctions ()
+ | LanguageFeature.EnforceAttributeTargetsUnionCaseDeclarations -> FSComp.SR.featureEnforceAttributeTargetsUnionCaseDeclarations ()
| LanguageFeature.LowerInterpolatedStringToConcat -> FSComp.SR.featureLowerInterpolatedStringToConcat ()
/// Get a version string associated with the given feature.
diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi
index 4888479d86..5be15a8828 100644
--- a/src/Compiler/Facilities/LanguageFeatures.fsi
+++ b/src/Compiler/Facilities/LanguageFeatures.fsi
@@ -77,6 +77,7 @@ type LanguageFeature =
| WarningWhenTailCallAttrOnNonRec
| BooleanReturningAndReturnTypeDirectedPartialActivePattern
| EnforceAttributeTargetsOnFunctions
+ | EnforceAttributeTargetsUnionCaseDeclarations
| LowerInterpolatedStringToConcat
/// LanguageVersion management
diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf
index 875c2a1e4b..c46e652979 100644
--- a/src/Compiler/xlf/FSComp.txt.cs.xlf
+++ b/src/Compiler/xlf/FSComp.txt.cs.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesVyvolá chyby pro přepsání jiných než virtuálních členů
diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf
index 7f878306f1..56f7493d1d 100644
--- a/src/Compiler/xlf/FSComp.txt.de.xlf
+++ b/src/Compiler/xlf/FSComp.txt.de.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesLöst Fehler für Außerkraftsetzungen nicht virtueller Member aus.
diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf
index 23f6709525..a35a6bce59 100644
--- a/src/Compiler/xlf/FSComp.txt.es.xlf
+++ b/src/Compiler/xlf/FSComp.txt.es.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesGenera errores para invalidaciones de miembros no virtuales
diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf
index 4777bed578..aeeb53ac93 100644
--- a/src/Compiler/xlf/FSComp.txt.fr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.fr.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesDéclenche des erreurs pour les remplacements de membres non virtuels
diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf
index 683a5a6cec..390160ee95 100644
--- a/src/Compiler/xlf/FSComp.txt.it.xlf
+++ b/src/Compiler/xlf/FSComp.txt.it.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesGenera errori per gli override dei membri non virtuali
diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf
index 7618f5faa1..e57dac6f31 100644
--- a/src/Compiler/xlf/FSComp.txt.ja.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ja.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overrides仮想メンバー以外のオーバーライドに対してエラーを発生させます
diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf
index 8b2ddd074b..2d6a4e5e22 100644
--- a/src/Compiler/xlf/FSComp.txt.ko.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ko.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overrides비가상 멤버 재정의에 대한 오류 발생
diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf
index 43f58a77a9..db56cd3ba4 100644
--- a/src/Compiler/xlf/FSComp.txt.pl.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pl.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesZgłasza błędy w przypadku przesłonięć elementów innych niż wirtualne
diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
index f3e8b58378..7566fb49ae 100644
--- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesGera erros para substituições de membros não virtuais
diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf
index d6f3358a69..90c39f565e 100644
--- a/src/Compiler/xlf/FSComp.txt.ru.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ru.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesВызывает ошибки при переопределениях невиртуальных элементов
diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf
index 2f313d8c49..496bc35ef9 100644
--- a/src/Compiler/xlf/FSComp.txt.tr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.tr.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overridesSanal olmayan üyelerde geçersiz kılmalar için hatalar oluştur
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
index d115f81d27..94bd2ea062 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overrides引发非虚拟成员替代的错误
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
index 2dadd07ff7..9334199ba0 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
@@ -297,6 +297,11 @@
Enforce AttributeTargets on functions
+
+ Enforce AttributeTargets on union case declarations
+ Enforce AttributeTargets on union case declarations
+
+ Raises errors for non-virtual members overrides引發非虛擬成員覆寫的錯誤
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsMethod01.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsMethod01.fs
index b8de3b3349..4018ae74be 100644
--- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsMethod01.fs
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsMethod01.fs
@@ -48,3 +48,10 @@ let test1 =
|> (=) 6
if not test1 then failwith "Failed: 1"
+
+[]
+type MethodLevelAttribute() =
+ inherit Attribute()
+
+type SomeUnion =
+| [] Case1 of int
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeUsage.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeUsage.fs
index b9d20fe0ca..5d930ea1e1 100644
--- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeUsage.fs
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeUsage.fs
@@ -64,6 +64,14 @@ module CustomAttributes_AttributeUsage =
|> verifyCompileAndRun
|> shouldSucceed
+ // SOURCE=AttributeTargetsIsMethod01.fs # AttributeTargetsIsMethod01.fs
+ []
+ let ``AttributeTargetsIsMethod01_fs preview`` compilation =
+ compilation
+ |> withLangVersionPreview
+ |> verifyCompileAndRun
+ |> shouldSucceed
+
// SOURCE=ConditionalAttribute.fs # ConditionalAttribute.fs
[]
let ``ConditionalAttribute_fs`` compilation =
@@ -352,5 +360,45 @@ module CustomAttributes_AttributeUsage =
|> withDiagnostics [
(Warning 2003, Line 5, Col 59, Line 5, Col 68, "The attribute System.Reflection.AssemblyFileVersionAttribute specified version '9.8.*.6', but this value is invalid and has been ignored")
]
-
-
+
+ // SOURCE=E_AttributeTargetIsField03.fs # E_AttributeTargetIsField03.fs
+ []
+ let ``E_AttributeTargetIsField03_fs`` compilation =
+ compilation
+ |> verifyCompile
+ |> shouldFail
+ |> withDiagnostics [
+ (Error 842, Line 14, Col 5, Line 14, Col 15, "This attribute is not valid for use on this language element")
+ ]
+
+ // SOURCE=E_AttributeTargetIsField03.fs # E_AttributeTargetIsField03.fs
+ []
+ let ``E_AttributeTargetIsField03_fs preview`` compilation =
+ compilation
+ |> withLangVersionPreview
+ |> verifyCompile
+ |> shouldFail
+ |> withDiagnostics [
+ (Error 842, Line 14, Col 5, Line 14, Col 15, "This attribute is not valid for use on this language element")
+ (Error 842, Line 15, Col 5, Line 15, Col 25, "This attribute is not valid for use on this language element")
+ ]
+
+
+ // SOURCE=E_AttributeTargetIsProperty01.fs # E_AttributeTargetIsField03.fs
+ []
+ let ``E_AttributeTargetIsProperty01_fs`` compilation =
+ compilation
+ |> verifyCompile
+ |> shouldSucceed
+
+ // SOURCE=E_AttributeTargetIsProperty01.fs # E_AttributeTargetIsField03.fs
+ []
+ let ``E_AttributeTargetIsProperty01_fs preview`` compilation =
+ compilation
+ |> withLangVersionPreview
+ |> verifyCompile
+ |> shouldFail
+ |> withDiagnostics [
+ (Error 842, Line 14, Col 5, Line 14, Col 18, "This attribute is not valid for use on this language element")
+ (Error 842, Line 15, Col 5, Line 15, Col 25, "This attribute is not valid for use on this language element")
+ ]
\ No newline at end of file
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsField03.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsField03.fs
new file mode 100644
index 0000000000..cc5fb20b49
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsField03.fs
@@ -0,0 +1,16 @@
+// This tests that AttributeTargets.Field is not allowed in union-case declaration
+
+open System
+
+[]
+type FieldLevelAttribute() =
+ inherit Attribute()
+
+[]
+type PropertyOrFieldLevelAttribute() =
+ inherit Attribute()
+
+type SomeUnion =
+| [] Case1 of int // Should fail
+| [] Case2 of int // Should fail
+
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsProperty01.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsProperty01.fs
new file mode 100644
index 0000000000..4627c3680d
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsProperty01.fs
@@ -0,0 +1,15 @@
+// This tests that AttributeTargets.Property is not allowed in union-case declaration
+
+open System
+
+[]
+type PropertyLevelAttribute() =
+ inherit Attribute()
+
+[]
+type PropertyOrFieldLevelAttribute() =
+ inherit Attribute()
+
+type SomeUnion =
+| [] Case1 of int // Should fail
+| [] Case2 of int // Should fail
\ No newline at end of file