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

Validate diagnostic ID in Experimental attribute #70397

Merged
merged 11 commits into from
Oct 20, 2023
Merged

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Oct 16, 2023

Corresponding spec update: dotnet/csharplang#7597

Relates to #68702
Relates to dotnet/runtime#85444

@jcouv jcouv self-assigned this Oct 16, 2023
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Oct 16, 2023
@jcouv jcouv marked this pull request as ready for review October 17, 2023 15:57
@jcouv jcouv requested a review from a team as a code owner October 17, 2023 15:57
@jcouv jcouv added this to the 17.9 milestone Oct 17, 2023
@jcouv jcouv changed the base branch from release/dev17.8 to main October 17, 2023 17:17
@jcouv
Copy link
Member Author

jcouv commented Oct 17, 2023

Moved out to 17.9


comp.VerifyDiagnostics(
// 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
// 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Copy link
Member

@cston cston Oct 17, 2023

Choose a reason for hiding this comment

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

error CS9204

Is WRN_Experimental ever reported as a warning? If so, are we testing that case? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added some command-line tests to configure the severity on various diagnostics

extends [mscorlib]System.Object
{
// Experimental("Diag 01")
.custom instance void System.Diagnostics.CodeAnalysis.ExperimentalAttribute::.ctor(string) = ( 01 00 07 44 69 61 67 20 30 31 00 00 )
Copy link
Member

@cston cston Oct 17, 2023

Choose a reason for hiding this comment

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

( 01 00 07 44 69 61 67 20 30 31 00 00 )

Consider using = { string('Diag 01') } instead. #Resolved

{
.get instance string System.Diagnostics.CodeAnalysis.ExperimentalAttribute::get_UrlFormat()
.set instance void System.Diagnostics.CodeAnalysis.ExperimentalAttribute::set_UrlFormat(string)
}
Copy link
Member

@cston cston Oct 17, 2023

Choose a reason for hiding this comment

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

Consider removing if not needed, including backing field declaration. #Resolved

@@ -7809,4 +7809,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ExpectedInterpolatedString" xml:space="preserve">
<value>Expected interpolated string</value>
</data>
<data name="ERR_InvalidExperimentalDiagID" xml:space="preserve">
<value>The argument to the 'Experimental' attribute must be a valid identifier</value>
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 18, 2023

Choose a reason for hiding this comment

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

The argument

"The first argument"? The spec implies that there could be more than one argument and the restriction is for the first one. #Closed

[Theory, CombinatorialData]
public void NullDiagnosticId(bool inSource)
[Fact]
public void NullDiagnosticId()
{
var libSrc = """
[System.Diagnostics.CodeAnalysis.Experimental(null)]
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 18, 2023

Choose a reason for hiding this comment

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

null

I think it would be good to test a scenario:

  • without an argument
  • the argument isn't a string. An integer literal 1, for example
    #Closed

if (arguments.Attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute)
&& !SyntaxFacts.IsValidIdentifier((string?)arguments.Attribute.CommonConstructorArguments[0].ValueInternal))
{
arguments.Diagnostics.DiagnosticBag.Add(ErrorCode.ERR_InvalidExperimentalDiagID, arguments.AttributeSyntaxOpt.Location);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 18, 2023

Choose a reason for hiding this comment

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

arguments.AttributeSyntaxOpt.Location

I think we have a helper that finds syntax for specific argument when there is one #Closed

[Theory, CombinatorialData]
public void DiagnosticIdWithTrailingNewline(bool inSource)
[Fact]
public void DiagnosticIdWithTrailingNewline()
{
var libSrc = """
[System.Diagnostics.CodeAnalysis.Experimental("Diag\n")]
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 18, 2023

Choose a reason for hiding this comment

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

("Diag\n")

Are we testing with more than one argument? #Closed

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 2)

if (arguments.Attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute)
&& !SyntaxFacts.IsValidIdentifier((string?)arguments.Attribute.CommonConstructorArguments[0].ValueInternal))
{
Debug.Assert(arguments.AttributeSyntaxOpt is not null);
Copy link
Member

@cston cston Oct 18, 2023

Choose a reason for hiding this comment

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

Debug.Assert(arguments.AttributeSyntaxOpt is not null);

This is already asserted earlier. #Resolved

cston
cston previously approved these changes Oct 18, 2023
""");
var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.CS9208.severity = warning
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 19, 2023

Choose a reason for hiding this comment

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

Does this have any effect on the scenario? If yes, then consider including observations without this line present. If no, consider removing. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

Or did you mean to attempt to suppress CS9211 instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, should have been CS9211. Thanks

""");
var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
[*.cs]
dotnet_diagnostic.CS9204.severity = warning
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 19, 2023

Choose a reason for hiding this comment

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

CS9204

Consider asserting that the number corresponds to WRN_Experimental #Closed

var src = """
C.M();

[System.Diagnostics.CodeAnalysis.Experimental("DiagID", "other")]
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 19, 2023

Choose a reason for hiding this comment

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

DiagID

Consider testing what happens if this value is not a valid identifier #Closed


comp.VerifyDiagnostics(
// 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
// 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 19, 2023

Choose a reason for hiding this comment

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

error CS9204

What happens if we try to suppress this diagnostic with pragma? #Closed

var comp = CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc });

comp.VerifyDiagnostics(
// 0.cs(1,1): error Diag\n: 'C' is for evaluation purposes only and is subject to change or removal in future updates.Suppress this diagnostic to proceed.
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 19, 2023

Choose a reason for hiding this comment

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

// 0.cs(1,1): error Diag\n: 'C' is for evaluation purposes only and is subject to change or removal in future updates.Suppress this diagnostic to proceed.

What happens if we try to suppress CS9204 with a pragma? #Closed

public void WhitespaceDiagnosticId_WithSuppression()
{
var src = """
#pragma warning disable CS9204
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 19, 2023

Choose a reason for hiding this comment

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

The effect of the pragma feels unexpected to me. However, this isn't primarily related to the goal of the PR. Therefore, not blocking #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

What's unexpected? We have a diagnostic that we're going to report as CS9204 and we suppress CS9204, therefore the diagnostic is suppressed.

Copy link
Contributor

Choose a reason for hiding this comment

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

What's unexpected?

Errors are usually not suppressable

Copy link
Member Author

Choose a reason for hiding this comment

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

The spec this is not an error, it is merely reported as an error

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Oct 19, 2023

        // 0.cs(1,1): error DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.

It would be good to test if we can suppress this error with pragma. Not blocking. #Closed


Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs:58 in 5d1c56d. [](commit_id = 5d1c56d, deletion_comment = False)

AlekseyTs
AlekseyTs previously approved these changes Oct 19, 2023
Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 8)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Oct 19, 2023

