File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\Utilities\TypeStyle\CSharpTypeStyleHelper.State.cs
Web Access
Project: src\src\CodeStyle\CSharp\Analyzers\Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle)
// 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.
 
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Simplification;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.CSharp.Utilities;
 
internal partial class CSharpTypeStyleHelper
{
    protected readonly struct State
    {
        public readonly UseVarPreference TypeStylePreference;
 
        private readonly NotificationOption2 _forBuiltInTypes;
        private readonly NotificationOption2 _whenTypeIsApparent;
        private readonly NotificationOption2 _elsewhere;
 
        public readonly bool IsInIntrinsicTypeContext;
        public readonly bool IsTypeApparentInContext;
 
        public State(
            SyntaxNode declaration, SemanticModel semanticModel,
            CSharpSimplifierOptions options, CancellationToken cancellationToken)
        {
            TypeStylePreference = default;
            IsInIntrinsicTypeContext = default;
            IsTypeApparentInContext = default;
 
            var styleForIntrinsicTypes = options.VarForBuiltInTypes;
            var styleForApparent = options.VarWhenTypeIsApparent;
            var styleForElsewhere = options.VarElsewhere;
 
            _forBuiltInTypes = styleForIntrinsicTypes.Notification;
            _whenTypeIsApparent = styleForApparent.Notification;
            _elsewhere = styleForElsewhere.Notification;
 
            this.TypeStylePreference = options.GetUseVarPreference();
 
            IsTypeApparentInContext =
                    declaration is VariableDeclarationSyntax varDecl
                 && IsTypeApparentInDeclaration(varDecl, semanticModel, TypeStylePreference, cancellationToken);
 
            IsInIntrinsicTypeContext =
                    IsPredefinedTypeInDeclaration(declaration, semanticModel)
                 || IsInferredPredefinedType(declaration, semanticModel);
        }
 
        public NotificationOption2 GetDiagnosticSeverityPreference()
            => IsInIntrinsicTypeContext ? _forBuiltInTypes :
               IsTypeApparentInContext ? _whenTypeIsApparent : _elsewhere;
 
        /// <summary>
        /// Returns true if type information could be gleaned by simply looking at the given statement.
        /// This typically means that the type name occurs in right hand side of an assignment.
        /// </summary>
        private static bool IsTypeApparentInDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, UseVarPreference stylePreferences, CancellationToken cancellationToken)
        {
            if (variableDeclaration.Variables.Count != 1)
            {
                return false;
            }
 
            var initializer = variableDeclaration.Variables[0].Initializer;
            if (initializer == null)
            {
                return false;
            }
 
            var initializerExpression = CSharpUseImplicitTypeHelper.GetInitializerExpression(initializer.Value);
            var declaredTypeSymbol = semanticModel.GetTypeInfo(variableDeclaration.Type.StripRefIfNeeded(), cancellationToken).Type;
            return TypeStyleHelper.IsTypeApparentInAssignmentExpression(stylePreferences, initializerExpression, semanticModel, declaredTypeSymbol, cancellationToken);
        }
 
        /// <summary>
        /// checks if the type represented by the given symbol is one of the
        /// simple types defined in the compiler.
        /// </summary>
        /// <remarks>
        /// From the IDE perspective, we also include object and string to be simplified
        /// to var. <see cref="SyntaxFacts.IsPredefinedType(SyntaxKind)"/> considers string
        /// and object but the compiler's implementation of IsIntrinsicType does not.
        /// </remarks>
        private static bool IsPredefinedTypeInDeclaration(SyntaxNode declarationStatement, SemanticModel semanticModel)
        {
            var typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);
 
            return typeSyntax != null
                ? IsMadeOfSpecialTypes(semanticModel.GetTypeInfo(typeSyntax.StripRefIfNeeded()).Type)
                : false;
        }
 
        /// <summary>
        /// Returns true for type that are arrays/nullable/pointer types of special types
        /// </summary>
        private static bool IsMadeOfSpecialTypes([NotNullWhen(true)] ITypeSymbol? type)
        {
            if (type == null)
            {
                return false;
            }
 
            while (true)
            {
                type = type.RemoveNullableIfPresent();
 
                if (type is IArrayTypeSymbol arrayType)
                {
                    type = arrayType.ElementType;
                    continue;
                }
 
                if (type is IPointerTypeSymbol pointerType)
                {
                    type = pointerType.PointedAtType;
                    continue;
                }
 
                return type.IsSpecialType();
            }
        }
 
        private static bool IsInferredPredefinedType(SyntaxNode declarationStatement, SemanticModel semanticModel)
        {
            var typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);
 
            return typeSyntax != null &&
                typeSyntax.IsTypeInferred(semanticModel) &&
                semanticModel.GetTypeInfo(typeSyntax).Type?.IsSpecialType() == true;
        }
 
        private static TypeSyntax? GetTypeSyntaxFromDeclaration(SyntaxNode declarationStatement)
            => declarationStatement switch
            {
                VariableDeclarationSyntax varDecl => varDecl.Type,
                ForEachStatementSyntax forEach => forEach.Type,
                DeclarationExpressionSyntax declExpr => declExpr.Type,
                _ => null,
            };
    }
}