diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md index 43ad717d7f..5a3dacc7d7 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -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 diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs new file mode 100644 index 0000000000..71580d9ab8 --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs @@ -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 +{ + /// + /// RS0046: Avoid 'Opt' suffix for nullable enable code + /// + [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 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 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)); + } + } + } +} diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs new file mode 100644 index 0000000000..c304f3c2cb --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -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 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 RemoveOptSuffixOnVariableAsync(Document document, ISymbol variableSymbol, string newName, CancellationToken cancellationToken) + => await Renamer.RenameSymbolAsync(document.Project.Solution, variableSymbol, newName, document.Project.Solution.Options, cancellationToken) + .ConfigureAwait(false); + } +} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs index 235314e533..20725695a6 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs @@ -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"; diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx index 2a42c9e8d2..880715070f 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx @@ -330,6 +330,15 @@ Expose member for testing + + Avoid the 'Opt' suffix in a nullable-enabled code. + + + Avoid the 'Opt' suffix in a nullable-enabled code + + + Avoid the 'Opt' suffix + Braces must not have blank lines between them @@ -348,4 +357,7 @@ Place statement on following line + + Remove the 'Opt' suffix + \ No newline at end of file diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf index 2028407605..6bc3574365 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Nepoužívejte několik prázdných řádků. diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf index f0b76a2553..4efb21d4f5 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Vermeiden Sie mehrere Leerzeilen. diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf index e61ef34c85..1a72c466e1 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Evitar varias líneas en blanco diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf index 87594f2d2d..8ebd09da3f 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Éviter plusieurs lignes vides diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf index a877e13379..c256b114be 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Evitare più righe vuote diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf index 7e2649c4a4..d5b7350114 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines 複数の空白行は使用できません diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf index bfcec87823..4eb2e4b8fa 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines 여러 빈 줄 방지 diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf index 1c8ff97f8d..424f30c290 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Unikaj wielu pustych wierszy diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf index e14e398da5..74c31fc1e5 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Evite várias linhas em branco diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf index 2595fa2c96..de80fc07bc 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Избегайте использования нескольких пустых строк diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf index ceb759712a..acf0517af0 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines Birden çok boş satırdan kaçının diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf index 17ea4877c4..031fbc81c0 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines 避免出现多个空白行 diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf index 925f03f006..7ce91d8339 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf @@ -2,6 +2,26 @@ + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Avoid the 'Opt' suffix in a nullable-enabled code. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Avoid the 'Opt' suffix in a nullable-enabled code + + + + Avoid the 'Opt' suffix + Avoid the 'Opt' suffix + + Avoid multiple blank lines 避免多個空白行 diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs new file mode 100644 index 0000000000..ddd974d5b5 --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -0,0 +1,268 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCode, + Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class CSharpAvoidOptSuffixForNullableEnableCodeTests + { + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCode_Diagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], [|otherInstanceOpt|]; + + public void Method1(string? [|sOpt|]) + { + string? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}"", _instanceOpt, otherInstanceOpt, sOpt, localOpt, otherLocalOpt); + } +}", + FixedCode = @" +#nullable enable + +public class Class1 +{ + private Class1? _instance, otherInstance; + + public void Method1(string? s) + { + string? local = null, otherLocal = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}"", _instance, otherInstance, s, local, otherLocal); + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCodeNonNullableType_NoDiagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1 _instanceOpt = new Class1(), otherInstanceOpt = new Class1(); + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCodeValueType_Diagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public enum MyEnum { A, } + +public class Class1 +{ + private MyEnum? [|_instanceOpt|], [|otherInstanceOpt|]; + + public void Method1(MyEnum? [|eOpt|]) + { + MyEnum? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}"", _instanceOpt, otherInstanceOpt, eOpt, localOpt, otherLocalOpt); + } +}", + FixedCode = @" +#nullable enable + +public enum MyEnum { A, } + +public class Class1 +{ + private MyEnum? _instance, otherInstance; + + public void Method1(MyEnum? e) + { + MyEnum? local = null, otherLocal = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}"", _instance, otherInstance, e, local, otherLocal); + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCodeNonNullableValueType_NoDiagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public enum MyEnum { A, } + +public class Class1 +{ + private MyEnum _instanceOpt = MyEnum.A, otherInstanceOpt = MyEnum.A; + + public void Method1(MyEnum eOpt) + { + MyEnum localOpt, otherLocalOpt; + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NonNullableEnabledCode_NoDiagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = + @" +public class Class1 +{ + private Class1 _instanceOpt, otherInstanceOpt; + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableDisabledCode_NoDiagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = +@" +#nullable disable + +public class Class1 +{ + private Class1 _instanceOpt, otherInstanceOpt; + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + }.RunAsync(); + } + + [Fact] + public async Task RS0046_PriorToCSharp8_NoDiagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp7_3, + TestCode = + @" +public class Class1 +{ + private Class1 _instanceOpt, otherInstanceOpt; + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + }.RunAsync(); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn-analyzers/issues/3707")] + public async Task RS0046_CSharp8_VariableWithoutOptAlreadyExists_DiagnosticButNoCodeFix() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], _instance; + + public void Method1(string? [|sOpt|], string? s) + { + string? [|localOpt|], local; + } +}", + FixedCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], _instance; + + public void Method1(string? [|sOpt|], string? s) + { + string? [|localOpt|], local; + } +}", + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_UnknownType_DiagnosticAndCodeFix() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private {|CS0246:Class2|}? [|_instanceOpt|]; + + public void Method1({|CS0246:Class2|}? [|sOpt|]) + { + {|CS0246:Class2|}? [|localOpt|]; + } +}", + FixedCode = @" +#nullable enable + +public class Class1 +{ + private {|CS0246:Class2|}? _instance; + + public void Method1({|CS0246:Class2|}? s) + { + {|CS0246:Class2|}? local; + } +}", + }.RunAsync(); + } + } +}