1
+ /** Provides classes for detecting duplicate or similar code. */
2
+
1
3
import cpp
2
4
3
5
private string relativePath ( File file ) { result = file .getRelativePath ( ) .replaceAll ( "\\" , "/" ) }
@@ -8,41 +10,59 @@ private predicate tokenLocation(string path, int sl, int sc, int ec, int el, Cop
8
10
tokens ( copy , index , sl , sc , ec , el )
9
11
}
10
12
13
+ /** A token block used for detection of duplicate and similar code. */
11
14
class Copy extends @duplication_or_similarity {
15
+ /** Gets the index of the last token in this block. */
12
16
private int lastToken ( ) { result = max ( int i | tokens ( this , i , _, _, _, _) | i ) }
13
17
18
+ /** Gets the index of the token in this block starting at the location `loc`, if any. */
14
19
int tokenStartingAt ( Location loc ) {
15
20
exists ( string filepath , int startline , int startcol |
16
21
loc .hasLocationInfo ( filepath , startline , startcol , _, _) and
17
22
tokenLocation ( filepath , startline , startcol , _, _, this , result )
18
23
)
19
24
}
20
25
26
+ /** Gets the index of the token in this block ending at the location `loc`, if any. */
21
27
int tokenEndingAt ( Location loc ) {
22
28
exists ( string filepath , int endline , int endcol |
23
29
loc .hasLocationInfo ( filepath , _, _, endline , endcol ) and
24
30
tokenLocation ( filepath , _, _, endline , endcol , this , result )
25
31
)
26
32
}
27
33
34
+ /** Gets the line on which the first token in this block starts. */
28
35
int sourceStartLine ( ) { tokens ( this , 0 , result , _, _, _) }
29
36
37
+ /** Gets the column on which the first token in this block starts. */
30
38
int sourceStartColumn ( ) { tokens ( this , 0 , _, result , _, _) }
31
39
40
+ /** Gets the line on which the last token in this block ends. */
32
41
int sourceEndLine ( ) { tokens ( this , lastToken ( ) , _, _, result , _) }
33
42
43
+ /** Gets the column on which the last token in this block ends. */
34
44
int sourceEndColumn ( ) { tokens ( this , lastToken ( ) , _, _, _, result ) }
35
45
46
+ /** Gets the number of lines containing at least (part of) one token in this block. */
36
47
int sourceLines ( ) { result = this .sourceEndLine ( ) + 1 - this .sourceStartLine ( ) }
37
48
49
+ /** Gets an opaque identifier for the equivalence class of this block. */
38
50
int getEquivalenceClass ( ) { duplicateCode ( this , _, result ) or similarCode ( this , _, result ) }
39
51
52
+ /** Gets the source file in which this block appears. */
40
53
File sourceFile ( ) {
41
54
exists ( string name | duplicateCode ( this , name , _) or similarCode ( this , name , _) |
42
55
name .replaceAll ( "\\" , "/" ) = relativePath ( result )
43
56
)
44
57
}
45
58
59
+ /**
60
+ * Holds if this element is at the specified location.
61
+ * The location spans column `startcolumn` of line `startline` to
62
+ * column `endcolumn` of line `endline` in file `filepath`.
63
+ * For more information, see
64
+ * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
65
+ */
46
66
predicate hasLocationInfo (
47
67
string filepath , int startline , int startcolumn , int endline , int endcolumn
48
68
) {
@@ -53,25 +73,30 @@ class Copy extends @duplication_or_similarity {
53
73
endcolumn = sourceEndColumn ( )
54
74
}
55
75
76
+ /** Gets a textual representation of this element. */
56
77
string toString ( ) { none ( ) }
57
78
}
58
79
80
+ /** A block of duplicated code. */
59
81
class DuplicateBlock extends Copy , @duplication {
60
82
override string toString ( ) { result = "Duplicate code: " + sourceLines ( ) + " duplicated lines." }
61
83
}
62
84
85
+ /** A block of similar code. */
63
86
class SimilarBlock extends Copy , @similarity {
64
87
override string toString ( ) {
65
88
result = "Similar code: " + sourceLines ( ) + " almost duplicated lines."
66
89
}
67
90
}
68
91
92
+ /** Gets a function with a body and a location. */
69
93
FunctionDeclarationEntry sourceMethod ( ) {
70
94
result .isDefinition ( ) and
71
95
exists ( result .getLocation ( ) ) and
72
96
numlines ( unresolveElement ( result .getFunction ( ) ) , _, _, _)
73
97
}
74
98
99
+ /** Gets the number of member functions in `c` with a body and a location. */
75
100
int numberOfSourceMethods ( Class c ) {
76
101
result =
77
102
count ( FunctionDeclarationEntry m |
@@ -108,20 +133,27 @@ private predicate duplicateStatement(
108
133
)
109
134
}
110
135
136
+ /**
137
+ * Holds if `m1` is a function with `total` lines, and `m2` is a function
138
+ * that has `duplicate` lines in common with `m1`.
139
+ */
111
140
predicate duplicateStatements (
112
141
FunctionDeclarationEntry m1 , FunctionDeclarationEntry m2 , int duplicate , int total
113
142
) {
114
143
duplicate = strictcount ( Stmt s | duplicateStatement ( m1 , m2 , s , _) ) and
115
144
total = strictcount ( statementInMethod ( m1 ) )
116
145
}
117
146
118
- /**
119
- * Find pairs of methods are identical
120
- */
147
+ /** Holds if `m` and other are identical functions. */
121
148
predicate duplicateMethod ( FunctionDeclarationEntry m , FunctionDeclarationEntry other ) {
122
149
exists ( int total | duplicateStatements ( m , other , total , total ) )
123
150
}
124
151
152
+ /**
153
+ * INTERNAL: do not use.
154
+ *
155
+ * Holds if `line` in `f` is similar to a line somewhere else.
156
+ */
125
157
predicate similarLines ( File f , int line ) {
126
158
exists ( SimilarBlock b | b .sourceFile ( ) = f and line in [ b .sourceStartLine ( ) .. b .sourceEndLine ( ) ] )
127
159
}
@@ -152,6 +184,7 @@ private predicate similarLinesCoveredFiles(File f, File otherFile) {
152
184
)
153
185
}
154
186
187
+ /** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */
155
188
predicate similarLinesCovered ( File f , int coveredLines , File otherFile ) {
156
189
exists ( int numLines | numLines = f .getMetrics ( ) .getNumberOfLines ( ) |
157
190
similarLinesCoveredFiles ( f , otherFile ) and
@@ -166,6 +199,11 @@ predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
166
199
)
167
200
}
168
201
202
+ /**
203
+ * INTERNAL: do not use.
204
+ *
205
+ * Holds if `line` in `f` is duplicated by a line somewhere else.
206
+ */
169
207
predicate duplicateLines ( File f , int line ) {
170
208
exists ( DuplicateBlock b |
171
209
b .sourceFile ( ) = f and line in [ b .sourceStartLine ( ) .. b .sourceEndLine ( ) ]
@@ -182,6 +220,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F
182
220
)
183
221
}
184
222
223
+ /** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */
185
224
predicate duplicateLinesCovered ( File f , int coveredLines , File otherFile ) {
186
225
exists ( int numLines | numLines = f .getMetrics ( ) .getNumberOfLines ( ) |
187
226
exists ( int coveredApprox |
@@ -206,6 +245,7 @@ predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
206
245
)
207
246
}
208
247
248
+ /** Holds if most of `f` (`percent`%) is similar to `other`. */
209
249
predicate similarFiles ( File f , File other , int percent ) {
210
250
exists ( int covered , int total |
211
251
similarLinesCovered ( f , covered , other ) and
@@ -216,6 +256,7 @@ predicate similarFiles(File f, File other, int percent) {
216
256
not duplicateFiles ( f , other , _)
217
257
}
218
258
259
+ /** Holds if most of `f` (`percent`%) is duplicated by `other`. */
219
260
predicate duplicateFiles ( File f , File other , int percent ) {
220
261
exists ( int covered , int total |
221
262
duplicateLinesCovered ( f , covered , other ) and
@@ -225,6 +266,10 @@ predicate duplicateFiles(File f, File other, int percent) {
225
266
)
226
267
}
227
268
269
+ /**
270
+ * Holds if most member functions of `c` (`numDup` out of `total`) are
271
+ * duplicates of member functions in `other`.
272
+ */
228
273
predicate mostlyDuplicateClassBase ( Class c , Class other , int numDup , int total ) {
229
274
numDup =
230
275
strictcount ( FunctionDeclarationEntry m1 |
@@ -240,6 +285,11 @@ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total)
240
285
( numDup * 100 ) / total > 80
241
286
}
242
287
288
+ /**
289
+ * Holds if most member functions of `c` are duplicates of member functions in
290
+ * `other`. Provides the human-readable `message` to describe the amount of
291
+ * duplication.
292
+ */
243
293
predicate mostlyDuplicateClass ( Class c , Class other , string message ) {
244
294
exists ( int numDup , int total |
245
295
mostlyDuplicateClassBase ( c , other , numDup , total ) and
@@ -264,12 +314,21 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) {
264
314
)
265
315
}
266
316
317
+ /** Holds if `f` and `other` are similar or duplicates. */
267
318
predicate fileLevelDuplication ( File f , File other ) {
268
319
similarFiles ( f , other , _) or duplicateFiles ( f , other , _)
269
320
}
270
321
322
+ /**
323
+ * Holds if most member functions of `c` are duplicates of member functions in
324
+ * `other`.
325
+ */
271
326
predicate classLevelDuplication ( Class c , Class other ) { mostlyDuplicateClass ( c , other , _) }
272
327
328
+ /**
329
+ * Holds if `line` in `f` should be allowed to be duplicated. This is the case
330
+ * for `#include` directives.
331
+ */
273
332
predicate whitelistedLineForDuplication ( File f , int line ) {
274
333
exists ( Include i | i .getFile ( ) = f and i .getLocation ( ) .getStartLine ( ) = line )
275
334
}
0 commit comments