UseAutoPropertyAnalyzer.cs 6.1 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
            if (argument.Expression != null)
48
            {
49 50 51 52 53 54 55
                var cancellationToken = context.CancellationToken;
                var symbolInfo = context.SemanticModel.GetSymbolInfo(argument.Expression, cancellationToken);
                AddIneligibleField(symbolInfo.Symbol, ineligibleFields);
                foreach (var symbol in symbolInfo.CandidateSymbols)
                {
                    AddIneligibleField(symbol, ineligibleFields);
                }
56 57 58 59 60 61 62 63 64 65 66 67
            }
        }

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

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

B
Bart Koelman 已提交
81
            return false;
82
        }
83

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

102
            return null;
103 104
        }

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

131
            return null;
132 133
        }

134
        protected override SyntaxNode GetNodeToFade(FieldDeclarationSyntax fieldDeclaration, VariableDeclaratorSyntax variableDeclarator)
135
        {
136
            return fieldDeclaration.Declaration.Variables.Count == 1
137 138 139 140 141
                ? fieldDeclaration
                : (SyntaxNode)variableDeclarator;
        }
    }
}