diff --git a/src/Microsoft.CodeAnalysis.Analyzers/CSharp/CSharpImmutableObjectMethodAnalyzer.cs b/src/Microsoft.CodeAnalysis.Analyzers/CSharp/CSharpImmutableObjectMethodAnalyzer.cs deleted file mode 100644 index da96c09977..0000000000 --- a/src/Microsoft.CodeAnalysis.Analyzers/CSharp/CSharpImmutableObjectMethodAnalyzer.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using Analyzer.Utilities; -using Analyzer.Utilities.Extensions; -using Microsoft.CodeAnalysis.Analyzers; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.CSharp.Analyzers -{ - using static CodeAnalysisDiagnosticsResources; - - /// - /// RS1014: - /// - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class CSharpImmutableObjectMethodAnalyzer : DiagnosticAnalyzer - { - public static readonly DiagnosticDescriptor DoNotIgnoreReturnValueDiagnosticRule = new( - DiagnosticIds.DoNotIgnoreReturnValueOnImmutableObjectMethodInvocation, - CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationTitle)), - CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationMessage)), - DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationDescription)), - customTags: WellKnownDiagnosticTagsExtensions.Telemetry); - - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotIgnoreReturnValueDiagnosticRule); - - private const string SolutionFullName = @"Microsoft.CodeAnalysis.Solution"; - private const string ProjectFullName = @"Microsoft.CodeAnalysis.Project"; - private const string DocumentFullName = @"Microsoft.CodeAnalysis.Document"; - private const string SyntaxNodeFullName = @"Microsoft.CodeAnalysis.SyntaxNode"; - private const string CompilationFullName = @"Microsoft.CodeAnalysis.Compilation"; - - private static readonly ImmutableArray s_immutableMethodNames = ImmutableArray.Create( - "Add", - "Remove", - "Replace", - "With"); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - - context.RegisterCompilationStartAction(compilationContext => - { - if (!compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(SolutionFullName, out var solutionSymbol) || - !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(ProjectFullName, out var projectSymbol) || - !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(DocumentFullName, out var documentSymbol) || - !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(SyntaxNodeFullName, out var syntaxNodeSymbol) || - !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(CompilationFullName, out var compilationSymbol)) - { - // Only register our node action if we can find the symbols for our immutable types - return; - } - - var immutableSymbols = ImmutableArray.Create(solutionSymbol, projectSymbol, documentSymbol, syntaxNodeSymbol, compilationSymbol); - compilationContext.RegisterSyntaxNodeAction(sc => AnalyzeInvocationForIgnoredReturnValue(sc, immutableSymbols), SyntaxKind.InvocationExpression); - }); - } - - public static void AnalyzeInvocationForIgnoredReturnValue(SyntaxNodeAnalysisContext context, ImmutableArray immutableTypeSymbols) - { - SemanticModel model = context.SemanticModel; - var candidateInvocation = (InvocationExpressionSyntax)context.Node; - - //We're looking for invocations that are direct children of expression statements - if (!candidateInvocation.Parent.IsKind(SyntaxKind.ExpressionStatement)) - { - return; - } - - //If we can't find the method symbol, quit - if (model.GetSymbolInfo(candidateInvocation, context.CancellationToken).Symbol is not IMethodSymbol methodSymbol) - { - return; - } - - //If the method doesn't start with something like "With" or "Replace", quit - string methodName = methodSymbol.Name; - if (!s_immutableMethodNames.Any(n => methodName.StartsWith(n, StringComparison.Ordinal))) - { - return; - } - - //If we're not in one of the known immutable types, quit - if (methodSymbol.ReceiverType is not INamedTypeSymbol parentType) - { - return; - } - - var baseTypesAndSelf = methodSymbol.ReceiverType.GetBaseTypes().ToList(); - baseTypesAndSelf.Add(parentType); - - if (!baseTypesAndSelf.Any(immutableTypeSymbols.Contains)) - { - return; - } - - Location location = candidateInvocation.GetLocation(); - Diagnostic diagnostic = Diagnostic.Create(DoNotIgnoreReturnValueDiagnosticRule, location, methodSymbol.ReceiverType.Name, methodSymbol.Name); - context.ReportDiagnostic(diagnostic); - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md index 0a644588b4..95e7943b47 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -4,5 +4,6 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- +RS1014 | MicrosoftCodeAnalysisCorrectness | Warning | ImmutableObjectMethodAnalyzer RS1035 | MicrosoftCodeAnalysisCorrectness | Error | SymbolIsBannedInAnalyzersAnalyzer RS1036 | MicrosoftCodeAnalysisCorrectness | Warning | SymbolIsBannedInAnalyzersAnalyzer diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx b/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx index 18a503334d..f667d01101 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx @@ -269,7 +269,7 @@ '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. - Do not ignore values returned by methods on immutable objects. + Do not ignore values returned by methods on immutable objects DiagnosticId for analyzers must be a non-null constant diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/ImmutableObjectMethodAnalyzer.cs b/src/Microsoft.CodeAnalysis.Analyzers/Core/ImmutableObjectMethodAnalyzer.cs new file mode 100644 index 0000000000..d81369491b --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/ImmutableObjectMethodAnalyzer.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1014: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class ImmutableObjectMethodAnalyzer : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor DoNotIgnoreReturnValueDiagnosticRule = new( + DiagnosticIds.DoNotIgnoreReturnValueOnImmutableObjectMethodInvocation, + CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationTitle)), + CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotIgnoreReturnValueDiagnosticRule); + + private const string SolutionFullName = @"Microsoft.CodeAnalysis.Solution"; + private const string ProjectFullName = @"Microsoft.CodeAnalysis.Project"; + private const string DocumentFullName = @"Microsoft.CodeAnalysis.Document"; + private const string SyntaxNodeFullName = @"Microsoft.CodeAnalysis.SyntaxNode"; + private const string CompilationFullName = @"Microsoft.CodeAnalysis.Compilation"; + + private static readonly ImmutableArray s_immutableMethodNames = ImmutableArray.Create( + "Add", + "Remove", + "Replace", + "With"); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + var compilation = context.Compilation; + var builder = ImmutableArray.CreateBuilder(); + var provider = WellKnownTypeProvider.GetOrCreate(compilation); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(SolutionFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(ProjectFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(DocumentFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(SyntaxNodeFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(CompilationFullName)); + var immutableTypeSymbols = builder.ToImmutable(); + if (immutableTypeSymbols.Length > 0) + { + context.RegisterOperationAction(context => AnalyzeInvocationForIgnoredReturnValue(context, immutableTypeSymbols), OperationKind.Invocation); + } + }); + + static void AddIfNotNull(ImmutableArray.Builder builder, INamedTypeSymbol? symbol) + { + if (symbol is not null) + { + builder.Add(symbol); + } + } + } + + public static void AnalyzeInvocationForIgnoredReturnValue(OperationAnalysisContext context, ImmutableArray immutableTypeSymbols) + { + var invocation = (IInvocationOperation)context.Operation; + if (invocation.Parent is not IExpressionStatementOperation) + { + return; + } + + // If the method doesn't start with something like "With" or "Replace", quit + string methodName = invocation.TargetMethod.Name; + if (!s_immutableMethodNames.Any(n => methodName.StartsWith(n, StringComparison.Ordinal))) + { + return; + } + + INamedTypeSymbol? type = null; + if (invocation.Instance is not null) + { + type = invocation.Instance.Type as INamedTypeSymbol; + } + else if (invocation.TargetMethod.IsExtensionMethod && invocation.TargetMethod.Parameters is [{ Type: var parameterType }, ..]) + { + type = parameterType as INamedTypeSymbol; + } + + // If we're not in one of the known immutable types, quit + if (type is not null && type.GetBaseTypesAndThis().Any(immutableTypeSymbols.Contains)) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(DoNotIgnoreReturnValueDiagnosticRule, type.Name, methodName)); + } + } + } +} diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf index 7c7bb71967..e182f32064 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Neignorujte hodnoty vrácené metodami u neměnných objektů. + Do not ignore values returned by methods on immutable objects + Neignorujte hodnoty vrácené metodami u neměnných objektů. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf index f248a7001c..577af1f446 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Keine von Methoden zu unveränderlichen Objekten zurückgegebenen Werte ignorieren + Do not ignore values returned by methods on immutable objects + Keine von Methoden zu unveränderlichen Objekten zurückgegebenen Werte ignorieren diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf index 00bb024171..fe5727536f 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - No omitir los valores devueltos por métodos en objetos inmutables. + Do not ignore values returned by methods on immutable objects + No omitir los valores devueltos por métodos en objetos inmutables. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf index ce38e4b292..26270fb5f4 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - N'ignorez pas les valeurs retournées par les méthodes sur les objets non modifiables. + Do not ignore values returned by methods on immutable objects + N'ignorez pas les valeurs retournées par les méthodes sur les objets non modifiables. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf index f6ba0a4364..0d89cb3619 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Non ignorare i valori restituiti dai metodi su oggetti non modificabili. + Do not ignore values returned by methods on immutable objects + Non ignorare i valori restituiti dai metodi su oggetti non modificabili. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf index a858898b70..249d6c8cf7 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - 不変オブジェクトのメソッドによって返される値を無視しないでください。 + Do not ignore values returned by methods on immutable objects + 不変オブジェクトのメソッドによって返される値を無視しないでください。 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf index 6be7183fd5..0f45b37bdf 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - 변경할 수 없는 개체의 메서드에서 반환된 값을 무시하지 마세요. + Do not ignore values returned by methods on immutable objects + 변경할 수 없는 개체의 메서드에서 반환된 값을 무시하지 마세요. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf index 75e2d1f329..435c37cfaa 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Nie ignoruj wartości zwracanych przez metody dla niezmienialnych obiektów. + Do not ignore values returned by methods on immutable objects + Nie ignoruj wartości zwracanych przez metody dla niezmienialnych obiektów. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf index 033aec8345..ddd788db18 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Não ignore valores retornados por métodos em objetos imutáveis. + Do not ignore values returned by methods on immutable objects + Não ignore valores retornados por métodos em objetos imutáveis. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf index e21575c4b9..e498410c39 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Не игнорируйте значения, возвращаемые методами для неизменяемых объектов. + Do not ignore values returned by methods on immutable objects + Не игнорируйте значения, возвращаемые методами для неизменяемых объектов. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf index 25479c6c48..1a12205dc9 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - Sabit nesneler üzerinde yöntemler tarafından döndürülen değerleri yoksaymayın. + Do not ignore values returned by methods on immutable objects + Sabit nesneler üzerinde yöntemler tarafından döndürülen değerleri yoksaymayın. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf index 8f3cf4bc26..d0f695c89b 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - 不要忽略通过不可变对象上的方法返回的值。 + Do not ignore values returned by methods on immutable objects + 不要忽略通过不可变对象上的方法返回的值。 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf index d6cccc2f25..4fcb7e3a42 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf @@ -571,8 +571,8 @@ - Do not ignore values returned by methods on immutable objects. - 請勿略過不可變物件上方法所傳回的值。 + Do not ignore values returned by methods on immutable objects + 請勿略過不可變物件上方法所傳回的值。 diff --git a/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs b/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs index 1690cae80f..ca427302d7 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs +++ b/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs @@ -4,7 +4,11 @@ using Microsoft.CodeAnalysis.Testing; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< - Microsoft.CodeAnalysis.CSharp.Analyzers.CSharpImmutableObjectMethodAnalyzer, + Microsoft.CodeAnalysis.Analyzers.ImmutableObjectMethodAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.ImmutableObjectMethodAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; namespace Microsoft.CodeAnalysis.Analyzers.UnitTests @@ -23,27 +27,58 @@ class TestSimple void M() { Document document = default(Document); - document.WithText(default(SourceText)); + {|#0:document.WithText(default(SourceText))|}; Project project = default(Project); - project.AddDocument(""Sample.cs"", default(SourceText)); + {|#1:project.AddDocument(""Sample.cs"", default(SourceText))|}; Solution solution = default(Solution); - solution.AddProject(""Sample"", ""Sample"", ""CSharp""); + {|#2:solution.AddProject(""Sample"", ""Sample"", ""CSharp"")|}; Compilation compilation = default(Compilation); - compilation.RemoveAllSyntaxTrees(); + {|#3:compilation.RemoveAllSyntaxTrees()|}; } } "; - DiagnosticResult documentExpected = GetCSharpExpectedDiagnostic(10, 9, "Document", "WithText"); - DiagnosticResult projectExpected = GetCSharpExpectedDiagnostic(13, 9, "Project", "AddDocument"); - DiagnosticResult solutionExpected = GetCSharpExpectedDiagnostic(16, 9, "Solution", "AddProject"); - DiagnosticResult compilationExpected = GetCSharpExpectedDiagnostic(19, 9, "Compilation", "RemoveAllSyntaxTrees"); + DiagnosticResult documentExpected = GetCSharpExpectedDiagnostic(0, "Document", "WithText"); + DiagnosticResult projectExpected = GetCSharpExpectedDiagnostic(1, "Project", "AddDocument"); + DiagnosticResult solutionExpected = GetCSharpExpectedDiagnostic(2, "Solution", "AddProject"); + DiagnosticResult compilationExpected = GetCSharpExpectedDiagnostic(3, "Compilation", "RemoveAllSyntaxTrees"); await VerifyCS.VerifyAnalyzerAsync(source, documentExpected, projectExpected, solutionExpected, compilationExpected); } + [Fact] + public async Task VisualBasicVerifyDiagnosticsAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text + +Class TestSimple + Sub M() + Dim document As Document = Nothing + {|#0:document.WithText(Nothing)|} + + Dim project As Project = Nothing + {|#1:project.AddDocument(""Sample.cs"", CType(Nothing, SourceText))|} + + Dim solution As Solution = Nothing + {|#2:solution.AddProject(""Sample"", ""Sample"", ""CSharp"")|} + + Dim compilation As Compilation = Nothing + {|#3:compilation.RemoveAllSyntaxTrees()|} + End Sub +End Class +"; + DiagnosticResult documentExpected = GetVisualBasicExpectedDiagnostic(0, "Document", "WithText"); + DiagnosticResult projectExpected = GetVisualBasicExpectedDiagnostic(1, "Project", "AddDocument"); + DiagnosticResult solutionExpected = GetVisualBasicExpectedDiagnostic(2, "Solution", "AddProject"); + DiagnosticResult compilationExpected = GetVisualBasicExpectedDiagnostic(3, "Compilation", "RemoveAllSyntaxTrees"); + + await VerifyVB.VerifyAnalyzerAsync(source, documentExpected, projectExpected, solutionExpected, compilationExpected); + } + [Fact] public async Task CSharp_VerifyDiagnosticOnExtensionMethodAsync() { @@ -56,13 +91,30 @@ class TestExtensionMethodTrivia void M() { SyntaxNode node = default(SyntaxNode); - node.WithLeadingTrivia(); + {|#0:node.WithLeadingTrivia()|}; } }"; - DiagnosticResult expected = GetCSharpExpectedDiagnostic(10, 9, "SyntaxNode", "WithLeadingTrivia"); + DiagnosticResult expected = GetCSharpExpectedDiagnostic(0, "SyntaxNode", "WithLeadingTrivia"); await VerifyCS.VerifyAnalyzerAsync(source, expected); } + [Fact] + public async Task VisualBasic_VerifyDiagnosticOnExtensionMethodAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text + +Class TestExtensionMethodTrivia + Sub M() + Dim node As SyntaxNode = Nothing + {|#0:node.WithLeadingTrivia()|} + End Sub +End Class"; + DiagnosticResult expected = GetVisualBasicExpectedDiagnostic(0, "SyntaxNode", "WithLeadingTrivia"); + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + [Fact] public async Task CSharp_VerifyNoDiagnosticAsync() { @@ -92,11 +144,35 @@ public void OtherMethod(Document document) await VerifyCS.VerifyAnalyzerAsync(source); } - private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string objectName, string methodName) => -#pragma warning disable RS0030 // Do not use banned APIs - VerifyCS.Diagnostic() - .WithLocation(line, column) -#pragma warning restore RS0030 // Do not use banned APIs - .WithArguments(objectName, methodName); + [Fact] + public async Task VisualBasic_VerifyNoDiagnosticAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text + +Namespace ConsoleApplication1 + Class TestNoDiagnostic + Public Function M() As Document + Dim document As Document = Nothing + Dim newDocument = document.WithText(Nothing) + document = document.WithText(Nothing) + + OtherMethod(document.WithText(Nothing)) + Return document.WithText(Nothing) + End Function + + Public Sub OtherMethod(document As Document) + End Sub + End Class +End Namespace"; + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int markupKey, string objectName, string methodName) => + VerifyCS.Diagnostic().WithLocation(markupKey).WithArguments(objectName, methodName); + + private static DiagnosticResult GetVisualBasicExpectedDiagnostic(int markupKey, string objectName, string methodName) => + VerifyVB.Diagnostic().WithLocation(markupKey).WithArguments(objectName, methodName); } }