Skip to content

Conversation

@edgarfgp
Copy link
Contributor

@edgarfgp edgarfgp commented Feb 29, 2024

Description

Fixes #13526

Old rule

  • Classes and Struct were using AttributeTargets.Class ||| AttributeTargets.Interface ||| AttributeTargets.Delegate ||| AttributeTargets.Struct ||| AttributeTargets.Enum

New rule

  • Classes use AttributeTargets.Class
  • Structs use AttributeTargets.Struct

Before

open System

[<AttributeUsage(AttributeTargets.Class)>]
type CustomClassAttribute() =
    inherit Attribute()

[<AttributeUsage(AttributeTargets.Struct)>]
type CustomStructAttribute() =
    inherit Attribute()

[<CustomClass; CustomStruct>] // No Error
type Class() = class end

[<CustomClass; CustomStruct>] // No Error
type Struct(x: int) = struct end

[<CustomClass>] // No Error
[<Struct>]
type Struct2(x: int) = ...

[<CustomStruct>] // No Error
[<Class>]
type Class2() = ...

After

open System

[<AttributeUsage(AttributeTargets.Class)>]
type CustomClassAttribute() =
    inherit Attribute()

[<AttributeUsage(AttributeTargets.Struct)>]
type CustomStructAttribute() =
    inherit Attribute()

[<CustomClass; CustomStruct>] // Error as expected as you can not use AttributeTargets.Struct in a class
type Class() = class end

[<CustomClass; CustomStruct>] // Error as expected as you can not use AttributeTargets.Class in a struct
type Struct(x: int) = struct end

[<CustomClass>] // Error as expected as you can not use AttributeTargets.Class in a struct
[<Struct>]
type Struct2(x: int) = ...

[<CustomStruct>] // Error as expected as you can not use AttributeTargets.Struct in a class
[<Class>]
type Class2() = ...

Checklist

  • Test cases added

  • Performance benchmarks added in case of performance changes

  • Release notes entry updated:

    Please make sure to add an entry with short succinct description of the change as well as link to this pull request to the respective release notes file, if applicable.

    Release notes files:

    • If anything under src/Compiler has been changed, please make sure to make an entry in docs/release-notes/.FSharp.Compiler.Service/<version>.md, where <version> is usually "highest" one, e.g. 42.8.200
    • If language feature was added (i.e. LanguageFeatures.fsi was changed), please add it to docs/releae-notes/.Language/preview.md
    • If a change to FSharp.Core was made, please make sure to edit docs/release-notes/.FSharp.Core/<version>.md where version is "highest" one, e.g. 8.0.200.

    Information about the release notes entries format can be found in the documentation.
    Example:

    If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 29, 2024

❗ Release notes required


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/FSharp.Core docs/release-notes/.FSharp.Core/8.0.300.md
src/Compiler docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
LanguageFeatures.fsi docs/release-notes/.Language/preview.md

@edgarfgp edgarfgp marked this pull request as ready for review March 1, 2024 14:02
@edgarfgp edgarfgp requested a review from a team as a code owner March 1, 2024 14:02
@edgarfgp
Copy link
Contributor Author

edgarfgp commented Mar 1, 2024

This is ready

@vzarytovskii
Copy link
Member

This will likely break the following scenario:

[<Struct; RequireQualifiedAccess>]
type x = { y: int }
let xx = { x.y = 123 } // Works and will emit "The record label 'y' is not defined" diagnostic without "x."

Will it now throw FS842? In case if it will, we probably can't have it as an error by default, too many things may break and either a bunch of fixes will be needed to all sorts of attributes in libraries or rewrites of the underlying types.

@edgarfgp
Copy link
Contributor Author

edgarfgp commented Mar 1, 2024

This will likely break the following scenario:

[<Struct; RequireQualifiedAccess>]
type x = { y: int }
let xx = { x.y = 123 } // Works and will emit "The record label 'y' is not defined" diagnostic without "x."

Will it now throw FS842? In case if it will, we probably can't have it as an error by default, too many things may break and either a bunch of fixes will be needed to all sorts of attributes in libraries or rewrites of the underlying types.

This is unaffected as this PR checks for SynTypeDefnSimpleRepr.General as opposed SynTypeDefnSimpleRepr.Record

@vzarytovskii
Copy link
Member

This will likely break the following scenario:

[<Struct; RequireQualifiedAccess>]
type x = { y: int }
let xx = { x.y = 123 } // Works and will emit "The record label 'y' is not defined" diagnostic without "x."

Will it now throw FS842? In case if it will, we probably can't have it as an error by default, too many things may break and either a bunch of fixes will be needed to all sorts of attributes in libraries or rewrites of the underlying types.

This is unaffected as this PR checks for SynTypeDefnSimpleRepr.General as opposed SynTypeDefnSimpleRepr.Record

Ah, good.

@vzarytovskii
Copy link
Member

What about something like

