using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Util; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { internal sealed class ImplicitToString : Expression { /// /// Gets the `ToString` method for the given type. /// private static IMethodSymbol? GetToStringMethod(ITypeSymbol? type) { if (type is null) { return null; } var toString = type .GetMembers() .OfType() .Where(method => method.GetName() == "ToString" && method.Parameters.Length == 0 ) .FirstOrDefault(); return toString ?? GetToStringMethod(type.BaseType); } private ImplicitToString(ExpressionNodeInfo info, IMethodSymbol toString) : base(new ExpressionInfo(info.Context, AnnotatedTypeSymbol.CreateNotAnnotated(toString.ReturnType), info.Location, ExprKind.METHOD_INVOCATION, info.Parent, info.Child, isCompilerGenerated: true, info.ExprValue)) { Factory.Create(info.SetParent(this, -1)); var target = Method.Create(Context, toString); Context.TrapWriter.Writer.expr_call(this, target); } /// /// Creates a new expression, adding a compiler generated `ToString` call if required. /// public static Expression Create(Context cx, ExpressionSyntax node, IExpressionParentEntity parent, int child) { var info = new ExpressionNodeInfo(cx, node, parent, child); return CreateFromNode(info.SetImplicitToString(!info.Type.IsStringType())); } /// /// Wraps the resulting expression in a `ToString` call, if a suitable `ToString` method is available. /// public static Expression Wrap(ExpressionNodeInfo info) { if (GetToStringMethod(info.Type?.Symbol) is IMethodSymbol toString) { return new ImplicitToString(info, toString); } return Factory.Create(info); } } }