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