@@ -16,19 +16,22 @@ module Cached {
1616 TResolved ( string qName ) {
1717 qName = builtin ( )
1818 or
19- qName = constantDefinition ( _)
19+ qName = namespaceDeclaration ( _)
2020 } or
21- TUnresolved ( Namespace n ) { not exists ( constantDefinition ( n ) ) }
21+ TUnresolved ( Namespace n ) { not exists ( namespaceDeclaration ( n ) ) }
2222
2323 cached
24- string constantDefinition ( ConstantWriteAccess n ) {
24+ string namespaceDeclaration ( Namespace n ) {
2525 isToplevel ( n ) and result = n .getName ( )
2626 or
2727 not isToplevel ( n ) and
2828 not exists ( n .getScopeExpr ( ) ) and
29- result = scopeAppend ( constantDefinition ( n .getEnclosingModule ( ) ) , n .getName ( ) )
29+ result = scopeAppend ( namespaceDeclaration ( n .getEnclosingModule ( ) ) , n .getName ( ) )
3030 or
31- result = scopeAppend ( resolveScopeExpr ( n .getScopeExpr ( ) ) , n .getName ( ) )
31+ exists ( string container |
32+ TResolved ( container ) = resolveScopeExpr ( n .getScopeExpr ( ) ) and
33+ result = scopeAppend ( container , n .getName ( ) )
34+ )
3235 }
3336}
3437
@@ -48,36 +51,47 @@ private predicate isDefinedConstant(string qualifiedModuleName) {
4851}
4952
5053/**
51- * Resolve a scope expression
54+ * Resolve constant read access (typically a scope expression) to a qualified module name.
55+ * `resolveScopeExpr/1` picks the best (lowest priority number) result of
56+ * `resolveScopeExpr/2` that resolves to a constant definition. If the constant
57+ * definition is a Namespace then it is returned, if it's a constant assignment then
58+ * the right-hand side of the assignment is resolved.
5259 */
53- private string resolveScopeExpr ( ConstantReadAccess r ) {
54- exists ( string container |
55- container =
56- min ( string c , int p |
57- isDefinedConstant ( c ) and
58- c = resolveScopeExpr ( r , p )
60+ private TResolved resolveScopeExpr ( ConstantReadAccess r ) {
61+ exists ( string qname |
62+ qname =
63+ min ( string qn , int p |
64+ isDefinedConstant ( qn ) and
65+ qn = resolveScopeExpr ( r , p )
5966 |
60- c order by p
67+ qn order by p
6168 )
6269 |
63- result = container and
64- container = [ builtin ( ) , constantDefinition ( any ( Namespace x ) ) ]
70+ result = TResolved ( qname )
6571 or
6672 exists ( ConstantAssignment a |
67- container = constantDefinition ( a ) and
73+ qname = constantDefinition0 ( a ) and
6874 result = resolveScopeExpr ( a .getParent ( ) .( Assignment ) .getRightOperand ( ) )
6975 )
7076 )
7177}
7278
73- private int maxDepth ( ) { result = max ( ConstantAccess c | | count ( c . getEnclosingModule + ( ) ) ) }
79+ private int maxDepth ( ) { result = 1 + max ( int level | exists ( enclosing ( _ , level ) ) ) }
7480
7581private ModuleBase enclosing ( ModuleBase m , int level ) {
7682 result = m and level = 0
7783 or
7884 result = enclosing ( m .getEnclosingModule ( ) , level - 1 )
7985}
8086
87+ /**
88+ * Resolve constant read access (typically a scope expression) to a qualified name. The
89+ * `priority` value indicates the precedence of the solution with respect to the lookup order.
90+ * A constant name without scope specifier is resolved against its enclosing modules (inner-most first);
91+ * if the constant is not found in any of the enclosing modules, then the constant will be resolved
92+ * with respect to the ancestors (prepends, includes, super classes, and their ancestors) of the
93+ * directly enclosing module.
94+ */
8195private string resolveScopeExpr ( ConstantReadAccess c , int priority ) {
8296 c .hasGlobalScope ( ) and result = c .getName ( ) and priority = 0
8397 or
@@ -110,6 +124,12 @@ private string qualifiedModuleName(ModuleBase m) {
110124 result = constantDefinition0 ( m )
111125}
112126
127+ /**
128+ * Get a qualified name for a constant definition. May return multiple qualified
129+ * names because we over-approximate when resolving scope resolutions and ignore
130+ * lookup order precedence. Taking lookup order into account here would lead to
131+ * non-monotonic recursion.
132+ */
113133private string constantDefinition0 ( ConstantWriteAccess c ) {
114134 c .hasGlobalScope ( ) and result = c .getName ( )
115135 or
@@ -122,6 +142,15 @@ private string constantDefinition0(ConstantWriteAccess c) {
122142 )
123143}
124144
145+ /**
146+ * The qualified names of the ancestors of a class/module. The ancestors should be an ordered list
147+ * of the ancestores of `prepend`ed modules, the module itself , the ancestors or `include`d modules
148+ * and the ancestors of the super class. The priority value only distinguishes the kind of ancestor,
149+ * it does not order the ancestors within a group of the same kind. This is an over-approximation, however,
150+ * computing the precise order is tricky because it depends on the evaluation/file loading order.
151+ */
152+ // TODO: the order of super classes can be determined more precisely even without knowing the evaluation
153+ // order, so we should be able to make this more precise.
125154private string ancestors ( string qname , int priority ) {
126155 result = ancestors ( prepends ( qname ) , _) and priority = 0
127156 or
0 commit comments