using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace StardewModdingAPI.ModBuildConfig.Analyzer
{
/// Provides generic utilities for SMAPI's Roslyn analyzers.
internal static class AnalyzerUtilities
{
/*********
** Public methods
*********/
/// Get the metadata for an explicit cast or 'x as y' expression.
/// The member access expression.
/// provides methods for asking semantic questions about syntax nodes.
/// The expression whose value is being converted.
/// The type being converted from.
/// The type being converted to.
/// Returns true if the node is a matched expression, else false.
public static bool TryGetCastOrAsInfo(SyntaxNode node, SemanticModel semanticModel, out ExpressionSyntax fromExpression, out TypeInfo fromType, out TypeInfo toType)
{
// (type)x
if (node is CastExpressionSyntax cast)
{
fromExpression = cast.Expression;
fromType = semanticModel.GetTypeInfo(fromExpression);
toType = semanticModel.GetTypeInfo(cast.Type);
return true;
}
// x as y
if (node is BinaryExpressionSyntax binary && binary.Kind() == SyntaxKind.AsExpression)
{
fromExpression = binary.Left;
fromType = semanticModel.GetTypeInfo(fromExpression);
toType = semanticModel.GetTypeInfo(binary.Right);
return true;
}
// invalid
fromExpression = null;
fromType = default(TypeInfo);
toType = default(TypeInfo);
return false;
}
/// Get the metadata for a member access expression.
/// The member access expression.
/// provides methods for asking semantic questions about syntax nodes.
/// The object type which has the member.
/// The type of the accessed member.
/// The name of the accessed member.
/// Returns true if the node is a member access expression, else false.
public static bool TryGetMemberInfo(SyntaxNode node, SemanticModel semanticModel, out ITypeSymbol declaringType, out TypeInfo memberType, out string memberName)
{
// simple access
if (node is MemberAccessExpressionSyntax memberAccess)
{
declaringType = semanticModel.GetTypeInfo(memberAccess.Expression).Type;
memberType = semanticModel.GetTypeInfo(node);
memberName = memberAccess.Name.Identifier.Text;
return true;
}
// conditional access
if (node is ConditionalAccessExpressionSyntax conditionalAccess && conditionalAccess.WhenNotNull is MemberBindingExpressionSyntax conditionalBinding)
{
declaringType = semanticModel.GetTypeInfo(conditionalAccess.Expression).Type;
memberType = semanticModel.GetTypeInfo(node);
memberName = conditionalBinding.Name.Identifier.Text;
return true;
}
// invalid
declaringType = null;
memberType = default(TypeInfo);
memberName = null;
return false;
}
/// Get the class types in a type's inheritance chain, including itself.
/// The initial type.
public static IEnumerable GetConcreteTypes(ITypeSymbol type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
}