Skip to content

Verify arguments length for diagnostic messages#60925

Merged
cston merged 10 commits intodotnet:mainfrom
cston:60862
May 2, 2022
Merged

Verify arguments length for diagnostic messages#60925
cston merged 10 commits intodotnet:mainfrom
cston:60862

Conversation

@cston
Copy link
Contributor

@cston cston commented Apr 23, 2022

Verify DiagnosticInfo() arguments array length matches the expected value from the diagnostic message (C# only).

Fixes #60862 for C#.

@ghost ghost added the Area-Compilers label Apr 23, 2022
}

[Conditional("DEBUG")]
internal static void AssertExpectedMessageArgumentsLength(CommonMessageProvider messageProvider, int errorCode, int actualLength)
Copy link
Member

@Youssef1313 Youssef1313 Apr 23, 2022

Choose a reason for hiding this comment

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

Is this path for compiler diagnostics only?

I think it would be beneficial to have the assert for all diagnostics (to prevent IDExxxx from having bugs too)

Then the following analyzer will need to be excluded:

[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class AnalyzerReportingMisformattedDiagnostic : DiagnosticAnalyzer
{
public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
"ID1",
"Title1",
"Symbol Name: {0}, Extra argument: {1}",
"Category1",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(symbolContext =>
{
// Report diagnostic with incorrect number of message format arguments.
symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Symbol.Locations[0], symbolContext.Symbol.Name));
}, SymbolKind.NamedType);
}
}

Not sure if that makes sense? #Resolved

Copy link
Contributor Author

@cston cston Apr 29, 2022

Choose a reason for hiding this comment

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

I've limited the change to C# compiler diagnostics for now, to keep the scope manageable. We can consider addressing other diagnostics, separately, as needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be beneficial to have the assert for all diagnostics (to prevent IDExxxx from having bugs too)

I have no objection, as long as this is a debug-only assertion. It's possible we have runtime scenarios where the diagnostic intentionally provides more placeholders than are used by the message.

Comment on lines +129 to +132
if (errorCode <= 0)
{
return;
}
Copy link
Member

@Youssef1313 Youssef1313 Apr 23, 2022

Choose a reason for hiding this comment

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

When can this happen?
#Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For ErrorCode.Void and ErrorCode.Unknown which are used as placeholders for errors.

{
return;
}
string message = messageProvider.LoadMessage(errorCode, language: null) ?? string.Empty;
Copy link
Member

@Youssef1313 Youssef1313 Apr 23, 2022

Choose a reason for hiding this comment

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

Can LoadMessage return null for a reason other than a bug? #Resolved

{
expectedLength = Math.Max(int.Parse(match.Value[1..^1]) + 1, expectedLength);
}
Debug.Assert(expectedLength == actualLength);
Copy link
Member

@Youssef1313 Youssef1313 Apr 23, 2022

Choose a reason for hiding this comment

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

Would Debug.Assert(expectedLength == matches.Count) be valid (in addition to the existing assert)? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

matches has entries for each argument use, even duplicates, so matches.Count may not equal expectedLength. But it makes sense to check that all arguments are used, so I've used a BitVector instead, thanks.

There is one resource string that fails this new check: ERR_IdentifierExpectedKW. I've disabled the check for that ErrorCode, rather than changing the resource string and the arguments array, to avoid breaking translated resources. The assert is useful for new cases where arguments are unused though.

[Conditional("DEBUG")]
internal static void AssertExpectedMessageArgumentsLength(CommonMessageProvider messageProvider, int errorCode, int actualLength)
{
#if DEBUG
Copy link
Member

@Youssef1313 Youssef1313 Apr 23, 2022

Choose a reason for hiding this comment

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

Is it needed? #Resolved

Copy link
Contributor Author

@cston cston Apr 23, 2022

Choose a reason for hiding this comment

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

It's not strictly necessary but it seemed reasonable to avoid emitting the method body in release builds.

return TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(
Compilation.Assembly.GlobalNamespace, identifierValueText, 0,
new CSDiagnosticInfo(ErrorCode.ERR_SingleTypeNameNotFound)));
new CSDiagnosticInfo(ErrorCode.ERR_SingleTypeNameNotFound, identifierValueText)));
Copy link
Member

@Youssef1313 Youssef1313 Apr 23, 2022

Choose a reason for hiding this comment

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

Which test failed this?

I think this change fixes #59417 but how can we observe that through a test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There were a number of tests that failed, including IOperationTests_IConversionExpression.ConversionExpression_Implicit_ReferenceConversion_InvalidSyntax and IOperationTests_ISizeOfExpression.TestSizeOfExpression_MissingArgument. The DiagnosticInfo is used in the resulting ExtendedErrorTypeSymbol but it's not clear how to construct a test where the ExtendedErrorTypeSymbol.ErrorInfo is reported from such cases though.

@cston cston force-pushed the 60862 branch 2 times, most recently from 5184fb4 to 5a6a9aa Compare April 24, 2022 15:45
@cston cston marked this pull request as ready for review April 25, 2022 01:05
@cston cston requested review from a team as code owners April 25, 2022 01:05
@cston cston requested a review from CyrusNajmabadi April 25, 2022 17:45
0 => false,
ErrorCode.Unknown => false,
ErrorCode.Void => false,
ErrorCode.WRN_XMLParseError => false, // should investigate failures here
Copy link
Member

@jcouv jcouv Apr 25, 2022

Choose a reason for hiding this comment

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

Tracking issue? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Investigated and added comment here.

int expectedLength = 0;
foreach (object? m in matches)
{
if (m is Match { } match)
Copy link
Member

@jcouv jcouv Apr 25, 2022

Choose a reason for hiding this comment

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

{ }

{ } unnecessary #Resolved

@jcouv jcouv self-assigned this Apr 25, 2022
Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM Thanks (iteration 7) with a couple of nits to consider

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 7)

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 10)

@cston cston merged commit 0c31b36 into dotnet:main May 2, 2022
@cston cston deleted the 60862 branch May 2, 2022 21:18
@ghost ghost added this to the Next milestone May 2, 2022
@Cosifne Cosifne modified the milestones: Next, 17.3 P2 May 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure that exact number of arguments are given for diagnostic message

6 participants