Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion ql/ql/src/codeql_ql/ast/Ast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ class ClasslessPredicate extends TClasslessPredicate, Predicate, ModuleDeclarati
override predicate isPrivate() { Predicate.super.isPrivate() }

/** Holds if this classless predicate is a signature predicate with no body. */
predicate isSignature() { not exists(this.getBody()) }
override predicate isSignature() { not exists(this.getBody()) }

override QLDoc getQLDoc() {
result = any(TopLevel m).getQLDocFor(this)
Expand Down Expand Up @@ -836,6 +836,12 @@ class Module extends TModule, ModuleDeclaration {
toMock(result) = mod.asRight().getMember(i)
}

pragma[nomagic]
Declaration getDeclaration(string name) {
result = this.getAMember() and
name = result.getName()
}

QLDoc getQLDocFor(AstNode m) {
exists(int i | result = this.getMember(i) and m = this.getMember(i + 1))
}
Expand Down Expand Up @@ -885,6 +891,33 @@ class ModuleMember extends TModuleMember, AstNode {
predicate isFinal() { this.hasAnnotation("final") }
}

private newtype TDeclarationKind =
TClassKind() or
TModuleKind() or
TPredicateKind(int arity) { arity = any(Predicate p).getArity() }

private TDeclarationKind getDeclarationKind(Declaration d) {
d instanceof Class and result = TClassKind()
or
d instanceof Module and result = TModuleKind()
or
d = any(Predicate p | result = TPredicateKind(p.getArity()))
}

/** Holds if module `m` must implement signature declaration `d` with name `name` and kind `kind`. */
pragma[nomagic]
private predicate mustImplement(Module m, string name, TDeclarationKind kind, Declaration d) {
d = m.getImplements(_).getResolvedType().getDeclaration().(Module).getAMember() and
name = d.getName() and
kind = getDeclarationKind(d)
}

pragma[nomagic]
private Declaration getDeclaration(Module m, string name, TDeclarationKind kind) {
result = m.getDeclaration(name) and
kind = getDeclarationKind(result)
}

/** A declaration. E.g. a class, type, predicate, newtype... */
class Declaration extends TDeclaration, AstNode {
/** Gets the name of this declaration. */
Expand All @@ -899,6 +932,16 @@ class Declaration extends TDeclaration, AstNode {
or
result = any(Class c).getQLDocFor(this)
}

predicate isSignature() { this.hasAnnotation("signature") }

/** Holds if this declaration implements `other`. */
predicate implements(Declaration other) {
exists(Module m, string name, TDeclarationKind kind |
this = getDeclaration(m, name, kind) and
mustImplement(m, name, kind, other)
)
}
}

/** An entity that can be declared in a module. */
Expand Down
10 changes: 10 additions & 0 deletions ql/ql/src/codeql_ql/style/DeadCodeQuery.qll
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ private AstNode aliveStep(AstNode prev) {
result = prev.(Module).getImplements(_)
or
result = prev.(PredicateExpr).getQualifier()
or
// a module argument is live if the constructed module is
result = prev.(ModuleExpr).getArgument(_)
or
// a type declaration is live if a reference to it is live
result = prev.(TypeExpr).getResolvedType().getDeclaration()
or
// a module member that implements a signature member is live if the module is
prev.(Module).getAMember() = result and
result.(Declaration).implements(_)
}

private AstNode deprecated() {
Expand Down
7 changes: 5 additions & 2 deletions ql/ql/test/queries/style/DeadCode/DeadCode.expected
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
| Foo.qll:2:21:2:25 | ClasslessPredicate dead1 | This code is never used, and it's not publicly exported. |
| Foo.qll:6:13:6:17 | ClasslessPredicate dead2 | This code is never used, and it's not publicly exported. |
| Foo.qll:4:21:4:25 | ClasslessPredicate dead1 | This code is never used, and it's not publicly exported. |
| Foo.qll:8:13:8:17 | ClasslessPredicate dead2 | This code is never used, and it's not publicly exported. |
| Foo.qll:46:16:46:21 | Module Input2 | This code is never used, and it's not publicly exported. |
| Foo.qll:56:16:56:17 | Module M2 | This code is never used, and it's not publicly exported. |
| Foo.qll:68:15:68:20 | Class CImpl2 | This code is never used, and it's not publicly exported. |
36 changes: 36 additions & 0 deletions ql/ql/test/queries/style/DeadCode/Foo.qll
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import ql

private module Mixed {
private predicate dead1() { none() }

Expand Down Expand Up @@ -30,3 +32,37 @@ private module Foo {
module ValidationMethod<Foo::bar/0 sig> {
predicate impl() { sig() }
}

signature module InputSig {
predicate foo();
}

module ParameterizedModule<InputSig Input> { }

private module Input1 implements InputSig {
predicate foo() { any() }
}

private module Input2 implements InputSig {
predicate foo() { any() }
}

private module Input3 implements InputSig {
predicate foo() { any() }
}

module M1 = ParameterizedModule<Input1>;

private module M2 = ParameterizedModule<Input2>;

import ParameterizedModule<Input3>

private module MImpl { }

module MPublic = MImpl;

private class CImpl1 extends AstNode { }

final class CPublic1 = CImpl1;

private class CImpl2 extends AstNode { }