C#: Implement range operator

This commit is contained in:
calum
2019-04-24 17:54:18 +01:00
committed by Calum Grant
parent 15e2248440
commit 318068b52f
8 changed files with 139 additions and 1 deletions

View File

@@ -719,3 +719,37 @@ class DiscardExpr extends Expr, @discard_expr {
private class UnknownExpr extends Expr, @unknown_expr {
override string toString() { result = "Expression" }
}
/**
* A range expression, used to create a `System.Range`. For example
* ```
* 1..3
* 1..^1
* 3..
* ..
* ..5
* ..^1
* ```
*/
class RangeExpr extends Expr, @range_expr {
override string toString() { result = "... .. ..." }
/** Gets the left hand operand of this range expression, if any. */
Expr getStart() { result = this.getChild(0) }
/** Gets the right hand operand of this range expression, if any. */
Expr getEnd() { result = this.getChild(1) }
/** Holds if this range expression has a left hand operand. */
predicate hasStart() { exists(getStart()) }
/** Holds if this range expression has a right hand operand. */
predicate hasEnd() { exists(getEnd()) }
}
/** An index expression, for example `^1` meaning "1 from the end". */
class IndexExpr extends Expr, @index_expr {
Expr getExpr() { result.getParent() = this }
override string toString() { result = "^..." }
}

View File

@@ -975,6 +975,8 @@ case @expr.kind of
| 109 = @local_function_invocation_expr
| 110 = @ref_expr
| 111 = @discard_expr
| 112 = @range_expr
| 113 = @index_expr
;
@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;

View File

@@ -0,0 +1,42 @@
// semmle-extractor-options: /langversion:8.0
using System;
class Ranges
{
void F()
{
var array = new int[] { 1, 2, 3, 4 };
var array2 = new int[2, 3];
var slice1 = array[1..3];
var slice2 = array[0..^1];
int x=2, y=3;
var slice3 = array[x..y];
var slice4 = array[..y];
var slice5 = array[x..];
var slice6 = array[..];
var slice7 = array[^10..^5];
var slice8 = array2[1..2, ..];
}
}
// These are temporary until qltest uses .NET Core 3.0.
namespace System
{
public readonly struct Index
{
public Index(int value, bool fromEnd = false) { }
public static implicit operator Index(int value) => default(Index);
}
public readonly struct Range
{
public Range(Index start, Index end) => throw null;
public static Range StartAt(System.Index start) => throw null;
public static Range EndAt(System.Index end) => throw null;
public static Range All => throw null;
public static Range Create(Index start, Index end) => throw null;
public static implicit operator int(Range r) => throw null;
}
}

View File

@@ -0,0 +1,28 @@
indexes
| ranges.cs:13:31:13:32 | ^... | ranges.cs:13:32:13:32 | 1 |
| ranges.cs:19:28:19:30 | ^... | ranges.cs:19:29:19:30 | 10 |
| ranges.cs:19:33:19:34 | ^... | ranges.cs:19:34:19:34 | 5 |
ranges
| ranges.cs:12:28:12:31 | ... .. ... |
| ranges.cs:13:28:13:32 | ... .. ... |
| ranges.cs:15:28:15:31 | ... .. ... |
| ranges.cs:16:28:16:30 | ... .. ... |
| ranges.cs:17:28:17:30 | ... .. ... |
| ranges.cs:18:28:18:29 | ... .. ... |
| ranges.cs:19:28:19:34 | ... .. ... |
| ranges.cs:20:29:20:32 | ... .. ... |
| ranges.cs:20:35:20:36 | ... .. ... |
rangeStart
| ranges.cs:12:28:12:31 | ... .. ... | ranges.cs:12:28:12:28 | 1 |
| ranges.cs:13:28:13:32 | ... .. ... | ranges.cs:13:28:13:28 | 0 |
| ranges.cs:15:28:15:31 | ... .. ... | ranges.cs:15:28:15:28 | access to local variable x |
| ranges.cs:17:28:17:30 | ... .. ... | ranges.cs:17:28:17:28 | access to local variable x |
| ranges.cs:19:28:19:34 | ... .. ... | ranges.cs:19:28:19:30 | ^... |
| ranges.cs:20:29:20:32 | ... .. ... | ranges.cs:20:29:20:29 | 1 |
rangeEnd
| ranges.cs:12:28:12:31 | ... .. ... | ranges.cs:12:31:12:31 | 3 |
| ranges.cs:13:28:13:32 | ... .. ... | ranges.cs:13:31:13:32 | ^... |
| ranges.cs:15:28:15:31 | ... .. ... | ranges.cs:15:31:15:31 | access to local variable y |
| ranges.cs:16:28:16:30 | ... .. ... | ranges.cs:16:30:16:30 | access to local variable y |
| ranges.cs:19:28:19:34 | ... .. ... | ranges.cs:19:33:19:34 | ^... |
| ranges.cs:20:29:20:32 | ... .. ... | ranges.cs:20:32:20:32 | 2 |

View File

@@ -0,0 +1,20 @@
import csharp
predicate getConversion(Expr expr, Expr unconvertedExpr) {
unconvertedExpr = expr.(Cast).getExpr()
or
unconvertedExpr = expr.(OperatorCall).getArgument(0) and
expr.(OperatorCall).getTarget() instanceof ConversionOperator
}
Expr stripConversions(Expr expr) {
if getConversion(expr, _) then getConversion(expr, result) else result = expr
}
query predicate indexes(IndexExpr e, Expr c) { c = e.getExpr() }
query predicate ranges(RangeExpr e) { any() }
query predicate rangeStart(RangeExpr e, Expr start) { start = stripConversions(e.getStart()) }
query predicate rangeEnd(RangeExpr e, Expr end) { end = stripConversions(e.getEnd()) }