UseAutoPropertyAnalyzer.cs 6.0 KB
Newer Older
1 2
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

3
using System.Collections.Concurrent;
4
using System.ComponentModel.Composition;
5
using System.Threading;
6 7 8
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
9
using Microsoft.CodeAnalysis.UseAutoProperty;
10 11 12

namespace Microsoft.CodeAnalysis.Editor.CSharp.UseAutoProperty
{
13 14
    [Export]
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
15
    internal class UseAutoPropertyAnalyzer : AbstractUseAutoPropertyAnalyzer<PropertyDeclarationSyntax, FieldDeclarationSyntax, VariableDeclaratorSyntax, ExpressionSyntax>
16
    {
C
Cyrus Najmabadi 已提交
17
        protected override bool SupportsReadOnlyProperties(Compilation compilation)
18 19 20 21
        {
            return ((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp6;
        }

22 23 24 25 26
        protected override bool SupportsPropertyInitializer(Compilation compilation)
        {
            return ((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp6;
        }

27
        protected override void RegisterIneligibleFieldsAction(CompilationStartAnalysisContext context, ConcurrentBag<IFieldSymbol> ineligibleFields)
28
        {
29
            context.RegisterSyntaxNodeAction(snac => AnalyzeArgument(ineligibleFields, snac), SyntaxKind.Argument);
30
        }
31

32 33 34 35 36
        protected override ExpressionSyntax GetFieldInitializer(VariableDeclaratorSyntax variable, CancellationToken cancellationToken)
        {
            return variable.Initializer?.Value;
        }

37 38 39 40 41 42 43 44 45 46
        private void AnalyzeArgument(ConcurrentBag<IFieldSymbol> ineligibleFields, SyntaxNodeAnalysisContext context)
        {
            // An argument will disqualify a field if that field is used in a ref/out position.  
            // We can't change such field references to be property references in C#.
            var argument = (ArgumentSyntax)context.Node;
            if (argument.RefOrOutKeyword.Kind() == SyntaxKind.None)
            {
                return;
            }

47 48 49 50
            var cancellationToken = context.CancellationToken;
            var symbolInfo = context.SemanticModel.GetSymbolInfo(argument.Expression, cancellationToken);
            AddIneligibleField(symbolInfo.Symbol, ineligibleFields);
            foreach (var symbol in symbolInfo.CandidateSymbols)
51
            {
52
                AddIneligibleField(symbol, ineligibleFields);
53 54 55 56 57 58 59 60 61 62 63 64
            }
        }

        private static void AddIneligibleField(ISymbol symbol, ConcurrentBag<IFieldSymbol> ineligibleFields)
        {
            var field = symbol as IFieldSymbol;
            if (field != null)
            {
                ineligibleFields.Add(field);
            }
        }

65
        private bool CheckExpressionSyntactically(ExpressionSyntax expression)
66
        {
B
Bart Koelman 已提交
67
            if (expression.IsKind(SyntaxKind.SimpleMemberAccessExpression))
68
            {
69 70 71
                var memberAccessExpression = (MemberAccessExpressionSyntax)expression;
                return memberAccessExpression.Expression.Kind() == SyntaxKind.ThisExpression &&
                    memberAccessExpression.Name.Kind() == SyntaxKind.IdentifierName;
72
            }
B
Bart Koelman 已提交
73 74 75 76
            else if (expression.IsKind(SyntaxKind.IdentifierName))
            {
                return true;
            }
77

B
Bart Koelman 已提交
78
            return false;
79
        }
80

81 82
        protected override ExpressionSyntax GetGetterExpression(IMethodSymbol getMethod, CancellationToken cancellationToken)
        {
83 84 85 86
            // Getter has to be of the form:
            //
            //     get { return field; } or
            //     get { return this.field; }
87
            var getAccessor = getMethod.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken) as AccessorDeclarationSyntax;
88 89
            var statements = getAccessor?.Body?.Statements;
            if (statements?.Count == 1)
90
            {
91 92
                var statement = statements.Value[0];
                if (statement.Kind() == SyntaxKind.ReturnStatement)
93
                {
94
                    var expr = ((ReturnStatementSyntax)statement).Expression;
95 96
                    return CheckExpressionSyntactically(expr) ? expr : null;
                }
97 98
            }

99
            return null;
100 101
        }

102
        protected override ExpressionSyntax GetSetterExpression(IMethodSymbol setMethod, SemanticModel semanticModel, CancellationToken cancellationToken)
103
        {
104 105 106 107
            // Setter has to be of the form:
            //
            //     set { field = value; } or
            //     set { this.field = value; }
108
            var setAccessor = setMethod.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken) as AccessorDeclarationSyntax;
109 110
            var statements = setAccessor?.Body?.Statements;
            if (statements?.Count == 1)
111
            {
112
                var statement = statements.Value[0];
B
Bart Koelman 已提交
113
                if (statement.IsKind(SyntaxKind.ExpressionStatement))
114
                {
115
                    var expressionStatement = (ExpressionStatementSyntax)statement;
116
                    if (expressionStatement.Expression.Kind() == SyntaxKind.SimpleAssignmentExpression)
117
                    {
118 119 120 121 122 123
                        var assignmentExpression = (AssignmentExpressionSyntax)expressionStatement.Expression;
                        if (assignmentExpression.Right.Kind() == SyntaxKind.IdentifierName &&
                            ((IdentifierNameSyntax)assignmentExpression.Right).Identifier.ValueText == "value")
                        {
                            return CheckExpressionSyntactically(assignmentExpression.Left) ? assignmentExpression.Left : null;
                        }
124 125 126 127
                    }
                }
            }

128
            return null;
129 130
        }

131
        protected override SyntaxNode GetNodeToFade(FieldDeclarationSyntax fieldDeclaration, VariableDeclaratorSyntax variableDeclarator)
132
        {
133
            return fieldDeclaration.Declaration.Variables.Count == 1
134 135 136 137
                ? fieldDeclaration
                : (SyntaxNode)variableDeclarator;
        }
    }
S
Sam Harwell 已提交
138
}