Skip to content

Commit f6ba9e3

Browse files
committed
C#: Extract call target when Range is not hardcoded as call argument.
1 parent f4c4e7d commit f6ba9e3

1 file changed

Lines changed: 21 additions & 12 deletions

File tree

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

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private Expression MakeFromRangeEndpoint(ExpressionSyntax syntax, IExpressionPar
7979
/// </summary>
8080
/// <param name="method">The method symbol to check.</param>
8181
/// <returns>True if the method is a slice method; false otherwise.</returns>
82-
private bool IsSliceWithRange(IMethodSymbol method, [NotNullWhen(true)] out RangeExpressionSyntax? range)
82+
private bool IsSlice(IMethodSymbol method, out RangeExpressionSyntax? range)
8383
{
8484
range = null;
8585

@@ -89,8 +89,7 @@ private bool IsSliceWithRange(IMethodSymbol method, [NotNullWhen(true)] out Rang
8989
}
9090

9191
return (method.Name == "Slice" || method.Name == "Substring")
92-
&& method.Parameters.Length == 2
93-
&& range is not null;
92+
&& method.Parameters.Length == 2;
9493
}
9594

9695
/// <summary>
@@ -111,21 +110,31 @@ private bool IsSliceWithRange(IMethodSymbol method, [NotNullWhen(true)] out Rang
111110
///
112111
/// Even though index expressions can't technically be used in this way, they signal that we
113112
/// could perceive ^b as "length - b".
113+
///
114+
/// Call arguments are only populated when a range expression is directly available in
115+
/// the list of arguments.
116+
/// This means that cases like below are not handled.
117+
/// System.Range x = 1..3;
118+
/// s[x]
114119
/// </summary>
115120
/// <param name="trapFile">The trap file to write to.</param>
116121
/// <param name="slice">The slice method symbol.</param>
117122
/// <param name="range">The range expression syntax.</param>
118-
private void PopulateSlice(TextWriter trapFile, IMethodSymbol slice, RangeExpressionSyntax range)
123+
private void PopulateSlice(TextWriter trapFile, IMethodSymbol slice, RangeExpressionSyntax? range)
119124
{
120-
var left = range.LeftOperand is ExpressionSyntax lsyntax
121-
? MakeFromRangeEndpoint(lsyntax, this, 0)
122-
: MakeZeroLiteral(this, 0);
125+
if (range is not null)
126+
{
127+
// Populate the call arguments in case
128+
var left = range.LeftOperand is ExpressionSyntax lsyntax
129+
? MakeFromRangeEndpoint(lsyntax, this, 0)
130+
: MakeZeroLiteral(this, 0);
123131

124-
var right = range.RightOperand is ExpressionSyntax rsyntax
125-
? MakeFromRangeEndpoint(rsyntax, this, 1)
126-
: MakeZeroFromEndExpression(this, 1);
132+
var right = range.RightOperand is ExpressionSyntax rsyntax
133+
? MakeFromRangeEndpoint(rsyntax, this, 1)
134+
: MakeZeroFromEndExpression(this, 1);
127135

128-
SetExprArgument(trapFile, left, right);
136+
SetExprArgument(trapFile, left, right);
137+
}
129138
trapFile.expr_call(this, Method.Create(Context, slice));
130139
}
131140

@@ -144,7 +153,7 @@ protected override void PopulateExpression(TextWriter trapFile)
144153
Create(Context, qualifier, this, -1);
145154

146155
var target = GetTargetSymbol();
147-
if (target is IMethodSymbol method && IsSliceWithRange(method, out var range))
156+
if (target is IMethodSymbol method && IsSlice(method, out var range))
148157
{
149158
// When an indexer on a span or string is used in conjunction with a range expression, the compiler translates
150159
// this into a call to the "Slice" or "Substring" method.

0 commit comments

Comments
 (0)