Commit 77487b8a authored by Glenn PLOUHINEC's avatar Glenn PLOUHINEC Committed by Gerson SUNYE
Browse files

Fix #858 : SuspiciousEqualsMethodName

parent a0134aec
......@@ -12,3 +12,5 @@ target/
*.launch
!analysis.launch
*.orig
......@@ -45,7 +45,7 @@
<mapEntry key="OUT" value="smm"/>
</mapAttribute>
<mapAttribute key="Path">
<mapEntry key="IN" value="/projet-2019/input/empty-statement-block.xmi"/>
<mapEntry key="IN" value="/projet-2019/input/suspicious-equals-method-name.xmi"/>
<mapEntry key="OUT" value="/projet-2019/output/metrics.xmi"/>
<mapEntry key="java" value="uri:http://www.eclipse.org/MoDisco/Java/0.2.incubation/java"/>
<mapEntry key="smm" value="uri:http://www.eclipse.org/MoDisco/SMM/1.0.Beta2/smm"/>
......
<?xml version="1.0" encoding="ASCII"?>
<java:Model xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://www.eclipse.org/MoDisco/Java/0.2.incubation/java" name="SuspiciousEqualsMethodName">
<ownedElements name="suspiciousEqualsMethodName">
<ownedElements xsi:type="java:ClassDeclaration" originalCompilationUnit="//@compilationUnits.0" name="Foo">
<modifier visibility="public"/>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equals">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be boolean equals" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<returnType type="//@orphanTypes.0"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equals">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be equals(Object)" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="s">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.1"/>
</parameters>
<returnType type="//@orphanTypes.4"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equals">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be boolean equals" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="s">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.1"/>
</parameters>
<returnType type="//@orphanTypes.0"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equals">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be equals(Object)" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o1">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o2">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<returnType type="//@orphanTypes.4"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equal">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be equals(Object)" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<returnType type="//@orphanTypes.4"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equal">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be equals(Object)" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="s">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.1"/>
</parameters>
<returnType type="//@orphanTypes.4"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equal">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be equals(Object)" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<returnType type="//@orphanTypes.0"/>
</bodyDeclarations>
<bodyDeclarations xsi:type="java:MethodDeclaration" originalCompilationUnit="//@compilationUnits.0" name="equal">
<modifier visibility="public"/>
<body originalCompilationUnit="//@compilationUnits.0">
<comments xsi:type="java:LineComment" originalCompilationUnit="//@compilationUnits.0" content="// oops, this probably was supposed to be equals(Object)" enclosedByParent="true"/>
</body>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o1">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<parameters originalCompilationUnit="//@compilationUnits.0" name="o2">
<modifier/>
<type type="//@ownedElements.1/@ownedPackages.0/@ownedElements.0"/>
</parameters>
<returnType type="//@orphanTypes.4"/>
</bodyDeclarations>
</ownedElements>
</ownedElements>
<ownedElements name="java" proxy="true">
<ownedPackages name="lang" proxy="true">
<ownedElements xsi:type="java:ClassDeclaration" name="Object" proxy="true" usagesInTypeAccess="//@ownedElements.0/@ownedElements.0/@bodyDeclarations.0/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.3/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.3/@parameters.1/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.4/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.6/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.7/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.7/@parameters.1/@type"/>
<ownedElements xsi:type="java:ClassDeclaration" name="String" proxy="true" usagesInTypeAccess="//@ownedElements.0/@ownedElements.0/@bodyDeclarations.1/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.2/@parameters.0/@type //@ownedElements.0/@ownedElements.0/@bodyDeclarations.5/@parameters.0/@type">
<superInterfaces type="//@ownedElements.1/@ownedPackages.1/@ownedElements.0"/>
<superInterfaces type="//@ownedElements.1/@ownedPackages.0/@ownedElements.2"/>
<superInterfaces type="//@ownedElements.1/@ownedPackages.0/@ownedElements.3"/>
</ownedElements>
<ownedElements xsi:type="java:InterfaceDeclaration" name="Comparable" proxy="true" usagesInTypeAccess="//@ownedElements.1/@ownedPackages.0/@ownedElements.1/@superInterfaces.1">
<typeParameters name="T" proxy="true"/>
</ownedElements>
<ownedElements xsi:type="java:InterfaceDeclaration" name="CharSequence" proxy="true" usagesInTypeAccess="//@ownedElements.1/@ownedPackages.0/@ownedElements.1/@superInterfaces.2"/>
</ownedPackages>
<ownedPackages name="io" proxy="true">
<ownedElements xsi:type="java:InterfaceDeclaration" name="Serializable" proxy="true" usagesInTypeAccess="//@ownedElements.1/@ownedPackages.0/@ownedElements.1/@superInterfaces.0"/>
</ownedPackages>
</ownedElements>
<orphanTypes xsi:type="java:PrimitiveTypeInt" name="int" usagesInTypeAccess="//@ownedElements.0/@ownedElements.0/@bodyDeclarations.0/@returnType //@ownedElements.0/@ownedElements.0/@bodyDeclarations.2/@returnType //@ownedElements.0/@ownedElements.0/@bodyDeclarations.6/@returnType"/>
<orphanTypes xsi:type="java:PrimitiveTypeLong" name="long"/>
<orphanTypes xsi:type="java:PrimitiveTypeFloat" name="float"/>
<orphanTypes xsi:type="java:PrimitiveTypeDouble" name="double"/>
<orphanTypes xsi:type="java:PrimitiveTypeBoolean" name="boolean" usagesInTypeAccess="//@ownedElements.0/@ownedElements.0/@bodyDeclarations.1/@returnType //@ownedElements.0/@ownedElements.0/@bodyDeclarations.3/@returnType //@ownedElements.0/@ownedElements.0/@bodyDeclarations.4/@returnType //@ownedElements.0/@ownedElements.0/@bodyDeclarations.5/@returnType //@ownedElements.0/@ownedElements.0/@bodyDeclarations.7/@returnType"/>
<orphanTypes xsi:type="java:PrimitiveTypeVoid" name="void"/>
<orphanTypes xsi:type="java:PrimitiveTypeChar" name="char"/>
<orphanTypes xsi:type="java:PrimitiveTypeShort" name="short"/>
<orphanTypes xsi:type="java:PrimitiveTypeByte" name="byte"/>
<compilationUnits name="Foo.java" originalFilePath="/home/glenn/photon-workspace/projetMDE/SuspiciousEqualsMethodName/src/suspiciousEqualsMethodName/Foo.java" commentList="//@ownedElements.0/@ownedElements.0/@bodyDeclarations.0/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.1/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.2/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.3/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.4/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.5/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.6/@body/@comments.0 //@ownedElements.0/@ownedElements.0/@bodyDeclarations.7/@body/@comments.0" package="//@ownedElements.0" types="//@ownedElements.0/@ownedElements.0"/>
</java:Model>
......@@ -75,6 +75,7 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) =
-- Error prone rules
--
thisModule.missingBreakInSwitch(),
thisModule.suspiciousEqualsMethodName(),
thisModule.doNotExtendJavaLangThrowable(),
thisModule.doNotExtendJavaLangError(),
......@@ -612,6 +613,25 @@ helper def: getAnnotationTypeDeclarationsFromBodyDeclaration(elem: java!BodyDecl
)
;
-- A Measure instance if the class violates the rule 'SuspiciousEqualsMethodName'
rule MeasureSuspiciousEqualsMethodName(method : java!MethodDeclaration) {
to
om: smm!ObservedMeasure (
measure <- noc,
measurements <- measurement
),
noc: smm!DimensionalMeasure (
name <- 'Suspicious Equals Method Name',
shortDescription <- 'The method name and parameter number are suspiciously close to equals(Object), which can denote an intention to override the equals(Object) method.'
),
measurement: smm!DirectMeasurement (
error <- 'The class ' + method.originalCompilationUnit.name + ' has a suspicious \'equals\' method name : ' + method.toString()
)
do {
noc;
}
}
-- A Measure instance if the class violates the rule 'AvoidUsingShortType'
rule MeasureAvoidUsingShortType(typeShort : java!VariableDeclarationStatement) {
to
......
......@@ -30,3 +30,65 @@ helper def: doNotExtendJavaLangThrowable() : Set(smm!Measure) =
-- collect all results and send an error message
->collect(it4|thisModule.MeasureDoNotExtendJavaLangThrowable(it4))
;
--------------------------------------------- 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();
--------------------------------------------------------------------------------------------------------
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment