private static async Task <Document> DeclareEnumValueAsCombinationOfNamesAsync( Document document, EnumMemberDeclarationSyntax enumMemberDeclaration, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(enumMemberDeclaration, cancellationToken); EnumSymbolInfo enumInfo = EnumSymbolInfo.Create(fieldSymbol.ContainingType); EnumFieldSymbolInfo fieldInfo = EnumFieldSymbolInfo.Create(fieldSymbol); List <EnumFieldSymbolInfo> values = enumInfo.Decompose(fieldInfo); values.Sort((f, g) => { if (f.HasCompositeValue()) { if (g.HasCompositeValue()) { return(f.Value.CompareTo(g.Value)); } else { return(-1); } } else if (g.HasCompositeValue()) { return(1); } return(f.Value.CompareTo(g.Value)); }); ExpressionSyntax oldValue = enumMemberDeclaration.EqualsValue.Value.WalkDownParentheses(); BinaryExpressionSyntax newValue = BitwiseOrExpression(CreateIdentifierName(values[0]), CreateIdentifierName(values[1])); for (int i = 2; i < values.Count; i++) { newValue = BitwiseOrExpression(newValue, CreateIdentifierName(values[i])); } newValue = newValue.WithFormatterAnnotation(); return(await document.ReplaceNodeAsync(oldValue, newValue, cancellationToken).ConfigureAwait(false)); }
private static void AnalyzeNamedType(SymbolAnalysisContext context) { var typeSymbol = (INamedTypeSymbol)context.Symbol; if (typeSymbol.IsImplicitlyDeclared) { return; } if (typeSymbol.TypeKind != TypeKind.Enum) { return; } bool hasFlagsAttribute = typeSymbol.HasAttribute(MetadataNames.System_FlagsAttribute); ImmutableArray <ISymbol> members = default; if (hasFlagsAttribute && DiagnosticRules.DeclareEnumMemberWithZeroValue.IsEffective(context)) { members = typeSymbol.GetMembers(); if (!ContainsFieldWithZeroValue(members)) { var enumDeclaration = (EnumDeclarationSyntax)typeSymbol.GetSyntax(context.CancellationToken); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.DeclareEnumMemberWithZeroValue, enumDeclaration.Identifier); } } EnumSymbolInfo enumInfo = default; if (hasFlagsAttribute && DiagnosticRules.CompositeEnumValueContainsUndefinedFlag.IsEffective(context)) { enumInfo = EnumSymbolInfo.Create(typeSymbol); foreach (EnumFieldSymbolInfo field in enumInfo.Fields) { if (field.HasValue && ConvertHelpers.CanConvertFromUInt64(field.Value, typeSymbol.EnumUnderlyingType.SpecialType) && !IsMaxValue(field.Value, typeSymbol.EnumUnderlyingType.SpecialType) && field.HasCompositeValue()) { foreach (ulong value in (field.GetFlags())) { if (!enumInfo.Contains(value)) { ReportUndefinedFlag(context, field.Symbol, value.ToString()); } } } } } if (hasFlagsAttribute && DiagnosticRules.DeclareEnumValueAsCombinationOfNames.IsEffective(context)) { if (members.IsDefault) { members = typeSymbol.GetMembers(); } foreach (ISymbol member in members) { if (!(member is IFieldSymbol fieldSymbol)) { continue; } if (!fieldSymbol.HasConstantValue) { break; } EnumFieldSymbolInfo fieldInfo = EnumFieldSymbolInfo.Create(fieldSymbol); if (!fieldInfo.HasCompositeValue()) { continue; } var declaration = (EnumMemberDeclarationSyntax)fieldInfo.Symbol.GetSyntax(context.CancellationToken); ExpressionSyntax expression = declaration.EqualsValue?.Value.WalkDownParentheses(); if (expression != null && (expression.IsKind(SyntaxKind.NumericLiteralExpression) || expression .DescendantNodes() .Any(f => f.IsKind(SyntaxKind.NumericLiteralExpression)))) { if (enumInfo.IsDefault) { enumInfo = EnumSymbolInfo.Create(typeSymbol); if (enumInfo.Fields.Any(f => !f.HasValue)) { break; } } List <EnumFieldSymbolInfo> values = enumInfo.Decompose(fieldInfo); if (values?.Count > 1) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.DeclareEnumValueAsCombinationOfNames, expression); } } } } if (hasFlagsAttribute && DiagnosticRules.UseBitShiftOperator.IsEffective(context)) { if (members.IsDefault) { members = typeSymbol.GetMembers(); } foreach (ISymbol member in members) { if (!(member is IFieldSymbol fieldSymbol)) { continue; } if (!fieldSymbol.HasConstantValue) { continue; } EnumFieldSymbolInfo fieldInfo = EnumFieldSymbolInfo.Create(fieldSymbol); if (fieldInfo.Value <= 1) { continue; } if (fieldInfo.HasCompositeValue()) { continue; } var declaration = (EnumMemberDeclarationSyntax)fieldInfo.Symbol.GetSyntax(context.CancellationToken); ExpressionSyntax expression = declaration.EqualsValue?.Value.WalkDownParentheses(); if (expression.IsKind(SyntaxKind.NumericLiteralExpression)) { var enumDeclaration = (EnumDeclarationSyntax)typeSymbol.GetSyntax(context.CancellationToken); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UseBitShiftOperator, enumDeclaration.Identifier); break; } } } if (DiagnosticRules.DuplicateEnumValue.IsEffective(context)) { if (enumInfo.IsDefault) { enumInfo = EnumSymbolInfo.Create(typeSymbol); } ImmutableArray <EnumFieldSymbolInfo> fields = enumInfo.Fields; if (fields.Length > 1) { EnumFieldSymbolInfo symbolInfo1 = fields[0]; EnumFieldSymbolInfo symbolInfo2 = default; for (int i = 1; i < fields.Length; i++, symbolInfo1 = symbolInfo2) { symbolInfo2 = fields[i]; if (!symbolInfo1.HasValue || !symbolInfo2.HasValue || symbolInfo1.Value != symbolInfo2.Value) { continue; } var enumMember1 = (EnumMemberDeclarationSyntax)symbolInfo1.Symbol.GetSyntax(context.CancellationToken); if (enumMember1 == null) { continue; } var enumMember2 = (EnumMemberDeclarationSyntax)symbolInfo2.Symbol.GetSyntax(context.CancellationToken); if (enumMember2 == null) { continue; } ExpressionSyntax value1 = enumMember1.EqualsValue?.Value?.WalkDownParentheses(); ExpressionSyntax value2 = enumMember2.EqualsValue?.Value?.WalkDownParentheses(); if (value1 == null) { if (value2 != null) { ReportDuplicateValue(context, enumMember1, value2); } } else if (value2 == null) { ReportDuplicateValue(context, enumMember2, value1); } else { SyntaxKind kind1 = value1.Kind(); SyntaxKind kind2 = value2.Kind(); if (kind1 == SyntaxKind.NumericLiteralExpression) { if (kind2 == SyntaxKind.NumericLiteralExpression) { var enumDeclaration = (EnumDeclarationSyntax)enumMember1.Parent; SeparatedSyntaxList <EnumMemberDeclarationSyntax> enumMembers = enumDeclaration.Members; if (enumMembers.IndexOf(enumMember1) < enumMembers.IndexOf(enumMember2)) { ReportDuplicateValue(context, value2); } else { ReportDuplicateValue(context, value1); } } else if (!string.Equals((value2 as IdentifierNameSyntax)?.Identifier.ValueText, enumMember1.Identifier.ValueText, StringComparison.Ordinal)) { ReportDuplicateValue(context, value1); } } else if (kind2 == SyntaxKind.NumericLiteralExpression && !string.Equals((value1 as IdentifierNameSyntax)?.Identifier.ValueText, enumMember2.Identifier.ValueText, StringComparison.Ordinal)) { ReportDuplicateValue(context, value2); } } } } } }