errorProne.atl 15.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
library errorProne;

---------------------------------- Missing break in switch---------------------------------------------

-- Returns a set of measures for each switch statement that violates the rule : missing break in switch
helper def: missingBreakInSwitch(): Set(smm!Measure) = 
	java!SwitchStatement.allInstances()
		-> select (switchStatement | thisModule.switchStatementContainsMissingBreaks(switchStatement))
		-> collect (switchStatement | thisModule.createMeasureForMissingBreakInSwitch(switchStatement))
;

-- Returns true if the switch statement contains missing breaks, false otherwise 
helper def: switchStatementContainsMissingBreaks(ss: java!SwitchStatement): Boolean = 
	-- If the number of switch cases (omitting those that empty => indicates intentional fallout) is bigger than
	-- the number of break statements => we don't have a break statement for every switch case 
	-- in that case, return true, else false
	(thisModule.nbBranchesOfASwitchStatement(ss) - thisModule.nbIntentionalFallThroughOfASwitchStatement(ss)) > thisModule.nbBreakStatementOfASwitchStatement(ss)
;

20
21
22
23
24
25
--------------------------------------------- DoNotExtendJavaLangThrowable ---------------------------------------------

-- Rule for metrics DoNotExtendJavaLangThrowable
helper def: doNotExtendJavaLangThrowable() : Set(smm!Measure) =
	-- select all class with a superTyper
	java!ClassDeclaration.allInstances()->select(it | it.superClass <> OclUndefined)
26
27
		-- select all class create by the user
		->select(it2| it2.proxy = false)
28
		-- select all class who extend Throwable
29
		->select(it3| it3.superClass.type.name = 'Throwable' or it3.superClass.type.name = 'java.lang.Throwable')
30
		-- collect all results and send an error message
31
32
		->collect(it4|thisModule.MeasureDoNotExtendJavaLangThrowable(it4))
	;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
--------------------------------------------- SuspiciousEqualsMethodName---------------------------------------------

--Detects the declaration of methods whose name and parameters looks like the equals(Object) method, which denote an intention to override the equals(Object) method.
--A method that looks like equals(Object) is named 'equals', or 'equal' wich is a common typo, and  the parameters and return type can be wrong typed.
helper def: suspiciousEqualsMethodName() : Set(smm!Measure) =
	java!MethodDeclaration.allInstances() 
		-> select(method | method.hasParameters() and method.hasReturnType())
		-> select(method | thisModule.isWrongEqualsDeclaration(method)) 
		-> collect(method | thisModule.MeasureSuspiciousEqualsMethodName(method));

--Detects if a method looks like the equals(Object) method.
helper def: isWrongEqualsDeclaration(method : java!MethodDeclaration) : Boolean = 
	method.name = 'equals' or method.name = 'equal' and --'equal' method name can be a typo
	(
		(
			method.parameters.size() = 1 and --there is only one parameter and the type of this parameter is wrong, or the return type of the method is wrong, or both.
		 	not(method.parameters.first().type.type.name = 'Object') or 
			not(method.returnType.type.name = 'boolean')
		)
		or
		(
			method.parameters.size() = 2 and --there are two parameters, and everything is well typed
			method.parameters->forAll(param | param.type.type.name = 'Object') and
			method.returnType.type.name = 'boolean'
		)
	)
	or
	(
		method.name = 'equal' and --the method is well typed but there is a typo
		method.parameters.size() = 1 and 
		method.parameters.first().type.type.name = 'Object' and
		method.returnType.type.name = 'boolean'
	);
	
	

	
--Allows to have the signature of a method as a String.
helper context java!MethodDeclaration def: toString() : String = 
	if self.hasReturnType() then 
		self.returnType.type.name
	else
		''
	endif 
	+ ' ' + self.name + '(' + 
	if self.hasParameters() then
		let params : String = self.parameters->collect(param | param.type.type.name + ' ' + param.name).toString()
		in params.substring(12, params.size()-2).regexReplaceAll('\'', '') 
	else
		''
	endif
	+ ')';
			
