1+ private import codeql.util.DenseRank
2+
3+ /**
4+ * Describes how to construct a condensed list from sparse but orderable data, and how that data
5+ * should be connected, with one such list per specified division.
6+ */
7+ signature module CondensedListSig {
8+ /**
9+ * The division specifies which items are connected into lists, with one list per division.
10+ *
11+ * For instance, if connecting variables defined in a file, the division will be the file.
12+ */
13+ class Division ;
14+
15+ /**
16+ * The class of the items to be condensed into lists.
17+ *
18+ * For instance, when connecting variables defined in a file, the items are the variables.
19+ */
20+ class Item {
21+ string toString ( ) ;
22+ }
23+
24+ /**
25+ * The index specifies the order of the items in the condensed list, and may be sparse (have
26+ * gaps).
27+ *
28+ * For instance, if connecting variables defined in a file, the index will be the line number of
29+ * the variable in the file.
30+ *
31+ * The sparse index (which may have gaps) is used to determine the ordering of the items in the
32+ * condensed list. Once the condensed list is created, the items in the list will automatically be
33+ * assigned a dense index (which has no gaps).
34+ *
35+ * There must be no duplicate indices for the same division for correctness.
36+ */
37+ int getSparseIndex ( Division d , Item l ) ;
38+ }
39+
40+ /**
41+ * A module to take orderable data (which may not be continuous) and condense it into one or more
42+ * dense lists, with one such list per specified division.
43+ *
44+ * To instantiate this module, you need to provide a `CondensedListSig` module that
45+ * specifies the spare index and division of the items to be connected.
46+ *
47+ * For instance, to create a condensed list of variables defined in every file, you can
48+ * create a `CondensedListSig` module that specifies the file as the division and
49+ * the line number as the sparse index.
50+ *
51+ * ```ql
52+ * module ConfigFileListConfig {
53+ * class Division = File;
54+ * class Item = Variable;
55+ * int getSparseIndex(File file, Variable var) {
56+ * file = var.getLocation().getFile() and
57+ * var.getLocation().getStartLine()
58+ * }
59+ * }
60+ *
61+ * import Condense<ConfigFileListConfig>
62+ *
63+ * from Condense::ListEntry l
64+ * select l, l.getItem(), l.getDenseIndex(), l.getNext(), l.getPrev(),
65+ * ```
66+ */
67+ module Condense< CondensedListSig Config> {
68+ newtype TList =
69+ THead ( Config:: Item l , Config:: Division t ) { denseRank ( t , l ) = 1 } or
70+ TCons ( ListEntry prev , Config:: Item l ) { prev .getDenseIndex ( ) = denseRank ( prev .getDivision ( ) , l ) - 1 }
71+
72+ private module DenseRankConfig implements DenseRankInputSig2 {
73+ class Ranked = Config:: Item ;
74+
75+ class C = Config:: Division ;
76+
77+ predicate getRank = Config:: getSparseIndex / 2 ;
78+ }
79+
80+ private import DenseRank2< DenseRankConfig >
81+
82+ class ListEntry extends TList {
83+ Config:: Division getDivision ( ) {
84+ this = THead ( _, result )
85+ or
86+ exists ( ListEntry prev | this = TCons ( prev , _) and result = prev .getDivision ( ) )
87+ }
88+
89+ string toString ( ) {
90+ result = getItem ( ) .toString ( ) + " [index " + getDenseIndex ( ) + "]"
91+ }
92+
93+ Config:: Item getItem ( ) {
94+ this = THead ( result , _)
95+ or
96+ this = TCons ( _, result )
97+ }
98+
99+ int getDenseIndex ( ) {
100+ result = denseRank ( getDivision ( ) , getItem ( ) )
101+ }
102+
103+ ListEntry getPrev ( ) { this = TCons ( result , _) }
104+
105+ ListEntry getNext ( ) { result .getPrev ( ) = this }
106+ }
107+ }
0 commit comments