diff --git a/src/Microsoft.Unity.Analyzers.Tests/MessageSignatureTests.cs b/src/Microsoft.Unity.Analyzers.Tests/MessageSignatureTests.cs index cd29bbd..2e9ea74 100644 --- a/src/Microsoft.Unity.Analyzers.Tests/MessageSignatureTests.cs +++ b/src/Microsoft.Unity.Analyzers.Tests/MessageSignatureTests.cs @@ -116,7 +116,7 @@ bool OnPreGeneratingCSProjectFiles(int foo) [Fact] public async Task MessageSignatureUnityLogic() { - // Unity allows to specify less parameters if you don't need them + // Unity allows to specify fewer parameters if you don't need them const string test = @" using UnityEngine; @@ -381,4 +381,32 @@ static async Awaitable OnPreGeneratingCSProjectFiles() await VerifyCSharpFixAsync(test, fixedTest); } + [Fact] + public async Task BadMessageSignatureWithUsedMethod() + { + const string test = @" +using UnityEditor; + +class Camera : Editor +{ + private void Foo() + { + OnSceneGUI(null); + } + + private void OnSceneGUI(object foo) + { + } +} +"; + + var diagnostic = ExpectDiagnostic() + .WithLocation(11, 18) + .WithArguments("OnSceneGUI"); + + await VerifyCSharpDiagnosticAsync(test, diagnostic); + + // In this special case, we do not provide a codefix, given it would break the code as the message is -wrongly- used elsewhere + await VerifyCSharpFixAsync(test, test); + } } diff --git a/src/Microsoft.Unity.Analyzers/CodeFixContextExtensions.cs b/src/Microsoft.Unity.Analyzers/CodeFixContextExtensions.cs index 4c7954c..03404b6 100644 --- a/src/Microsoft.Unity.Analyzers/CodeFixContextExtensions.cs +++ b/src/Microsoft.Unity.Analyzers/CodeFixContextExtensions.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.FindSymbols; namespace Microsoft.Unity.Analyzers; @@ -31,4 +32,18 @@ internal static class CodeFixContextExtensions .OfType() .FirstOrDefault(predicate); } + + public static async Task IsReferencedAsync(this CodeFixContext context, SyntaxNode declaration) + { + var semanticModel = await context.Document + .GetSemanticModelAsync(context.CancellationToken) + .ConfigureAwait(false); + + var symbol = semanticModel?.GetDeclaredSymbol(declaration); + if (symbol == null) + return false; + + var references = await SymbolFinder.FindReferencesAsync(symbol, context.Document.Project.Solution, context.CancellationToken); + return references.Any(r => r.Locations.Any()); + } } diff --git a/src/Microsoft.Unity.Analyzers/MessageSignature.cs b/src/Microsoft.Unity.Analyzers/MessageSignature.cs index 58a8b56..9e04cf7 100644 --- a/src/Microsoft.Unity.Analyzers/MessageSignature.cs +++ b/src/Microsoft.Unity.Analyzers/MessageSignature.cs @@ -100,14 +100,18 @@ public class MessageSignatureCodeFix : CodeFixProvider public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var methodDeclaration = await context.GetFixableNodeAsync(); - if (methodDeclaration == null) + var method = await context.GetFixableNodeAsync(); + if (method == null) + return; + + // Do not provide a code fix if the message is -wrongly- referenced elsewhere + if (await context.IsReferencedAsync(method)) return; context.RegisterCodeFix( CodeAction.Create( Strings.MessageSignatureCodeFixTitle, - ct => FixMethodDeclarationSignatureAsync(context.Document, methodDeclaration, ct), + ct => FixMethodDeclarationSignatureAsync(context.Document, method, ct), FixableDiagnosticIds.Single()), // using DiagnosticId as equivalence key for BatchFixer context.Diagnostics); }