--Indicates whether a MethodDeclaration has a return type.
helper context java!MethodDeclaration def: hasReturnType() : Boolean = 
	not self.returnType.oclIsUndefined();

--Indicates whether a MethodDeclaration has parameters.
helper context java!MethodDeclaration def: hasParameters() : Boolean = 
	not self.parameters.isEmpty();

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
--------------------------------------------- CompareObjectsWithEquals---------------------------------------------

--Detects a wrong usage of objects comparison, which use the '==' operator instead of 'equals' method.
helper def: compareObjectsWithEquals() : Set(smm!Measure) =
	java!InfixExpression.allInstances()
	->select(expression | expression.operator.name = '==')
	->select(expression | thisModule.isWrongCompareObjectsUse(expression))
	->collect(expression | thisModule.MeasureCompareObjectsWithEquals(expression));

--Detects if the right and left operands are Objects.
helper def: isWrongCompareObjectsUse(expression : java!InfixExpression) : Boolean = 
	thisModule.isObject(expression.leftOperand) and thisModule.isObject(expression.rightOperand);

	
----------isObject() helpers
--Indicates if an operand is an Object, using polymorphism, with various '<java!Expression>.isObject()' helpers.
helper def: isObject(operand : java!Expression) : Boolean = 
	operand.isObject();

--Default helper: an unrecognized type is evaluated.
helper context java!Expression def: isObject() : Boolean = 
	false;

helper context java!SingleVariableAccess def: isObject() : Boolean = 
	thisModule.variableIsObject(self.variable);

--An attribute 'this.xxx' of the class is evaluated
helper context java!FieldAccess def: isObject() : Boolean = 
	thisModule.variableIsObject(self.field.variable);

--The object 'this' is currently evaluated.
helper context java!ThisExpression def: isObject() : Boolean = 
	self.originalCompilationUnit.types
		->exists(typeInstance | (typeInstance.oclIsTypeOf(java!ClassDeclaration)));

--A constructor is used in the evaluation.
helper context java!ClassInstanceCreation def: isObject() : Boolean = 
	self.type.type.oclIsTypeOf(java!ClassDeclaration);
----------
----------variableIsObject() helpers
--Differents variables types.
helper def: variableIsObject(variable : java!VariableDeclaration) : Boolean = 
	variable.variableIsObject();

helper context java!SingleVariableDeclaration def: variableIsObject() : Boolean = 
	self.type.type.oclIsTypeOf(java!ClassDeclaration);

helper context java!VariableDeclarationFragment def: variableIsObject() : Boolean = 
	self.variablesContainer.type.type.oclIsTypeOf(java!ClassDeclaration);
----------


146
--------------------------------------------------------------------------------------------------------
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

------------------------------------------- AvoidCallingFinalize----------------------------------------------
-- Rule for metrics AvoidCallingFinalize
helper def: avoidCallingFinalize() : Set(smm!Measure) = 
	java!MethodInvocation.allInstances()
		->select(methode | methode.method.name = 'finalize')
		->collect(methode | thisModule.MeasureAvoidCallingFinalize(methode));

------------------------------------------- EmptyTryBlock----------------------------------------------
-- Rule for metrics EmptyTryBlock
helper def: emptyTryBlock() : Set(smm!Measure) =
	java!TryStatement.allInstances()
		->select( t | t.body.statements.size() = 0)
		->collect(t | thisModule.MeasureEmptyTryBlock(t));

------------------------------------------- EmptyWhileStmt----------------------------------------------
-- Rule for metrics EmptyWhileStmt
helper def: emptyWhileStmt() : Set(smm!Measure) =
	java!WhileStatement.allInstances()
		->select( w | w.body.statements.size() = 0)
		->collect(w | thisModule.MeasureEmptyWhileStmt(w));

