|
// 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,
};
}
}
|