diff --git a/src/ErrorProne.NET.CoreAnalyzers.Tests/AsyncAnalyzers/NullConditionalOperatorAnalyzerTests.cs b/src/ErrorProne.NET.CoreAnalyzers.Tests/AsyncAnalyzers/NullConditionalOperatorAnalyzerTests.cs index ab57af8..a4caa51 100644 --- a/src/ErrorProne.NET.CoreAnalyzers.Tests/AsyncAnalyzers/NullConditionalOperatorAnalyzerTests.cs +++ b/src/ErrorProne.NET.CoreAnalyzers.Tests/AsyncAnalyzers/NullConditionalOperatorAnalyzerTests.cs @@ -23,5 +23,22 @@ public async System.Threading.Tasks.Task Foo(MyClass m) "; await VerifyCS.VerifyAsync(code); } - } + + [Test] + public async Task No_Warn_When_Nullability_Is_Enabled() + { + // In this case the compiler will emit a warning. + string code = @" +#nullable enable +public class MyClass +{ + public async System.Threading.Tasks.Task Foo(MyClass? m) + { + await m?.Foo(null); + } +} +"; + await VerifyCS.VerifyAsync(code); + } + } } \ No newline at end of file diff --git a/src/ErrorProne.NET.CoreAnalyzers/AsyncAnalyzers/NullConditionalOperatorAnalyzer.cs b/src/ErrorProne.NET.CoreAnalyzers/AsyncAnalyzers/NullConditionalOperatorAnalyzer.cs index e4c23ec..ba88146 100644 --- a/src/ErrorProne.NET.CoreAnalyzers/AsyncAnalyzers/NullConditionalOperatorAnalyzer.cs +++ b/src/ErrorProne.NET.CoreAnalyzers/AsyncAnalyzers/NullConditionalOperatorAnalyzer.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; namespace ErrorProne.NET.AsyncAnalyzers { @@ -24,7 +25,26 @@ public NullConditionalOperatorAnalyzer() /// protected override void InitializeCore(AnalysisContext context) { - context.RegisterSyntaxNodeAction(AnalyzeAwaitExpression, SyntaxKind.AwaitExpression); + context.RegisterOperationAction(AnalyzeAwaitOperation, OperationKind.Await); + //context.RegisterSyntaxNodeAction(AnalyzeAwaitExpression, SyntaxKind.AwaitExpression); + } + + private void AnalyzeAwaitOperation(OperationAnalysisContext context) + { + var operation = (IAwaitOperation) context.Operation; + // Check if the awaited operation is a conditional access operation + if (operation.Operation is IConditionalAccessOperation) + { + if (operation.Operation.Type?.NullableAnnotation == NullableAnnotation.Annotated) + { + // If the type is annotated, we don't report diagnostics. + return; + } + + var location = operation.Syntax.GetLocation(); + var diagnostic = Diagnostic.Create(Rule, location); + context.ReportDiagnostic(diagnostic); + } } private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context) diff --git a/src/ErrorProne.NET.CoreAnalyzers/DiagnosticDescriptors.cs b/src/ErrorProne.NET.CoreAnalyzers/DiagnosticDescriptors.cs index b243ccc..a72879b 100644 --- a/src/ErrorProne.NET.CoreAnalyzers/DiagnosticDescriptors.cs +++ b/src/ErrorProne.NET.CoreAnalyzers/DiagnosticDescriptors.cs @@ -303,7 +303,7 @@ internal static class DiagnosticDescriptors /// public static readonly DiagnosticDescriptor EPC37 = new DiagnosticDescriptor( nameof(EPC37), - title: "Do not validate arguments in async methods", + title: "Do not validate arguments in async methods eagerly", messageFormat: "Argument validation in async method '{0}' will not fail eagerly. Consider using a wrapper method or Task.FromException.", category: AsyncCategory, // Info by default, since it might generate quite a bit of warnings for a codebase.