private static async Task <Document> DeclareExplicitValueAsync( Document document, EnumDeclarationSyntax enumDeclaration, INamedTypeSymbol enumSymbol, ImmutableArray <ulong> values, SemanticModel semanticModel, CancellationToken cancellationToken) { bool isFlags = enumSymbol.HasAttribute(MetadataNames.System_FlagsAttribute); List <ulong> reservedValues = values.ToList(); SeparatedSyntaxList <EnumMemberDeclarationSyntax> members = enumDeclaration.Members; SeparatedSyntaxList <EnumMemberDeclarationSyntax> newMembers = members; for (int i = 0; i < members.Count; i++) { if (members[i].EqualsValue == null) { IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(members[i], cancellationToken); ulong?value = null; if (isFlags) { Optional <ulong> optional = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(reservedValues); if (optional.HasValue && ConvertHelpers.CanConvert(optional.Value, enumSymbol.EnumUnderlyingType.SpecialType)) { value = optional.Value; } } else { value = SymbolUtility.GetEnumValueAsUInt64(fieldSymbol.ConstantValue, enumSymbol); } if (value != null) { reservedValues.Add(value.Value); EqualsValueClauseSyntax equalsValue = EqualsValueClause(NumericLiteralExpression(value.Value, enumSymbol.EnumUnderlyingType.SpecialType)); EnumMemberDeclarationSyntax newMember = members[i].WithEqualsValue(equalsValue); newMembers = newMembers.ReplaceAt(i, newMember); } } } EnumDeclarationSyntax newEnumDeclaration = enumDeclaration.WithMembers(newMembers); return(await document.ReplaceNodeAsync(enumDeclaration, newEnumDeclaration, cancellationToken).ConfigureAwait(false)); }
private static Task <Document> RefactorAsync( Document document, EnumDeclarationSyntax enumDeclaration, INamedTypeSymbol enumSymbol, SemanticModel semanticModel, CancellationToken cancellationToken) { ulong value = 0; SpecialType numericType = enumSymbol.EnumUnderlyingType.SpecialType; IEnumerable <EnumMemberDeclarationSyntax> newMembers = (enumSymbol.HasAttribute(MetadataNames.System_FlagsAttribute)) ? enumDeclaration.Members.Select(CreateNewFlagsMember) : enumDeclaration.Members.Select(CreateNewMember); EnumDeclarationSyntax newEnumDeclaration = enumDeclaration.WithMembers(newMembers.ToSeparatedSyntaxList()); return(document.ReplaceNodeAsync(enumDeclaration, newEnumDeclaration, cancellationToken)); EnumMemberDeclarationSyntax CreateNewFlagsMember(EnumMemberDeclarationSyntax enumMember) { if (!ConvertHelpers.CanConvert(value, numericType)) { return(enumMember); } IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(enumMember, cancellationToken); if (fieldSymbol.HasConstantValue && FlagsUtility <ulong> .Instance.IsComposite(SymbolUtility.GetEnumValueAsUInt64(fieldSymbol.ConstantValue, enumSymbol))) { return(enumMember); } EnumMemberDeclarationSyntax newEnumMember = CreateNewEnumMember(enumMember, value, numericType); value = (value == 0) ? 1 : value * 2; return(newEnumMember); } EnumMemberDeclarationSyntax CreateNewMember(EnumMemberDeclarationSyntax enumMember) { if (!ConvertHelpers.CanConvert(value, numericType)) { return(enumMember); } EnumMemberDeclarationSyntax newEnumMember = CreateNewEnumMember(enumMember, value, numericType); value++; return(newEnumMember); } }
public static void ComputeRefactoring( RefactoringContext context, EnumDeclarationSyntax enumDeclaration, SemanticModel semanticModel) { INamedTypeSymbol enumSymbol = semanticModel.GetDeclaredSymbol(enumDeclaration, context.CancellationToken); if (enumSymbol?.HasAttribute(MetadataNames.System_FlagsAttribute) != true) { return; } SeparatedSyntaxList <EnumMemberDeclarationSyntax> members = enumDeclaration.Members; if (!members.Any(f => f.EqualsValue == null)) { return; } ImmutableArray <ulong> values = GetExplicitValues(enumDeclaration, semanticModel, context.CancellationToken); Optional <ulong> optional = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(values); if (!optional.HasValue) { return; } if (!ConvertHelpers.CanConvert(optional.Value, enumSymbol.EnumUnderlyingType.SpecialType)) { return; } Document document = context.Document; context.RegisterRefactoring( "Declare explicit values", ct => RefactorAsync(document, enumDeclaration, enumSymbol, values, startFromHighestExistingValue: false, cancellationToken: ct), EquivalenceKey); if (members.Any(f => f.EqualsValue != null)) { Optional <ulong> optional2 = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(values, startFromHighestExistingValue : true); if (optional2.HasValue && optional.Value != optional2.Value) { context.RegisterRefactoring( $"Declare explicit values (starting from {optional2.Value})", ct => RefactorAsync(document, enumDeclaration, enumSymbol, values, startFromHighestExistingValue: true, cancellationToken: ct), StartFromHighestExistingValueEquivalenceKey); } } }
public static void ComputeRefactoring( RefactoringContext context, EnumDeclarationSyntax enumDeclaration, SemanticModel semanticModel) { Document document = context.Document; INamedTypeSymbol enumSymbol = semanticModel.GetDeclaredSymbol(enumDeclaration, context.CancellationToken); if (enumSymbol?.HasAttribute(MetadataNames.System_FlagsAttribute) == true) { ImmutableArray <ulong> values = GetConstantValues(enumSymbol); Optional <ulong> optional = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(values); if (optional.HasValue && ConvertHelpers.CanConvert(optional.Value, enumSymbol.EnumUnderlyingType.SpecialType)) { context.RegisterRefactoring( "Generate enum member", ct => RefactorAsync(document, enumDeclaration, enumSymbol, optional.Value, ct), EquivalenceKey); Optional <ulong> optional2 = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(values, startFromHighestExistingValue : true); if (optional2.HasValue && ConvertHelpers.CanConvert(optional2.Value, enumSymbol.EnumUnderlyingType.SpecialType) && optional.Value != optional2.Value) { context.RegisterRefactoring( $"Generate enum member (with value {optional2.Value})", ct => RefactorAsync(document, enumDeclaration, enumSymbol, optional2.Value, ct), StartFromHighestExistingValueEquivalenceKey); } } } else { context.RegisterRefactoring( "Generate enum member", ct => RefactorAsync(document, enumDeclaration, enumSymbol, null, ct), EquivalenceKey); } }
private static async Task <Document> RefactorAsync( Document document, EnumDeclarationSyntax enumDeclaration, INamedTypeSymbol enumSymbol, ImmutableArray <ulong> values, bool startFromHighestExistingValue, CancellationToken cancellationToken) { SeparatedSyntaxList <EnumMemberDeclarationSyntax> members = enumDeclaration.Members; List <ulong> valuesList = values.ToList(); for (int i = 0; i < members.Count; i++) { if (members[i].EqualsValue == null) { Optional <ulong> optional = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(valuesList, startFromHighestExistingValue); if (optional.HasValue && ConvertHelpers.CanConvert(optional.Value, enumSymbol.EnumUnderlyingType.SpecialType)) { valuesList.Add(optional.Value); EqualsValueClauseSyntax equalsValue = EqualsValueClause( Token(TriviaList(ElasticSpace), SyntaxKind.EqualsToken, TriviaList(ElasticSpace)), CSharpFactory.NumericLiteralExpression(optional.Value, enumSymbol.EnumUnderlyingType.SpecialType)); EnumMemberDeclarationSyntax newMember = members[i].WithEqualsValue(equalsValue); members = members.ReplaceAt(i, newMember); } else { break; } } } EnumDeclarationSyntax newNode = enumDeclaration.WithMembers(members); return(await document.ReplaceNodeAsync(enumDeclaration, newNode, cancellationToken).ConfigureAwait(false)); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out EnumDeclarationSyntax enumDeclaration)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.AddNewLineBeforeEnumMember: { CodeAction codeAction = CodeAction.Create( "Add newline", cancellationToken => AddNewLineBeforeEnumMemberRefactoring.RefactorAsync(context.Document, enumDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.SortEnumMembers: { CodeAction codeAction = CodeAction.Create( $"Sort '{enumDeclaration.Identifier}' members", cancellationToken => SortEnumMembersAsync(context.Document, enumDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.EnumShouldDeclareExplicitValues: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol enumSymbol = semanticModel.GetDeclaredSymbol(enumDeclaration, context.CancellationToken); EnumSymbolInfo enumInfo = EnumSymbolInfo.Create(enumSymbol); ImmutableArray <ulong> values = enumInfo .Fields .Where(f => f.HasValue && ((EnumMemberDeclarationSyntax)f.Symbol.GetSyntax(context.CancellationToken)).EqualsValue != null) .Select(f => f.Value) .ToImmutableArray(); Optional <ulong> optional = FlagsUtility <ulong> .Instance.GetUniquePowerOfTwo(values); if (!optional.HasValue || !ConvertHelpers.CanConvert(optional.Value, enumSymbol.EnumUnderlyingType.SpecialType)) { return; } CodeAction codeAction = CodeAction.Create( "Declare explicit values", ct => DeclareExplicitValueAsync(context.Document, enumDeclaration, enumSymbol, values, semanticModel, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
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.CanConvert(value, enumSymbol.EnumUnderlyingType.SpecialType)) { return(false); } } return(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 && !context.IsAnalyzerSuppressed(DiagnosticDescriptors.DeclareEnumMemberWithZeroValue)) { members = typeSymbol.GetMembers(); if (!ContainsFieldWithZeroValue(members)) { var enumDeclaration = (EnumDeclarationSyntax)typeSymbol.GetSyntax(context.CancellationToken); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.DeclareEnumMemberWithZeroValue, enumDeclaration.Identifier); } } EnumSymbolInfo enumInfo = default; if (hasFlagsAttribute && !context.IsAnalyzerSuppressed(DiagnosticDescriptors.CompositeEnumValueContainsUndefinedFlag)) { enumInfo = EnumSymbolInfo.Create(typeSymbol); foreach (EnumFieldSymbolInfo field in enumInfo.Fields) { if (field.HasValue && ConvertHelpers.CanConvert(field.Value, typeSymbol.EnumUnderlyingType.SpecialType) && !IsMaxValue(field.Value, typeSymbol.EnumUnderlyingType.SpecialType) && field.HasCompositeValue()) { foreach (ulong value in (field.DecomposeValue())) { if (!enumInfo.Contains(value)) { ReportUndefinedFlag(context, field.Symbol, value.ToString()); } } } } } if (hasFlagsAttribute && !context.IsAnalyzerSuppressed(DiagnosticDescriptors.DeclareEnumValueAsCombinationOfNames)) { 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, DiagnosticDescriptors.DeclareEnumValueAsCombinationOfNames, expression); } } } } if (hasFlagsAttribute && !context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseBitShiftOperator)) { 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, DiagnosticDescriptors.UseBitShiftOperator, enumDeclaration.Identifier); break; } } } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.DuplicateEnumValue)) { 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); } } } } } }