@@ -124,21 +124,38 @@ class PrintASTNode extends TPrintASTNode {
124124 * regular parent/child relation traversal.
125125 */
126126 final PrintASTNode getChild ( int childIndex ) {
127- exists ( int nonConvertedIndex , boolean isConverted |
128- mapIndex ( childIndex , nonConvertedIndex , isConverted )
129- |
130- if isConverted = false
131- then result = getChildInternal ( childIndex )
132- else
133- exists ( Expr expr |
134- expr = getChildInternal ( nonConvertedIndex ) .( ASTNode ) .getAST ( ) and
135- expr .getFullyConverted ( ) instanceof Conversion and
136- result .( ASTNode ) .getAST ( ) = expr .getFullyConverted ( ) and
137- not expr instanceof Conversion
138- )
127+ // The exact value of `childIndex` doesn't matter, as long as we preserve the correct order.
128+ result =
129+ rank [ childIndex ] ( PrintASTNode child , int nonConvertedIndex , boolean isConverted |
130+ childAndAccessorPredicate ( child , _, nonConvertedIndex , isConverted )
131+ |
132+ // Unconverted children come first, then sort by original child index within each group.
133+ child order by isConverted , nonConvertedIndex
134+ )
135+ }
136+
137+ /**
138+ * Gets the node for the `.getFullyConverted()` version of the child originally at index
139+ * `childIndex`, if that node has any conversions.
140+ */
141+ private PrintASTNode getConvertedChild ( int childIndex ) {
142+ exists ( Expr expr |
143+ expr = getChildInternal ( childIndex ) .( ASTNode ) .getAST ( ) and
144+ expr .getFullyConverted ( ) instanceof Conversion and
145+ result .( ASTNode ) .getAST ( ) = expr .getFullyConverted ( ) and
146+ not expr instanceof Conversion
139147 )
140148 }
141149
150+ /**
151+ * Gets the child access predicate for the `.getFullyConverted()` version of the child originally
152+ * at index `childIndex`, if that node has any conversions.
153+ */
154+ private string getConvertedChildAccessorPredicate ( int childIndex ) {
155+ exists ( getConvertedChild ( childIndex ) ) and
156+ result = getChildAccessorPredicateInternal ( childIndex ) + ".getFullyConverted()"
157+ }
158+
142159 /**
143160 * Holds if this node should be printed in the output. By default, all nodes
144161 * within a function are printed, but the query can override
@@ -170,19 +187,39 @@ class PrintASTNode extends TPrintASTNode {
170187 result = toString ( )
171188 }
172189
190+ /**
191+ * Holds if there is a child node `child` for original child index `nonConvertedIndex` with
192+ * predicate name `childPredicate`. If the original child at that index has any conversions, there
193+ * will be two result tuples for this predicate: one with the original child and predicate, with
194+ * `isConverted = false`, and the other with the `.getFullyConverted()` version of the child and
195+ * predicate, with `isConverted = true`. For a child without any conversions, there will be only
196+ * one result tuple, with `isConverted = false`.
197+ */
198+ private predicate childAndAccessorPredicate (
199+ PrintASTNode child , string childPredicate , int nonConvertedIndex , boolean isConverted
200+ ) {
201+ child = getChildInternal ( nonConvertedIndex ) and
202+ childPredicate = getChildAccessorPredicateInternal ( nonConvertedIndex ) and
203+ isConverted = false
204+ or
205+ child = getConvertedChild ( nonConvertedIndex ) and
206+ childPredicate = getConvertedChildAccessorPredicate ( nonConvertedIndex ) and
207+ isConverted = true
208+ }
209+
173210 /**
174211 * Gets the QL predicate that can be used to access the child at `childIndex`.
175212 * May not always return a QL predicate, see for example `FunctionNode`.
176213 */
177214 final string getChildAccessorPredicate ( int childIndex ) {
178- exists ( getChild ( childIndex ) ) and
179- exists ( int nonConvertedIndex , boolean isConverted |
180- mapIndex ( childIndex , nonConvertedIndex , isConverted )
181- |
182- if isConverted = false
183- then result = getChildAccessorPredicateInternal ( childIndex )
184- else result = getChildAccessorPredicateInternal ( nonConvertedIndex ) + ".getFullyConverted()"
185- )
215+ // The exact value of ` childIndex` doesn't matter, as long as we preserve the correct order.
216+ result =
217+ rank [ childIndex ] ( string childPredicate , int nonConvertedIndex , boolean isConverted |
218+ childAndAccessorPredicate ( _ , childPredicate , nonConvertedIndex , isConverted )
219+ |
220+ // Unconverted children come first, then sort by original child index within each group.
221+ childPredicate order by isConverted , nonConvertedIndex
222+ )
186223 }
187224
188225 /**
@@ -191,40 +228,6 @@ class PrintASTNode extends TPrintASTNode {
191228 */
192229 abstract string getChildAccessorPredicateInternal ( int childIndex ) ;
193230
194- /**
195- * Holds either if a `childIndex` is a synthesized child coming from a conversion (`isConverted = true`)
196- * or if the child is a regular child node.
197- * In the former case, `nonConvertedIndex` is the index of the regular, unconverted child node.
198- * Note: This predicate contains the mapping between the converted and nonconverted indexes, but
199- * if `nonConvertedIndex` does not have conversions attached, there will be no node at `childIndex`.
200- */
201- final predicate mapIndex ( int childIndex , int nonConvertedIndex , boolean isConverted ) {
202- exists ( getChildInternal ( childIndex ) ) and
203- nonConvertedIndex = childIndex and
204- isConverted = false
205- or
206- not exists ( getChildInternal ( childIndex ) ) and
207- exists ( getChildInternal ( nonConvertedIndex ) ) and
208- isConverted = true and
209- // to get the desired order of the extended `childIndex`es (including the synthesized children for conversions)
210- // we keep the indexes in the following three disjoint intervals:
211- // [minIndex, maxIndex: original children, without conversion
212- // [|-1| + |maxIndex|, |minIndex| + |maxIndex|]: conversion children with negative `nonConvertedIndex`
213- // [0 + |maxIndex| + |minIndex| + 1, maxIndex + |maxIndex| + |minIndex| + 1]: for conversion children
214- // with positive `nonConvertedIndex`
215- exists ( int minIndex , int maxIndex |
216- minIndex = min ( int n | exists ( getChildInternal ( n ) ) ) and
217- maxIndex = max ( int n | exists ( getChildInternal ( n ) ) )
218- |
219- if nonConvertedIndex < 0
220- then
221- // note that we swap the order of the negative indexes here, so that the conversion child for `-2`
222- // comes before the conversion child for `-1`
223- childIndex = ( minIndex - 1 - nonConvertedIndex ) .abs ( ) + maxIndex .abs ( )
224- else childIndex = nonConvertedIndex + maxIndex .abs ( ) + minIndex .abs ( ) + 1
225- )
226- }
227-
228231 /**
229232 * Gets the `Function` that contains this node.
230233 */
0 commit comments