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