diff --git a/src/NetEscapades.EnumGenerators.Generators/Diagnostics/AnalyzerHelpers.cs b/src/NetEscapades.EnumGenerators.Generators/Diagnostics/AnalyzerHelpers.cs index d706d48..6be407c 100644 --- a/src/NetEscapades.EnumGenerators.Generators/Diagnostics/AnalyzerHelpers.cs +++ b/src/NetEscapades.EnumGenerators.Generators/Diagnostics/AnalyzerHelpers.cs @@ -22,15 +22,13 @@ public static (INamedTypeSymbol? enumExtensionsAttr, ExternalEnumDictionary? ext var externalEnumTypes = new ExternalEnumDictionary(SymbolEqualityComparer.Default); if (externalEnumExtensionsAttr is not null) { - foreach (var attribute in compilation.Assembly.GetAttributes()) + // Check current assembly + ScanAssemblyAttributes(compilation.Assembly, externalEnumExtensionsAttr, externalEnumTypes); + + // Check referenced assemblies + foreach (var referencedAssembly in compilation.SourceModule.ReferencedAssemblySymbols) { - if (attribute.AttributeClass is { IsGenericType: true } attrClass && - SymbolEqualityComparer.Default.Equals(attrClass.ConstructedFrom, externalEnumExtensionsAttr) && - attrClass.TypeArguments is [INamedTypeSymbol { TypeKind: TypeKind.Enum } enumType]) - { - var details = ExtractExtensionClassDetails(enumType, attribute); - externalEnumTypes.Add(enumType, details); - } + ScanAssemblyAttributes(referencedAssembly, externalEnumExtensionsAttr, externalEnumTypes); } } @@ -67,6 +65,23 @@ public static bool IsEnumWithExtensions( return false; } + private static void ScanAssemblyAttributes( + IAssemblySymbol assembly, + INamedTypeSymbol externalEnumExtensionsAttr, + ExternalEnumDictionary externalEnumTypes) + { + foreach (var attribute in assembly.GetAttributes()) + { + if (attribute.AttributeClass is { IsGenericType: true } attrClass && + SymbolEqualityComparer.Default.Equals(attrClass.ConstructedFrom, externalEnumExtensionsAttr) && + attrClass.TypeArguments is [INamedTypeSymbol { TypeKind: TypeKind.Enum } enumType]) + { + var details = ExtractExtensionClassDetails(enumType, attribute); + externalEnumTypes[enumType] = details; + } + } + } + private static string ExtractExtensionClassDetails( ITypeSymbol receiverType, AttributeData attributeData) diff --git a/tests/NetEscapades.EnumGenerators.Tests/IsDefinedAnalyzerTests.cs b/tests/NetEscapades.EnumGenerators.Tests/IsDefinedAnalyzerTests.cs index 04c99ee..845f504 100644 --- a/tests/NetEscapades.EnumGenerators.Tests/IsDefinedAnalyzerTests.cs +++ b/tests/NetEscapades.EnumGenerators.Tests/IsDefinedAnalyzerTests.cs @@ -1,5 +1,8 @@ using System; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; using NetEscapades.EnumGenerators.Diagnostics.UsageAnalyzers; using Xunit; @@ -571,6 +574,70 @@ public void TestMethod() await VerifyAnalyzerAsync(test, EnableState.Disabled); } + [Fact] + public async Task IsDefinedOnEnumFromReferencedAssemblyWithAssemblyLevelEnumExtensionsShouldHaveDiagnostic() + { + var mainCode = """ + using System; + using ExternalNamespace; + + namespace ConsoleApplication1 + { + public class TestClass + { + public void TestMethod() + { + var value = ExternalEnum.Value1; + var isDefined = {|NEEG006:Enum.IsDefined(typeof(ExternalEnum), value)|}; + } + } + } + + namespace ExternalNamespace + { + // This code would be generated, just hacked in here for simplicity + public static class ExternalEnumExtensions + { + public static bool IsDefined(ExternalEnum value) => true; + } + } + """; + + var externalCode = $$""" + [assembly: NetEscapades.EnumGenerators.EnumExtensions()] + + namespace ExternalNamespace + { + public enum ExternalEnum + { + Value1 = 1, + Value2 = 2, + } + } + + {{TestHelpers.LoadEmbeddedAttribute()}} + {{TestHelpers.LoadEmbeddedMetadataSource()}} + """; + + var test = new CSharpAnalyzerTest + { + TestCode = mainCode, + }; + + test.TestState.AdditionalProjects.Add("ExternalProject", new ProjectState("ExternalProject", LanguageNames.CSharp, "external_", ".cs")); + test.TestState.AdditionalProjects["ExternalProject"].Sources.Add(("external_Code.cs", externalCode)); + test.TestState.AdditionalProjectReferences.Add("ExternalProject"); + + test.TestState.AnalyzerConfigFiles.Add( + ("/.editorconfig", + $""" + is_global = true + {UsageAnalyzerConfig.EnableKey} = true + """)); + + await test.RunAsync(); + } + private static string GetTestCodeWithExternalEnum(string testCode) => $$""" using System; using System.Collections.Generic;