@@ -99,9 +99,7 @@ module API {
9999 */
100100 pragma [ inline]
101101 DataFlow:: Node getAValueReachableFromSource ( ) {
102- exists ( DataFlow:: LocalSourceNode src | Impl:: use ( this , src ) |
103- Impl:: trackUseNode ( src ) .flowsTo ( result )
104- )
102+ result = getAValueReachableFromSourceInline ( this )
105103 }
106104
107105 /**
@@ -121,7 +119,19 @@ module API {
121119 * end
122120 * ```
123121 */
124- DataFlow:: LocalSourceNode asSource ( ) { Impl:: use ( this , result ) }
122+ pragma [ inline]
123+ DataFlow:: LocalSourceNode asSource ( ) { result = pragma [ only_bind_out ] ( this ) .asSourceInternal ( ) }
124+
125+ /**
126+ * INTERNAL USE ONLY.
127+ *
128+ * Same as `asSource()` but without join-order hints.
129+ */
130+ cached
131+ DataFlow:: LocalSourceNode asSourceInternal ( ) {
132+ Impl:: forceCachingInSameStage ( ) and
133+ Impl:: use ( this , result )
134+ }
125135
126136 /**
127137 * Gets a data-flow node where this value leaves the current codebase and flows into an
@@ -167,6 +177,7 @@ module API {
167177 /**
168178 * Gets a call to a method on the receiver represented by this API component.
169179 */
180+ pragma [ inline]
170181 DataFlow:: CallNode getAMethodCall ( string method ) { result = this .getReturn ( method ) .asSource ( ) }
171182
172183 /**
@@ -177,15 +188,29 @@ module API {
177188 * - A submodule of a module
178189 * - An attribute of an object
179190 */
180- bindingset [ m]
181- bindingset [ result ]
182- Node getMember ( string m ) { result = this .getASuccessor ( Label:: member ( m ) ) }
191+ pragma [ inline]
192+ Node getMember ( string m ) { result = pragma [ only_bind_out ] ( this ) .getMemberInternal ( m ) }
193+
194+ /**
195+ * INTERNAL USE ONLY.
196+ *
197+ * Same as `getMember` but without join-order hints.
198+ */
199+ cached
200+ Node getMemberInternal ( string m ) {
201+ Impl:: forceCachingInSameStage ( ) and
202+ result = this .getASuccessor ( Label:: member ( m ) )
203+ }
183204
184205 /**
185206 * Gets a node representing a member of this API component where the name of the member may
186207 * or may not be known statically.
187208 */
188- Node getAMember ( ) { result = this .getASuccessor ( Label:: member ( _) ) }
209+ cached
210+ Node getAMember ( ) {
211+ Impl:: forceCachingInSameStage ( ) and
212+ result = this .getASuccessor ( Label:: member ( _) )
213+ }
189214
190215 /**
191216 * Gets a node representing an instance of this API component, that is, an object whose
@@ -198,41 +223,75 @@ module API {
198223 * This predicate may have multiple results when there are multiple constructor calls invoking this API component.
199224 * Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls.
200225 */
226+ pragma [ inline]
201227 Node getInstance ( ) { result = this .getASubclass ( ) .getReturn ( "new" ) }
202228
203229 /**
204230 * Gets a node representing a call to `method` on the receiver represented by this node.
205231 */
232+ pragma [ inline]
206233 MethodAccessNode getMethod ( string method ) {
234+ result = pragma [ only_bind_out ] ( this ) .getMethodInternal ( method )
235+ }
236+
237+ /**
238+ * INTERNAL USE ONLY.
239+ *
240+ * Same as `getMethod` but without join-order hints.
241+ */
242+ cached
243+ MethodAccessNode getMethodInternal ( string method ) {
244+ Impl:: forceCachingInSameStage ( ) and
207245 result = this .getASubclass ( ) .getASuccessor ( Label:: method ( method ) )
208246 }
209247
210248 /**
211249 * Gets a node representing the result of this call.
212250 */
213- Node getReturn ( ) { result = this .getASuccessor ( Label:: return ( ) ) }
251+ pragma [ inline]
252+ Node getReturn ( ) { result = pragma [ only_bind_out ] ( this ) .getReturnInternal ( ) }
253+
254+ /**
255+ * INTERNAL USE ONLY.
256+ *
257+ * Same as `getReturn()` but without join-order hints.
258+ */
259+ cached
260+ Node getReturnInternal ( ) {
261+ Impl:: forceCachingInSameStage ( ) and result = this .getASuccessor ( Label:: return ( ) )
262+ }
214263
215264 /**
216265 * Gets a node representing the result of calling a method on the receiver represented by this node.
217266 */
267+ pragma [ inline]
218268 Node getReturn ( string method ) { result = this .getMethod ( method ) .getReturn ( ) }
219269
220270 /** Gets an API node representing the `n`th positional parameter. */
221- pragma [ nomagic]
222- Node getParameter ( int n ) { result = this .getASuccessor ( Label:: parameter ( n ) ) }
271+ cached
272+ Node getParameter ( int n ) {
273+ Impl:: forceCachingInSameStage ( ) and
274+ result = this .getASuccessor ( Label:: parameter ( n ) )
275+ }
223276
224277 /** Gets an API node representing the given keyword parameter. */
225- pragma [ nomagic ]
278+ cached
226279 Node getKeywordParameter ( string name ) {
280+ Impl:: forceCachingInSameStage ( ) and
227281 result = this .getASuccessor ( Label:: keywordParameter ( name ) )
228282 }
229283
230284 /** Gets an API node representing the block parameter. */
231- Node getBlock ( ) { result = this .getASuccessor ( Label:: blockParameter ( ) ) }
285+ cached
286+ Node getBlock ( ) {
287+ Impl:: forceCachingInSameStage ( ) and
288+ result = this .getASuccessor ( Label:: blockParameter ( ) )
289+ }
232290
233291 /**
234292 * Gets a `new` call to the function represented by this API component.
235293 */
294+ pragma [ inline]
236295 DataFlow:: ExprNode getAnInstantiation ( ) { result = this .getInstance ( ) .asSource ( ) }
237296
238297 /**
@@ -255,12 +314,17 @@ module API {
255314 * ```
256315 * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only.
257316 */
258- Node getAnImmediateSubclass ( ) { result = this .getASuccessor ( Label:: subclass ( ) ) }
317+ cached
318+ Node getAnImmediateSubclass ( ) {
319+ Impl:: forceCachingInSameStage ( ) and result = this .getASuccessor ( Label:: subclass ( ) )
320+ }
259321
260322 /**
261323 * Gets a node representing the `content` stored on the base object.
262324 */
325+ cached
263326 Node getContent ( DataFlow:: Content content ) {
327+ Impl:: forceCachingInSameStage ( ) and
264328 result = this .getASuccessor ( Label:: content ( content ) )
265329 }
266330
@@ -274,10 +338,16 @@ module API {
274338 }
275339
276340 /** Gets a node representing the instance field of the given `name`, which must include the `@` character. */
277- Node getField ( string name ) { result = this .getContent ( DataFlowPrivate:: TFieldContent ( name ) ) }
341+ cached
342+ Node getField ( string name ) {
343+ Impl:: forceCachingInSameStage ( ) and
344+ result = this .getContent ( DataFlowPrivate:: TFieldContent ( name ) )
345+ }
278346
279347 /** Gets a node representing an element of this collection (known or unknown). */
348+ cached
280349 Node getAnElement ( ) {
350+ Impl:: forceCachingInSameStage ( ) and
281351 result = this .getContents ( any ( DataFlow:: ContentSet set | set .isAnyElement ( ) ) )
282352 }
283353
@@ -363,6 +433,16 @@ module API {
363433 int getDepth ( ) { result = Impl:: distanceFromRoot ( this ) }
364434 }
365435
436+ bindingset [ node]
437+ pragma [ inline_late]
438+ private DataFlow:: Node getAValueReachableFromSourceInline ( Node node ) {
439+ exists ( DataFlow:: LocalSourceNode src , DataFlow:: LocalSourceNode dst |
440+ Impl:: use ( node , pragma [ only_bind_into ] ( src ) ) and
441+ pragma [ only_bind_into ] ( dst ) = Impl:: trackUseNode ( src ) and
442+ dst .flowsTo ( result )
443+ )
444+ }
445+
366446 /** The root node of an API graph. */
367447 class Root extends Node , Impl:: MkRoot {
368448 override string toString ( ) { result = "root" }
@@ -443,7 +523,10 @@ module API {
443523 * you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`,
444524 * use `getTopLevelMember("Gem").getMember("Version")`.
445525 */
446- Node getTopLevelMember ( string m ) { result = root ( ) .getMember ( m ) }
526+ cached
527+ Node getTopLevelMember ( string m ) {
528+ Impl:: forceCachingInSameStage ( ) and result = root ( ) .getMemberInternal ( m )
529+ }
447530
448531 /**
449532 * Provides the actual implementation of API graphs, cached for performance.
@@ -469,6 +552,32 @@ module API {
469552 */
470553 cached
471554 private module Impl {
555+ cached
556+ predicate forceCachingInSameStage ( ) { any ( ) }
557+
558+ cached
559+ predicate forceCachingBackref ( ) {
560+ 1 = 1
561+ or
562+ exists ( getTopLevelMember ( _) )
563+ or
564+ exists (
565+ any ( Node n )
566+ .getMemberInternal ( "foo" )
567+ .getAMember ( )
568+ .getMethodInternal ( "foo" )
569+ .getReturnInternal ( )
570+ .getParameter ( 0 )
571+ .getKeywordParameter ( "foo" )
572+ .getBlock ( )
573+ .getAnImmediateSubclass ( )
574+ .getContent ( _)
575+ .getField ( _)
576+ .getAnElement ( )
577+ .asSourceInternal ( )
578+ )
579+ }
580+
472581 cached
473582 newtype TApiNode =
474583 /** The root of the API graph. */
0 commit comments