From a646dfc4b901c64f91e91069c7becb981949d1a5 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 8 Jun 2026 12:49:04 +0200 Subject: [PATCH] C#: Extract call target when Range is not hardcoded as call argument. --- .../Entities/Expressions/ElementAccess.cs | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs index a5a03ad3fec..d7a0ada9a1d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs @@ -79,7 +79,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions /// /// The method symbol to check. /// True if the method is a slice method; false otherwise. - private bool IsSliceWithRange(IMethodSymbol method, [NotNullWhen(true)] out RangeExpressionSyntax? range) + private bool IsSlice(IMethodSymbol method, out RangeExpressionSyntax? range) { range = null; @@ -89,8 +89,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } return (method.Name == "Slice" || method.Name == "Substring") - && method.Parameters.Length == 2 - && range is not null; + && method.Parameters.Length == 2; } /// @@ -111,21 +110,31 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions /// /// Even though index expressions can't technically be used in this way, they signal that we /// could perceive ^b as "length - b". + /// + /// Call arguments are only populated when a range expression is directly available in + /// the list of arguments. + /// This means that cases like below are not handled. + /// System.Range x = 1..3; + /// s[x] /// /// The trap file to write to. /// The slice method symbol. /// The range expression syntax. - private void PopulateSlice(TextWriter trapFile, IMethodSymbol slice, RangeExpressionSyntax range) + private void PopulateSlice(TextWriter trapFile, IMethodSymbol slice, RangeExpressionSyntax? range) { - var left = range.LeftOperand is ExpressionSyntax lsyntax - ? MakeFromRangeEndpoint(lsyntax, this, 0) - : MakeZeroLiteral(this, 0); + if (range is not null) + { + // Populate the call arguments in case + var left = range.LeftOperand is ExpressionSyntax lsyntax + ? MakeFromRangeEndpoint(lsyntax, this, 0) + : MakeZeroLiteral(this, 0); - var right = range.RightOperand is ExpressionSyntax rsyntax - ? MakeFromRangeEndpoint(rsyntax, this, 1) - : MakeZeroFromEndExpression(this, 1); + var right = range.RightOperand is ExpressionSyntax rsyntax + ? MakeFromRangeEndpoint(rsyntax, this, 1) + : MakeZeroFromEndExpression(this, 1); - SetExprArgument(trapFile, left, right); + SetExprArgument(trapFile, left, right); + } trapFile.expr_call(this, Method.Create(Context, slice)); } @@ -144,7 +153,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions Create(Context, qualifier, this, -1); var target = GetTargetSymbol(); - if (target is IMethodSymbol method && IsSliceWithRange(method, out var range)) + if (target is IMethodSymbol method && IsSlice(method, out var range)) { // When an indexer on a span or string is used in conjunction with a range expression, the compiler translates // this into a call to the "Slice" or "Substring" method.