@@ -120,7 +120,7 @@ class Module_ extends FileOrModule, TModule {
120120 }
121121}
122122
123- private predicate resolveQualifiedName ( Import imp , ContainerOrModule m , int i ) {
123+ private predicate resolveImportQualifier ( Import imp , ContainerOrModule m , int i ) {
124124 not m = TFile ( any ( File f | f .getExtension ( ) = "ql" ) ) and
125125 exists ( string q | q = imp .getQualifiedName ( i ) |
126126 i = 0 and
@@ -139,7 +139,6 @@ private predicate resolveQualifiedName(Import imp, ContainerOrModule m, int i) {
139139 m = TFolder ( c )
140140 )
141141 or
142- q = imp .getQualifiedName ( i ) and
143142 exists ( ContainerOrModule container | container = getEnclosingModule ( imp ) .getEnclosing + ( ) |
144143 definesModule ( container , q , m , _) and
145144 (
@@ -154,25 +153,18 @@ private predicate resolveQualifiedName(Import imp, ContainerOrModule m, int i) {
154153 )
155154 or
156155 exists ( Folder_ mid |
157- resolveQualifiedName ( imp , mid , i - 1 ) and
156+ resolveImportQualifier ( imp , mid , i - 1 ) and
158157 m .getEnclosing ( ) = mid and
159158 q = m .getName ( )
160159 )
161160 )
162161}
163162
164- private predicate resolveSelectionName ( Import imp , ContainerOrModule m , int i ) {
163+ private predicate resolveImportQualifier ( Import imp , ContainerOrModule m ) {
165164 ( m .( File_ ) .getFile ( ) .getExtension ( ) = "qll" or not m instanceof File_ ) and
166165 exists ( int last |
167- resolveQualifiedName ( imp , m , last ) and
168- last = count ( int j | exists ( imp .getQualifiedName ( j ) ) ) - 1
169- ) and
170- not m instanceof Folder_ and
171- i = - 1
172- or
173- exists ( ContainerOrModule mid |
174- resolveSelectionName ( imp , mid , i - 1 ) and
175- definesModule ( mid , imp .getSelectionName ( i ) , m , true )
166+ last = max ( int j | exists ( imp .getQualifiedName ( j ) ) ) and
167+ resolveImportQualifier ( imp , m , last )
176168 )
177169}
178170
@@ -202,18 +194,10 @@ private module Cached {
202194 TModule ( Module m )
203195 }
204196
205- /** Holds if import statement `imp` resolves to `m`. */
206- cached
207- predicate resolve ( Import imp , FileOrModule m ) {
208- exists ( int last |
209- resolveSelectionName ( imp , m , last ) and
210- last = count ( int j | exists ( imp .getSelectionName ( j ) ) ) - 1
211- )
212- }
213-
214197 /** Holds if module expression `me` resolves to `m`. */
215198 cached
216199 predicate resolveModuleRef ( TypeRef me , FileOrModule m ) {
200+ // base case, resolving a typeref without a qualifier (only moduleexpr can have qualifiers)
217201 not m = TFile ( any ( File f | f .getExtension ( ) = "ql" ) ) and
218202 not exists ( me .( ModuleExpr ) .getQualifier ( ) ) and
219203 exists ( ContainerOrModule enclosing , string name | resolveModuleRefHelper ( me , enclosing , name ) |
@@ -234,17 +218,69 @@ private module Cached {
234218 )
235219 )
236220 or
221+ // recursive case, resolving the qualifier.
237222 exists ( FileOrModule mid |
238223 resolveModuleRef ( me .( ModuleExpr ) .getQualifier ( ) , mid ) and
239224 definesModule ( mid , me .( ModuleExpr ) .getName ( ) , m , true )
240225 )
241226 }
242227
228+ /**
229+ * Gets the module that the lookup for `ref` should start at.
230+ * For most type references this will simply be the enclosing module.
231+ *
232+ * However, for module expressions inside imports, this will be determined
233+ * by the qualified name of the import (everything before the module expression).
234+ *
235+ * E.g. for an import `import foo.Bar::Baz`, the qualified name of the import is "foo",
236+ * and the module expression is "Bar::Baz".
237+ */
238+ private ContainerOrModule getStartModule ( TypeRef ref ) {
239+ if isInsideImport ( ref )
240+ then
241+ exists ( Import i | ref = i .getModuleExpr ( ) .getQualifier * ( ) |
242+ resolveImportQualifier ( i , result )
243+ or
244+ not exists ( i .getQualifiedName ( _) ) and
245+ (
246+ result = getEnclosingModule ( ref )
247+ or
248+ exists ( YAML:: QLPack pack |
249+ pack .getAFileInPack ( ) = ref .getLocation ( ) .getFile ( ) and
250+ result = TFolder ( pack .getADependency * ( ) .getFile ( ) .getParentContainer ( ) )
251+ )
252+ )
253+ )
254+ else result = getEnclosingModule ( ref )
255+ }
256+
257+ /** Holds of `me` is part of an import statement. */
258+ pragma [ noinline]
259+ private predicate isInsideImport ( ModuleExpr me ) {
260+ me = any ( Import i ) .getModuleExpr ( ) .getQualifier * ( )
261+ }
262+
263+ /** Gets the enclosing module/container, but stops after the first folder (so no folder -> folder step). */
264+ private ContainerOrModule getEnclosingModuleNoFolderStep ( ContainerOrModule m ) {
265+ result = m .getEnclosing ( ) and
266+ not (
267+ result instanceof Folder_ and
268+ m instanceof Folder_
269+ )
270+ }
271+
243272 pragma [ noinline]
244273 private predicate resolveModuleRefHelper ( TypeRef me , ContainerOrModule enclosing , string name ) {
245- enclosing = getEnclosingModule ( me ) .getEnclosing * ( ) and
274+ // The scope is all enclosing modules, the immidiatly containing folder, not the parent folders.
275+ enclosing = getEnclosingModuleNoFolderStep * ( getStartModule ( me ) ) and
246276 name = [ me .( ModuleExpr ) .getName ( ) , me .( TypeExpr ) .getClassName ( ) ] and
247- ( not me instanceof ModuleExpr or not enclosing instanceof Folder_ ) // module expressions are not imports, so they can't resolve to a file (which is contained in a folder).
277+ not exists ( me .( ModuleExpr ) .getQualifier ( ) ) and
278+ (
279+ // module expressions are not imports, so they can't resolve to a file (which is contained in a folder).
280+ ( not me instanceof ModuleExpr or not enclosing instanceof Folder_ )
281+ or
282+ isInsideImport ( me ) // unless it actually is an import.
283+ )
248284 }
249285}
250286
@@ -296,7 +332,7 @@ private predicate definesModule(
296332 // import X
297333 exists ( Import imp , ContainerOrModule m0 |
298334 container = getEnclosingModule ( imp ) and
299- resolve ( imp , m0 ) and
335+ resolveModuleRef ( imp . getModuleExpr ( ) , m0 ) and
300336 not exists ( imp .importedAs ( ) ) and
301337 definesModule ( m0 , name , m , true ) and
302338 public = getPublicBool ( imp )
@@ -306,7 +342,7 @@ private predicate definesModule(
306342 exists ( Import imp |
307343 container = getEnclosingModule ( imp ) and
308344 name = imp .importedAs ( ) and
309- resolve ( imp , m ) and
345+ resolveModuleRef ( imp . getModuleExpr ( ) , m ) and
310346 public = getPublicBool ( imp )
311347 )
312348 or
@@ -320,24 +356,6 @@ private predicate definesModule(
320356}
321357
322358module ModConsistency {
323- query predicate noResolve ( Import imp ) {
324- not resolve ( imp , _) and
325- not imp .getLocation ( )
326- .getFile ( )
327- .getAbsolutePath ( )
328- .regexpMatch ( ".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*" )
329- }
330-
331- query predicate multipleResolve ( Import imp , int c , ContainerOrModule m ) {
332- c = strictcount ( ContainerOrModule m0 | resolve ( imp , m0 ) ) and
333- c > 1 and
334- resolve ( imp , m ) and
335- not imp .getLocation ( )
336- .getFile ( )
337- .getAbsolutePath ( )
338- .regexpMatch ( ".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*" )
339- }
340-
341359 // This can happen with parameterized modules.
342360 /*
343361 * query predicate multipleResolveModuleRef(ModuleExpr me, int c, ContainerOrModule m) {
@@ -362,4 +380,16 @@ module ModConsistency {
362380 mod instanceof ModuleExpr and
363381 count ( mod .( ModuleExpr ) .getName ( ) ) >= 2
364382 }
383+
384+ query predicate uniqueResolve ( Import i ) {
385+ count ( FileOrModule mod |
386+ mod = i .getResolvedModule ( ) and
387+ // don't count the alias reference, only the resolved.
388+ not exists ( mod .asModule ( ) .getAlias ( ) )
389+ ) >= 2 and
390+ // paramerized modules are not treated nicely, so we ignore them here.
391+ not i .getResolvedModule ( ) .getEnclosing * ( ) .asModule ( ) .hasParameter ( _, _, _)
392+ }
393+
394+ query predicate noResolve ( Import i ) { not exists ( i .getResolvedModule ( ) ) }
365395}
0 commit comments