-
Notifications
You must be signed in to change notification settings - Fork 470
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
[WIP] resx analyzer #3566
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.IO; | ||
using System.Text.RegularExpressions; | ||
using System.Xml; | ||
using Analyzer.Utilities; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Text; | ||
|
||
namespace Roslyn.Diagnostics.Analyzers | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] | ||
public sealed class DefineResourceEntryCorrectly : DiagnosticAnalyzer | ||
{ | ||
private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.DefineResourceEntryCorrectlyTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); | ||
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.DefineResourceEntryCorrectlyMessage), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); | ||
private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.DefineResourceEntryCorrectlyDescription), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); | ||
|
||
internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor( | ||
RoslynDiagnosticIds.DefineResourceEntryCorrectlyRuleId, | ||
s_localizableTitle, | ||
s_localizableMessage, | ||
DiagnosticCategory.RoslynDiagnosticsDesign, | ||
DiagnosticSeverity.Info, | ||
isEnabledByDefault: true, | ||
description: s_localizableDescription, | ||
helpLinkUri: null, | ||
customTags: WellKnownDiagnosticTags.Telemetry); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); | ||
|
||
private static readonly Regex s_dataNameRegex = new Regex("<data.*?name=\"(.*?)\"", RegexOptions.Compiled); | ||
private static readonly Regex s_valueRegex = new Regex("<value>(.*?)</value>", RegexOptions.Compiled); | ||
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's never 100% clear to me when
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI |
||
private const StringComparison s_ResxContentStringComparison = StringComparison.Ordinal; | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.EnableConcurrentExecution(); | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
|
||
context.RegisterCompilationAction(context => | ||
{ | ||
foreach (var file in context.Options.AdditionalFiles) | ||
{ | ||
context.CancellationToken.ThrowIfCancellationRequested(); | ||
|
||
var fileExtension = Path.GetExtension(file.Path); | ||
if (!fileExtension.Equals(".resx", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
continue; | ||
} | ||
|
||
var isDescription = false; | ||
var sourceText = file.GetText(context.CancellationToken); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
foreach (var line in sourceText.Lines) | ||
{ | ||
var text = line.ToString().Trim(); | ||
|
||
var match = s_dataNameRegex.Match(text); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
if (match.Success) | ||
{ | ||
isDescription = match.Groups[1].Value.EndsWith("Description", s_ResxContentStringComparison); | ||
continue; | ||
} | ||
|
||
match = s_valueRegex.Match(text); | ||
if (match.Success) | ||
{ | ||
var endsWithPeriod = match.Groups[1].Value.EndsWith(".", s_ResxContentStringComparison); | ||
|
||
if (endsWithPeriod && !isDescription) | ||
{ | ||
var linePositionSpan = sourceText.Lines.GetLinePositionSpan(line.Span); | ||
var location = Location.Create(file.Path, line.Span, linePositionSpan); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do we want to report? Full line? Only the |
||
context.ReportDiagnostic(Diagnostic.Create(Rule, location)); | ||
} | ||
else if (isDescription && !endsWithPeriod) | ||
{ | ||
var linePositionSpan = sourceText.Lines.GetLinePositionSpan(line.Span); | ||
var location = Location.Create(file.Path, line.Span, linePositionSpan); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
context.ReportDiagnostic(Diagnostic.Create(Rule, location)); | ||
} | ||
else | ||
{ | ||
// do nothing | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#3352 is taking
RS0046