Swift: extract IfConfigDecl

This also adds `UnresolvedDeclRefExpr` tests, as `IfConfigDecl`
consistently introduces those.
This commit is contained in:
Paolo Tranquilli
2022-07-11 15:11:13 +02:00
parent 7d5dd384c3
commit 39406436bf
34 changed files with 293 additions and 9 deletions

View File

@@ -270,6 +270,16 @@ EnumCaseDecl:
IfConfigDecl:
_extends: Decl
_children:
clauses: IfConfigClause*
IfConfigClause:
_extends: Locatable
_children:
condition: Expr?
elements: AstNode*
is_active: predicate
_dir: decl
ImportDecl:
_extends: Decl
@@ -545,7 +555,6 @@ TypeExpr:
UnresolvedDeclRefExpr:
_extends: Expr
name: string?
_pragma: qltest_skip # we should really never extract these
UnresolvedDotExpr:
_extends: Expr

View File

@@ -78,7 +78,7 @@ class SwiftDispatcher {
waitingForNewLabel = e;
visit(e);
if (auto l = store.get(e)) {
if constexpr (!std::is_base_of_v<swift::TypeBase, E>) {
if constexpr (std::is_base_of_v<LocatableTag, TrapTagOf<E>>) {
attachLocation(e, *l);
}
return *l;
@@ -95,6 +95,10 @@ class SwiftDispatcher {
return fetchLabelFromUnion<AstNodeTag>(node);
}
TrapLabel<IfConfigClauseTag> fetchLabel(const swift::IfConfigClause& clause) {
return fetchLabel(&clause);
}
// Due to the lazy emission approach, we must assign a label to a corresponding AST node before
// it actually gets emitted to handle recursive cases such as recursive calls, or recursive type
// declarations
@@ -143,6 +147,15 @@ class SwiftDispatcher {
attachLocation(locatable->getStartLoc(), locatable->getEndLoc(), locatableLabel);
}
void attachLocation(const swift::IfConfigClause* clause, TrapLabel<LocatableTag> locatableLabel) {
attachLocation(clause->Loc, clause->Loc, locatableLabel);
}
// Emits a Location TRAP entry and attaches it to a `Locatable` trap label for a given `SourceLoc`
void attachLocation(swift::SourceLoc loc, TrapLabel<LocatableTag> locatableLabel) {
attachLocation(loc, loc, locatableLabel);
}
// Emits a Location TRAP entry for a list of swift entities and attaches it to a `Locatable` trap
// label
template <typename Locatable>
@@ -217,7 +230,8 @@ class SwiftDispatcher {
swift::Expr,
swift::Pattern,
swift::TypeRepr,
swift::TypeBase>;
swift::TypeBase,
swift::IfConfigClause>;
void attachLocation(swift::SourceLoc start,
swift::SourceLoc end,
@@ -274,6 +288,7 @@ class SwiftDispatcher {
// TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors,
// which are to be introduced in follow-up PRs
virtual void visit(swift::Decl* decl) = 0;
virtual void visit(const swift::IfConfigClause* clause) = 0;
virtual void visit(swift::Stmt* stmt) = 0;
virtual void visit(swift::StmtCondition* cond) = 0;
virtual void visit(swift::CaseLabelItem* item) = 0;

View File

@@ -47,6 +47,7 @@ MAP_TAG(Expr);
#include <swift/AST/ExprNodes.def>
MAP_TAG(Decl);
MAP_TAG(IfConfigClause);
#define ABSTRACT_DECL(CLASS, PARENT) MAP_SUBTAG(CLASS##Decl, PARENT)
#define DECL(CLASS, PARENT) ABSTRACT_DECL(CLASS, PARENT)
#include <swift/AST/DeclNodes.def>

View File

@@ -358,4 +358,18 @@ void DeclVisitor::fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl
fillValueDecl(decl, entry);
}
codeql::IfConfigDecl DeclVisitor::translateIfConfigDecl(const swift::IfConfigDecl& decl) {
auto entry = dispatcher_.createEntry(decl);
entry.clauses = dispatcher_.fetchRepeatedLabels(decl.getClauses());
return entry;
}
codeql::IfConfigClause DeclVisitor::translateIfConfigClause(const swift::IfConfigClause& clause) {
auto entry = dispatcher_.createEntry(clause);
entry.condition = dispatcher_.fetchOptionalLabel(clause.Cond);
entry.elements = dispatcher_.fetchRepeatedLabels(clause.Elements);
entry.is_active = clause.isActive;
return entry;
}
} // namespace codeql

View File

@@ -14,6 +14,11 @@ namespace codeql {
class DeclVisitor : public AstVisitorBase<DeclVisitor> {
public:
using AstVisitorBase<DeclVisitor>::AstVisitorBase;
using AstVisitorBase<DeclVisitor>::visit;
void visit(const swift::IfConfigClause* clause) {
dispatcher_.emit(translateIfConfigClause(*clause));
}
std::variant<codeql::ConcreteFuncDecl, codeql::ConcreteFuncDeclsTrap> translateFuncDecl(
const swift::FuncDecl& decl);
@@ -52,6 +57,8 @@ class DeclVisitor : public AstVisitorBase<DeclVisitor> {
codeql::ExtensionDecl translateExtensionDecl(const swift::ExtensionDecl& decl);
codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl);
std::optional<codeql::ModuleDecl> translateModuleDecl(const swift::ModuleDecl& decl);
codeql::IfConfigDecl translateIfConfigDecl(const swift::IfConfigDecl& decl);
codeql::IfConfigClause translateIfConfigClause(const swift::IfConfigClause& clause);
private:
std::string mangledName(const swift::ValueDecl& decl);

View File

@@ -21,6 +21,7 @@ class SwiftVisitor : private SwiftDispatcher {
private:
void visit(swift::Decl* decl) override { declVisitor.visit(decl); }
void visit(const swift::IfConfigClause* clause) override { declVisitor.visit(clause); }
void visit(swift::Stmt* stmt) override { stmtVisitor.visit(stmt); }
void visit(swift::StmtCondition* cond) override { stmtVisitor.visitStmtCondition(cond); }
void visit(swift::CaseLabelItem* item) override { stmtVisitor.visitCaseLabelItem(item); }

View File

@@ -24,6 +24,7 @@ import codeql.swift.elements.decl.FuncDecl
import codeql.swift.elements.decl.GenericContext
import codeql.swift.elements.decl.GenericTypeDecl
import codeql.swift.elements.decl.GenericTypeParamDecl
import codeql.swift.elements.decl.IfConfigClause
import codeql.swift.elements.decl.IfConfigDecl
import codeql.swift.elements.decl.ImportDecl
import codeql.swift.elements.decl.InfixOperatorDecl

View File

@@ -0,0 +1,4 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.decl.IfConfigClause
class IfConfigClause extends IfConfigClauseBase { }

View File

@@ -1,4 +1,9 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.expr.UnresolvedDeclRefExpr
class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase { }
class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase {
override string toString() {
result = getName() + " (unresolved)"
or
not hasName() and result = "(unresolved)"
}
}

View File

@@ -28,6 +28,12 @@ Element getAnImmediateChild(Element e) {
or
enum_element_decl_params(e, _, x)
or
if_config_clause_conditions(e, x)
or
if_config_clause_elements(e, _, x)
or
if_config_decl_clauses(e, _, x)
or
pattern_binding_decl_inits(e, _, x)
or
pattern_binding_decl_patterns(e, _, x)

View File

@@ -0,0 +1,30 @@
// generated by codegen/codegen.py
import codeql.swift.elements.AstNode
import codeql.swift.elements.expr.Expr
import codeql.swift.elements.Locatable
class IfConfigClauseBase extends @if_config_clause, Locatable {
override string getAPrimaryQlClass() { result = "IfConfigClause" }
Expr getCondition() {
exists(Expr x |
if_config_clause_conditions(this, x) and
result = x.resolve()
)
}
predicate hasCondition() { exists(getCondition()) }
AstNode getElement(int index) {
exists(AstNode x |
if_config_clause_elements(this, index, x) and
result = x.resolve()
)
}
AstNode getAnElement() { result = getElement(_) }
int getNumberOfElements() { result = count(getAnElement()) }
predicate isActive() { if_config_clause_is_active(this) }
}

View File

@@ -1,6 +1,18 @@
// generated by codegen/codegen.py
import codeql.swift.elements.decl.Decl
import codeql.swift.elements.decl.IfConfigClause
class IfConfigDeclBase extends @if_config_decl, Decl {
override string getAPrimaryQlClass() { result = "IfConfigDecl" }
IfConfigClause getClause(int index) {
exists(IfConfigClause x |
if_config_decl_clauses(this, index, x) and
result = x.resolve()
)
}
IfConfigClause getAClause() { result = getClause(_) }
int getNumberOfClauses() { result = count(getAClause()) }
}

View File

@@ -36,6 +36,7 @@ files(
@locatable =
@ast_node
| @condition_element
| @if_config_clause
;
#keyset[id]
@@ -643,6 +644,35 @@ if_config_decls( //dir=decl
unique int id: @if_config_decl
);
#keyset[id, index]
if_config_decl_clauses( //dir=decl
int id: @if_config_decl ref,
int index: int ref,
int clause: @if_config_clause ref
);
if_config_clauses( //dir=decl
unique int id: @if_config_clause
);
#keyset[id]
if_config_clause_conditions( //dir=decl
int id: @if_config_clause ref,
int condition: @expr ref
);
#keyset[id, index]
if_config_clause_elements( //dir=decl
int id: @if_config_clause ref,
int index: int ref,
int element: @ast_node ref
);
#keyset[id]
if_config_clause_is_active( //dir=decl
int id: @if_config_clause ref
);
import_decls( //dir=decl
unique int id: @import_decl,
int module: @module_decl ref

View File

@@ -0,0 +1,6 @@
| if_config.swift:1:1:1:1 | IfConfigClause | isActive: | no |
| if_config.swift:4:1:4:1 | IfConfigClause | isActive: | no |
| if_config.swift:7:1:7:1 | IfConfigClause | isActive: | yes |
| if_config_active.swift:3:1:3:1 | IfConfigClause | isActive: | yes |
| if_config_active.swift:6:1:6:1 | IfConfigClause | isActive: | no |
| if_config_active.swift:9:1:9:1 | IfConfigClause | isActive: | no |

View File

@@ -0,0 +1,10 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigClause x, string isActive
where
toBeTested(x) and
not x.isUnknown() and
if x.isActive() then isActive = "yes" else isActive = "no"
select x, "isActive:", isActive

View File

@@ -0,0 +1,4 @@
| if_config.swift:1:1:1:1 | IfConfigClause | if_config.swift:1:5:1:5 | FOO (unresolved) |
| if_config.swift:4:1:4:1 | IfConfigClause | if_config.swift:4:9:4:19 | call to ... |
| if_config_active.swift:3:1:3:1 | IfConfigClause | if_config_active.swift:3:5:3:5 | FOO (unresolved) |
| if_config_active.swift:6:1:6:1 | IfConfigClause | if_config_active.swift:6:9:6:17 | call to ... |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigClause x
where toBeTested(x) and not x.isUnknown()
select x, x.getCondition()

View File

@@ -0,0 +1,18 @@
| if_config.swift:1:1:1:1 | IfConfigClause | 0 | if_config.swift:2:1:2:16 | { ... } |
| if_config.swift:1:1:1:1 | IfConfigClause | 1 | if_config.swift:2:5:2:5 | foo |
| if_config.swift:1:1:1:1 | IfConfigClause | 2 | if_config.swift:3:1:3:12 | { ... } |
| if_config.swift:4:1:4:1 | IfConfigClause | 0 | if_config.swift:5:1:5:16 | { ... } |
| if_config.swift:4:1:4:1 | IfConfigClause | 1 | if_config.swift:5:5:5:5 | bar |
| if_config.swift:4:1:4:1 | IfConfigClause | 2 | if_config.swift:6:1:6:12 | { ... } |
| if_config.swift:7:1:7:1 | IfConfigClause | 0 | if_config.swift:8:1:8:16 | { ... } |
| if_config.swift:7:1:7:1 | IfConfigClause | 1 | if_config.swift:8:5:8:5 | baz |
| if_config.swift:7:1:7:1 | IfConfigClause | 2 | if_config.swift:9:1:9:12 | { ... } |
| if_config_active.swift:3:1:3:1 | IfConfigClause | 0 | if_config_active.swift:4:1:4:16 | { ... } |
| if_config_active.swift:3:1:3:1 | IfConfigClause | 1 | if_config_active.swift:4:5:4:5 | foo |
| if_config_active.swift:3:1:3:1 | IfConfigClause | 2 | if_config_active.swift:5:1:5:12 | { ... } |
| if_config_active.swift:6:1:6:1 | IfConfigClause | 0 | if_config_active.swift:7:1:7:16 | { ... } |
| if_config_active.swift:6:1:6:1 | IfConfigClause | 1 | if_config_active.swift:7:5:7:5 | bar |
| if_config_active.swift:6:1:6:1 | IfConfigClause | 2 | if_config_active.swift:8:1:8:12 | { ... } |
| if_config_active.swift:9:1:9:1 | IfConfigClause | 0 | if_config_active.swift:10:1:10:16 | { ... } |
| if_config_active.swift:9:1:9:1 | IfConfigClause | 1 | if_config_active.swift:10:5:10:5 | baz |
| if_config_active.swift:9:1:9:1 | IfConfigClause | 2 | if_config_active.swift:11:1:11:12 | { ... } |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigClause x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getElement(index)

View File

@@ -0,0 +1,10 @@
#if FOO
var foo: Int = 1
print("foo")
#elseif os(watchOS)
var bar: Int = 2
print("bar")
#else
var baz: Int = 3
print("baz")
#endif

View File

@@ -0,0 +1,12 @@
//codeql-extractor-options: -D FOO
#if FOO
var foo: Int = 1
print("foo")
#elseif os(macOS)
var bar: Int = 2
print("bar")
#else
var baz: Int = 3
print("baz")
#endif

View File

@@ -0,0 +1 @@
| if_config.swift:1:1:10:1 | #if ... |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigDecl x
where toBeTested(x) and not x.isUnknown()
select x

View File

@@ -0,0 +1,3 @@
| if_config.swift:1:1:10:1 | #if ... | 0 | if_config.swift:1:1:1:1 | IfConfigClause |
| if_config.swift:1:1:10:1 | #if ... | 1 | if_config.swift:4:1:4:1 | IfConfigClause |
| if_config.swift:1:1:10:1 | #if ... | 2 | if_config.swift:7:1:7:1 | IfConfigClause |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigDecl x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getClause(index)

View File

@@ -1,4 +0,0 @@
// generated by codegen/codegen.py
After a swift source file is added in this directory and codegen/codegen.py is run again, test queries
will appear and this file will be deleted

View File

@@ -0,0 +1,10 @@
#if FOO
var foo: Int = 1
print("foo")
#elseif BAR
var bar: Int = 2
print("bar")
#else
var baz: Int = 3
print("baz")
#endif

View File

@@ -0,0 +1,10 @@
| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) |
| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) |
| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) |
| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) |
| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) |
| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) |
| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) |
| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) |
| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) |
| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedDeclRefExpr x
where toBeTested(x) and not x.isUnknown()
select x

View File

@@ -0,0 +1,10 @@
| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) | FOO |
| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) | && |
| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) | os |
| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) | Windows |
| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) | print |
| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) | BAR |
| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) | \|\| |
| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) | arch |
| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) | i386 |
| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) | print |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedDeclRefExpr x
where toBeTested(x) and not x.isUnknown()
select x, x.getName()

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedDeclRefExpr x
where toBeTested(x) and not x.isUnknown()
select x, x.getType()

View File

@@ -0,0 +1,10 @@
//codeql-extractor-options: -D BAR
// conditions and inactive branches in conditional compilation blocks are not resolved
#if FOO && os(Windows)
print(1)
#elseif BAR || arch(i386)
print(2)
#else
print(3)
#endif