diff --git a/input/compare-objects-with-equals.xmi b/input/compare-objects-with-equals.xmi new file mode 100644 index 0000000000000000000000000000000000000000..d3b240ce7ebf28b6a53ad1973a04020e2a6d648b --- /dev/null +++ b/input/compare-objects-with-equals.xmi @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index 27292affc907adbd97483ebc504d51fb52b98713..95a3afe029a6ed3cec7140f1df60ae7f024b0701 100644 --- a/src/main/atl/analysis.atl +++ b/src/main/atl/analysis.atl @@ -45,7 +45,7 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) = -- thisModule.shortMethodName(), thisModule.tooManyStaticImports(), - thisModule.AvoidDollarSigns(), + thisModule.avoidDollarSigns(), thisModule.shortClassName(), thisModule.extendsObject(), thisModule.unnecessaryReturn(), @@ -81,9 +81,10 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) = thisModule.suspiciousEqualsMethodName(), thisModule.doNotExtendJavaLangThrowable(), thisModule.doNotExtendJavaLangError(), + thisModule.compareObjectsWithEquals(), - -- Best practicices rules - thisModule.avoidThrowingNewInstanceOfSameException() + -- Best practices rules + thisModule.avoidThrowingNewInstanceOfSameException(), thisModule.switchDensity() }; @@ -811,4 +812,27 @@ rule MeasureUnnecessaryReturn(state : java!ReturnStatement) { do { noc; } +} + + +--------------------------------------------- MeasureCompareObjectsWithEquals --------------------------------------------- +--A measure instance if the class violates the rule MeasureCompareObjectsWithEquals. +rule MeasureCompareObjectsWithEquals(expression : java!InfixExpression) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'CompareObjectsWithEquals', + shortDescription <- 'Use equals() to compare object references; avoid comparing them with ==.' + + ), + measurement: smm!DirectMeasurement ( + error<-'The Class '+ expression.originalCompilationUnit.name + ' is using == instead of equals() to compare objects.' + ) + + do { + noc; + } } diff --git a/src/main/atl/errorProne.atl b/src/main/atl/errorProne.atl index 5c974bc41d90e50be3b28e67d3c0d91f46e64633..2cbaa873b394b047b78869fa3ae0d81d3c9309cf 100644 --- a/src/main/atl/errorProne.atl +++ b/src/main/atl/errorProne.atl @@ -91,4 +91,56 @@ helper context java!MethodDeclaration def: hasReturnType() : Boolean = helper context java!MethodDeclaration def: hasParameters() : Boolean = not self.parameters.isEmpty(); +--------------------------------------------- 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 '.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); +---------- + + --------------------------------------------------------------------------------------------------------