C#: Simplify extraction of is expressions and case statements

This commit is contained in:
Tom Hvitved
2019-05-28 13:16:56 +02:00
parent bd1920c948
commit 8c1cab2d03
13 changed files with 80 additions and 77 deletions

View File

@@ -131,19 +131,14 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
private void PopulatePattern(PatternSyntax pattern, TypeSyntax optionalType, SyntaxToken varKeyword, VariableDesignationSyntax designation)
{
bool isVar = optionalType is null;
if (!isVar)
Expressions.TypeAccess.Create(cx, optionalType, this, 1);
var isVar = optionalType is null;
if (!(designation is null) && cx.Model(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Type.Create(cx, symbol.Type);
if (isVar)
new Expression(new ExpressionInfo(cx, type, cx.Create(varKeyword.GetLocation()), ExprKind.TYPE_ACCESS, this, 1, false, null));
VariableDeclaration.Create(cx, symbol, type, cx.Create(pattern.GetLocation()), cx.Create(designation.GetLocation()), isVar, this, 2);
}
else if (!isVar)
Expressions.TypeAccess.Create(cx, optionalType, this, 1);
}
protected override void Populate()
@@ -152,7 +147,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
switch (Syntax.Pattern)
{
case ConstantPatternSyntax constantPattern:
Create(cx, constantPattern.Expression, this, 3);
Create(cx, constantPattern.Expression, this, 1);
return;
case VarPatternSyntax varPattern:
PopulatePattern(varPattern, null, varPattern.VarKeyword, varPattern.Designation);
@@ -161,7 +156,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
PopulatePattern(declPattern, declPattern.Type, default(SyntaxToken), declPattern.Designation);
return;
case RecursivePatternSyntax recPattern:
new RecursivePattern(cx, recPattern, this, 3);
new RecursivePattern(cx, recPattern, this, 1);
return;
default:
throw new InternalError(Syntax, "Is pattern not handled");

View File

@@ -67,27 +67,23 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
private CasePattern(Context cx, CasePatternSwitchLabelSyntax node, Switch parent, int child)
: base(cx, node, parent, child) { }
private void PopulatePattern(PatternSyntax pattern, TypeSyntax optionalType, SyntaxToken varKeyword, VariableDesignationSyntax designation)
private void PopulatePattern(PatternSyntax pattern, TypeSyntax optionalType, VariableDesignationSyntax designation)
{
var isVar = optionalType is null;
if (!isVar)
Expressions.TypeAccess.Create(cx, optionalType, this, 1);
switch (designation)
{
case SingleVariableDesignationSyntax _:
if (cx.Model(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Type.Create(cx, symbol.Type);
if (isVar)
new Expression(new ExpressionInfo(cx, type, cx.Create(varKeyword.GetLocation()), ExprKind.TYPE_ACCESS, this, 1, false, null));
Expressions.VariableDeclaration.Create(cx, symbol, type, cx.Create(pattern.GetLocation()), cx.Create(designation.GetLocation()), isVar, this, 0);
}
break;
case DiscardDesignationSyntax discard:
new Expressions.Discard(cx, discard, this, 0);
if (!isVar)
Expressions.TypeAccess.Create(cx, optionalType, this, 0);
else
new Expressions.Discard(cx, discard, this, 0);
break;
case null:
break;
@@ -104,10 +100,10 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
switch (Stmt.Pattern)
{
case VarPatternSyntax varPattern:
PopulatePattern(varPattern, null, varPattern.VarKeyword, varPattern.Designation);
PopulatePattern(varPattern, null, varPattern.Designation);
break;
case DeclarationPatternSyntax declarationPattern:
PopulatePattern(declarationPattern, declarationPattern.Type, default(SyntaxToken), declarationPattern.Designation);
PopulatePattern(declarationPattern, declarationPattern.Type, declarationPattern.Designation);
break;
case ConstantPatternSyntax pattern:
Expression.Create(cx, pattern.Expression, this, 0);
@@ -121,7 +117,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
if (Stmt.WhenClause != null)
{
Expression.Create(cx, Stmt.WhenClause.Condition, this, 2);
Expression.Create(cx, Stmt.WhenClause.Condition, this, 1);
}
}

View File

@@ -72,7 +72,8 @@ class ConstantNullnessCondition extends ConstantCondition {
ConstantNullnessCondition() {
forex(ControlFlow::Node cfn | cfn = this.getAControlFlowNode() |
exists(ControlFlow::SuccessorTypes::NullnessSuccessor t, ControlFlow::Node s |
s = cfn.getASuccessorByType(t) |
s = cfn.getASuccessorByType(t)
|
b = t.getValue() and
not s.isJoin()
) and

View File

@@ -306,9 +306,8 @@ module AssignableInternal {
/** A local variable declaration at the top-level of a pattern. */
class TopLevelPatternDecl extends LocalVariableDeclExpr {
private PatternMatch pm;
TopLevelPatternDecl() {
this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr()
}
TopLevelPatternDecl() { this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr() }
PatternMatch getMatch() { result = pm }
}

View File

@@ -19,17 +19,8 @@ predicate expr_parent_top_level_adjusted(Expr child, int i, @top_level_exprorstm
}
/**
* Holds if `case` statement `cs` is a type check of the form `case int _`,
* where `ta` is the type access `int`.
*/
private predicate discardTypeCaseStmt(CaseStmt cs, TypeAccess ta) {
expr_parent(ta, 1, cs) and
expr_parent(any(DiscardExpr de), 0, cs)
}
/**
* The `expr_parent()` relation adjusted for expandable assignments, `is` expressions,
* and `case` statements. For example, the assignment `x += y` is extracted as
* The `expr_parent()` relation adjusted for expandable assignments. For example,
* the assignment `x += y` is extracted as
*
* ```
* +=
@@ -78,35 +69,7 @@ private predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement par
not ao.hasExpandedAssignment() and
expr_parent(child, i, parent)
)
else
if parent instanceof IsExpr
then
i = 0 and
expr_parent(child, i, parent)
or
// e.g. `x is string s` or `x is null`
i = 1 and
expr_parent(child, any(int j | j in [2 .. 3]), parent)
or
// e.g. `x is string`
i = 1 and
not expr_parent(_, any(int j | j in [2 .. 3]), parent) and
expr_parent(child, i, parent)
else
if parent instanceof CaseStmt
then
// e.g. `case string s:` or `case 5:`
i = 0 and
expr_parent(child, i, parent) and
not discardTypeCaseStmt(parent, _)
or
// e.g. `case string _`
i = 0 and
discardTypeCaseStmt(parent, child)
or
i = 1 and
expr_parent(child, 2, parent)
else expr_parent(child, i, parent)
else expr_parent(child, i, parent)
}
/**

View File

@@ -417,9 +417,7 @@ predicate switchMatching(Switch s, Case c, PatternExpr pe) {
pe = c.getPattern()
}
private predicate mustHaveMatchingCompletion(Switch s, PatternExpr pe) {
switchMatching(s, _, pe)
}
private predicate mustHaveMatchingCompletion(Switch s, PatternExpr pe) { switchMatching(s, _, pe) }
/**
* Holds if a normal completion of `cfe` must be a matching completion. Thats is,

View File

@@ -313,6 +313,7 @@ class ConstantPatternExpr extends PatternExpr {
*/
class TypePatternExpr extends PatternExpr {
private Type t;
TypePatternExpr() {
t = this.(TypeAccess).getTarget() or
t = this.(LocalVariableDeclExpr).getVariable().getType()
@@ -517,7 +518,7 @@ class Case extends PatternMatch, @case {
/**
* Gets the `when` expression of this case, if any. For example, `s.Length < 10`
* in `string s when s.Length < 10 => s`
*/
*/
Expr getCondition() { none() }
/** Gets the body of this `case`. */

View File

@@ -1,6 +1,7 @@
import csharp
from Case c, boolean isVar, VariablePatternExpr vpe
where vpe = c.getPattern() and
if vpe.isImplicitlyTyped() then isVar = true else isVar = false
where
vpe = c.getPattern() and
if vpe.isImplicitlyTyped() then isVar = true else isVar = false
select c, vpe, vpe.getType().toString(), isVar

View File

@@ -66,6 +66,4 @@ query predicate isRecursivePatternExprWithDecl(
decl = expr.getRecursivePattern().getVariableDeclExpr()
}
query predicate labeledPatternExpr(LabeledPatternExpr e, string s) {
s = e.getLabel()
}
query predicate labeledPatternExpr(LabeledPatternExpr e, string s) { s = e.getLabel() }

View File

@@ -11,7 +11,7 @@ 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 indexes(IndexExpr e, Expr c) { c = e.getExpr() }
query predicate ranges(RangeExpr e) { any() }

View File

@@ -1,6 +1,8 @@
import csharp
from string inout, Ssa::ExplicitDefinition def, Ssa::Definition targetDef, ControlFlow::Node call, boolean additionalCalls
from
string inout, Ssa::ExplicitDefinition def, Ssa::Definition targetDef, ControlFlow::Node call,
boolean additionalCalls
where
inout = "in" and def.isCapturedVariableDefinitionFlowIn(targetDef, call, additionalCalls)
or

View File

@@ -0,0 +1,47 @@
class Expr extends @expr {
string toString() { none() }
}
class ControlFlowElement extends @control_flow_element {
string toString() { none() }
}
private predicate discardTypeCaseStmt(@case cs, @type_access_expr ta) {
expr_parent(ta, 1, cs) and
expr_parent(any(@discard_expr de), 0, cs)
}
private predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) {
if parent instanceof @is_expr
then
i = 0 and
expr_parent(child, i, parent)
or
// e.g. `x is string s` or `x is null`
i = 1 and
expr_parent(child, any(int j | j in [2 .. 3]), parent)
or
// e.g. `x is string`
i = 1 and
not expr_parent(_, any(int j | j in [2 .. 3]), parent) and
expr_parent(child, i, parent)
else
if parent instanceof @case
then
// e.g. `case string s:` or `case 5:`
i = 0 and
expr_parent(child, i, parent) and
not discardTypeCaseStmt(parent, _)
or
// e.g. `case string _`
i = 0 and
discardTypeCaseStmt(parent, child)
or
i = 1 and
expr_parent(child, 2, parent)
else expr_parent(child, i, parent)
}
from Expr child, int i, ControlFlowElement parent
where expr_parent_adjusted(child, i, parent)
select child, i, parent

View File

@@ -1,2 +1,4 @@
description: Add C#8 expression and union types supporting ranges, recursive patterns and switch expressions.
compatibility: backwards
expr_parent.rel: run expr_parent.qlo