private static bool IsFlag(SyntaxNodeAnalysisContext context, EnumMemberDeclarationSyntax declaration) { if (context.SemanticModel.GetDeclaredSymbol(declaration, context.CancellationToken) is IFieldSymbol fieldSymbol && fieldSymbol.HasConstantValue) { EnumFieldSymbolInfo fieldInfo = EnumFieldSymbolInfo.Create(fieldSymbol); return(fieldInfo.Value > 1 && !fieldInfo.HasCompositeValue()); } return(false); }
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)); }
public static async Task <Document> RefactorAsync( Document document, EnumMemberDeclarationSyntax enumMemberDeclaration, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); IFieldSymbol enumMemberSymbol = semanticModel.GetDeclaredSymbol(enumMemberDeclaration, cancellationToken); ImmutableArray <EnumFieldSymbolInfo> infos = EnumFieldSymbolInfo.CreateRange(enumMemberSymbol.ContainingType); ExpressionSyntax value = enumMemberDeclaration.EqualsValue?.Value; var info = new EnumFieldSymbolInfo(enumMemberSymbol); List <EnumFieldSymbolInfo> values = info.Decompose(infos); values.Sort((f, g) => { if (f.IsComposite()) { if (g.IsComposite()) { return(((IComparable)f.Value).CompareTo((IComparable)g.Value)); } else { return(-1); } } else if (g.IsComposite()) { return(1); } return(((IComparable)f.Value).CompareTo((IComparable)g.Value)); }); BinaryExpressionSyntax newValue = BitwiseOrExpression(values[0].ToIdentifierName(), values[1].ToIdentifierName()); for (int i = 2; i < values.Count; i++) { newValue = BitwiseOrExpression(newValue, values[i].ToIdentifierName()); } newValue = newValue.WithFormatterAnnotation(); return(await document.ReplaceNodeAsync(value, newValue, cancellationToken).ConfigureAwait(false)); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out EnumMemberDeclarationSyntax enumMemberDeclaration)) { return; } Document document = context.Document; foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.DeclareEnumValueAsCombinationOfNames: { CodeAction codeAction = CodeAction.Create( "Declare value as combination of names", ct => DeclareEnumValueAsCombinationOfNamesAsync(document, enumMemberDeclaration, ct), base.GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.DuplicateEnumValue: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); var enumDeclaration = (EnumDeclarationSyntax)enumMemberDeclaration.Parent; IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(enumMemberDeclaration, context.CancellationToken); EnumFieldSymbolInfo enumFieldSymbolInfo = EnumFieldSymbolInfo.Create(fieldSymbol); string valueText = FindMemberByValue(enumDeclaration, enumFieldSymbolInfo, semanticModel, context.CancellationToken).Identifier.ValueText; CodeAction codeAction = CodeAction.Create( $"Change enum value to '{valueText}'", ct => ChangeEnumValueAsync(document, enumMemberDeclaration, valueText, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
private static async Task <Document> UseBitShiftOperatorAsync( Document document, EnumDeclarationSyntax enumDeclaration, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); EnumDeclarationSyntax newEnumDeclaration = enumDeclaration.ReplaceNodes( GetExpressionsToRewrite(), (expression, _) => { Optional <object> constantValue = semanticModel.GetConstantValue(expression, cancellationToken); var power = (int)Math.Log(Convert.ToDouble(constantValue.Value), 2); BinaryExpressionSyntax leftShift = LeftShiftExpression(NumericLiteralExpression(1), NumericLiteralExpression(power)); return(leftShift.WithTriviaFrom(expression)); }); return(await document.ReplaceNodeAsync(enumDeclaration, newEnumDeclaration, cancellationToken).ConfigureAwait(false)); IEnumerable <ExpressionSyntax> GetExpressionsToRewrite() { foreach (EnumMemberDeclarationSyntax member in enumDeclaration.Members) { ExpressionSyntax expression = member.EqualsValue?.Value.WalkDownParentheses(); if (expression != null && semanticModel.GetDeclaredSymbol(member, cancellationToken) is IFieldSymbol fieldSymbol && fieldSymbol.HasConstantValue) { EnumFieldSymbolInfo fieldInfo = EnumFieldSymbolInfo.Create(fieldSymbol); if (fieldInfo.Value > 1 && !fieldInfo.HasCompositeValue()) { yield return(expression); } } } } }
private static bool ContainsFieldWithZeroValue(ImmutableArray <ISymbol> members) { foreach (ISymbol member in members) { if (member.Kind == SymbolKind.Field) { var fieldSymbol = (IFieldSymbol)member; if (fieldSymbol.HasConstantValue) { EnumFieldSymbolInfo fieldInfo = EnumFieldSymbolInfo.Create(fieldSymbol); if (fieldInfo.Value == 0) { return(true); } } } } return(false); }
public static void ComputeRefactoring( RefactoringContext context, EnumDeclarationSyntax enumDeclaration, SemanticModel semanticModel) { SeparatedSyntaxList <EnumMemberDeclarationSyntax> members = enumDeclaration.Members; if (!members.Any()) { return; } if (members.All(f => f.EqualsValue?.Value == null)) { return; } INamedTypeSymbol enumSymbol = semanticModel.GetDeclaredSymbol(enumDeclaration, context.CancellationToken); bool isFlags = enumSymbol.HasAttribute(MetadataNames.System_FlagsAttribute); if (!AreNewValuesDifferentFromExistingValues()) { return; } Document document = context.Document; context.RegisterRefactoring( "Declare explicit values (overwrite existing values)", ct => RefactorAsync(document, enumDeclaration, enumSymbol, semanticModel, ct), EquivalenceKey); bool AreNewValuesDifferentFromExistingValues() { ulong value = 0; foreach (EnumMemberDeclarationSyntax member in enumDeclaration.Members) { IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(member, context.CancellationToken); EnumFieldSymbolInfo fieldSymbolInfo = EnumFieldSymbolInfo.Create(fieldSymbol); if (!fieldSymbolInfo.HasValue) { return(true); } if (isFlags && fieldSymbolInfo.HasCompositeValue()) { continue; } if (value != fieldSymbolInfo.Value) { return(true); } if (isFlags) { value = (value == 0) ? 1 : value * 2; } else { value++; } if (!ConvertHelpers.CanConvertFromUInt64(value, enumSymbol.EnumUnderlyingType.SpecialType)) { return(false); } } return(false); } }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out EnumMemberDeclarationSyntax enumMemberDeclaration)) { return; } Document document = context.Document; Diagnostic diagnostic = context.Diagnostics[0]; switch (diagnostic.Id) { case DiagnosticIdentifiers.DeclareEnumValueAsCombinationOfNames: { CodeAction codeAction = CodeAction.Create( "Declare value as combination of names", ct => DeclareEnumValueAsCombinationOfNamesAsync(document, enumMemberDeclaration, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.DuplicateEnumValue: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); var enumDeclaration = (EnumDeclarationSyntax)enumMemberDeclaration.Parent; IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(enumMemberDeclaration, context.CancellationToken); EnumFieldSymbolInfo enumFieldSymbolInfo = EnumFieldSymbolInfo.Create(fieldSymbol); string valueText = FindMemberByValue(enumDeclaration, enumFieldSymbolInfo, semanticModel, context.CancellationToken).Identifier.ValueText; CodeAction codeAction = CodeAction.Create( $"Change enum value to '{valueText}'", ct => ChangeEnumValueAsync(document, enumMemberDeclaration, valueText, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.NormalizeFormatOfEnumFlagValue: { EnumFlagValueStyle style = document.GetConfigOptions(enumMemberDeclaration.SyntaxTree).GetEnumFlagValueStyle(); if (style == EnumFlagValueStyle.ShiftOperator) { CodeAction codeAction = CodeAction.Create( "Use '<<' operator", ct => UseBitShiftOperatorAsync(document, enumMemberDeclaration, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else if (style == EnumFlagValueStyle.DecimalNumber) { CodeAction codeAction = CodeAction.Create( "Convert to decimal", ct => ConvertToDecimalNumberAsync(document, enumMemberDeclaration, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else { throw new InvalidOperationException(); } break; } } }
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); } } } } } }
public static void AnalyzeNamedType(SymbolAnalysisContext context) { var enumSymbol = (INamedTypeSymbol)context.Symbol; if (enumSymbol.TypeKind != TypeKind.Enum) { return; } if (!enumSymbol.HasAttribute(MetadataNames.System_FlagsAttribute)) { return; } var infos = default(ImmutableArray <EnumFieldSymbolInfo>); foreach (ISymbol member in enumSymbol.GetMembers()) { if (member is IFieldSymbol fieldSymbol) { if (!fieldSymbol.HasConstantValue) { return; } var info = new EnumFieldSymbolInfo(fieldSymbol); if (info.IsComposite()) { var declaration = (EnumMemberDeclarationSyntax)info.Symbol.GetSyntax(context.CancellationToken); ExpressionSyntax valueExpression = declaration.EqualsValue?.Value; if (valueExpression != null && (valueExpression.IsKind(SyntaxKind.NumericLiteralExpression) || valueExpression .DescendantNodes() .Any(f => f.IsKind(SyntaxKind.NumericLiteralExpression)))) { if (infos.IsDefault) { infos = EnumFieldSymbolInfo.CreateRange(enumSymbol); if (infos.IsDefault) { return; } } List <EnumFieldSymbolInfo> values = info.Decompose(infos); if (values?.Count > 1) { context.ReportDiagnostic( DiagnosticDescriptors.DeclareEnumValueAsCombinationOfNames, valueExpression); } } } } } }