Skip to content

Commit 0c69899

Browse files
committed
use resolveConstanteWriteAccess instead, add a few more test cases
1 parent 3df7793 commit 0c69899

File tree

4 files changed

+51
-21
lines changed

4 files changed

+51
-21
lines changed

ruby/ql/lib/codeql/ruby/ast/Constant.qll

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,6 @@ class ConstantAccess extends Expr, TConstantAccess {
4040
*/
4141
predicate hasGlobalScope() { none() }
4242

43-
// gets the full name
44-
string getFullName() {
45-
exists(ConstantAccess ca | this.getScopeExpr() = ca |
46-
result = ca.getFullName() + "::" + this.getName())
47-
or
48-
// TODO if the getScopeExpr is not a constant, try to figure out which constants it could be?
49-
not exists(ConstantAccess ca | this.getScopeExpr() = ca) and
50-
result = this.getName()
51-
}
52-
5343
override string toString() { result = this.getName() }
5444

5545
override AstNode getAChild(string pred) {
@@ -194,16 +184,34 @@ class ConstantWriteAccess extends ConstantAccess {
194184
*
195185
* the constant `Baz` has the fully qualified name `Foo::Bar::Baz`, and
196186
* `CONST_A` has the fully qualified name `Foo::CONST_A`.
187+
*
188+
* Important note: This can return more than one value, because there are
189+
* situations where there can be multiple possible "fully qualified" names.
190+
* For example:
191+
* ```
192+
* module Mod4
193+
* include Mod1
194+
* module Mod3::Mod5 end
195+
* end
196+
* ```
197+
* In the above snippet, `Mod5` has two valid fully qualified names it can be
198+
* referred to by: `Mod1::Mod3::Mod5`, or `Mod4::Mod3::Mod5`.
199+
*
200+
* Another example has to do with the order in which module definitions are
201+
* executed at runtime. Because of the way that ruby dynamically looks up
202+
* constants up the namespace chain, the fully qualified name of a nested
203+
* constant can be ambiguous from just statically looking at the AST.
197204
*/
198-
string getQualifiedName() {
199-
/* get the qualified name for the parent module, then append w */
200-
exists(ConstantWriteAccess parent | parent = this.getEnclosingModule() |
201-
result = parent.getQualifiedName() + "::" + this.getFullName()
202-
)
203-
or
204-
/* base case - there's no parent module */
205-
not exists(ConstantWriteAccess parent | parent = this.getEnclosingModule()) and
206-
result = this.getFullName()
205+
string getAQualifiedName() {
206+
result = resolveConstantWriteAccess(this)
207+
}
208+
209+
/**
210+
* gets a qualified name for this constant. Deprecated in favor of
211+
* `getAQualifiedName` because this can return more than one value
212+
*/
213+
deprecated string getQualifiedName() {
214+
result = this.getAQualifiedName()
207215
}
208216
}
209217

ruby/ql/test/library-tests/ast/constants/constants.expected

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ constantAccess
4242
| constants.rb:50:3:51:5 | ClassB | write | ClassB | ClassDeclaration |
4343
| constants.rb:50:9:50:15 | ModuleB | read | ModuleB | ConstantReadAccess |
4444
| constants.rb:50:27:50:30 | Base | read | Base | ConstantReadAccess |
45+
| constants.rb:54:1:56:3 | Mod1 | write | Mod1 | ModuleDeclaration |
46+
| constants.rb:55:3:55:17 | Mod3 | write | Mod3 | ModuleDeclaration |
47+
| constants.rb:58:1:61:3 | Mod4 | write | Mod4 | ModuleDeclaration |
48+
| constants.rb:59:11:59:14 | Mod1 | read | Mod1 | ConstantReadAccess |
49+
| constants.rb:60:3:60:23 | Mod5 | write | Mod5 | ModuleDeclaration |
50+
| constants.rb:60:10:60:13 | Mod3 | read | Mod3 | ConstantReadAccess |
4551
getConst
4652
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
4753
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
@@ -79,4 +85,11 @@ constantWriteAccessQualifiedName
7985
| constants.rb:44:1:47:3 | ModuleB | ModuleA::ModuleB |
8086
| constants.rb:45:3:46:5 | ClassB | ModuleA::ModuleB::ClassB |
8187
| constants.rb:49:1:52:3 | ModuleA | ModuleA |
82-
| constants.rb:50:3:51:5 | ClassB | ModuleA::ModuleB::ClassB |
88+
| constants.rb:50:3:51:5 | ClassB | ModuleA::ModuleB::ClassB |
89+
| constants.rb:50:3:51:5 | ClassB | ModuleB::ClassB |
90+
| constants.rb:54:1:56:3 | Mod1 | Mod1 |
91+
| constants.rb:55:3:55:17 | Mod3 | Mod1::Mod3 |
92+
| constants.rb:58:1:61:3 | Mod4 | Mod4 |
93+
| constants.rb:60:3:60:23 | Mod5 | Mod1::Mod3::Mod5 |
94+
| constants.rb:60:3:60:23 | Mod5 | Mod4::Mod3::Mod5 |
95+
| constants.rb:60:3:60:23 | Mod5 | Mod3::Mod5 |

ruby/ql/test/library-tests/ast/constants/constants.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ query Expr lookupConst(Module m, string name) { result = M::lookupConst(m, name)
1818
query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() }
1919

2020
query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) {
21-
w.getQualifiedName() = qualifiedName
21+
w.getAQualifiedName() = qualifiedName
2222
}

ruby/ql/test/library-tests/ast/constants/constants.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,12 @@ module ModuleA
5050
class ModuleB::ClassB < Base
5151
end
5252
end
53+
54+
module Mod1
55+
module Mod3 end
56+
end
57+
58+
module Mod4
59+
include Mod1
60+
module Mod3::Mod5 end
61+
end

0 commit comments

Comments
 (0)