diff options
Diffstat (limited to 'src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs')
-rw-r--r-- | src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs new file mode 100644 index 00000000..68b5001e --- /dev/null +++ b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace StardewModdingAPI.ModBuildConfig.Analyzer +{ + /// <summary>Provides generic utilities for SMAPI's Roslyn analyzers.</summary> + internal static class AnalyzerUtilities + { + /********* + ** Public methods + *********/ + /// <summary>Get the metadata for an explicit cast or 'x as y' expression.</summary> + /// <param name="node">The member access expression.</param> + /// <param name="semanticModel">provides methods for asking semantic questions about syntax nodes.</param> + /// <param name="fromExpression">The expression whose value is being converted.</param> + /// <param name="fromType">The type being converted from.</param> + /// <param name="toType">The type being converted to.</param> + /// <returns>Returns true if the node is a matched expression, else false.</returns> + 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; + } + + /// <summary>Get the metadata for a member access expression.</summary> + /// <param name="node">The member access expression.</param> + /// <param name="semanticModel">provides methods for asking semantic questions about syntax nodes.</param> + /// <param name="declaringType">The object type which has the member.</param> + /// <param name="memberType">The type of the accessed member.</param> + /// <param name="memberName">The name of the accessed member.</param> + /// <returns>Returns true if the node is a member access expression, else false.</returns> + 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; + } + + /// <summary>Get the class types in a type's inheritance chain, including itself.</summary> + /// <param name="type">The initial type.</param> + public static IEnumerable<ITypeSymbol> GetConcreteTypes(ITypeSymbol type) + { + while (type != null) + { + yield return type; + type = type.BaseType; + } + } + } +} |