<RequireQualifiedAccess; Struct; NoComparison>]
type ValueOrCancelled<'TResult> =
    | Value of result: 'TResult
    | Cancelled of ``exception``: OperationCanceledException

There might be a bunch of similar cases, with rqa in particular. At the very least we should extend the attribute to structs (possibly other targets).

@edgarfgp
Copy link
Contributor Author

edgarfgp commented Mar 1, 2024

What about something like

<RequireQualifiedAccess; Struct; NoComparison>]
type ValueOrCancelled<'TResult> =
    | Value of result: 'TResult
    | Cancelled of ``exception``: OperationCanceledException

There might be a bunch of similar cases, with rqa in particular. At the very least we should extend the attribute to structs (possibly other targets).

This is also unaffected as this is PR does NOT check SynTypeDefnSimpleRepr.Union only checks for SynTypeDefnSimpleRepr.General

  • SynTypeDefnSimpleRepr.General will cover cases like:
type MyClass() = class end

[<Class>]
type MyClass = ...

type MyStruct(x: int) = struct end

[<Struct>]
type MyStruct(x: int) = ...

So records, union and it generic counterparts still works as before.

Currently RequireQualifiedAccessAttribute is defined as follows. But I guess we can add AttributeTargets.Struct too

    [<AttributeUsage(AttributeTargets.Class, AllowMultiple=false)>]
    [<Sealed>]
    type RequireQualifiedAccessAttribute() =
        inherit Attribute()

@edgarfgp
Copy link
Contributor Author

edgarfgp commented Mar 4, 2024

@auduchinok Could you have a look this one please ?

@KevinRansom
Copy link
Contributor

This feels more like a bug fix than a language feature, does it really need to be tied to a language version flag?

@edgarfgp
Copy link
Contributor Author

edgarfgp commented Mar 6, 2024

This feels more like a bug fix than a language feature, does it really need to be tied to a language version flag?

Agreed it is a bug, But based on previous PR's on AttributeTargets this code will stop compiling in the next version so we do not want to break existing code, that being said happy to remove the language version flag if the team is happy with

Copy link
Contributor

@psfinaki psfinaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done and well tested. Thanks!
@T-Gro?

@vzarytovskii vzarytovskii enabled auto-merge (squash) March 11, 2024 10:51
@T-Gro
Copy link
Member

T-Gro commented Mar 11, 2024

On top of RQA and AutoOpen, attributes where I think this change can potentially introduce breaks in user code:

  • AutoSerializableAttribute
  • StructuralComparisonAttribute
  • StructuralEqualityAttribute
  • ReflectedDefinitionAttribute (e.g. on a struct without fields and with static members)

(I am omitting all attributes working on DUs/records, as this is not the scope of this PR - but might be in the scope of future ones)

@edgarfgp
Copy link
Contributor Author

  • ReflectedDefinitionAttribute

@T-Gro Yeah I think the mentioned attributes should updated to target AttributeTargets.Struct orAttributeTargets.Class when needed. It would be good to do that in a separate PR ?

@vzarytovskii
Copy link
Member

vzarytovskii commented Mar 11, 2024

TypeProviderAttribute also needs to be looked at.

@edgarfgp
Copy link
Contributor Author

TypeProviderAttribute also needs to be looked at.

@vzarytovskii Can you create an issue with all the Attributes that needs updating. Happy to do the actual work

@vzarytovskii
Copy link
Member

vzarytovskii commented Mar 11, 2024

TypeProviderAttribute also needs to be looked at.

@vzarytovskii Can you create an issue with all the Attributes that needs updating. Happy to do the actual work

I will update them as part of this PR, so potentially breaking change won't accidentally leak into merge/release.

@vzarytovskii
Copy link
Member

TypeProviderAttribute also needs to be looked at.

@vzarytovskii Can you create an issue with all the Attributes that needs updating. Happy to do the actual work

I will update them as part of this PR, so potentially breaking change won't accidentally leak into merge/release.

Done

@vzarytovskii
Copy link
Member

Nice finding @edgarfgp . See the other comment - I would rather go by extending RQA and AutoOpen to also cover structs, instead of banning it.

This is done

@edgarfgp
Copy link
Contributor Author

Thanks @vzarytovskii You rock 🎸

@vzarytovskii
Copy link
Member

@T-Gro can you re-review pls?

auto-merge was automatically disabled March 12, 2024 11:07

Head branch was pushed to by a user without write access

Copy link
Member

@T-Gro T-Gro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now, thanks for incorporating the changes needed.

@psfinaki
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@edgarfgp
Copy link
Contributor Author

I guess this PR is taking all merge conflicts today haha

@edgarfgp
Copy link
Contributor Author

Can we have this now in ?

@psfinaki psfinaki enabled auto-merge (squash) March 13, 2024 16:25
@psfinaki psfinaki merged commit 4f388c0 into dotnet:main Mar 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

AttributeUsage doesn't distinguish AttributeTargets.Class and AttributeTargets.Struct

5 participants