@@ -76,7 +76,7 @@ private TypeExpr getABaseType(Class cls, boolean abstractExtension) {
7676}
7777
7878pragma [ nomagic]
79- predicate rawEdge ( PathNode pred , PathNode succ ) {
79+ private predicate basicEdge ( PathNode pred , PathNode succ ) {
8080 exists ( Predicate predicat | pred .asAstNode ( ) = predicat |
8181 succ .asAstNode ( ) .getEnclosingPredicate ( ) = predicat
8282 or
@@ -138,11 +138,36 @@ predicate rawEdge(PathNode pred, PathNode succ) {
138138 pred .asAstNode ( ) = top and
139139 succ .asAstNode ( ) .getFile ( ) = top .getFile ( )
140140 )
141+ or
142+ // Add bidirectional edges between a class declaration and its type.
143+ exists ( Class cls |
144+ pred .asAstNode ( ) = cls and succ .asType ( ) = cls .getType ( )
145+ or
146+ succ .asAstNode ( ) = cls and pred .asType ( ) = cls .getType ( )
147+ )
141148}
142149
143- private PathNode getAPredecessor ( PathNode node ) { rawEdge ( result , node ) }
144-
145- private PathNode getASuccessor ( PathNode node ) { rawEdge ( node , result ) }
150+ private predicate cacheEdge ( PathNode pred , PathNode succ ) {
151+ // At a cached module, add bidirectional edges to every cached member
152+ exists ( Module mod , Declaration decl |
153+ mod .hasAnnotation ( "cached" ) and
154+ decl = mod .getAChild ( ) and
155+ decl .hasAnnotation ( "cached" )
156+ |
157+ pred .asAstNode ( ) = mod and succ .asAstNode ( ) = decl
158+ or
159+ succ .asAstNode ( ) = mod and pred .asAstNode ( ) = decl
160+ )
161+ or
162+ // At a cached class, add edges from the class to its cached member predicates
163+ exists ( Class cls , Predicate member |
164+ cls .hasAnnotation ( "cached" ) and
165+ member = cls .getAClassPredicate ( ) and
166+ member .hasAnnotation ( "cached" ) and
167+ pred .asAstNode ( ) = cls and
168+ succ .asAstNode ( ) = member
169+ )
170+ }
146171
147172signature module DependencyConfig {
148173 /**
@@ -154,9 +179,30 @@ signature module DependencyConfig {
154179 * Holds if a transitive dependency from a source to `sink` should be reported.
155180 */
156181 predicate isSink ( PathNode sink ) ;
182+
183+ /**
184+ * Holds if the `cached` members of a `cached` module or class should be unified.
185+ *
186+ * Whether to set this depends on your use-case:
187+ * - If you wish to know why one predicate causes another predicate to be evaluated, this should be `any()`.
188+ * - If you wish to investigate recursion patterns or understand why the value of one predicate
189+ * is influenced by another predicate, it should be `none()`.
190+ */
191+ predicate followCacheDependencies ( ) ;
157192}
158193
159194module PathGraph< DependencyConfig C> {
195+ private predicate rawEdge ( PathNode pred , PathNode succ ) {
196+ basicEdge ( pred , succ )
197+ or
198+ C:: followCacheDependencies ( ) and
199+ cacheEdge ( pred , succ )
200+ }
201+
202+ private PathNode getAPredecessor ( PathNode node ) { rawEdge ( result , node ) }
203+
204+ private PathNode getASuccessor ( PathNode node ) { rawEdge ( node , result ) }
205+
160206 private PathNode reachableFromSource ( ) {
161207 C:: isSource ( result )
162208 or
0 commit comments