| File: Rules\EnumsMustMatch.cs | Web Access |
| Project: src\sdk\src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompatibility\Microsoft.DotNet.ApiCompatibility.csproj (Microsoft.DotNet.ApiCompatibility) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.CodeAnalysis; namespace Microsoft.DotNet.ApiCompatibility.Rules { /// <summary> /// This class implements a rule to check that the constant values for an enum's fields don't change. /// </summary> public class EnumsMustMatch : IRule { private readonly IRuleSettings _settings; public EnumsMustMatch(IRuleSettings settings, IRuleRegistrationContext context) { _settings = settings; context.RegisterOnTypeSymbolAction(RunOnTypeSymbol); } private void RunOnTypeSymbol(ITypeSymbol? left, ITypeSymbol? right, MetadataInformation leftMetadata, MetadataInformation rightMetadata, IList<CompatDifference> differences) { // Ensure that this rule only runs on enums. if (!IsEnum(left) || !IsEnum(right)) { return; } // Enum must be a named type to access its underlying type. if (left is not INamedTypeSymbol l || right is not INamedTypeSymbol r) { return; } // Get enum's underlying type. if (l.EnumUnderlyingType is not INamedTypeSymbol leftType || r.EnumUnderlyingType is not INamedTypeSymbol rightType) { return; } // Check that the underlying types are equal and if not, emit a diagnostic. if (!_settings.SymbolEqualityComparer.Equals(leftType, rightType)) { differences.Add(new CompatDifference( leftMetadata, rightMetadata, DiagnosticIds.EnumTypesMustMatch, string.Format(Resources.EnumTypesMustMatch, left.Name, leftType, rightType), DifferenceType.Changed, right)); return; } // If so, compare their fields. // Build a map of the enum's fields, keyed by the field names. Dictionary<string, IFieldSymbol> leftMembers = left.GetMembers() .Where(a => a.Kind == SymbolKind.Field) .Select(a => (IFieldSymbol)a) .ToDictionary(a => a.Name); Dictionary<string, IFieldSymbol> rightMembers = right.GetMembers() .Where(a => a.Kind == SymbolKind.Field) .Select(a => (IFieldSymbol)a) .ToDictionary(a => a.Name); // For each field that is present in the left and right, check that their constant values match. // Otherwise, emit a diagnostic. foreach (KeyValuePair<string, IFieldSymbol> lEntry in leftMembers) { if (!rightMembers.TryGetValue(lEntry.Key, out IFieldSymbol? rField)) { continue; } if (lEntry.Value.ConstantValue is not object lval || rField.ConstantValue is not object rval || !lval.Equals(rval)) { differences.Add(new CompatDifference( leftMetadata, rightMetadata, DiagnosticIds.EnumValuesMustMatch, string.Format(Resources.EnumValuesMustMatch, left.Name, lEntry.Key, lEntry.Value.ConstantValue, rField.ConstantValue), DifferenceType.Changed, rField)); } } } private static bool IsEnum(ITypeSymbol? sym) => sym is not null && sym.TypeKind == TypeKind.Enum; } }