Skip to content

Commit f4105ee

Browse files
authored
Merge pull request github#19089 from michaelnebel/csharp/improvestringinterpolation
C#: Extract string interpolation alignment and format.
2 parents 0339601 + 8e1282b commit f4105ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+6380
-124
lines changed

csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/old.dbscheme

Lines changed: 1460 additions & 0 deletions
Large diffs are not rendered by default.

csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/semmlecode.csharp.dbscheme

Lines changed: 1459 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
class Expr extends @expr {
2+
string toString() { none() }
3+
}
4+
5+
class TypeOrRef extends @type_or_ref {
6+
string toString() { none() }
7+
}
8+
9+
class InterpolatedStringInsertExpr extends Expr, @interpolated_string_insert_expr { }
10+
11+
private predicate remove_expr(Expr e) {
12+
exists(InterpolatedStringInsertExpr ie |
13+
e = ie
14+
or
15+
// Alignment
16+
expr_parent(e, 1, ie)
17+
or
18+
// Format
19+
expr_parent(e, 2, ie)
20+
)
21+
}
22+
23+
query predicate new_expressions(Expr e, int kind, TypeOrRef t) {
24+
expressions(e, kind, t) and
25+
// Remove the syntheetic intert expression and previously un-extracted children
26+
not remove_expr(e)
27+
}
28+
29+
query predicate new_expr_parent(Expr e, int child, Expr parent) {
30+
expr_parent(e, child, parent) and
31+
not remove_expr(e) and
32+
not remove_expr(parent)
33+
or
34+
// Use the string interpolation as parent instead of the synthetic insert expression
35+
exists(InterpolatedStringInsertExpr ie |
36+
expr_parent(e, 0, ie) and
37+
expr_parent(ie, child, parent)
38+
)
39+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
description: Remove `interpolated_string_insert_expr` kind.
2+
compatibility: backwards
3+
expressions.rel: run string_interpol_insert.qlo new_expressions
4+
expr_parent.rel: run string_interpol_insert.qlo new_expr_parent

csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public AnnotatedTypeSymbol(ITypeSymbol? symbol, NullableAnnotation nullability)
2929
symbol is null ? (AnnotatedTypeSymbol?)null : new AnnotatedTypeSymbol(symbol, NullableAnnotation.None);
3030
}
3131

32+
internal static class AnnotatedTypeSymbolExtensions
33+
{
34+
/// <summary>
35+
/// Returns true if the type is a string type.
36+
/// </summary>
37+
public static bool IsStringType(this AnnotatedTypeSymbol? type) =>
38+
type.HasValue && type.Value.Symbol?.SpecialType == SpecialType.System_String;
39+
}
40+
3241
internal static class SymbolExtensions
3342
{
3443
/// <summary>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ private Expression CreateChild(Context cx, ExpressionSyntax node, int child)
1818
{
1919
// If this is a "+" expression we might need to wrap the child expressions
2020
// in ToString calls
21-
return Kind == ExprKind.ADD
21+
return Kind == ExprKind.ADD && Type.IsStringType()
2222
? ImplicitToString.Create(cx, node, this, child)
2323
: Create(cx, node, this, child);
2424
}

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,13 @@ private ImplicitToString(ExpressionNodeInfo info, IMethodSymbol toString) : base
3939
Context.TrapWriter.Writer.expr_call(this, target);
4040
}
4141

42-
private static bool IsStringType(AnnotatedTypeSymbol? type) =>
43-
type.HasValue && type.Value.Symbol?.SpecialType == SpecialType.System_String;
44-
4542
/// <summary>
4643
/// Creates a new expression, adding a compiler generated `ToString` call if required.
4744
/// </summary>
48-
public static Expression Create(Context cx, ExpressionSyntax node, Expression parent, int child)
45+
public static Expression Create(Context cx, ExpressionSyntax node, IExpressionParentEntity parent, int child)
4946
{
5047
var info = new ExpressionNodeInfo(cx, node, parent, child);
51-
return CreateFromNode(info.SetImplicitToString(IsStringType(parent.Type) && !IsStringType(info.Type)));
48+
return CreateFromNode(info.SetImplicitToString(!info.Type.IsStringType()));
5249
}
5350

5451
/// <summary>

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.IO;
2-
using Microsoft.CodeAnalysis;
32
using Microsoft.CodeAnalysis.CSharp;
43
using Microsoft.CodeAnalysis.CSharp.Syntax;
54
using Semmle.Extraction.Kinds;
@@ -21,15 +20,7 @@ protected override void PopulateExpression(TextWriter trapFile)
2120
{
2221
case SyntaxKind.Interpolation:
2322
var interpolation = (InterpolationSyntax)c;
24-
var exp = interpolation.Expression;
25-
if (Context.GetTypeInfo(exp).Type is ITypeSymbol type && !type.ImplementsIFormattable())
26-
{
27-
ImplicitToString.Create(Context, exp, this, child++);
28-
}
29-
else
30-
{
31-
Create(Context, exp, this, child++);
32-
}
23+
new InterpolatedStringInsert(Context, interpolation, this, child++);
3324
break;
3425
case SyntaxKind.InterpolatedStringText:
3526
// Create a string literal
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp.Syntax;
3+
using Semmle.Extraction.Kinds;
4+
5+
namespace Semmle.Extraction.CSharp.Entities.Expressions
6+
{
7+
internal class InterpolatedStringInsert : Expression
8+
{
9+
public InterpolatedStringInsert(Context cx, InterpolationSyntax syntax, Expression parent, int child) :
10+
base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.INTERPOLATED_STRING_INSERT, parent, child, isCompilerGenerated: false, null))
11+
{
12+
var exp = syntax.Expression;
13+
if (parent.Type.IsStringType() &&
14+
cx.GetTypeInfo(exp).Type is ITypeSymbol type &&
15+
!type.ImplementsIFormattable())
16+
{
17+
ImplicitToString.Create(cx, exp, this, 0);
18+
}
19+
else
20+
{
21+
Create(cx, exp, this, 0);
22+
}
23+
24+
// Hardcode the child number of the optional alignment clause to 1 and format clause to 2.
25+
// This simplifies the logic in QL.
26+
if (syntax.AlignmentClause?.Value is ExpressionSyntax alignment)
27+
{
28+
Create(cx, alignment, this, 1);
29+
}
30+
31+
if (syntax.FormatClause is InterpolationFormatClauseSyntax format)
32+
{
33+
var f = format.FormatStringToken.ValueText;
34+
var t = AnnotatedTypeSymbol.CreateNotAnnotated(cx.Compilation.GetSpecialType(SpecialType.System_String));
35+
new Expression(new ExpressionInfo(cx, t, cx.CreateLocation(format.GetLocation()), ExprKind.UTF16_STRING_LITERAL, this, 2, isCompilerGenerated: false, f));
36+
}
37+
38+
}
39+
}
40+
41+
}

csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public enum ExprKind
132132
UTF8_STRING_LITERAL = 135,
133133
COLLECTION = 136,
134134
SPREAD_ELEMENT = 137,
135+
INTERPOLATED_STRING_INSERT = 138,
135136
DEFINE_SYMBOL = 999,
136137
}
137138
}

0 commit comments

Comments
 (0)