Skip to content

Commit

Permalink
Merge pull request #3352 from Evangelink/RS0037
Browse files Browse the repository at this point in the history
RS0046: Avoid Opt suffix in nullable-enabled code
  • Loading branch information
mavasani committed Jun 6, 2020
2 parents 15b67df + 5493e1b commit 596f8f4
Show file tree
Hide file tree
Showing 19 changed files with 679 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RS0038 | RoslynDiagnosticsMaintainability | Warning | PreferNullLiteral
RS0046 | RoslynDiagnosticsDesign | Warning | CSharpAvoidOptSuffixForNullableEnableCode
RS0100 | RoslynDiagnosticsMaintainability | Warning | CSharpWrapStatementsDiagnosticAnalyzer
RS0102 | RoslynDiagnosticsMaintainability | Warning | CSharpBracePlacementDiagnosticAnalyzer
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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 Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Analyzer.Utilities.Lightup;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Diagnostics.Analyzers;

namespace Roslyn.Diagnostics.CSharp.Analyzers
{
/// <summary>
/// RS0046: Avoid 'Opt' suffix for nullable enable code
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class CSharpAvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer
{
internal const string OptSuffix = "Opt";

private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources));
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeMessage), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources));
private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeDescription), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources));

internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(
RoslynDiagnosticIds.AvoidOptSuffixForNullableEnableCodeRuleId,
s_localizableTitle,
s_localizableMessage,
DiagnosticCategory.RoslynDiagnosticsDesign,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: s_localizableDescription,
helpLinkUri: null,
customTags: WellKnownDiagnosticTags.Telemetry);

public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterSyntaxNodeAction(context =>
{
var parameter = (ParameterSyntax)context.Node;
ReportOnInvalidIdentifier(parameter.Identifier, context.SemanticModel, context.ReportDiagnostic);
}, SyntaxKind.Parameter);

context.RegisterSyntaxNodeAction(context =>
{
var variableDeclarator = (VariableDeclaratorSyntax)context.Node;
ReportOnInvalidIdentifier(variableDeclarator.Identifier, context.SemanticModel, context.ReportDiagnostic);
}, SyntaxKind.VariableDeclarator);
}

private static void ReportOnInvalidIdentifier(SyntaxToken identifier, SemanticModel semanticModel, Action<Diagnostic> reportAction)
{
if (!identifier.Text.EndsWith(OptSuffix, StringComparison.Ordinal) ||
!semanticModel.GetNullableContext(identifier.SpanStart).AnnotationsEnabled())
{
return;
}

var symbol = semanticModel.GetDeclaredSymbol(identifier.Parent);
if (symbol?.GetMemberOrLocalOrParameterType()?.NullableAnnotation() == Analyzer.Utilities.Lightup.NullableAnnotation.Annotated)
{
reportAction(identifier.CreateDiagnostic(Rule));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Rename;
using Roslyn.Diagnostics.Analyzers;

namespace Roslyn.Diagnostics.CSharp.Analyzers
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
[Shared]
public sealed class CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CSharpAvoidOptSuffixForNullableEnableCode.Rule.Id);

public override FixAllProvider GetFixAllProvider()
=> WellKnownFixAllProviders.BatchFixer;

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var title = RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeCodeFixTitle;

foreach (var diagnostic in context.Diagnostics)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var variable = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
if (variable == null)
{
continue;
}

var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var variableSymbol = semanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (variableSymbol == null || variableSymbol.Name.Length <= CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length)
{
continue;
}

var newName = variableSymbol.Name.Substring(0, variableSymbol.Name.Length - CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length);

// There is no symbol matching the new name so we can register the codefix
if (semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, variableSymbol.ContainingType, newName).IsEmpty)
{
context.RegisterCodeFix(
CodeAction.Create(
title,
cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, variableSymbol, newName, cancellationToken),
equivalenceKey: title),
diagnostic);
}
}
}

private static async Task<Solution> RemoveOptSuffixOnVariableAsync(Document document, ISymbol variableSymbol, string newName, CancellationToken cancellationToken)
=> await Renamer.RenameSymbolAsync(document.Project.Solution, variableSymbol, newName, document.Project.Solution.Options, cancellationToken)
.ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ internal static class RoslynDiagnosticIds
public const string DoNotCallGetTestAccessorRuleId = "RS0043";
public const string CreateTestAccessorRuleId = "RS0044";
public const string ExposeMemberForTestingRuleId = "RS0045";
public const string AvoidOptSuffixForNullableEnableCodeRuleId = "RS0046";

public const string WrapStatementsRuleId = "RS0100";
public const string BlankLinesRuleId = "RS0101";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,15 @@
<data name="ExposeMemberForTestingTitle" xml:space="preserve">
<value>Expose member for testing</value>
</data>
<data name="AvoidOptSuffixForNullableEnableCodeDescription" xml:space="preserve">
<value>Avoid the 'Opt' suffix in a nullable-enabled code.</value>
</data>
<data name="AvoidOptSuffixForNullableEnableCodeMessage" xml:space="preserve">
<value>Avoid the 'Opt' suffix in a nullable-enabled code</value>
</data>
<data name="AvoidOptSuffixForNullableEnableCodeTitle" xml:space="preserve">
<value>Avoid the 'Opt' suffix</value>
</data>
<data name="BracePlacementMessage" xml:space="preserve">
<value>Braces must not have blank lines between them</value>
</data>
Expand All @@ -348,4 +357,7 @@
<data name="Place_statement_on_following_line" xml:space="preserve">
<value>Place statement on following line</value>
</data>
<data name="AvoidOptSuffixForNullableEnableCodeCodeFixTitle" xml:space="preserve">
<value>Remove the 'Opt' suffix</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="cs" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">Nepoužívejte několik prázdných řádků.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="de" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">Vermeiden Sie mehrere Leerzeilen.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="es" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">Evitar varias líneas en blanco</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="fr" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">Éviter plusieurs lignes vides</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="it" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">Evitare più righe vuote</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ja" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">複数の空白行は使用できません</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ko" original="../RoslynDiagnosticsAnalyzersResources.resx">
<body>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeCodeFixTitle">
<source>Remove the 'Opt' suffix</source>
<target state="new">Remove the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeDescription">
<source>Avoid the 'Opt' suffix in a nullable-enabled code.</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code.</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeMessage">
<source>Avoid the 'Opt' suffix in a nullable-enabled code</source>
<target state="new">Avoid the 'Opt' suffix in a nullable-enabled code</target>
<note />
</trans-unit>
<trans-unit id="AvoidOptSuffixForNullableEnableCodeTitle">
<source>Avoid the 'Opt' suffix</source>
<target state="new">Avoid the 'Opt' suffix</target>
<note />
</trans-unit>
<trans-unit id="BlankLinesMessage">
<source>Avoid multiple blank lines</source>
<target state="translated">여러 빈 줄 방지</target>
Expand Down
Loading

0 comments on commit 596f8f4

Please sign in to comment.