------------------------------------------- MeasureDontImportSun----------------------------------------------
-- Rule for metrics DontImportSun
helper def: dontImportSun() : Set(smm!Measure) = 
	java!Package.allInstances()
		->select(p | p.name = 'sun' and p.ownedElements.size() = 0 and p.proxy = true)
		->collect(p | thisModule.MeasureDontImportSun(p.model.compilationUnits));

-------------------------------------------- FinalizeShouldBeProtected ---------------------------------------
-- Rule for metrics FinalizeShouldBeProtected
helper def: finalizeShouldBeProtected() : Set(smm!Measure) = 
	java!MethodDeclaration.allInstances()
		->select(m | m.name = 'finalize' and m.modifier.visibility.toString() <> 'protected')
		->collect(m | thisModule.MesureFinalizeShouldBeProtected(m));

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
--------------------------------------------- EmptySwitchStatement ---------------------------------------------


--Detects the use of empty switch statements. These blocks serve no purpose and should be removed
helper def: emptySwitchStatement() : Set(smm!Measure) =
	java!SwitchStatement.allInstances()
		->select(switchStatement | switchStatement.statements.isEmpty())
		->collect(switchStatement | thisModule.MeasureEmptySwitchStatement(switchStatement));

------------------------------------------ EmptySynchronizedBlock ---------------------------------------------


--Detects the use of empty synchronized block. These blocks serve no purpose and should be removed
helper def: emptySynchronizedBlock() : Set(smm!Measure) =
	java!SynchronizedStatement.allInstances()
	   ->select(synchronizedBlock | synchronizedBlock.body.statements.isEmpty() )
	   ->collect(synchronizedBlock | thisModule.MeasureEmptySynchronizedBlock(synchronizedBlock));
		
---------------------------------------- EmptyFinallyBlock----------------------------------------------
-- Rule for metrics EmptyFinallyBlock
helper def: emptyFinallyBlock() : Set(smm!Measure) =
	java!TryStatement.allInstances()
		->select( finallyBlock | finallyBlock.finally.statements.isEmpty())
		->collect(finallyBlock| thisModule.MeasureEmptyFinallyBlock(finallyBlock));


----------------------------------------- EmptyFinalizer----------------------------------------------
-- Rule for metrics EmptyFinalizer
helper def: emptyfinalizeMethod() : Set(smm!Measure) =
	java!MethodDeclaration.allInstances()
		->select( finalizeMethod | finalizeMethod.name = 'finalize' and finalizeMethod.body.statements.isEmpty())
		->collect(finalizeMethod| thisModule. MeasureEmptyFinalizer(finalizeMethod));



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297

--- Issues #780, #784, #799, #819, #862, #871

--- helper AvoidCatchingNPE 
--- get all the catchClause inside the class
--- check if the name of type for the typeAccess is equals to 'NullPointerException'
--- if it is the case make collection and make a call for MeasureAvoidCatchingNPE rule
--- with the detected VariableDeclaration
helper def: AvoidCatchingNPE() : Set(smm!Measure) =
	java!CatchClause.allInstances()
		->select (w | w.exception.type.type.name = 'NullPointerException')
		->collect(w | thisModule.MeasureAvoidCatchingNPE(w));


--- helper AvoidEnumAsIdentifier 

-- modisco do not allow to convert a class with error
-- and a field with enum decleration is already detected by Java compiler
-- so we need at least to change the name of enum to Enum to work
--- get all the declared variable inside the class
--- check if their name are defined and 
--- if it's different than reserved key word 'enum''
--- if it is not the case make collection and make a call for MeasureAvoidEnumAsIdentifier rule
--- with the detected VariableDeclaration
helper def: AvoidEnumAsIdentifier() : Set(smm!Measure) =
	java!VariableDeclaration.allInstances()
		->select (w | w.name <> OclUndefined )
		->select (w | w.name = 'enum')
		->collect(w | thisModule.MeasureAvoidEnumAsIdentifier(w));


