Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce attribute targets on functions #16692

Merged
merged 25 commits into from Feb 26, 2024

Conversation

edgarfgp
Copy link
Contributor

@edgarfgp edgarfgp commented Feb 12, 2024

Description

Fixes #8547

Currently the compiler does not enforce AttributeTargets in values and function bindings

Before

open System

[<AttributeUsage(AttributeTargets.Method)>]
type MethodOnlyAttribute() = 
  inherit System.Attribute()

[<MethodOnly>] // Compiles as expected 
let someFunction () = "abc"

[<MethodOnly>] //  Compiles
let someValue =  "def"

[<AttributeUsage(AttributeTargets.Field)>]
type FieldOnlyAttribute() = 
  inherit System.Attribute()

[<FieldOnly>] // Compiles 
let someFunction () = "abc"

[<FieldOnly>]  // Compiles
let someValue =  "def"

After

open System

[<AttributeUsage(AttributeTargets.Method)>]
type MethodOnlyAttribute() = 
  inherit System.Attribute()

[<MethodOnly>] // Compiles as expected
let someFunction () = "abc"

[<MethodOnly>] // Does not compile as expected
let someValue =  "def"

[<AttributeUsage(AttributeTargets.Field)>]
type FieldOnlyAttribute() = 
  inherit System.Attribute()

[<FieldOnly>]  // Does not compile as expected
let someFunction () = "abc"

[<FieldOnly>]  // Compiles as expected
let someValue =  "def"

Important

AttributeTargets are not covered by the lang spec The new rules are based on me reading document and using sharplab to see what is the produced code in C#

Old Rules

  • Values and Function bindings were both using AttributeTargets.Field ||| AttributeTargets.Method ||| AttributeTargets.Property ||| AttributeTargets.ReturnValue

New Rules

  • Values : AttributeTargets.Field ||| AttributeTargets.Property ||| AttributeTargets.ReturnValue
  • Function bindings: AttributeTargets.Method ||| AttributeTargets.ReturnValue

Links

Note: this PR only covers module and class let bindings for now. Will create follow up PR for members, properties etc

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.

Copy link
Contributor

github-actions bot commented Feb 12, 2024

❗ Release notes required


✅ Found changes and release notes in following paths:

Change path Release notes path Description
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 February 22, 2024 15:22
@edgarfgp edgarfgp requested a review from a team as a code owner February 22, 2024 15:22
@edgarfgp
Copy link
Contributor Author

edgarfgp commented Feb 22, 2024

This is ready

@vzarytovskii
Copy link
Member

Could you please add a small description of the new rules? Like

  • Method target:
    • Before: methods, let bindings
    • After: methods, let bindings for functions
      etc, if it's not too much of a hassle.

Will help discoverability of the change + when F# 9 release notes will be prepared.

@edgarfgp
Copy link
Contributor Author

Could you please add a small description of the new rules? Like

Yeah I was working on this. Updated the PR description.

Please check the tests and sharplab links and let me know if we can add more test cases

Copy link
Member

@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.

Nice, useful and clear work, thanks for the good PR description, testing and splitting changes into multiple PRs.

As for the TailCall, yeah I think we should at least put the check behind the flag. Maybe we can have even a separate message specifically for the TailCall case although that does sound a bit ad-hoc.

@edgarfgp
Copy link
Contributor Author

edgarfgp commented Feb 23, 2024

Nice, useful and clear work, thanks for the good PR description, testing and splitting changes into multiple PRs.

As for the TailCall, yeah I think we should at least put the check behind the flag. Maybe we can have even a separate message specifically for the TailCall case although that does sound a bit ad-hoc.

@vzarytovskii @psfinaki with this PR and checking for the language version flag. We will get

  • F# 8 single warning The TailCall attribute should only be applied to recursive functions. as it is now
  • F# 9 a waring like previous version + the error for AttributeTargets.

Currently we have 2 error number and messages for enforcing the right AttributeTarget in let bindings

  • 3861,chkTailCallAttrOnNonRec,"The TailCall attribute should only be applied to recursive functions."
  • 842,tcAttributeIsNotValidForLanguageElement,"This attribute is not valid for use on this language element"

My proposal would be to in F#9 use in both cases 842(Potentially improve the error message to be more friendly and omit / replace 3861

The most used scenario for this ie when you use a testing library like Xunit and you use the wrong target your tests silently be ignored

module MyTests

open Xunit

[<Fact>]
let ``This test will be ignored`` = 
  Assert.True(false)

[<Fact>
let ``This test will not be ignored`` () = 
  Assert.True(false)

Even the compiler had couple tests suffering from this See #16610

Edit: Xunit related issue xunit/xunit#2064

Copy link
Member

@vzarytovskii vzarytovskii left a comment

Choose a reason for hiding this comment

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

Yeah, that works, thank!

@vzarytovskii vzarytovskii enabled auto-merge (squash) February 24, 2024 19:30
@psfinaki
Copy link
Member

psfinaki commented Feb 26, 2024

Oh actually if we get rid of that thing with tests, it would be awesome, I've stumbled on that myself multiple times :)

@vzarytovskii vzarytovskii merged commit 1816538 into dotnet:main Feb 26, 2024
29 checks passed
@edgarfgp
Copy link
Contributor Author

edgarfgp commented Mar 4, 2024

@auduchinok FYI more AttributeTargets changes

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.

F# compiler does not enforce attribute targets
4 participants