@@ -3,6 +3,7 @@ private import codeql.ruby.CFG
33private import internal.AST
44private import internal.Module
55private import internal.TreeSitter
6+ private import internal.Scope
67
78/**
89 * A representation of a run-time `module` or `class` value.
@@ -23,6 +24,22 @@ class Module extends TModule {
2324 /** Gets an `include`d module. */
2425 Module getAnIncludedModule ( ) { result = getAnIncludedModule ( this ) }
2526
27+ /** Gets the super class or an included or prepended module. */
28+ Module getAnImmediateAncestor ( ) {
29+ result = [ this .getSuperClass ( ) , this .getAPrependedModule ( ) , this .getAnIncludedModule ( ) ]
30+ }
31+
32+ /** Gets a direct subclass or module including or prepending this one. */
33+ Module getAnImmediateDescendent ( ) { this = result .getAnImmediateAncestor ( ) }
34+
35+ /** Gets a module that is transitively subclassed, included, or prepended by this module. */
36+ pragma [ inline]
37+ Module getAnAncestor ( ) { result = this .getAnImmediateAncestor * ( ) }
38+
39+ /** Gets a module that transitively subclasses, includes, or prepends this module. */
40+ pragma [ inline]
41+ Module getADescendent ( ) { result = this .getAnImmediateDescendent * ( ) }
42+
2643 /** Holds if this module is a class. */
2744 pragma [ noinline]
2845 predicate isClass ( ) {
@@ -63,6 +80,99 @@ class Module extends TModule {
6380 loc .getStartColumn ( )
6481 )
6582 }
83+
84+ /** Gets a constant or `self` access that refers to this module. */
85+ private Expr getAnImmediateReferenceBase ( ) {
86+ resolveConstantReadAccess ( result ) = this
87+ or
88+ result .( SelfVariableAccess ) .getVariable ( ) = this .getADeclaration ( ) .getModuleSelfVariable ( )
89+ }
90+
91+ /** Gets a singleton class that augments this module object. */
92+ SingletonClass getASingletonClass ( ) { result .getValue ( ) = this .getAnImmediateReferenceBase ( ) }
93+
94+ /**
95+ * Gets a singleton method on this module, either declared as a singleton method
96+ * or an instance method on a singleton class.
97+ *
98+ * Does not take inheritance into account.
99+ */
100+ MethodBase getAnOwnSingletonMethod ( ) {
101+ result .( SingletonMethod ) .getObject ( ) = this .getAnImmediateReferenceBase ( )
102+ or
103+ result = this .getASingletonClass ( ) .getAMethod ( ) .( Method )
104+ }
105+
106+ /**
107+ * Gets an instance method named `name` declared in this module.
108+ *
109+ * Does not take inheritance into account.
110+ */
111+ Method getOwnInstanceMethod ( string name ) { result = this .getADeclaration ( ) .getMethod ( name ) }
112+
113+ /**
114+ * Gets an instance method declared in this module.
115+ *
116+ * Does not take inheritance into account.
117+ */
118+ Method getAnOwnInstanceMethod ( ) { result = this .getADeclaration ( ) .getMethod ( _) }
119+
120+ /**
121+ * Gets the instance method named `name` available in this module, including methods inherited
122+ * from ancestors.
123+ */
124+ Method getInstanceMethod ( string name ) { result = lookupMethod ( this , name ) }
125+
126+ /**
127+ * Gets an instance method available in this module, including methods inherited
128+ * from ancestors.
129+ */
130+ Method getAnInstanceMethod ( ) { result = lookupMethod ( this , _) }
131+
132+ /** Gets a constant or `self` access that refers to this module. */
133+ Expr getAnImmediateReference ( ) {
134+ result = this .getAnImmediateReferenceBase ( )
135+ or
136+ result .( SelfVariableAccess ) .getVariable ( ) .getDeclaringScope ( ) = this .getAnOwnSingletonMethod ( )
137+ }
138+
139+ pragma [ nomagic]
140+ private string getEnclosingModuleName ( ) {
141+ exists ( string qname |
142+ qname = this .getQualifiedName ( ) and
143+ result = qname .regexpReplaceAll ( "::[^:]*$" , "" ) and
144+ qname != result
145+ )
146+ }
147+
148+ pragma [ nomagic]
149+ private string getOwnModuleName ( ) {
150+ result = this .getQualifiedName ( ) .regexpReplaceAll ( "^.*::" , "" )
151+ }
152+
153+ /**
154+ * Gets the enclosing module, as it appears in the qualified name of this module.
155+ *
156+ * For example, the parent module of `A::B` is `A`, and `A` itself has no parent module.
157+ */
158+ pragma [ nomagic]
159+ Module getParentModule ( ) { result .getQualifiedName ( ) = this .getEnclosingModuleName ( ) }
160+
161+ /**
162+ * Gets a module named `name` declared inside this one (not aliased), provided
163+ * that such a module is defined or reopened in the current codebase.
164+ *
165+ * For example, for `A::B` the nested module named `C` would be `A::B::C`.
166+ *
167+ * Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
168+ * module whose qualified name is not `A::B::C`, then it will not be found by
169+ * this predicate.
170+ */
171+ pragma [ nomagic]
172+ Module getNestedModule ( string name ) {
173+ result .getParentModule ( ) = this and
174+ result .getOwnModuleName ( ) = name
175+ }
66176}
67177
68178/**
@@ -141,6 +251,46 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
141251
142252 /** Gets the representation of the run-time value of this module or class. */
143253 Module getModule ( ) { none ( ) }
254+
255+ /**
256+ * Gets the `self` variable in the module-level scope.
257+ *
258+ * Does not include the `self` variable from any of the methods in the module.
259+ */
260+ SelfVariable getModuleSelfVariable ( ) { result .getDeclaringScope ( ) = this }
261+
262+ /** Gets the nearest enclosing `Namespace` or `Toplevel`, possibly this module itself. */
263+ Namespace getNamespaceOrToplevel ( ) {
264+ result = this
265+ or
266+ not this instanceof Namespace and
267+ result = this .getEnclosingModule ( ) .getNamespaceOrToplevel ( )
268+ }
269+
270+ /**
271+ * Gets an expression denoting the super class or an included or prepended module.
272+ *
273+ * For example, `C` is an ancestor expression of `M` in each of the following examples:
274+ * ```rb
275+ * class M < C
276+ * end
277+ *
278+ * module M
279+ * include C
280+ * prepend C
281+ * end
282+ * ```
283+ */
284+ Expr getAnAncestorExpr ( ) {
285+ exists ( MethodCall call |
286+ call .getReceiver ( ) .( SelfVariableAccess ) .getVariable ( ) = this .getModuleSelfVariable ( ) and
287+ call .getMethodName ( ) = [ "include" , "prepend" ] and
288+ result = call .getArgument ( 0 ) and
289+ scopeOfInclSynth ( call ) = this // only permit calls directly in the module scope, not in a block
290+ )
291+ or
292+ result = this .( ClassDeclaration ) .getSuperclassExpr ( )
293+ }
144294}
145295
146296/**
0 commit comments