diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index 2687dc328e9..60394d22e23 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -1,5 +1,6 @@ ### Fixed +* Enforce `AttributeTargets` on records. ([PR #17207](https://github.com/dotnet/fsharp/pull/17207)) * Fix a false positive of the `[]` analysis in combination with async. ([Issue #17237](https://github.com/dotnet/fsharp/issues/17237), [PR #17241](https://github.com/dotnet/fsharp/pull/17241)) * Extended #help directive in fsi to show documentation in the REPL. ([PR #17140](https://github.com/dotnet/fsharp/pull/17140)) * Fix internal error when dotting into delegates with multiple type parameters. ([PR #17227](https://github.com/dotnet/fsharp/pull/17227)) diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index 3f9a780974b..a92b5213d8e 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -8,7 +8,8 @@ * Cache delegate in query extensions. ([PR #17130](https://github.com/dotnet/fsharp/pull/17130)) * Update `AllowNullLiteralAttribute` to also use `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173)) +* Update `StructAttribute ` to also use `AttributeTargets.Class` ([PR #17207](https://github.com/dotnet/fsharp/pull/17207)) ### Breaking Changes -* Fixed argument exception throwing inconsistency - accessing an out-of-bounds collection index will now throw `ArgumentOutOfRangeException` instead of `ArgumentException` ([#17328](https://github.com/dotnet/fsharp/pull/17328)) +* Fixed argument exception throwing inconsistency - accessing an out-of-bounds collection index will now throw `ArgumentOutOfRangeException` instead of `ArgumentException` ([#17328](https://github.com/dotnet/fsharp/pull/17328)) \ No newline at end of file diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index be083e74871..b85bce8a198 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -428,6 +428,7 @@ module TcRecdUnionAndEnumDeclarations = let g = cenv.g let m = id.idRange let attrs, _ = TcAttributesWithPossibleTargets false cenv env AttributeTargets.FieldDecl synAttrs + let attrsForProperty, attrsForField = attrs |> List.partition (fun (attrTargets, _) -> (attrTargets &&& AttributeTargets.Property) <> enum 0) let attrsForProperty = (List.map snd attrsForProperty) let attrsForField = (List.map snd attrsForField) @@ -2840,6 +2841,7 @@ module EstablishTypeDefinitionCores = // Allow failure of constructor resolution because Vals for members in the same recursive group are not yet available let attrs, getFinalAttrs = TcAttributesCanFail cenv envinner AttributeTargets.TyconDecl synAttrs let hasMeasureAttr = HasFSharpAttribute g g.attrib_MeasureAttribute attrs + let hasStructAttr = HasFSharpAttribute g g.attrib_StructAttribute attrs let isStructRecordOrUnionType = match synTyconRepr with @@ -2896,6 +2898,12 @@ module EstablishTypeDefinitionCores = // Run InferTyconKind to raise errors on inconsistent attribute sets InferTyconKind g (SynTypeDefnKind.Record, attrs, [], [], inSig, true, m) |> ignore + + if g.langVersion.SupportsFeature(LanguageFeature.EnforceAttributeTargets) then + if hasStructAttr then + TcAttributesWithPossibleTargets false cenv envinner AttributeTargets.Struct synAttrs |> ignore + else + TcAttributesWithPossibleTargets false cenv envinner AttributeTargets.Class synAttrs |> ignore // Note: the table of record fields is initially empty TFSharpTyconRepr (Construct.NewEmptyFSharpTyconData TFSharpRecord) diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 556fb1bcde5..d95659a14f2 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -180,7 +180,7 @@ namespace Microsoft.FSharp.Core inherit Attribute() member _.CompiledName = compiledName - [] + [] [] type StructAttribute() = inherit Attribute() diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 2b3bcf1ac7c..cb8c890171c 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -199,7 +199,7 @@ namespace Microsoft.FSharp.Core /// Adding this attribute to a type causes it to be represented using a CLI struct. /// /// Attributes - [] + [] [] type StructAttribute = inherit Attribute diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsClass.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsClass.fs index 8f587dcf099..a2728dace4e 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsClass.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsClass.fs @@ -24,3 +24,11 @@ type SemanticClassificationItem = type ILTableName(idx: int) = member __.Index = idx static member FromIndex n = ILTableName n + +[] +[] +type Record = { Prop: string } + +[] +[] +type StructRecord = { Prop: string } diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsProperty.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsProperty.fs index 42c94f1e474..ae150dc3034 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsProperty.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeTargetsIsProperty.fs @@ -7,4 +7,4 @@ type PropertyLevelAttribute() = type U = | [] A - | [] B + | [] B \ No newline at end of file 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 1e68d9d5ee8..1e5b49a1d83 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeUsage.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AttributeUsage.fs @@ -638,4 +638,25 @@ type InterruptibleLazy<'T> private (valueFactory: unit -> 'T) = (Error 842, Line 14, Col 3, Line 14, Col 15, "This attribute is not valid for use on this language element") (Error 842, Line 18, Col 3, Line 18, Col 14, "This attribute is not valid for use on this language element") (Error 842, Line 19, Col 3, Line 19, Col 15, "This attribute is not valid for use on this language element") + ] + + // SOURCE= E_AttributeTargetIsClass02.fs # E_AttributeTargetIsClass02.fs + [] + let ``E_AttributeTargetIsClass02_fs`` compilation = + compilation + |> verifyCompile + |> shouldSucceed + + // SOURCE=E_AttributeTargetIsClass02.fs # E_AttributeTargetIsClass02.fs + [] + let ``E_AttributeTargetIsClass02_fs preview`` compilation = + compilation + |> withLangVersionPreview + |> verifyCompile + |> shouldFail + |> withDiagnostics [ + (Error 842, Line 15, Col 3, Line 15, Col 18, "This attribute is not valid for use on this language element") + (Error 842, Line 16, Col 3, Line 16, Col 15, "This attribute is not valid for use on this language element") + (Error 842, Line 20, Col 3, Line 20, Col 14, "This attribute is not valid for use on this language element") + (Error 842, Line 21, Col 3, Line 21, Col 18, "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_AttributeTargetIsClass02.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsClass02.fs new file mode 100644 index 00000000000..f95f54b8354 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsClass02.fs @@ -0,0 +1,24 @@ +open System + +[] +type ClassTargetAttribute() = + inherit Attribute() + +[] +type InterfaceTargetAttribute() = + inherit Attribute() + +[] +type StructTargetAttribute() = + inherit Attribute() + +[] +[] +[] +type Record = { Prop: string } + +[] +[] +[] +[] +type StructRecord = { Prop: string } \ No newline at end of file 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 index 4627c3680dd..65a9ea17bd8 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsProperty01.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/E_AttributeTargetIsProperty01.fs @@ -12,4 +12,5 @@ type PropertyOrFieldLevelAttribute() = type SomeUnion = | [] Case1 of int // Should fail -| [] Case2 of int // Should fail \ No newline at end of file +| [] Case2 of int // Should fail +