55
66namespace Semmle . Extraction . CSharp . Entities . Expressions
77{
8- internal sealed class ImplicitCast : Expression
8+ internal sealed class Implicit : Expression
99 {
10- public Expression Expr
11- {
12- get ;
13- private set ;
14- }
15-
16- private ImplicitCast ( ExpressionNodeInfo info )
10+ private Implicit ( ExpressionNodeInfo info )
1711 : base ( new ExpressionInfo ( info . Context , info . ConvertedType , info . Location , ExprKind . CAST , info . Parent , info . Child , isCompilerGenerated : true , info . ExprValue ) )
1812 {
19- Expr = Factory . Create ( new ExpressionNodeInfo ( Context , info . Node , this , 0 ) ) ;
13+ Factory . Create ( new ExpressionNodeInfo ( Context , info . Node , this , 0 ) ) ;
2014 }
2115
22- private ImplicitCast ( ExpressionNodeInfo info , IMethodSymbol method )
23- : base ( new ExpressionInfo ( info . Context , info . ConvertedType , info . Location , ExprKind . OPERATOR_INVOCATION , info . Parent , info . Child , isCompilerGenerated : true , info . ExprValue ) )
16+ private Implicit ( ExpressionNodeInfo info , IMethodSymbol method , ExprKind kind , int child )
17+ : base ( new ExpressionInfo ( info . Context , info . ConvertedType , info . Location , kind , info . Parent , info . Child , isCompilerGenerated : true , info . ExprValue ) )
2418 {
25- Expr = Factory . Create ( info . SetParent ( this , 0 ) ) ;
19+ Factory . Create ( info . SetParent ( this , child ) ) ;
2620
27- AddOperatorCall ( method ) ;
21+ AddCall ( method ) ;
2822 }
2923
30- private ImplicitCast ( ExpressionInfo info , IMethodSymbol method , object value ) : base ( info )
24+ private Implicit ( ExpressionInfo info , IMethodSymbol method , object value ) : base ( info )
3125 {
32- Expr = Literal . CreateGenerated ( Context , this , 0 , method . Parameters [ 0 ] . Type , value , info . Location ) ;
26+ Literal . CreateGenerated ( Context , this , 0 , method . Parameters [ 0 ] . Type , value , info . Location ) ;
3327
34- AddOperatorCall ( method ) ;
28+ AddCall ( method ) ;
3529 }
3630
37- private void AddOperatorCall ( IMethodSymbol method )
31+ private void AddCall ( IMethodSymbol method )
3832 {
3933 var target = Method . Create ( Context , method ) ;
4034 Context . TrapWriter . Writer . expr_call ( this , target ) ;
@@ -72,7 +66,7 @@ ExpressionInfo create(ExprKind kind, string? v) =>
7266 if ( method is not null )
7367 {
7468 var info = create ( ExprKind . OPERATOR_INVOCATION , null ) ;
75- return new ImplicitCast ( info , method , value ) ;
69+ return new Implicit ( info , method , value ) ;
7670 }
7771 else
7872 {
@@ -81,6 +75,21 @@ ExpressionInfo create(ExprKind kind, string? v) =>
8175 }
8276 }
8377
78+ /// <summary>
79+ /// Gets the `ToString` method for the given type.
80+ /// </summary>
81+ private static IMethodSymbol ? GetToStringMethod ( ITypeSymbol type )
82+ {
83+ return type
84+ . GetMembers ( )
85+ . OfType < IMethodSymbol > ( )
86+ . Where ( method =>
87+ method . GetName ( ) == "ToString" &&
88+ method . Parameters . Length == 0
89+ )
90+ . FirstOrDefault ( ) ;
91+ }
92+
8493 /// <summary>
8594 /// Creates a new generated cast expression.
8695 /// </summary>
@@ -130,7 +139,7 @@ info.Parent is ExplicitObjectCreation objectCreation &&
130139 }
131140
132141 if ( resolvedType . Symbol is not null )
133- return new ImplicitCast ( info , conversion . MethodSymbol ) ;
142+ return new Implicit ( info , conversion . MethodSymbol , ExprKind . OPERATOR_INVOCATION , 0 ) ;
134143 }
135144
136145 var implicitUpcast = conversion . IsImplicit &&
@@ -144,7 +153,19 @@ resolvedType.Symbol is null ||
144153
145154 if ( ! conversion . IsIdentity && ! implicitUpcast )
146155 {
147- return new ImplicitCast ( info ) ;
156+ return new Implicit ( info ) ;
157+ }
158+
159+ // Implicit call to ToString.
160+ if ( ! conversion . IsIdentity &&
161+ resolvedType . Symbol is not null &&
162+ implicitUpcast && // Maybe write the condition explicitly.
163+ info . Parent is Expression par && // TODO: Only choose a specific set of parents (maybe BinaryExpression and StringInterpolation expressions?)
164+ par . Type . HasValue && par . Type . Value . Symbol ? . SpecialType == SpecialType . System_String )
165+ {
166+ return GetToStringMethod ( resolvedType . Symbol ) is IMethodSymbol toString
167+ ? new Implicit ( info , toString , ExprKind . METHOD_INVOCATION , - 1 )
168+ : Factory . Create ( info ) ;
148169 }
149170
150171 if ( conversion . IsIdentity && conversion . IsImplicit &&
@@ -153,7 +174,7 @@ convertedType.Symbol is IPointerTypeSymbol &&
153174 {
154175 // int[] -> int*
155176 // string -> char*
156- return new ImplicitCast ( info ) ;
177+ return new Implicit ( info ) ;
157178 }
158179
159180 // Default: Just create the expression without a conversion.
0 commit comments