CSharpUseExplicitTypeHelper.cs 6.7 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
4 5 6 7 8 9 10

using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;

11 12
#if CODE_STYLE
using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions;
13
using Microsoft.CodeAnalysis.CSharp.Internal.CodeStyle.TypeStyle;
14 15
#else
using Microsoft.CodeAnalysis.Options;
16
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
17 18
#endif

19 20 21 22 23 24 25 26 27 28
namespace Microsoft.CodeAnalysis.CSharp.Utilities
{
    internal sealed class CSharpUseExplicitTypeHelper : CSharpTypeStyleHelper
    {
        public static CSharpUseExplicitTypeHelper Instance = new CSharpUseExplicitTypeHelper();

        private CSharpUseExplicitTypeHelper()
        {
        }

C
Cyrus Najmabadi 已提交
29
        protected override bool IsStylePreferred(in State state)
30 31 32 33 34
        {
            var stylePreferences = state.TypeStylePreference;

            if (state.IsInIntrinsicTypeContext)
            {
C
Cyrus Najmabadi 已提交
35
                return !stylePreferences.HasFlag(UseVarPreference.ForBuiltInTypes);
36 37 38
            }
            else if (state.IsTypeApparentInContext)
            {
C
Cyrus Najmabadi 已提交
39
                return !stylePreferences.HasFlag(UseVarPreference.WhenTypeIsApparent);
40 41 42
            }
            else
            {
C
Cyrus Najmabadi 已提交
43
                return !stylePreferences.HasFlag(UseVarPreference.Elsewhere);
44 45 46 47 48
            }
        }

        protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
49
            if (!variableDeclaration.Type.StripRefIfNeeded().IsVar)
50 51 52 53 54 55 56 57 58 59 60
            {
                // If the type is not 'var', this analyze has no work to do
                return false;
            }

            // The base analyzer may impose further limitations
            return base.ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken);
        }

        protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
61
            if (!forEachStatement.Type.StripRefIfNeeded().IsVar)
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
            {
                // If the type is not 'var', this analyze has no work to do
                return false;
            }

            // The base analyzer may impose further limitations
            return base.ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken);
        }

        internal override bool TryAnalyzeVariableDeclaration(
            TypeSyntax typeName, SemanticModel semanticModel,
            OptionSet optionSet, CancellationToken cancellationToken)
        {
            // var (x, y) = e;
            // foreach (var (x, y) in e) ...
77 78
            if (typeName.IsParentKind(SyntaxKind.DeclarationExpression, out DeclarationExpressionSyntax declExpression) &&
                declExpression.Designation.IsKind(SyntaxKind.ParenthesizedVariableDesignation))
79
            {
80
                return true;
81 82 83 84
            }

            // If it is currently not var, explicit typing exists, return. 
            // this also takes care of cases where var is mapped to a named type via an alias or a class declaration.
85
            if (!typeName.StripRefIfNeeded().IsTypeInferred(semanticModel))
86 87 88 89
            {
                return false;
            }

90
            if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration, out VariableDeclarationSyntax variableDeclaration) &&
91 92 93
                typeName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
            {
                // check assignment for variable declarations.
94
                var variable = variableDeclaration.Variables.First();
95 96 97 98 99 100
                if (!AssignmentSupportsStylePreference(
                        variable.Identifier, typeName, variable.Initializer.Value,
                        semanticModel, optionSet, cancellationToken))
                {
                    return false;
                }
101 102 103 104 105 106

                // This error case is handled by a separate code fix (UseExplicitTypeForConst).
                if ((variableDeclaration.Parent as LocalDeclarationStatementSyntax)?.IsConst == true)
                {
                    return false;
                }
107
            }
108 109
            else if (typeName.Parent is ForEachStatementSyntax foreachStatement &&
                     foreachStatement.Type == typeName)
110 111
            {
                if (!AssignmentSupportsStylePreference(
D
dotnet-bot 已提交
112
                        foreachStatement.Identifier, typeName, foreachStatement.Expression,
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
                        semanticModel, optionSet, cancellationToken))
                {
                    return false;
                }
            }

            return true;
        }

        protected override bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (!declaration.Type.IsVar)
            {
                // If the type is not 'var', this analyze has no work to do
                return false;
            }

            // The base analyzer may impose further limitations
            return base.ShouldAnalyzeDeclarationExpression(declaration, semanticModel, cancellationToken);
        }

        /// <summary>
        /// Analyzes the assignment expression and rejects a given declaration if it is unsuitable for explicit typing.
        /// </summary>
        /// <returns>
        /// false, if explicit typing cannot be used.
        /// true, otherwise.
        /// </returns>
        protected override bool AssignmentSupportsStylePreference(
            SyntaxToken identifier,
            TypeSyntax typeName,
            ExpressionSyntax initializer,
            SemanticModel semanticModel,
            OptionSet optionSet,
            CancellationToken cancellationToken)
        {
            // is or contains an anonymous type
            // cases :
            //        var anon = new { Num = 1 };
            //        var enumerableOfAnons = from prod in products select new { prod.Color, prod.Price };
153
            var declaredType = semanticModel.GetTypeInfo(typeName.StripRefIfNeeded(), cancellationToken).Type;
154 155 156 157 158 159 160 161 162 163 164
            if (declaredType.ContainsAnonymousType())
            {
                return false;
            }

            // cannot find type if initializer resolves to an ErrorTypeSymbol
            var initializerTypeInfo = semanticModel.GetTypeInfo(initializer, cancellationToken);
            return !initializerTypeInfo.Type.IsErrorType();
        }
    }
}