@@ -253,6 +253,11 @@ module API {
253253 /** A use of an API member at the node `nd`. */
254254 MkUse ( DataFlow:: Node nd ) { use ( _, _, nd ) }
255255
256+ private string resolveTopLevel ( ConstantReadAccess read ) {
257+ TResolved ( result ) = resolveScopeExpr ( read ) and
258+ not result .matches ( "%::%" )
259+ }
260+
256261 /**
257262 * Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
258263 * `lbl` in the API graph.
@@ -265,58 +270,63 @@ module API {
265270 lbl = Label:: member ( read .getName ( ) ) and
266271 read = access .getExpr ( )
267272 |
268- TResolved ( name ) = resolveScopeExpr ( read ) and
269- not name .matches ( "%::%" )
273+ name = resolveTopLevel ( read )
270274 or
271275 name = read .getName ( ) and
272- not exists ( resolveScopeExpr ( read ) ) and
276+ not exists ( resolveTopLevel ( read ) ) and
273277 not exists ( read .getScopeExpr ( ) )
274278 )
275279 or
276- exists ( DataFlow :: LocalSourceNode src , DataFlow :: LocalSourceNode pred |
280+ exists ( ExprCfgNode node |
277281 // First, we find a predecessor of the node `ref` that we want to determine. The predecessor
278282 // is any node that is a type-tracked use of a data flow node (`src`), which is itself a
279283 // reference to the API node `base`. Thus, `pred` and `src` both represent uses of `base`.
280284 //
281285 // Once we have identified the predecessor, we define its relation to the successor `ref` as
282286 // well as the label on the edge from `pred` to `ref`. This label describes the nature of
283287 // the relationship between `pred` and `ref`.
284- use ( base , src ) and pred = trackUseNode ( src )
288+ useExpr ( node , base )
285289 |
286290 // // Referring to an attribute on a node that is a use of `base`:
287291 // pred = `Rails` part of `Rails::Whatever`
288292 // lbl = `Whatever`
289293 // ref = `Rails::Whatever`
290- exists ( ExprNodes:: ConstantAccessCfgNode c , DataFlow:: Node node , ConstantReadAccess read |
291- not exists ( resolveScopeExpr ( read ) ) and
292- pred .flowsTo ( node ) and
293- node .asExpr ( ) = c .getScopeExpr ( ) and
294+ exists ( ExprNodes:: ConstantAccessCfgNode c , ConstantReadAccess read |
295+ not exists ( resolveTopLevel ( read ) ) and
296+ node = c .getScopeExpr ( ) and
294297 lbl = Label:: member ( read .getName ( ) ) and
295298 ref .asExpr ( ) = c and
296299 read = c .getExpr ( )
297300 )
298301 or
299302 // Calling a method on a node that is a use of `base`
300- exists ( ExprNodes:: MethodCallCfgNode call , DataFlow:: Node node , string name |
301- pred .flowsTo ( node ) and
302- node .asExpr ( ) = call .getReceiver ( ) and
303+ exists ( ExprNodes:: MethodCallCfgNode call , string name |
304+ node = call .getReceiver ( ) and
303305 name = call .getExpr ( ) .getMethodName ( ) and
304306 lbl = Label:: return ( name ) and
305307 name != "new" and
306308 ref .asExpr ( ) = call
307309 )
308310 or
309311 // Calling the `new` method on a node that is a use of `base`, which creates a new instance
310- exists ( ExprNodes:: MethodCallCfgNode call , DataFlow:: Node node |
311- pred .flowsTo ( node ) and
312- node .asExpr ( ) = call .getReceiver ( ) and
312+ exists ( ExprNodes:: MethodCallCfgNode call |
313+ node = call .getReceiver ( ) and
313314 lbl = Label:: instance ( ) and
314315 call .getExpr ( ) .getMethodName ( ) = "new" and
315316 ref .asExpr ( ) = call
316317 )
317318 )
318319 }
319320
321+ pragma [ nomagic]
322+ private predicate useExpr ( ExprCfgNode node , TApiNode base ) {
323+ exists ( DataFlow:: LocalSourceNode src , DataFlow:: LocalSourceNode pred |
324+ use ( base , src ) and
325+ pred = trackUseNode ( src ) and
326+ pred .flowsTo ( any ( DataFlow:: ExprNode n | n .getExprNode ( ) = node ) )
327+ )
328+ }
329+
320330 /**
321331 * Holds if `ref` is a use of node `nd`.
322332 */
@@ -328,7 +338,10 @@ module API {
328338 *
329339 * The flow from `src` to that node may be inter-procedural.
330340 */
331- private DataFlow:: LocalSourceNode trackUseNode ( DataFlow:: LocalSourceNode src , TypeTracker t ) {
341+ private DataFlow:: LocalSourceNode trackUseNode ( DataFlow:: Node src , TypeTracker t ) {
342+ // Declaring `src` to be a `SourceNode` currently causes a redundant check in the
343+ // recursive case, so instead we check it explicitly here.
344+ src instanceof DataFlow:: LocalSourceNode and
332345 t .start ( ) and
333346 use ( _, src ) and
334347 result = src
0 commit comments