--- helper CloneMethodMustBePublic 
--- get all the declaredMethods inside the class
--- check if is clone methode or not
--- check if its modifier is defined 
--- check if its visibilty access is different from public
--- if it is the case make collection and make a call for MeasureExcessiveClassLength rule
--- with the detected MethodDeclaration
helper def: CloneMethodMustBePublic() : Set(smm!Measure) =
	java!MethodDeclaration.allInstances()
		->select (w | w.modifier <> OclUndefined )
		->select (w | w.name = 'clone')
		->select (w | w.modifier <> 'public')
		->collect(w | thisModule.MeasureCloneMethodMustBePublic(w));


--- helper EmptyInitializer 
--- get all the the used Initializer make a selection on all those that  
--- has nothing inside theire body (empty initialize), 
--- check if the contained statments size equals to 0 
--- if there is collect them and make a call for MeasureEmptyInitializer rule 
--- with the detected Initializer
helper def: EmptyInitializer() : Set(smm!Measure) = 
	java!Initializer.allInstances()
		->select( w | w.body.statements.size() = 0)
		->collect(w | thisModule.MeasureEmptyInitializer(w));

--- helper UnconditionalIfStatement 
--- get all the the used ifstatements and make a selection on all those that contains
--- an only expression in which its type is BooleanLiteral type like (true) or (false)
--- if there is collect them and make a call for MeasureUnconditionalIfStatement rule
--- with the detected ifstatments 

helper def: UnconditionalIfStatement() : Set(smm!Measure) =
	java!IfStatement.allInstances()
		->select( w | w.expression.oclIsTypeOf(java!BooleanLiteral))
		->collect(w | thisModule.MeasureUnconditionalIfStatement(w));


--- helper UseProperClassLoader 
--- get all the MethodInvocation inside the class
--- check if the invocked method name is equals to getClassLoader
--- if it is the case make collection and make a call for MeasureUseProperClassLoader rule
--- with the detected MethodInvocation
helper def: UseProperClassLoader() : Set(smm!Measure) =
	java!MethodInvocation.allInstances()
		->select (w | w.method.name='getClassLoader')
		->collect(w | thisModule.MeasureUseProperClassLoader(w));


298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
-----------------------------------------avoidCatchingThrowable----------------------------------------------
-- Rule for metrics avoidCatchingThrowable
helper def: avoidCatchingThrowable() : Set(smm!Measure) =
	java!CatchClause.allInstances()
		->select(catch | catch.exception.type.type.name = 'Throwable')
		->collect(catch | thisModule.MeasureAvoidCatchingThrowable(catch));

-----------------------------------------DoNotCallGarbageCollectionExplicitly----------------------------------------------
-- Rule for metrics DoNotCallGarbageCollectionExplicitly
helper def: doNotCallGarbageCollectionExplicitly() : Set(smm!Measure) =
	java!MethodInvocation.allInstances()
		->select(method | method.method.name = 'gc' and method.method.proxy)
		->collect(method | thisModule.MeasureDoNotCallGarbageCollectionExplicitly(method));

-----------------------------------------EmptyCatchBlock----------------------------------------------
-- Rule for metrics EmptyCatchBlock
helper def: emptyCatchBlock() : Set(smm!Measure) =
	java!CatchClause.allInstances()
		->select(catch | catch.body.statements.size() = 0)
		->collect(catch | thisModule.MeasureEmptyCatchBlock(catch));

-----------------------------------------ImportFromSamePackage----------------------------------------------
-- Rule for metrics ImportFromSamePackage
helper def: importFromSamePackage() : Set(smm!Measure) =
	java!ImportDeclaration.allInstances()
		->select(import | import.importedElement = import.originalCompilationUnit.package)
		->collect(import | thisModule.MeasureImportFromSamePackage(import));

-----------------------------------------MethodWithSameNameAsEnclosingClass----------------------------------------------
-- Rule for metrics MethodWithSameNameAsEnclosingClass
helper def: methodWithSameNameAsEnclosingClass() : Set(smm!Measure) =
	java!MethodDeclaration.allInstances()
		->select(method | method.name = method.abstractTypeDeclaration.name)
		->collect(method | thisModule.MeasureMethodWithSameNameAsEnclosingClass(method));