diff --git a/ql/src/codeql_ruby/ast/Call.qll b/ql/src/codeql_ruby/ast/Call.qll index b38b2b37263..366aab182a1 100644 --- a/ql/src/codeql_ruby/ast/Call.qll +++ b/ql/src/codeql_ruby/ast/Call.qll @@ -85,6 +85,21 @@ class MethodCall extends Call, TMethodCall { */ Expr getReceiver() { none() } + /** + * Holds if the receiver is `self` or there is no receiver, which has the same + * meaning as an explict `self`. For example: + * + * ```rb + * self.foo + * foo + * ``` + */ + predicate receiverIsSelf() { + this.getReceiver() instanceof Self + or + not exists(this.getReceiver()) + } + /** * Gets the name of the method being called. For example, in: * diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index d449a576e4a..17661c281ba 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -8,9 +8,18 @@ private import internal.TreeSitter * A representation of a run-time `module` or `class` value. */ class Module extends TModule { - /** Get a declaration of this module, if any. */ + /** Gets a declaration of this module, if any. */ ModuleBase getADeclaration() { result.getModule() = this } + /** Gets the super class of this module, if any. */ + Module getSuperClass() { result = getSuperClass(this) } + + /** Gets a `prepend`ed module. */ + Module getAPrependedModule() { result = getAPrependedModule(this) } + + /** Gets an `include`d module. */ + Module getAnIncludedModule() { result = getAnIncludedModule(this) } + /** Gets a textual representation of this module. */ string toString() { this = TResolved(result) diff --git a/ql/src/codeql_ruby/ast/internal/Module.qll b/ql/src/codeql_ruby/ast/internal/Module.qll index a1f128d0a08..d7708271e11 100644 --- a/ql/src/codeql_ruby/ast/internal/Module.qll +++ b/ql/src/codeql_ruby/ast/internal/Module.qll @@ -34,6 +34,58 @@ private module Cached { result = scopeAppend(container, n.getName()) ) } + + cached + Module getSuperClass(Module cls) { + cls = TResolved("Object") and result = TResolved("BasicObject") + or + cls = TResolved("Module") and result = TResolved("Object") + or + cls = TResolved("Class") and result = TResolved("Module") + or + not cls = TResolved(builtin()) and + ( + exists(ClassDeclaration d | + d = cls.getADeclaration() and + result = resolveScopeExpr(d.getSuperclassExpr()) + ) + or + result = TResolved("Object") and + forex(ClassDeclaration d | d = cls.getADeclaration() | + not exists(resolveScopeExpr(d.getSuperclassExpr())) + ) + ) + } + + cached + Module getAnIncludedModule(Module m) { + m = TResolved("Object") and result = TResolved("Kernel") + or + exists(IncludeOrPrependCall c | + c.getMethodName() = "include" and + ( + m = resolveScopeExpr(c.getReceiver()) + or + m = enclosingModule(c).getModule() and + c.receiverIsSelf() + ) and + result = resolveScopeExpr(c.getAnArgument()) + ) + } + + cached + Module getAPrependedModule(Module m) { + exists(IncludeOrPrependCall c | + c.getMethodName() = "prepend" and + ( + m = resolveScopeExpr(c.getReceiver()) + or + m = enclosingModule(c).getModule() and + c.receiverIsSelf() + ) and + result = resolveScopeExpr(c.getAnArgument()) + ) + } } import Cached @@ -242,3 +294,31 @@ private string qualifiedModuleName(string container, string name) { container = "Object" and name = result ) } + +private Module getAncestors(Module m) { + result = m or + result = getAncestors(m.getAnIncludedModule()) or + result = getAncestors(m.getAPrependedModule()) +} + +Method getMethod(TModule owner, string name) { + exists(ModuleBase m | m.getModule() = owner and result = m.getMethod(name)) +} + +private Method lookupMethod0(Module m, string name) { + result = lookupMethod0(m.getAPrependedModule(), name) + or + not exists(getMethod(getAncestors(m.getAPrependedModule()), name)) and + ( + result = getMethod(m, name) + or + not exists(getMethod(m, name)) and result = lookupMethod0(m.getAnIncludedModule(), name) + ) +} + +Method lookupMethod(Module m, string name) { + result = lookupMethod0(m, name) + or + not exists(lookupMethod0(m, name)) and + result = lookupMethod(m.getSuperClass(), name) +} diff --git a/ql/src/codeql_ruby/controlflow/internal/Cfg.ql b/ql/src/codeql_ruby/controlflow/internal/Cfg.ql index ded6b103222..a0b0200b346 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Cfg.ql +++ b/ql/src/codeql_ruby/controlflow/internal/Cfg.ql @@ -1,5 +1,6 @@ /** * @kind graph + * @id rb/test/cfg */ import codeql_ruby.CFG diff --git a/ql/test/library-tests/ast/Ast.ql b/ql/test/library-tests/ast/Ast.ql index babafaac548..4cd412f83a0 100644 --- a/ql/test/library-tests/ast/Ast.ql +++ b/ql/test/library-tests/ast/Ast.ql @@ -1,5 +1,6 @@ /** * @kind graph + * @id rb/test/print-ast */ import codeql_ruby.printAst diff --git a/ql/test/library-tests/ast/modules/modules.expected b/ql/test/library-tests/ast/modules/modules.expected index f7f52051674..61352f7c78e 100644 --- a/ql/test/library-tests/ast/modules/modules.expected +++ b/ql/test/library-tests/ast/modules/modules.expected @@ -101,49 +101,3 @@ modulesInModules | modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:97:3:98:5 | Z | Z | | modules.rb:101:1:105:3 | PrependTest | modules.rb:103:3:104:5 | Y | Y | | modules.rb:107:1:110:3 | MM | modules.rb:108:3:109:5 | MM | MM | -moduleTypes -| classes.rb:2:1:56:3 | classes.rb | file://:0:0:0:0 | Object | -| classes.rb:3:1:4:3 | Foo | modules.rb:4:1:24:3 | Foo | -| classes.rb:7:1:8:3 | Bar | modules.rb:37:1:46:3 | Bar | -| classes.rb:11:1:12:3 | Baz | classes.rb:11:1:12:3 | Baz | -| classes.rb:15:1:15:20 | MyModule | classes.rb:15:1:15:20 | MyModule | -| classes.rb:16:1:17:3 | MyClass | classes.rb:16:1:17:3 | MyModule::MyClass | -| classes.rb:20:1:37:3 | Wibble | classes.rb:20:1:37:3 | Wibble | -| classes.rb:32:3:33:5 | ClassInWibble | classes.rb:32:3:33:5 | Wibble::ClassInWibble | -| classes.rb:35:3:36:5 | ModuleInWibble | classes.rb:35:3:36:5 | Wibble::ModuleInWibble | -| classes.rb:55:1:56:3 | MyClassInGlobalScope | classes.rb:55:1:56:3 | MyClassInGlobalScope | -| modules.rb:1:1:2:3 | Empty | modules.rb:1:1:2:3 | Empty | -| modules.rb:1:1:122:1 | modules.rb | file://:0:0:0:0 | Object | -| modules.rb:4:1:24:3 | Foo | modules.rb:4:1:24:3 | Foo | -| modules.rb:5:3:14:5 | Bar | modules.rb:5:3:14:5 | Foo::Bar | -| modules.rb:6:5:7:7 | ClassInFooBar | modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | -| modules.rb:19:3:20:5 | ClassInFoo | modules.rb:19:3:20:5 | Foo::ClassInFoo | -| modules.rb:26:1:35:3 | Foo | modules.rb:4:1:24:3 | Foo | -| modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | -| modules.rb:37:1:46:3 | Bar | modules.rb:37:1:46:3 | Bar | -| modules.rb:48:1:57:3 | Bar | modules.rb:5:3:14:5 | Foo::Bar | -| modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | -| modules.rb:60:1:61:3 | MyModuleInGlobalScope | modules.rb:60:1:61:3 | MyModuleInGlobalScope | -| modules.rb:63:1:81:3 | Test | modules.rb:63:1:81:3 | Test | -| modules.rb:65:3:68:5 | Foo1 | modules.rb:65:3:68:5 | Test::Foo1 | -| modules.rb:66:5:67:7 | Bar | modules.rb:66:5:67:7 | Test::Foo1::Bar | -| modules.rb:70:3:74:5 | Foo2 | modules.rb:70:3:74:5 | Test::Foo2 | -| modules.rb:71:5:71:19 | Foo2 | modules.rb:71:5:71:19 | Test::Foo2::Foo2 | -| modules.rb:72:5:73:7 | Bar | modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | -| modules.rb:76:3:80:5 | Foo3 | modules.rb:76:3:80:5 | Test::Foo3 | -| modules.rb:78:5:79:7 | Bar | modules.rb:37:1:46:3 | Bar | -| modules.rb:83:1:86:3 | Other | modules.rb:83:1:86:3 | Other | -| modules.rb:84:3:85:5 | Foo1 | modules.rb:84:3:85:5 | Other::Foo1 | -| modules.rb:88:1:93:3 | IncludeTest | modules.rb:88:1:93:3 | IncludeTest | -| modules.rb:91:3:92:5 | Y | modules.rb:91:3:92:5 | Test::Foo1::Y | -| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:95:1:99:3 | IncludeTest2 | -| modules.rb:97:3:98:5 | Z | modules.rb:97:3:98:5 | Test::Foo1::Z | -| modules.rb:101:1:105:3 | PrependTest | modules.rb:101:1:105:3 | PrependTest | -| modules.rb:103:3:104:5 | Y | modules.rb:103:3:104:5 | Test::Foo2::Y | -| modules.rb:107:1:110:3 | MM | modules.rb:107:1:110:3 | MM | -| modules.rb:108:3:109:5 | MM | modules.rb:108:3:109:5 | MM::MM | -| modules.rb:112:1:113:3 | YY | modules.rb:112:1:113:3 | YY | -| modules.rb:115:1:118:3 | XX | modules.rb:115:1:118:3 | XX | -| modules.rb:116:7:117:9 | YY | modules.rb:116:7:117:9 | XX::YY | -| modules.rb:120:1:121:3 | Baz | modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | -| toplevel.rb:1:1:5:23 | toplevel.rb | file://:0:0:0:0 | Object | diff --git a/ql/test/library-tests/ast/modules/modules.ql b/ql/test/library-tests/ast/modules/modules.ql index 21adb23e633..9ed9c7b4fa2 100644 --- a/ql/test/library-tests/ast/modules/modules.ql +++ b/ql/test/library-tests/ast/modules/modules.ql @@ -23,5 +23,3 @@ query predicate classesInModules(ModuleDeclaration mod, ClassDeclaration klass, query predicate modulesInModules(ModuleDeclaration mod, ModuleDeclaration child, string name) { child = mod.getModule(name) } - -query predicate moduleTypes(ModuleBase def, Module type) { type = def.getModule() } diff --git a/ql/test/library-tests/modules/ancestors.expected b/ql/test/library-tests/modules/ancestors.expected new file mode 100644 index 00000000000..4d00bf4dd04 --- /dev/null +++ b/ql/test/library-tests/modules/ancestors.expected @@ -0,0 +1,96 @@ +#-----| Object +#-----| include -> Kernel +#-----| super -> BasicObject + +#-----| Class +#-----| super -> Module + +#-----| Kernel + +#-----| Module +#-----| super -> Object + +#-----| BasicObject + +hello.rb: +# 1| EnglishWords + +# 11| Greeting +#-----| super -> Object +#-----| include -> EnglishWords + +# 18| HelloWorld +#-----| super -> Greeting + +modules.rb: +# 1| Empty + +# 4| Foo + +# 37| Bar +#-----| super -> Object + +# 60| MyModuleInGlobalScope + +# 63| Test + +# 83| Other + +# 88| IncludeTest +#-----| include -> Test + +# 95| IncludeTest2 +#-----| include -> Test + +# 101| PrependTest +#-----| prepend -> Test + +# 107| MM + +# 112| YY +#-----| super -> Object + +# 115| XX + +# 5| Foo::Bar + +# 19| Foo::ClassInFoo +#-----| super -> Object + +# 30| Foo::ClassInAnotherDefinitionOfFoo +#-----| super -> Object + +# 65| Test::Foo1 + +# 70| Test::Foo2 + +# 76| Test::Foo3 + +# 84| Other::Foo1 + +# 116| XX::YY +#-----| super -> YY + +# 6| Foo::Bar::ClassInFooBar +#-----| super -> Object + +# 71| Test::Foo2::Foo2 + +# 108| MM::MM + +# 49| Foo::Bar::ClassInAnotherDefinitionOfFooBar +#-----| super -> Object + +# 66| Test::Foo1::Bar +#-----| super -> Object + +# 91| Test::Foo1::Y + +# 97| Test::Foo1::Z + +# 103| Test::Foo2::Y + +# 72| Test::Foo2::Foo2::Bar +#-----| super -> Object + +# 120| Test::Foo1::Bar::Baz diff --git a/ql/test/library-tests/modules/ancestors.ql b/ql/test/library-tests/modules/ancestors.ql new file mode 100644 index 00000000000..897ddf94f35 --- /dev/null +++ b/ql/test/library-tests/modules/ancestors.ql @@ -0,0 +1,21 @@ +/** + * @kind graph + * @id rb/test/ancestors + */ + +import ruby + +query predicate nodes(Module node, string key, string value) { + key = "semmle.label" and value = node.toString() +} + +query predicate edges(Module source, Module target, string key, string value) { + key = "semmle.label" and + ( + target = source.getSuperClass() and value = "super" + or + target = source.getAPrependedModule() and value = "prepend" + or + target = source.getAnIncludedModule() and value = "include" + ) +} diff --git a/ql/test/library-tests/modules/hello.rb b/ql/test/library-tests/modules/hello.rb new file mode 100644 index 00000000000..1cfa0f7e4f3 --- /dev/null +++ b/ql/test/library-tests/modules/hello.rb @@ -0,0 +1,22 @@ +module EnglishWords + def hello + return "hello" + end + def world + return "world" + end +end + + +class Greeting + include EnglishWords + def message + return hello + end +end + +class HelloWorld < Greeting + def message + return super + " " + world + "!" + end +end \ No newline at end of file diff --git a/ql/test/library-tests/modules/methods.expected b/ql/test/library-tests/modules/methods.expected new file mode 100644 index 00000000000..1a745517cfd --- /dev/null +++ b/ql/test/library-tests/modules/methods.expected @@ -0,0 +1,26 @@ +getMethod +| hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world | +| hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message | +| hello.rb:18:1:22:3 | HelloWorld | message | hello.rb:19:5:21:7 | message | +| modules.rb:4:1:24:3 | Foo | method_in_another_definition_of_foo | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | +| modules.rb:4:1:24:3 | Foo | method_in_foo | modules.rb:16:3:17:5 | method_in_foo | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_another_definition_of_foo_bar | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_foo_bar | modules.rb:9:5:10:7 | method_in_foo_bar | +| modules.rb:37:1:46:3 | Bar | method_a | modules.rb:38:3:39:5 | method_a | +| modules.rb:37:1:46:3 | Bar | method_b | modules.rb:41:3:42:5 | method_b | +lookupMethod +| hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world | +| hello.rb:11:1:16:3 | Greeting | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message | +| hello.rb:11:1:16:3 | Greeting | world | hello.rb:5:5:7:7 | world | +| hello.rb:18:1:22:3 | HelloWorld | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:18:1:22:3 | HelloWorld | message | hello.rb:19:5:21:7 | message | +| hello.rb:18:1:22:3 | HelloWorld | world | hello.rb:5:5:7:7 | world | +| modules.rb:4:1:24:3 | Foo | method_in_another_definition_of_foo | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | +| modules.rb:4:1:24:3 | Foo | method_in_foo | modules.rb:16:3:17:5 | method_in_foo | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_another_definition_of_foo_bar | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_foo_bar | modules.rb:9:5:10:7 | method_in_foo_bar | +| modules.rb:37:1:46:3 | Bar | method_a | modules.rb:38:3:39:5 | method_a | +| modules.rb:37:1:46:3 | Bar | method_b | modules.rb:41:3:42:5 | method_b | diff --git a/ql/test/library-tests/modules/methods.ql b/ql/test/library-tests/modules/methods.ql new file mode 100644 index 00000000000..323a4c61476 --- /dev/null +++ b/ql/test/library-tests/modules/methods.ql @@ -0,0 +1,6 @@ +import ruby +import codeql_ruby.ast.internal.Module as M + +query MethodBase getMethod(Module m, string name) { result = M::getMethod(m, name) } + +query MethodBase lookupMethod(Module m, string name) { result = M::lookupMethod(m, name) } diff --git a/ql/test/library-tests/modules/modules.expected b/ql/test/library-tests/modules/modules.expected new file mode 100644 index 00000000000..fc802f224d7 --- /dev/null +++ b/ql/test/library-tests/modules/modules.expected @@ -0,0 +1,100 @@ +getModule +| file://:0:0:0:0 | BasicObject | +| file://:0:0:0:0 | Class | +| file://:0:0:0:0 | Kernel | +| file://:0:0:0:0 | Module | +| file://:0:0:0:0 | Object | +| hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:11:1:16:3 | Greeting | +| hello.rb:18:1:22:3 | HelloWorld | +| modules.rb:1:1:2:3 | Empty | +| modules.rb:4:1:24:3 | Foo | +| modules.rb:5:3:14:5 | Foo::Bar | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | +| modules.rb:37:1:46:3 | Bar | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | +| modules.rb:63:1:81:3 | Test | +| modules.rb:65:3:68:5 | Test::Foo1 | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | +| modules.rb:70:3:74:5 | Test::Foo2 | +| modules.rb:71:5:71:19 | Test::Foo2::Foo2 | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | +| modules.rb:76:3:80:5 | Test::Foo3 | +| modules.rb:83:1:86:3 | Other | +| modules.rb:84:3:85:5 | Other::Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:91:3:92:5 | Test::Foo1::Y | +| modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:97:3:98:5 | Test::Foo1::Z | +| modules.rb:101:1:105:3 | PrependTest | +| modules.rb:103:3:104:5 | Test::Foo2::Y | +| modules.rb:107:1:110:3 | MM | +| modules.rb:108:3:109:5 | MM::MM | +| modules.rb:112:1:113:3 | YY | +| modules.rb:115:1:118:3 | XX | +| modules.rb:116:7:117:9 | XX::YY | +| modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | +getADeclaration +| file://:0:0:0:0 | Object | hello.rb:1:1:22:3 | hello.rb | +| file://:0:0:0:0 | Object | modules.rb:1:1:122:1 | modules.rb | +| hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:11:1:16:3 | Greeting | hello.rb:11:1:16:3 | Greeting | +| hello.rb:18:1:22:3 | HelloWorld | hello.rb:18:1:22:3 | HelloWorld | +| modules.rb:1:1:2:3 | Empty | modules.rb:1:1:2:3 | Empty | +| modules.rb:4:1:24:3 | Foo | modules.rb:4:1:24:3 | Foo | +| modules.rb:4:1:24:3 | Foo | modules.rb:26:1:35:3 | Foo | +| modules.rb:5:3:14:5 | Foo::Bar | modules.rb:5:3:14:5 | Bar | +| modules.rb:5:3:14:5 | Foo::Bar | modules.rb:48:1:57:3 | Bar | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | modules.rb:6:5:7:7 | ClassInFooBar | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | modules.rb:19:3:20:5 | ClassInFoo | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | +| modules.rb:37:1:46:3 | Bar | modules.rb:37:1:46:3 | Bar | +| modules.rb:37:1:46:3 | Bar | modules.rb:78:5:79:7 | Bar | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | modules.rb:60:1:61:3 | MyModuleInGlobalScope | +| modules.rb:63:1:81:3 | Test | modules.rb:63:1:81:3 | Test | +| modules.rb:65:3:68:5 | Test::Foo1 | modules.rb:65:3:68:5 | Foo1 | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | modules.rb:66:5:67:7 | Bar | +| modules.rb:70:3:74:5 | Test::Foo2 | modules.rb:70:3:74:5 | Foo2 | +| modules.rb:71:5:71:19 | Test::Foo2::Foo2 | modules.rb:71:5:71:19 | Foo2 | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | modules.rb:72:5:73:7 | Bar | +| modules.rb:76:3:80:5 | Test::Foo3 | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:83:1:86:3 | Other | modules.rb:83:1:86:3 | Other | +| modules.rb:84:3:85:5 | Other::Foo1 | modules.rb:84:3:85:5 | Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:91:3:92:5 | Test::Foo1::Y | modules.rb:91:3:92:5 | Y | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:97:3:98:5 | Test::Foo1::Z | modules.rb:97:3:98:5 | Z | +| modules.rb:101:1:105:3 | PrependTest | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:103:3:104:5 | Test::Foo2::Y | modules.rb:103:3:104:5 | Y | +| modules.rb:107:1:110:3 | MM | modules.rb:107:1:110:3 | MM | +| modules.rb:108:3:109:5 | MM::MM | modules.rb:108:3:109:5 | MM | +| modules.rb:112:1:113:3 | YY | modules.rb:112:1:113:3 | YY | +| modules.rb:115:1:118:3 | XX | modules.rb:115:1:118:3 | XX | +| modules.rb:116:7:117:9 | XX::YY | modules.rb:116:7:117:9 | YY | +| modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | modules.rb:120:1:121:3 | Baz | +getSuperClass +| file://:0:0:0:0 | Class | file://:0:0:0:0 | Module | +| file://:0:0:0:0 | Module | file://:0:0:0:0 | Object | +| file://:0:0:0:0 | Object | file://:0:0:0:0 | BasicObject | +| hello.rb:11:1:16:3 | Greeting | file://:0:0:0:0 | Object | +| hello.rb:18:1:22:3 | HelloWorld | hello.rb:11:1:16:3 | Greeting | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | file://:0:0:0:0 | Object | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | file://:0:0:0:0 | Object | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | file://:0:0:0:0 | Object | +| modules.rb:37:1:46:3 | Bar | file://:0:0:0:0 | Object | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | file://:0:0:0:0 | Object | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | file://:0:0:0:0 | Object | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | file://:0:0:0:0 | Object | +| modules.rb:112:1:113:3 | YY | file://:0:0:0:0 | Object | +| modules.rb:116:7:117:9 | XX::YY | modules.rb:112:1:113:3 | YY | +getAPrependedModule +| modules.rb:101:1:105:3 | PrependTest | modules.rb:63:1:81:3 | Test | +getAnIncludedModule +| file://:0:0:0:0 | Object | file://:0:0:0:0 | Kernel | +| hello.rb:11:1:16:3 | Greeting | hello.rb:1:1:8:3 | EnglishWords | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:63:1:81:3 | Test | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:63:1:81:3 | Test | diff --git a/ql/test/library-tests/modules/modules.ql b/ql/test/library-tests/modules/modules.ql new file mode 100644 index 00000000000..a29d2e93756 --- /dev/null +++ b/ql/test/library-tests/modules/modules.ql @@ -0,0 +1,11 @@ +import ruby + +query Module getModule() { any() } + +query ModuleBase getADeclaration(Module m) { result = m.getADeclaration() } + +query Module getSuperClass(Module m) { result = m.getSuperClass() } + +query Module getAPrependedModule(Module m) { result = m.getAPrependedModule() } + +query Module getAnIncludedModule(Module m) { result = m.getAnIncludedModule() } diff --git a/ql/test/library-tests/modules/modules.rb b/ql/test/library-tests/modules/modules.rb new file mode 100644 index 00000000000..8287b0a1bc4 --- /dev/null +++ b/ql/test/library-tests/modules/modules.rb @@ -0,0 +1,122 @@ +module Empty +end + +module Foo + module Bar + class ClassInFooBar + end + + def method_in_foo_bar + end + + puts 'module Foo::Bar' + $global_var = 0 + end + + def method_in_foo + end + + class ClassInFoo + end + + puts 'module Foo' + $global_var = 1 +end + +module Foo + def method_in_another_definition_of_foo + end + + class ClassInAnotherDefinitionOfFoo + end + + puts 'module Foo again' + $global_var = 2 +end + +module Bar + def method_a + end + + def method_b + end + + puts 'module Bar' + $global_var = 3 +end + +module Foo::Bar + class ClassInAnotherDefinitionOfFooBar + end + + def method_in_another_definition_of_foo_bar + end + + puts 'module Foo::Bar again' + $global_var = 4 +end + +# a module where the name is a scope resolution using the global scope +module ::MyModuleInGlobalScope +end + +module Test + + module Foo1 + class Foo1::Bar + end + end + + module Foo2 + module Foo2 end + class Foo2::Bar + end + end + + module Foo3 + Foo3 = Object + class Foo3::Bar + end + end +end + +module Other + module Foo1 + end +end + +module IncludeTest + include ::Test + Object.module_eval { prepend Other } + module Foo1::Y + end +end + +module IncludeTest2 + include Test + module Foo1::Z + end +end + +module PrependTest + prepend ::Test + module Foo2::Y + end +end + +module MM + module MM::MM + end +end + +class YY +end + +module XX + class YY < YY + end +end + +module Test::Foo1::Bar::Baz +end + diff --git a/ql/test/library-tests/modules/superclasses.expected b/ql/test/library-tests/modules/superclasses.expected new file mode 100644 index 00000000000..38c127ff92b --- /dev/null +++ b/ql/test/library-tests/modules/superclasses.expected @@ -0,0 +1,91 @@ +#-----| Object +#-----| -> BasicObject + +#-----| Class +#-----| -> Module + +#-----| Kernel + +#-----| Module +#-----| -> Object + +#-----| BasicObject + +hello.rb: +# 1| EnglishWords + +# 11| Greeting +#-----| -> Object + +# 18| HelloWorld +#-----| -> Greeting + +modules.rb: +# 1| Empty + +# 4| Foo + +# 37| Bar +#-----| -> Object + +# 60| MyModuleInGlobalScope + +# 63| Test + +# 83| Other + +# 88| IncludeTest + +# 95| IncludeTest2 + +# 101| PrependTest + +# 107| MM + +# 112| YY +#-----| -> Object + +# 115| XX + +# 5| Foo::Bar + +# 19| Foo::ClassInFoo +#-----| -> Object + +# 30| Foo::ClassInAnotherDefinitionOfFoo +#-----| -> Object + +# 65| Test::Foo1 + +# 70| Test::Foo2 + +# 76| Test::Foo3 + +# 84| Other::Foo1 + +# 116| XX::YY +#-----| -> YY + +# 6| Foo::Bar::ClassInFooBar +#-----| -> Object + +# 71| Test::Foo2::Foo2 + +# 108| MM::MM + +# 49| Foo::Bar::ClassInAnotherDefinitionOfFooBar +#-----| -> Object + +# 66| Test::Foo1::Bar +#-----| -> Object + +# 91| Test::Foo1::Y + +# 97| Test::Foo1::Z + +# 103| Test::Foo2::Y + +# 72| Test::Foo2::Foo2::Bar +#-----| -> Object + +# 120| Test::Foo1::Bar::Baz diff --git a/ql/test/library-tests/modules/superclasses.ql b/ql/test/library-tests/modules/superclasses.ql new file mode 100644 index 00000000000..d2141aa38ff --- /dev/null +++ b/ql/test/library-tests/modules/superclasses.ql @@ -0,0 +1,12 @@ +/** + * @kind graph + * @id rb/test/supertypes + */ + +import ruby + +query predicate nodes(Module node, string key, string value) { + key = "semmle.label" and value = node.toString() +} + +query predicate edges(Module source, Module target) { target = source.getSuperClass() }