Is this check already implemented for VB? #Resolved

@jcouv
Copy link
Member Author

jcouv commented Oct 19, 2023

Added VB implementation

@jcouv jcouv dismissed stale reviews from AlekseyTs and cston October 19, 2023 21:15

Added VB

@jcouv
Copy link
Member Author

jcouv commented Oct 19, 2023

        // 0.cs(1,1): error DiagID1: 'C' is for evaluation purposes only and is subject to change or removal in future updates.

I think that's already covered by test Suppressed at line 2031


In reply to: 1771422464


Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs:58 in 5d1c56d. [](commit_id = 5d1c56d, deletion_comment = False)

@jcouv jcouv requested a review from cston October 19, 2023 21:45
End Sub

<Fact>
Public Sub ExperimentalWithValidDiagnosticID_WarnForDiagID()
Copy link
Member

@cston cston Oct 19, 2023

Choose a reason for hiding this comment

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

WarnForDiagID

"WarnForExperimental"? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

This test has dotnet_diagnostic.DiagID.severity = warning

cston
cston previously approved these changes Oct 19, 2023
@@ -204,6 +204,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
DirectCast(arguments.Diagnostics, BindingDiagnosticBag).Add(ERRID.ERR_DoNotUseCompilerFeatureRequired, arguments.AttributeSyntaxOpt.Location)
ElseIf arguments.Attribute.IsTargetAttribute(Me, AttributeDescription.RequiredMemberAttribute) Then
DirectCast(arguments.Diagnostics, BindingDiagnosticBag).Add(ERRID.ERR_DoNotUseRequiredMember, arguments.AttributeSyntaxOpt.Location)
ElseIf arguments.Attribute.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) AndAlso
Not SyntaxFacts.IsValidIdentifier(DirectCast(arguments.Attribute.CommonConstructorArguments(0).ValueInternal, String)) Then
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 20, 2023

Choose a reason for hiding this comment

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

Consider using a nested if instead. So that we wouldn't keep checking for other well-known attributes once we run into the Experimental attribute. We might add checks for other attributes below in the future. #Closed

@@ -226,6 +226,14 @@ protected void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments<At
return;
}

if (arguments.Attribute.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute)
&& !SyntaxFacts.IsValidIdentifier((string?)arguments.Attribute.CommonConstructorArguments[0].ValueInternal))
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 20, 2023

Choose a reason for hiding this comment

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

&& !SyntaxFacts.IsValidIdentifier((string?)arguments.Attribute.CommonConstructorArguments[0].ValueInternal))

Similar refactoring suggestion as for VB #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

On the second thought, never mind. It looks like we do want to call DecodeWellKnownAttributeImpl below.

{
var attrArgumentLocation = arguments.Attribute.GetAttributeArgumentSyntaxLocation(parameterIndex: 0, arguments.AttributeSyntaxOpt);
arguments.Diagnostics.DiagnosticBag.Add(ErrorCode.ERR_InvalidExperimentalDiagID, attrArgumentLocation);
return;
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 20, 2023

Choose a reason for hiding this comment

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

return

Shouldn't we still call DecodeWellKnownAttributeImpl below? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so. The decoding of experimental attribute is done in EarlyDecodeWellKnownAttributes, before we get to DecodeWellKnownAttribute

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think so. The decoding of experimental attribute is done in EarlyDecodeWellKnownAttributes, before we get to DecodeWellKnownAttribute

First, the code should be robust to future changes and it feels reasonable to me that the fact you are referring to, if it is a fact, won't hold in the future.

Second, I actually see decoding done in DecodeWellKnownAttributeImpl for a SourceModuleSymbol today, for example.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, you're right. The return broke things.

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 9)

@jcouv jcouv dismissed cston’s stale review October 20, 2023 19:43

Updated code

@jcouv jcouv requested review from cston and AlekseyTs October 20, 2023 19:44
Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 11), assuming CI is passing.

@jcouv jcouv merged commit e027092 into dotnet:main Oct 20, 2023
25 checks passed
@jcouv jcouv deleted the exp-validation branch October 20, 2023 21:05
@ghost ghost modified the milestones: 17.9, Next Oct 20, 2023
@jjonescz jjonescz modified the milestones: Next, 17.9 P1 Oct 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants