diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs b/src/Microsoft.CodeQuality.Analyzers/Core/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs
index 11d959783e..15d41e7d91 100644
--- a/src/Microsoft.CodeQuality.Analyzers/Core/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs
+++ b/src/Microsoft.CodeQuality.Analyzers/Core/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs
@@ -147,16 +147,23 @@ private static void AnalyzeCompilationStart(CompilationStartAnalysisContext cont
}
, SymbolKind.NamedType);
- context.RegisterSymbolAction((saContext) =>
+ var eventArgsType = WellKnownTypes.EventArgs(context.Compilation);
+ if (eventArgsType != null)
{
- const string eventHandlerString = "EventHandler";
- var eventSymbol = saContext.Symbol as IEventSymbol;
- if (!eventSymbol.Type.Name.EndsWith(eventHandlerString, StringComparison.Ordinal))
+ context.RegisterSymbolAction((saContext) =>
{
- saContext.ReportDiagnostic(eventSymbol.CreateDiagnostic(DefaultRule, eventSymbol.Type.Name, eventHandlerString));
- }
- },
- SymbolKind.Event);
+ const string eventHandlerString = "EventHandler";
+ var eventSymbol = (IEventSymbol)saContext.Symbol;
+ if (!eventSymbol.Type.Name.EndsWith(eventHandlerString, StringComparison.Ordinal) &&
+ eventSymbol.Type.IsInSource() &&
+ eventSymbol.Type.TypeKind == TypeKind.Delegate &&
+ ((INamedTypeSymbol)eventSymbol.Type).DelegateInvokeMethod?.HasEventHandlerSignature(eventArgsType) == true)
+ {
+ saContext.ReportDiagnostic(eventSymbol.CreateDiagnostic(DefaultRule, eventSymbol.Type.Name, eventHandlerString));
+ }
+ },
+ SymbolKind.Event);
+ }
}
}
}
diff --git a/src/Microsoft.CodeQuality.Analyzers/UnitTests/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs b/src/Microsoft.CodeQuality.Analyzers/UnitTests/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs
index a6d2f0a2ea..72be63b886 100644
--- a/src/Microsoft.CodeQuality.Analyzers/UnitTests/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs
+++ b/src/Microsoft.CodeQuality.Analyzers/UnitTests/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs
@@ -1079,6 +1079,31 @@ Inherits Attribute
End Class");
}
+ [Fact, WorkItem(1822, "https://github.com/dotnet/roslyn-analyzers/issues/1822")]
+ public void CA1710_SystemAction_CSharp()
+ {
+ VerifyCSharp(@"
+using System;
+
+public class C
+{
+ public event Action MyEvent;
+}");
+ }
+
+ [Fact, WorkItem(1822, "https://github.com/dotnet/roslyn-analyzers/issues/1822")]
+ public void CA1710_CustomDelegate_CSharp()
+ {
+ VerifyCSharp(@"
+using System;
+
+public class C
+{
+ public delegate void MyDelegate(int param);
+ public event MyDelegate MyEvent;
+}");
+ }
+
private static DiagnosticResult GetCA1710BasicResultAt(int line, int column, string symbolName, string replacementName, bool isSpecial = false)
{
return GetBasicResultAt(
diff --git a/src/Utilities/Extensions/IMethodSymbolExtensions.cs b/src/Utilities/Extensions/IMethodSymbolExtensions.cs
index 26533aba9b..bd66bfb61d 100644
--- a/src/Utilities/Extensions/IMethodSymbolExtensions.cs
+++ b/src/Utilities/Extensions/IMethodSymbolExtensions.cs
@@ -382,5 +382,16 @@ public static int GetParameterIndex(this IMethodSymbol methodSymbol, IParameterS
throw new ArgumentException("Invalid paramater", nameof(parameterSymbol));
}
+
+ ///
+ /// Returns true for void returning methods with two parameters, where
+ /// the first parameter is of type and the second
+ /// parameter inherits from or equals type.
+ ///
+ public static bool HasEventHandlerSignature(this IMethodSymbol method, INamedTypeSymbol eventArgsType)
+ => eventArgsType != null &&
+ method.Parameters.Length == 2 &&
+ method.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
+ method.Parameters[1].Type.DerivesFrom(eventArgsType, baseTypesOnly: true);
}
}
diff --git a/src/Utilities/Extensions/ISymbolExtensions.cs b/src/Utilities/Extensions/ISymbolExtensions.cs
index 8d29dd2313..acb79ff834 100644
--- a/src/Utilities/Extensions/ISymbolExtensions.cs
+++ b/src/Utilities/Extensions/ISymbolExtensions.cs
@@ -552,5 +552,13 @@ public static bool HasAttribute(this ISymbol symbol, INamedTypeSymbol attribute)
{
return symbol.GetAttributes().Any(attr => attr.AttributeClass.Equals(attribute));
}
+
+ ///
+ /// Indicates if a symbol has at least one location in source.
+ ///
+ public static bool IsInSource(this ISymbol symbol)
+ {
+ return symbol.Locations.Any(l => l.IsInSource);
+ }
}
}