mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
C#: Simplify extraction of is expressions and case statements
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user