Skip to content

Commit a306bb7

Browse files
committed
C#: Make synthetic ToString calls in binary add expressions.
1 parent dad9420 commit a306bb7

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public static void CreateDeferred(Context cx, ExpressionSyntax node, IExpression
129129
cx.PopulateLater(() => Create(cx, node, parent, child));
130130
}
131131

132-
private static bool ContainsPattern(SyntaxNode node) =>
132+
protected static bool ContainsPattern(SyntaxNode node) =>
133133
node is PatternSyntax || node is VariableDesignationSyntax || node.ChildNodes().Any(ContainsPattern);
134134

135135
/// <summary>

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,35 @@ private Binary(ExpressionNodeInfo info)
1414

1515
public static Expression Create(ExpressionNodeInfo info) => new Binary(info).TryPopulate();
1616

17+
private Expression CreateChild(Context cx, ExpressionSyntax node, int child)
18+
{
19+
// If this is a "+" expression we might need to wrap the child expressions
20+
// in ToString calls
21+
return Kind == ExprKind.ADD
22+
? ImplicitToString.Create(cx, node, this, child)
23+
: Create(cx, node, this, child);
24+
}
25+
26+
/// <summary>
27+
/// Creates an expression from a syntax node.
28+
/// Inserts type conversion as required.
29+
/// Population is deferred to avoid overflowing the stack.
30+
/// </summary>
31+
private void CreateDeferred(Context cx, ExpressionSyntax node, int child)
32+
{
33+
if (ContainsPattern(node))
34+
// Expressions with patterns should be created right away, as they may introduce
35+
// local variables referenced in `LocalVariable::GetAlreadyCreated()`
36+
CreateChild(cx, node, child);
37+
else
38+
cx.PopulateLater(() => CreateChild(cx, node, child));
39+
}
40+
1741
protected override void PopulateExpression(TextWriter trapFile)
1842
{
1943
OperatorCall(trapFile, Syntax);
20-
CreateDeferred(Context, Syntax.Left, this, 0);
21-
CreateDeferred(Context, Syntax.Right, this, 1);
44+
CreateDeferred(Context, Syntax.Left, 0);
45+
CreateDeferred(Context, Syntax.Right, 1);
2246
}
2347

2448
private static ExprKind GetKind(Context cx, BinaryExpressionSyntax node)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Linq;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using Semmle.Extraction.CSharp.Util;
5+
using Semmle.Extraction.Kinds;
6+
7+
8+
namespace Semmle.Extraction.CSharp.Entities.Expressions
9+
{
10+
internal sealed class ImplicitToString : Expression
11+
{
12+
/// <summary>
13+
/// Gets the `ToString` method for the given type.
14+
/// </summary>
15+
private static IMethodSymbol? GetToStringMethod(ITypeSymbol? type)
16+
{
17+
return type?
18+
.GetMembers()
19+
.OfType<IMethodSymbol>()
20+
.Where(method =>
21+
method.GetName() == "ToString" &&
22+
method.Parameters.Length == 0
23+
)
24+
.FirstOrDefault();
25+
}
26+
27+
private ImplicitToString(ExpressionNodeInfo info, IMethodSymbol toString) : base(new ExpressionInfo(info.Context, info.ConvertedType, info.Location, ExprKind.METHOD_INVOCATION, info.Parent, info.Child, isCompilerGenerated: true, info.ExprValue))
28+
{
29+
Factory.Create(info.SetParent(this, -1));
30+
31+
var target = Method.Create(Context, toString);
32+
Context.TrapWriter.Writer.expr_call(this, target);
33+
}
34+
35+
private static bool IsStringType(AnnotatedTypeSymbol? type) =>
36+
type.HasValue && type.Value.Symbol?.SpecialType == SpecialType.System_String;
37+
38+
/// <summary>
39+
/// Creates a new expression, adding implicit `ToString` as required.
40+
/// </summary>
41+
public static Expression Create(Context cx, ExpressionSyntax node, Expression parent, int child)
42+
{
43+
var info = new ExpressionNodeInfo(cx, node, parent, child);
44+
if (IsStringType(parent.Type) &&
45+
!IsStringType(info.Type) &&
46+
GetToStringMethod(info.ConvertedType.Symbol) is IMethodSymbol toString)
47+
{
48+
return new ImplicitToString(info, toString);
49+
}
50+
return CreateFromNode(info);
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)