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);
+----------
+
+
--------------------------------------------------------------------------------------------------------