From 07c87e8d52f54767b461408b58ae6c5b80d1ea7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 6 Mar 2020 15:22:08 +0100 Subject: [PATCH 01/12] RS0037: Avoid Opt suffix in nullable-enabled code --- ...harpAvoidOptSuffixForNullableEnableCode.cs | 26 +++++++ .../AvoidOptSuffixForNullableEnableCode.cs | 55 ++++++++++++++ .../Core/RoslynDiagnosticIds.cs | 1 + .../RoslynDiagnosticsAnalyzersResources.resx | 9 +++ ...RoslynDiagnosticsAnalyzersResources.cs.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.de.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.es.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.fr.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.it.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.ja.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.ko.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.pl.xlf | 15 ++++ ...lynDiagnosticsAnalyzersResources.pt-BR.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.ru.xlf | 15 ++++ ...RoslynDiagnosticsAnalyzersResources.tr.xlf | 15 ++++ ...nDiagnosticsAnalyzersResources.zh-Hans.xlf | 15 ++++ ...nDiagnosticsAnalyzersResources.zh-Hant.xlf | 15 ++++ ...voidOptSuffixForNullableEnableCodeTests.cs | 73 +++++++++++++++++++ 18 files changed, 359 insertions(+) create mode 100644 src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs create mode 100644 src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs create mode 100644 src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs new file mode 100644 index 0000000000..3446e245b7 --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs @@ -0,0 +1,26 @@ +// 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.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpAvoidOptSuffixForNullableEnableCode : AvoidOptSuffixForNullableEnableCode + { + protected override bool IsNullableEnabledContext(CompilationOptions compilationOptions, IEnumerable parseOptions) + { + var hasAnyCSharp8Part = parseOptions.OfType().Any(option => option.LanguageVersion >= LanguageVersion.CSharp8); + + return hasAnyCSharp8Part && IsNullableEnabledContext(compilationOptions); + } + + private static bool IsNullableEnabledContext(CompilationOptions compilationOptions) + => compilationOptions is CSharpCompilationOptions csharpCompilationOptions && + csharpCompilationOptions.NullableContextOptions == NullableContextOptions.Enable; + } +} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs new file mode 100644 index 0000000000..22f17ace94 --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs @@ -0,0 +1,55 @@ +// 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.Generic; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + public abstract class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer + { + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); + + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdMessage), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdDescription), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); + + internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor( + RoslynDiagnosticIds.AvoidOptSuffixForNullableEnableCodeRuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.RoslynDiagnosticsDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: s_localizableDescription, + helpLinkUri: null, + customTags: WellKnownDiagnosticTags.Telemetry); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public sealed override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSymbolAction(context => + { + if (context.Symbol.Name.EndsWith("Opt", System.StringComparison.Ordinal)) + { + var parseOptions = context.Symbol.DeclaringSyntaxReferences + .Select(syntaxRef => syntaxRef.GetSyntax(context.CancellationToken)?.SyntaxTree?.Options); + + if (IsNullableEnabledContext(context.Compilation.Options, parseOptions)) + { + context.ReportDiagnostic(context.Symbol.CreateDiagnostic(Rule)); + } + } + }, SymbolKind.Parameter, SymbolKind.Field); + } + + protected abstract bool IsNullableEnabledContext(CompilationOptions compilationOptions, IEnumerable parseOptions); + } +} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs index 29d3133cc2..2dffea01ff 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs @@ -41,5 +41,6 @@ internal static class RoslynDiagnosticIds public const string RestrictedInternalsVisibleToRuleId = "RS0035"; public const string AnnotatePublicApiRuleId = "RS0036"; public const string ShouldAnnotateApiFilesRuleId = "RS0037"; + public const string AvoidOptSuffixForNullableEnableCodeRuleId = "RS0037"; } } diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx index 6e8d260878..9809700ef0 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx @@ -255,4 +255,13 @@ Fix numbered comments + + Avoid the 'Opt' suffix in a nullable-enabled code. + + + Avoid the 'Opt' suffix in a nullable-enabled code + + + Avoid 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 6e4f252b13..0ef898b457 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Explicitně definujte importující konstruktor. diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf index a2d9ee8e3e..98c2694a4e 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Importierenden Konstruktor explizit definieren diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf index c5a52362eb..4e71cfdc1d 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Definir explícitamente el constructor de importación diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf index b62e974e8f..8bf8c131d2 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Définir explicitement le constructeur d'importation diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf index e9c25c392c..d4851ef24d 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Definire esplicitamente il costruttore di importazione diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf index bd90e4230c..95464b68b6 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor インポート コンストラクターを明示的に定義します diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf index 75c54724f8..f8c3718bcc 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor 명시적으로 가져오기 생성자 정의 diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf index 11175c75a6..2b8720657c 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Jawnie zdefiniuj konstruktor importujący 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 eb1f79057b..c3e69bdb4a 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,21 @@ + + 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 + + Explicitly define the importing constructor Definir explicitamente o construtor de importação diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf index 3085a1043f..36a784ae67 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor Явно определите конструктор импорта. diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf index 77851355ed..ce2425ebae 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf @@ -2,6 +2,21 @@ + + 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 + + Explicitly define the importing constructor İçeri aktarma oluşturucusunu açıkça tanımlayı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 009875caef..16d1000cf0 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,21 @@ + + 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 + + Explicitly define the importing constructor 显式定义导入构造函数 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 c6666b38dd..c016d3c632 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,21 @@ + + 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 + + Explicitly define the importing constructor 明確定義匯入建構函式 diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs new file mode 100644 index 0000000000..08e6408c24 --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs @@ -0,0 +1,73 @@ +// 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, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class AvoidOptSuffixForNullableEnableCodeTests + { + [Fact] + public async Task RS0037_CSharp8_NullableEnabledCode_Diagnostic() + { + await new VerifyCS.Test + { + TestCode = + @" +#nullable enable + +public class Class1 +{ + private Class1? _instanceOpt; + + public void Method1(string? sOpt) + { + } +}", + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Fact] + public async Task RS0037_CSharp8_NonNullableEnabledCode_NoDiagnostic() + { + await new VerifyCS.Test + { + TestCode = + @" +public class Class1 +{ + private Class1? _instanceOpt; + + public void Method1(string? sOpt) + { + } +}", + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Fact] + public async Task RS0037_PriorToCSharp8_NoDiagnostic() + { + await new VerifyCS.Test + { + TestCode = + @" +public class Class1 +{ + private Class1 _instanceOpt; + + public void Method1(string sOpt) + { + } +}", + LanguageVersion = LanguageVersion.CSharp7_3 + }.RunAsync(); + } + } +} From a2239381d41dd4974bf74dbb7860909320f6029b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 20 Mar 2020 09:50:32 +0100 Subject: [PATCH 02/12] Rename tests --- .../UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs index 08e6408c24..d46985a2ce 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs @@ -12,7 +12,7 @@ namespace Roslyn.Diagnostics.Analyzers.UnitTests public class AvoidOptSuffixForNullableEnableCodeTests { [Fact] - public async Task RS0037_CSharp8_NullableEnabledCode_Diagnostic() + public async Task RS0042_CSharp8_NullableEnabledCode_Diagnostic() { await new VerifyCS.Test { @@ -33,7 +33,7 @@ public void Method1(string? sOpt) } [Fact] - public async Task RS0037_CSharp8_NonNullableEnabledCode_NoDiagnostic() + public async Task RS0042_CSharp8_NonNullableEnabledCode_NoDiagnostic() { await new VerifyCS.Test { @@ -52,7 +52,7 @@ public void Method1(string? sOpt) } [Fact] - public async Task RS0037_PriorToCSharp8_NoDiagnostic() + public async Task RS0042_PriorToCSharp8_NoDiagnostic() { await new VerifyCS.Test { From 35547654c2bc8cdc3ad4e82578c43a341edd5b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 3 Apr 2020 00:54:01 +0200 Subject: [PATCH 03/12] Use correct API to detect nullable enable context --- ...harpAvoidOptSuffixForNullableEnableCode.cs | 26 ------------ .../Core/AnalyzerReleases.Unshipped.md | 1 + .../AvoidOptSuffixForNullableEnableCode.cs | 18 +++----- ...voidOptSuffixForNullableEnableCodeTests.cs | 42 +++++++++++++++---- 4 files changed, 41 insertions(+), 46 deletions(-) delete mode 100644 src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs deleted file mode 100644 index 3446e245b7..0000000000 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Diagnostics.Analyzers; - -namespace Roslyn.Diagnostics.CSharp.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class CSharpAvoidOptSuffixForNullableEnableCode : AvoidOptSuffixForNullableEnableCode - { - protected override bool IsNullableEnabledContext(CompilationOptions compilationOptions, IEnumerable parseOptions) - { - var hasAnyCSharp8Part = parseOptions.OfType().Any(option => option.LanguageVersion >= LanguageVersion.CSharp8); - - return hasAnyCSharp8Part && IsNullableEnabledContext(compilationOptions); - } - - private static bool IsNullableEnabledContext(CompilationOptions compilationOptions) - => compilationOptions is CSharpCompilationOptions csharpCompilationOptions && - csharpCompilationOptions.NullableContextOptions == NullableContextOptions.Enable; - } -} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md index df143839cc..9e291a6425 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -2,6 +2,7 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0040 | RoslynDiagnosticsReliability | Warning | DefaultableTypeShouldHaveDefaultableFieldsAnalyzer +RS0042 | RoslynDiagnosticsDesign | Disabled | AvoidOptSuffixForNullableEnableCode ### Changed Rules Rule ID | New Category | New Severity | Old Category | Old Severity | Notes diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs index 22f17ace94..27c0d3515b 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs @@ -1,6 +1,5 @@ // 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.Generic; using System.Collections.Immutable; using System.Linq; using Analyzer.Utilities; @@ -10,7 +9,8 @@ namespace Roslyn.Diagnostics.Analyzers { - public abstract class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer { private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); @@ -37,19 +37,13 @@ public sealed override void Initialize(AnalysisContext context) context.RegisterSymbolAction(context => { - if (context.Symbol.Name.EndsWith("Opt", System.StringComparison.Ordinal)) + if (context.Symbol.Name.EndsWith("Opt", System.StringComparison.Ordinal) && + context.Symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is { } node && + context.Compilation.GetSemanticModel(node.SyntaxTree).GetNullableContext(node.SpanStart).AnnotationsEnabled()) { - var parseOptions = context.Symbol.DeclaringSyntaxReferences - .Select(syntaxRef => syntaxRef.GetSyntax(context.CancellationToken)?.SyntaxTree?.Options); - - if (IsNullableEnabledContext(context.Compilation.Options, parseOptions)) - { - context.ReportDiagnostic(context.Symbol.CreateDiagnostic(Rule)); - } + context.ReportDiagnostic(context.Symbol.CreateDiagnostic(Rule)); } }, SymbolKind.Parameter, SymbolKind.Field); } - - protected abstract bool IsNullableEnabledContext(CompilationOptions compilationOptions, IEnumerable parseOptions); } } diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs index d46985a2ce..a70bd0e813 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs @@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.CSharp; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< - Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCode, + Roslyn.Diagnostics.Analyzers.AvoidOptSuffixForNullableEnableCode, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; namespace Roslyn.Diagnostics.Analyzers.UnitTests @@ -16,19 +16,24 @@ public async Task RS0042_CSharp8_NullableEnabledCode_Diagnostic() { await new VerifyCS.Test { + LanguageVersion = LanguageVersion.CSharp8, TestCode = @" #nullable enable public class Class1 { - private Class1? _instanceOpt; + private Class1? _instanceOpt; // RS0042 - public void Method1(string? sOpt) + public void Method1(string? sOpt) // RS0042 { } }", - LanguageVersion = LanguageVersion.CSharp8 + ExpectedDiagnostics = + { + VerifyCS.Diagnostic().WithSpan(6, 21, 6, 33), + VerifyCS.Diagnostic().WithSpan(8, 33, 8, 37), + } }.RunAsync(); } @@ -37,17 +42,38 @@ public async Task RS0042_CSharp8_NonNullableEnabledCode_NoDiagnostic() { await new VerifyCS.Test { + LanguageVersion = LanguageVersion.CSharp8, TestCode = @" public class Class1 { - private Class1? _instanceOpt; + private Class1 _instanceOpt; + + public void Method1(string sOpt) + { + } +}", + }.RunAsync(); + } - public void Method1(string? sOpt) + [Fact] + public async Task RS0042_CSharp8_NullableDisabledCode_NoDiagnostic() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = +@" +#nullable disable + +public class Class1 +{ + private Class1 _instanceOpt; + + public void Method1(string sOpt) { } }", - LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -56,6 +82,7 @@ public async Task RS0042_PriorToCSharp8_NoDiagnostic() { await new VerifyCS.Test { + LanguageVersion = LanguageVersion.CSharp7_3, TestCode = @" public class Class1 @@ -66,7 +93,6 @@ public void Method1(string sOpt) { } }", - LanguageVersion = LanguageVersion.CSharp7_3 }.RunAsync(); } } From 1aefae69578f44bc8d012dd746d2cd4b56f3a6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 6 Apr 2020 10:26:24 +0200 Subject: [PATCH 04/12] Rewrite the analyzer as a SyntaxNode analyzer --- ...harpAvoidOptSuffixForNullableEnableCode.cs | 47 +++++++++++++++++++ .../AvoidOptSuffixForNullableEnableCode.cs | 23 +-------- ...voidOptSuffixForNullableEnableCodeTests.cs | 2 +- 3 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs new file mode 100644 index 0000000000..deda9d7d5c --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs @@ -0,0 +1,47 @@ +// 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 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 +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpAvoidOptSuffixForNullableEnableCode : AvoidOptSuffixForNullableEnableCode + { + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSyntaxNodeAction(context => + { + var parameter = (ParameterSyntax)context.Node; + + if (parameter.Identifier.Text.EndsWith("Opt", System.StringComparison.Ordinal) && + context.SemanticModel.GetNullableContext(parameter.SpanStart).AnnotationsEnabled()) + { + context.ReportDiagnostic(parameter.Identifier.CreateDiagnostic(Rule)); + } + }, SyntaxKind.Parameter); + + context.RegisterSyntaxNodeAction(context => + { + var fieldDeclaration = (FieldDeclarationSyntax)context.Node; + + foreach (var variable in fieldDeclaration.Declaration.Variables) + { + if (variable.Identifier.Text.EndsWith("Opt", System.StringComparison.Ordinal) && + context.SemanticModel.GetNullableContext(variable.SpanStart).AnnotationsEnabled()) + { + context.ReportDiagnostic(variable.Identifier.CreateDiagnostic(Rule)); + } + } + }, SyntaxKind.FieldDeclaration); + } + } +} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs index 27c0d3515b..d379c5b017 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs @@ -1,16 +1,13 @@ // 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.Linq; using Analyzer.Utilities; -using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Roslyn.Diagnostics.Analyzers { - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer + public abstract class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer { private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); @@ -28,22 +25,6 @@ public sealed class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer helpLinkUri: null, customTags: WellKnownDiagnosticTags.Telemetry); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - - public sealed override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - - context.RegisterSymbolAction(context => - { - if (context.Symbol.Name.EndsWith("Opt", System.StringComparison.Ordinal) && - context.Symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is { } node && - context.Compilation.GetSemanticModel(node.SyntaxTree).GetNullableContext(node.SpanStart).AnnotationsEnabled()) - { - context.ReportDiagnostic(context.Symbol.CreateDiagnostic(Rule)); - } - }, SymbolKind.Parameter, SymbolKind.Field); - } + public sealed override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); } } diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs index a70bd0e813..1fe7450f2f 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs @@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.CSharp; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< - Roslyn.Diagnostics.Analyzers.AvoidOptSuffixForNullableEnableCode, + Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCode, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; namespace Roslyn.Diagnostics.Analyzers.UnitTests From b5a5f3452861f0c6a91a6569ae29f5349e92318d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 27 Apr 2020 16:56:04 +0200 Subject: [PATCH 05/12] Update tests --- .../AvoidOptSuffixForNullableEnableCodeTests.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs index 1fe7450f2f..6a9844ecc8 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs @@ -12,7 +12,7 @@ namespace Roslyn.Diagnostics.Analyzers.UnitTests public class AvoidOptSuffixForNullableEnableCodeTests { [Fact] - public async Task RS0042_CSharp8_NullableEnabledCode_Diagnostic() + public async Task RS0046_CSharp8_NullableEnabledCode_Diagnostic() { await new VerifyCS.Test { @@ -23,22 +23,17 @@ public async Task RS0042_CSharp8_NullableEnabledCode_Diagnostic() public class Class1 { - private Class1? _instanceOpt; // RS0042 + private Class1? [|_instanceOpt|]; - public void Method1(string? sOpt) // RS0042 + public void Method1(string? [|sOpt|]) { } }", - ExpectedDiagnostics = - { - VerifyCS.Diagnostic().WithSpan(6, 21, 6, 33), - VerifyCS.Diagnostic().WithSpan(8, 33, 8, 37), - } }.RunAsync(); } [Fact] - public async Task RS0042_CSharp8_NonNullableEnabledCode_NoDiagnostic() + public async Task RS0046_CSharp8_NonNullableEnabledCode_NoDiagnostic() { await new VerifyCS.Test { @@ -57,7 +52,7 @@ public void Method1(string sOpt) } [Fact] - public async Task RS0042_CSharp8_NullableDisabledCode_NoDiagnostic() + public async Task RS0046_CSharp8_NullableDisabledCode_NoDiagnostic() { await new VerifyCS.Test { @@ -78,7 +73,7 @@ public void Method1(string sOpt) } [Fact] - public async Task RS0042_PriorToCSharp8_NoDiagnostic() + public async Task RS0046_PriorToCSharp8_NoDiagnostic() { await new VerifyCS.Test { From a0d446d29359b85048775db4a7cfd71a02d019d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sat, 2 May 2020 20:35:50 +0200 Subject: [PATCH 06/12] Address review comments --- .../CSharp/AnalyzerReleases.Unshipped.md | 1 + ...harpAvoidOptSuffixForNullableEnableCode.cs | 57 +++++++++++++------ ...fixForNullableEnableCodeCodeFixProvider.cs | 56 ++++++++++++++++++ .../Core/AnalyzerReleases.Unshipped.md | 1 - .../AvoidOptSuffixForNullableEnableCode.cs | 30 ---------- .../Core/RoslynDiagnosticIds.cs | 1 - ...oidOptSuffixForNullableEnableCodeTests.cs} | 44 +++++++++++--- 7 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs delete mode 100644 src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs rename src/Roslyn.Diagnostics.Analyzers/UnitTests/{AvoidOptSuffixForNullableEnableCodeTests.cs => CSharpAvoidOptSuffixForNullableEnableCodeTests.cs} (65%) 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 index deda9d7d5c..fc2ee98d37 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs @@ -1,5 +1,8 @@ // 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; @@ -10,9 +13,31 @@ namespace Roslyn.Diagnostics.CSharp.Analyzers { + /// + /// RS0046: Avoid 'Opt' suffix for nullable enable code + /// [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class CSharpAvoidOptSuffixForNullableEnableCode : AvoidOptSuffixForNullableEnableCode + public sealed class CSharpAvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer { + internal const string OptSuffix = "Opt"; + + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdMessage), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdDescription), 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(); @@ -21,27 +46,23 @@ public override void Initialize(AnalysisContext context) context.RegisterSyntaxNodeAction(context => { var parameter = (ParameterSyntax)context.Node; - - if (parameter.Identifier.Text.EndsWith("Opt", System.StringComparison.Ordinal) && - context.SemanticModel.GetNullableContext(parameter.SpanStart).AnnotationsEnabled()) - { - context.ReportDiagnostic(parameter.Identifier.CreateDiagnostic(Rule)); - } + ReportOnInvalidIdentifier(parameter.Identifier, context.SemanticModel, context.ReportDiagnostic); }, SyntaxKind.Parameter); context.RegisterSyntaxNodeAction(context => { - var fieldDeclaration = (FieldDeclarationSyntax)context.Node; - - foreach (var variable in fieldDeclaration.Declaration.Variables) - { - if (variable.Identifier.Text.EndsWith("Opt", System.StringComparison.Ordinal) && - context.SemanticModel.GetNullableContext(variable.SpanStart).AnnotationsEnabled()) - { - context.ReportDiagnostic(variable.Identifier.CreateDiagnostic(Rule)); - } - } - }, SyntaxKind.FieldDeclaration); + 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()) + { + 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..c99d5055d7 --- /dev/null +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -0,0 +1,56 @@ +// 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 Microsoft.CodeAnalysis.Text; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider))] + [Shared] + public sealed class CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CSharpAvoidOptSuffixForNullableEnableCode.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.TestExportsShouldNotBeDiscoverableCodeFix, + cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(CSharpAvoidOptSuffixForNullableEnableCode)), + diagnostic); + } + + return Task.CompletedTask; + } + + private static async Task RemoveOptSuffixOnVariableAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var variable = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + var variableSymbol = semanticModel.GetDeclaredSymbol(variable, cancellationToken); + + return await Renamer.RenameSymbolAsync( + document.Project.Solution, + variableSymbol, + variableSymbol.Name.Substring(0, variableSymbol.Name.Length - CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length), + document.Project.Solution.Options, + cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md index e21a40a4ae..f19b317173 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -6,7 +6,6 @@ RS0042 | RoslynDiagnosticsReliability | Warning | DoNotCopyValue RS0043 | RoslynDiagnosticsMaintainability | Warning | DoNotCallGetTestAccessor RS0044 | RoslynDiagnosticsMaintainability | Hidden | CreateTestAccessor RS0045 | RoslynDiagnosticsMaintainability | Hidden | ExposeMemberForTesting -RS0046 | RoslynDiagnosticsDesign | Disabled | AvoidOptSuffixForNullableEnableCode RS0101 | RoslynDiagnosticsMaintainability | Warning | AbstractBlankLinesDiagnosticAnalyzer ### Changed Rules diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs deleted file mode 100644 index d379c5b017..0000000000 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AvoidOptSuffixForNullableEnableCode.cs +++ /dev/null @@ -1,30 +0,0 @@ -// 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 Analyzer.Utilities; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Roslyn.Diagnostics.Analyzers -{ - public abstract class AvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer - { - private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); - - private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdMessage), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); - private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdDescription), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); - - internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor( - RoslynDiagnosticIds.AvoidOptSuffixForNullableEnableCodeRuleId, - s_localizableTitle, - s_localizableMessage, - DiagnosticCategory.RoslynDiagnosticsDesign, - DiagnosticSeverity.Warning, - isEnabledByDefault: false, - description: s_localizableDescription, - helpLinkUri: null, - customTags: WellKnownDiagnosticTags.Telemetry); - - public sealed override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - } -} diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs index 24c3b2477e..d463d571dd 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs @@ -50,7 +50,6 @@ internal static class RoslynDiagnosticIds 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"; public const string BracePlacementRuleId = "RS0102"; diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs similarity index 65% rename from src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs rename to src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs index 6a9844ecc8..8ab49c7e28 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/AvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -5,11 +5,11 @@ using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCode, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider>; namespace Roslyn.Diagnostics.Analyzers.UnitTests { - public class AvoidOptSuffixForNullableEnableCodeTests + public class CSharpAvoidOptSuffixForNullableEnableCodeTests { [Fact] public async Task RS0046_CSharp8_NullableEnabledCode_Diagnostic() @@ -17,18 +17,43 @@ public async Task RS0046_CSharp8_NullableEnabledCode_Diagnostic() await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp8, - TestCode = - @" + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], [|otherInstanceOpt|]; + + public void Method1(string? [|sOpt|]) + { + string? [|localOpt|], [|otherLocalOpt|]; + } +}", + FixedCode = @" #nullable enable public class Class1 { - private Class1? [|_instanceOpt|]; + private Class1? _instanceOpt, [|otherInstanceOpt|]; public void Method1(string? [|sOpt|]) { + string? [|localOpt|], [|otherLocalOpt|]; + } +}", + BatchFixedCode = @" +#nullable enable + +public class Class1 +{ + private Class1? _instance, otherInstance; + + public void Method1(string? s) + { + string? other, otherLocalOpt; } }", + }.RunAsync(); } @@ -42,10 +67,11 @@ public async Task RS0046_CSharp8_NonNullableEnabledCode_NoDiagnostic() @" public class Class1 { - private Class1 _instanceOpt; + private Class1 _instanceOpt, otherInstanceOpt; public void Method1(string sOpt) { + string localOpt, otherLocalOpt; } }", }.RunAsync(); @@ -63,10 +89,11 @@ public async Task RS0046_CSharp8_NullableDisabledCode_NoDiagnostic() public class Class1 { - private Class1 _instanceOpt; + private Class1 _instanceOpt, otherInstanceOpt; public void Method1(string sOpt) { + string localOpt, otherLocalOpt; } }", }.RunAsync(); @@ -82,10 +109,11 @@ public async Task RS0046_PriorToCSharp8_NoDiagnostic() @" public class Class1 { - private Class1 _instanceOpt; + private Class1 _instanceOpt, otherInstanceOpt; public void Method1(string sOpt) { + string localOpt, otherLocalOpt; } }", }.RunAsync(); From cfaf9f37fc833aab0a2e567f94024771e58396a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sat, 2 May 2020 23:16:59 +0200 Subject: [PATCH 07/12] WIP --- ...fixForNullableEnableCodeCodeFixProvider.cs | 25 +++++++++++-------- .../RoslynDiagnosticsAnalyzersResources.resx | 3 +++ ...RoslynDiagnosticsAnalyzersResources.cs.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.de.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.es.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.fr.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.it.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.ja.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.ko.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.pl.xlf | 5 ++++ ...lynDiagnosticsAnalyzersResources.pt-BR.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.ru.xlf | 5 ++++ ...RoslynDiagnosticsAnalyzersResources.tr.xlf | 5 ++++ ...nDiagnosticsAnalyzersResources.zh-Hans.xlf | 5 ++++ ...nDiagnosticsAnalyzersResources.zh-Hant.xlf | 5 ++++ ...voidOptSuffixForNullableEnableCodeTests.cs | 13 +--------- 16 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs index c99d5055d7..9743622d4e 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -13,7 +13,7 @@ namespace Roslyn.Diagnostics.CSharp.Analyzers { - [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider))] + [ExportCodeFixProvider(LanguageNames.CSharp)] [Shared] public sealed class CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider : CodeFixProvider { @@ -24,20 +24,22 @@ public override FixAllProvider GetFixAllProvider() public override Task RegisterCodeFixesAsync(CodeFixContext context) { + var title = RoslynDiagnosticsAnalyzersResources.TestExportsShouldNotBeDiscoverableCodeFix; + foreach (var diagnostic in context.Diagnostics) { context.RegisterCodeFix( CodeAction.Create( - RoslynDiagnosticsAnalyzersResources.TestExportsShouldNotBeDiscoverableCodeFix, + title, cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), - equivalenceKey: nameof(CSharpAvoidOptSuffixForNullableEnableCode)), + equivalenceKey: title), diagnostic); } return Task.CompletedTask; } - private static async Task RemoveOptSuffixOnVariableAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + private static async Task RemoveOptSuffixOnVariableAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -45,12 +47,15 @@ private static async Task RemoveOptSuffixOnVariableAsync(Document docu var variable = root.FindNode(sourceSpan, getInnermostNodeForTie: true); var variableSymbol = semanticModel.GetDeclaredSymbol(variable, cancellationToken); - return await Renamer.RenameSymbolAsync( - document.Project.Solution, - variableSymbol, - variableSymbol.Name.Substring(0, variableSymbol.Name.Length - CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length), - document.Project.Solution.Options, - cancellationToken).ConfigureAwait(false); + var newSolution = await Renamer.RenameSymbolAsync( + document.Project.Solution, + variableSymbol, + variableSymbol.Name.Substring(0, variableSymbol.Name.Length - CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length), + document.Project.Solution.Options, + cancellationToken) + .ConfigureAwait(false); + + return newSolution.GetDocument(document.Id)!; } } } diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx index 72984117ba..1fa2f11602 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx @@ -366,4 +366,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 df9d1a4850..0e340d3435 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf index 016f30e7bc..069ca44d84 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf index c37a760162..f8e30b52ce 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf index b3dde5bc49..580433009f 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf index 60b597fea4..c158c70588 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf index 98c95854cc..a83740dd2a 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines 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 6d3b121bf9..fba675f4f1 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines 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 9d63a90a13..fd956cb39a 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines 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 9cb4217770..ceed1b0ed8 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf index b3ab8f6af9..b942ad80e3 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines 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 c8e878b1fc..5112544fd9 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines 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 32bfc60fc8..522a25cd44 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines 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 559adc7946..e1ff1611cf 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf @@ -17,6 +17,11 @@ Avoid the 'Opt' suffix + + Remove the 'Opt' suffix + Remove the 'Opt' suffix + + Avoid multiple blank lines Avoid multiple blank lines diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs index 8ab49c7e28..0b29f9d55c 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -32,18 +32,6 @@ public void Method1(string? [|sOpt|]) FixedCode = @" #nullable enable -public class Class1 -{ - private Class1? _instanceOpt, [|otherInstanceOpt|]; - - public void Method1(string? [|sOpt|]) - { - string? [|localOpt|], [|otherLocalOpt|]; - } -}", - BatchFixedCode = @" -#nullable enable - public class Class1 { private Class1? _instance, otherInstance; @@ -53,6 +41,7 @@ public void Method1(string? s) string? other, otherLocalOpt; } }", + NumberOfFixAllIterations = 2, }.RunAsync(); } From fd87fe9553c2128b8155ef4b818594c02be793f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 6 May 2020 23:31:03 +0200 Subject: [PATCH 08/12] Fix broken code fix --- .../CSharpAvoidOptSuffixForNullableEnableCodeTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs index 0b29f9d55c..0dc5d47edf 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -38,10 +38,9 @@ public class Class1 public void Method1(string? s) { - string? other, otherLocalOpt; + string? local, otherLocal; } }", - NumberOfFixAllIterations = 2, }.RunAsync(); } From 5ab9ec7ffcad193a98ba538e8328e6abd6c17ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 27 May 2020 17:59:33 +0200 Subject: [PATCH 09/12] Address review comments on code-fix --- ...fixForNullableEnableCodeCodeFixProvider.cs | 71 ++++++++++++------- .../Core/RoslynDiagnosticIds.cs | 1 + 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs index 9743622d4e..3d62da5905 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -1,14 +1,14 @@ // 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.Composition; using System.Threading; using System.Threading.Tasks; +using Analyzer.Utilities; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Rename; -using Microsoft.CodeAnalysis.Text; using Roslyn.Diagnostics.Analyzers; namespace Roslyn.Diagnostics.CSharp.Analyzers @@ -22,40 +22,61 @@ public sealed class CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider : C public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; - public override Task RegisterCodeFixesAsync(CodeFixContext context) + public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var title = RoslynDiagnosticsAnalyzersResources.TestExportsShouldNotBeDiscoverableCodeFix; + var title = RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitleCodeFixTitle; foreach (var diagnostic in context.Diagnostics) { - context.RegisterCodeFix( - CodeAction.Create( - title, - cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), - equivalenceKey: title), - diagnostic); - } + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root == null) + { + continue; + } + + 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) + { + continue; + } - return Task.CompletedTask; + 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( + new MyCodeAction( + title, + cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, variableSymbol, newName, cancellationToken), + equivalenceKey: title), + diagnostic); + } + } } - private static async Task RemoveOptSuffixOnVariableAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + private static async Task RemoveOptSuffixOnVariableAsync(Document document, ISymbol variableSymbol, string newName, CancellationToken cancellationToken) { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - - var variable = root.FindNode(sourceSpan, getInnermostNodeForTie: true); - var variableSymbol = semanticModel.GetDeclaredSymbol(variable, cancellationToken); - - var newSolution = await Renamer.RenameSymbolAsync( - document.Project.Solution, - variableSymbol, - variableSymbol.Name.Substring(0, variableSymbol.Name.Length - CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length), - document.Project.Solution.Options, - cancellationToken) + var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, variableSymbol, newName, document.Project.Solution.Options, cancellationToken) .ConfigureAwait(false); return newSolution.GetDocument(document.Id)!; } + + // Needed for Telemetry (https://github.com/dotnet/roslyn/issues/4919) + private class MyCodeAction : DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) + : base(title, createChangedDocument, equivalenceKey) + { + } + } } } diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs index d463d571dd..24c3b2477e 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs @@ -50,6 +50,7 @@ internal static class RoslynDiagnosticIds 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"; public const string BracePlacementRuleId = "RS0102"; From fcd72f25a346854c9f6f8371a76e043d19e4b06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 28 May 2020 11:52:01 +0200 Subject: [PATCH 10/12] Add more tests and ensure no diag on non-nullable type --- ...harpAvoidOptSuffixForNullableEnableCode.cs | 10 +- ...fixForNullableEnableCodeCodeFixProvider.cs | 19 +- ...voidOptSuffixForNullableEnableCodeTests.cs | 162 +++++++++++++++++- 3 files changed, 173 insertions(+), 18 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs index fc2ee98d37..a13b099ddc 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs @@ -58,8 +58,14 @@ public override void Initialize(AnalysisContext context) private static void ReportOnInvalidIdentifier(SyntaxToken identifier, SemanticModel semanticModel, Action reportAction) { - if (identifier.Text.EndsWith(OptSuffix, StringComparison.Ordinal) && - semanticModel.GetNullableContext(identifier.SpanStart).AnnotationsEnabled()) + 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 index 3d62da5905..9c42104b17 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -29,11 +29,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) foreach (var diagnostic in context.Diagnostics) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - if (root == null) - { - continue; - } - var variable = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); if (variable == null) { @@ -62,19 +57,15 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } } - private static async Task RemoveOptSuffixOnVariableAsync(Document document, ISymbol variableSymbol, string newName, CancellationToken cancellationToken) - { - var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, variableSymbol, newName, document.Project.Solution.Options, cancellationToken) + 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); - return newSolution.GetDocument(document.Id)!; - } - // Needed for Telemetry (https://github.com/dotnet/roslyn/issues/4919) - private class MyCodeAction : DocumentChangeAction + private class MyCodeAction : SolutionChangeAction { - public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) - : base(title, createChangedDocument, equivalenceKey) + public MyCodeAction(string title, Func> createChangedSolution, string equivalenceKey) + : base(title, createChangedSolution, equivalenceKey) { } } diff --git a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs index 0dc5d47edf..50530ca7f3 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -26,7 +26,9 @@ public class Class1 public void Method1(string? [|sOpt|]) { - string? [|localOpt|], [|otherLocalOpt|]; + string? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}"", _instanceOpt, otherInstanceOpt, sOpt, localOpt, otherLocalOpt); } }", FixedCode = @" @@ -38,7 +40,97 @@ public class Class1 public void Method1(string? s) { - string? local, otherLocal; + 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; } }", @@ -103,6 +195,72 @@ public void Method1(string sOpt) { string localOpt, otherLocalOpt; } +}", + }.RunAsync(); + } + + [Fact] + 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? s, string? {|CS0100: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(); } From 36ae247f2c34475588e73b704d8e50e985222751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 3 Jun 2020 22:16:57 +0200 Subject: [PATCH 11/12] Address code review comments - Fix resource names - Skip broken test - Avoid error in case the name has changed and is too short --- .../CSharpAvoidOptSuffixForNullableEnableCode.cs | 6 +++--- ...ptSuffixForNullableEnableCodeCodeFixProvider.cs | 2 +- .../Core/RoslynDiagnosticsAnalyzersResources.resx | 6 +++--- .../xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.de.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.es.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.it.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf | 14 +++++++------- .../RoslynDiagnosticsAnalyzersResources.pt-BR.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf | 14 +++++++------- .../xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf | 14 +++++++------- ...RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf | 14 +++++++------- ...RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf | 14 +++++++------- ...harpAvoidOptSuffixForNullableEnableCodeTests.cs | 4 ++-- 17 files changed, 100 insertions(+), 100 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs index a13b099ddc..71580d9ab8 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs @@ -21,9 +21,9 @@ public sealed class CSharpAvoidOptSuffixForNullableEnableCode : DiagnosticAnalyz { internal const string OptSuffix = "Opt"; - private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitle), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); - private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdMessage), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); - private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdDescription), RoslynDiagnosticsAnalyzersResources.ResourceManager, typeof(RoslynDiagnosticsAnalyzersResources)); + 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, diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs index 9c42104b17..42ee421959 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -37,7 +37,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); var variableSymbol = semanticModel.GetDeclaredSymbol(variable, context.CancellationToken); - if (variableSymbol == null) + if (variableSymbol == null || variableSymbol.Name.Length <= CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length) { continue; } diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx index caea7cc4de..c9934779b9 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx @@ -330,13 +330,13 @@ 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 diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf index cb88e543e8..ffc509a8e4 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 2a0849cd23..39ad1b42a3 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 958f3fd349..a8982c6a70 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 552fa35e1d..0d20eba168 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 799a0ec21c..46b49d7340 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 834d25b750..08fed21e32 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 fd3921bc3e..94f87ec5d1 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 ad2e8ee932..52d32e63c8 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 a05b37c32b..debdbf8238 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 2d956d5a3e..b3e678d105 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 2c7be2584b..3eaccca3fa 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 f01b283547..2a50fd4ab9 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 3053a24dd4..92e481e937 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf @@ -2,26 +2,26 @@ - + 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 - - Remove the 'Opt' suffix Remove the 'Opt' suffix + + 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 index 50530ca7f3..ddd974d5b5 100644 --- a/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs +++ b/src/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -199,7 +199,7 @@ public void Method1(string sOpt) }.RunAsync(); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn-analyzers/issues/3707")] public async Task RS0046_CSharp8_VariableWithoutOptAlreadyExists_DiagnosticButNoCodeFix() { await new VerifyCS.Test @@ -224,7 +224,7 @@ public class Class1 { private Class1? [|_instanceOpt|], _instance; - public void Method1(string? s, string? {|CS0100:s|}) + public void Method1(string? [|sOpt|], string? s) { string? [|localOpt|], local; } From 5493e1babfc413989628076c2b0635bf6d5bdeff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 4 Jun 2020 18:01:55 +0200 Subject: [PATCH 12/12] Address final review comments --- ...SuffixForNullableEnableCodeCodeFixProvider.cs | 16 +++------------- .../RoslynDiagnosticsAnalyzersResources.resx | 2 +- .../RoslynDiagnosticsAnalyzersResources.cs.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.de.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.es.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.fr.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.it.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.ja.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.ko.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.pl.xlf | 10 +++++----- ...RoslynDiagnosticsAnalyzersResources.pt-BR.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.ru.xlf | 10 +++++----- .../RoslynDiagnosticsAnalyzersResources.tr.xlf | 10 +++++----- ...slynDiagnosticsAnalyzersResources.zh-Hans.xlf | 10 +++++----- ...slynDiagnosticsAnalyzersResources.zh-Hant.xlf | 10 +++++----- 15 files changed, 69 insertions(+), 79 deletions(-) diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs index 42ee421959..c304f3c2cb 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -1,12 +1,11 @@ // 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.Composition; using System.Threading; using System.Threading.Tasks; -using Analyzer.Utilities; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Rename; using Roslyn.Diagnostics.Analyzers; @@ -24,7 +23,7 @@ public override FixAllProvider GetFixAllProvider() public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var title = RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeRuleIdTitleCodeFixTitle; + var title = RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeCodeFixTitle; foreach (var diagnostic in context.Diagnostics) { @@ -48,7 +47,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) if (semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, variableSymbol.ContainingType, newName).IsEmpty) { context.RegisterCodeFix( - new MyCodeAction( + CodeAction.Create( title, cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, variableSymbol, newName, cancellationToken), equivalenceKey: title), @@ -60,14 +59,5 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) 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); - - // Needed for Telemetry (https://github.com/dotnet/roslyn/issues/4919) - private class MyCodeAction : SolutionChangeAction - { - public MyCodeAction(string title, Func> createChangedSolution, string equivalenceKey) - : base(title, createChangedSolution, equivalenceKey) - { - } - } } } diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx index c9934779b9..880715070f 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx +++ b/src/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx @@ -357,7 +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 ffc509a8e4..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf index 39ad1b42a3..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf index a8982c6a70..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf index 0d20eba168..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf index 46b49d7340..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf index 08fed21e32..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf index 94f87ec5d1..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf index 52d32e63c8..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix 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 debdbf8238..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf index b3e678d105..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf index 3eaccca3fa..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix 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 2a50fd4ab9..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix 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 92e481e937..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,11 @@ + + 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. @@ -12,11 +17,6 @@ Avoid the 'Opt' suffix in a nullable-enabled code - - Remove the 'Opt' suffix - Remove the 'Opt' suffix - - Avoid the 'Opt' suffix Avoid the 'Opt' suffix