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;
                }
                }
            }
        }
Exemplo n.º 5
0
        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);
                        }
                    }
                }
            }
        }
Exemplo n.º 6
0
        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);
        }
Exemplo n.º 7
0
        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;
            }
            }
        }
Exemplo n.º 9
0
        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);
                            }
                        }
                    }
                }
            }
        }