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

[WIP] resx analyzer #3566

Closed
wants to merge 1 commit into from
Closed

Conversation

Evangelink
Copy link
Member

Contributes to #3525

@Evangelink Evangelink requested a review from a team as a code owner April 28, 2020 12:04
@@ -6,6 +6,7 @@ RS0042 | RoslynDiagnosticsReliability | Warning | DoNotCopyValue
RS0043 | RoslynDiagnosticsMaintainability | Warning | DoNotCallGetTestAccessor
RS0044 | RoslynDiagnosticsMaintainability | Hidden | CreateTestAccessor
RS0045 | RoslynDiagnosticsMaintainability | Hidden | ExposeMemberForTesting
RS0047 | RoslynDiagnosticsDesign | Info | DefineResourceEntryCorrectly
Copy link
Member Author

Choose a reason for hiding this comment

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

#3352 is taking RS0046

@Evangelink Evangelink changed the title First version of resx analyzer [WIP] resx analyzer Apr 28, 2020
}

var isDescription = false;
var sourceText = file.GetText(context.CancellationToken);
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 have tried a couple of resx/xml loader but we either don't have the right dll or this is failing because of the first line of the resx which doesn't seem to be xml compliant. Let me know if you know a better way that doing line by line reading.

Comment on lines +35 to +36
private static readonly Regex s_dataNameRegex = new Regex("<data.*?name=\"(.*?)\"", RegexOptions.Compiled);
private static readonly Regex s_valueRegex = new Regex("<value>(.*?)</value>", RegexOptions.Compiled);
Copy link
Member Author

Choose a reason for hiding this comment

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

It's never 100% clear to me when Compiled is better.

Comment on lines +35 to +36
private static readonly Regex s_dataNameRegex = new Regex("<data.*?name=\"(.*?)\"", RegexOptions.Compiled);
private static readonly Regex s_valueRegex = new Regex("<value>(.*?)</value>", RegexOptions.Compiled);
Copy link
Member Author

Choose a reason for hiding this comment

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

FYI .*? allows a non-greedy version of .* this prevents unwanting characters! Besides, it's usually better in term of performances because it reduces the backtracking.

{
var text = line.ToString().Trim();

var match = s_dataNameRegex.Match(text);
Copy link
Member Author

Choose a reason for hiding this comment

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

If we go for this line by line check, do we want to have a StartsWith check before the regex match?

@Evangelink
Copy link
Member Author

Tagging @mavasani and @sharwell to get your opinion on whether or not the direction looks good to you?!

if (endsWithPeriod && !isDescription)
{
var linePositionSpan = sourceText.Lines.GetLinePositionSpan(line.Span);
var location = Location.Create(file.Path, line.Span, linePositionSpan);
Copy link
Member Author

@Evangelink Evangelink Apr 28, 2020

Choose a reason for hiding this comment

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

Where do we want to report? Full line? Only the .?

else if (isDescription && !endsWithPeriod)
{
var linePositionSpan = sourceText.Lines.GetLinePositionSpan(line.Span);
var location = Location.Create(file.Path, line.Span, linePositionSpan);
Copy link
Member Author

Choose a reason for hiding this comment

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

Where do we want to report? Full line? The last character of the sentence?

@mavasani
Copy link
Contributor

mavasani commented Apr 28, 2020

@Evangelink Looking at the current implementation, I think my proposed idea of reporting diagnostics on resx files has few big drawbacks:

  1. We cannot implement or offer a code fix. Code fixes are currently only supported for diagnostics on source file
  2. CompilationAction: This diagnostic will not be reported in live analysis when you open a resx file. This is unfortunately the drawback of diagnostics with location in additional files, which I had not completely thought through.
  3. False positives: We are assuming the relationship between between resource strings and their usage in analyzer based on just the resx name suffix, like Title, Description, etc. This makes it not useful as a generic analyzer for all analyzer authors.

I think we should take the following direction:

  1. Lets target this analyzer should be added to 'Microsoft.CodeAnalysis.Analyzers', so it can be used by all analyzer authors.
  2. Instead of directly reading resx and flagging the resource strings, let us try to approach this from the view of types deriving from DiagnosticAnalyzer, so we generate diagnostics in these source files. More specifically:
    1. Enhance DiagnosticDescriptorCreationAnalyzer.AnalyzeTitle, so if title is a constant string, and has any periods or newlines, we report a diagnostic that title is recommended to be a short string without periods or newlines.
    2. For cases where title argument is not a constant string, but a LocalizableString, we need to handle 2 cases:
      1. Argument is an object creation of type LocalizableResourceString. We can process the arguments to this creation and identify the name of resource string used for title (const string for the 1st argument) + name/type of the resources file (System.Type argument for the 3rd argument). We can map this to the resource string in resx file and then generate a similar diagnostic if it has periods or newlines.
      2. Argument is a field reference of type LocalizableString or LocalizableResourceString or const string. For const string, it should be easy to report the diagnostic. For LocalizableString or LocalizableResourceString, we need to actually analyze the field intializer. We can setup this analysis by registering a SymbolStartAction for named types. We register an Operation action with OperationKind.FieldInitializer and use this callback to analyze each field initializer of type LocalizableResourceString, and build a map from field symbol to a tuple (string resourceName, string resxFileName). In DiagnosticDescriptorCreationAnalyzer.AnalyzeTitle callback, we build a set of field symbols of type LocalizableString or LocalizableResourceString that are used as title arguments to descriptors. We register a SymbolEndAction which analyzes both these data structures to then link which resx string is used as title argument, and then report a similar diagnostic in this end action. NOTE: We should probably guard this analysis from executing only if at least one resx file was provided as an additional file, otherwise we cannot report any diagnostics here.
  3. We can repeat similar analysis for Message and Description arguments as done for Title above, but with different validation: Message should not have any newline + it should not end with a period, unless it has at least one more period (long, multi sentence messages are common). Description should always have a period to end.
  4. All the above diagnostics would be enabled by default, and also valid for any third party analyzer.
  5. Additionally, we can add separate diagnostic descriptors for: Resource strings used for title, message or description should always end with "Title", "Message", "Description" respectively. I am in two minds if these diagnostics will be too noisy or not - we should maybe have these off by default and just enable them for our repo.
  6. Code Fix: We should write a code fix that fixes all the above cases, with a FixAll operation. That is going to be the biggest benefit here.

Sorry for the long comment, we can discuss more offline for further ideas.

@Evangelink Evangelink closed this Apr 29, 2020
@Evangelink Evangelink deleted the resx-analyzer branch July 13, 2020 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants