From cf11da091d6fb2678107796d64e2d0a208bbee05 Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Mon, 25 Nov 2019 11:47:35 +0100 Subject: [PATCH 01/10] ShortInstantiation LongInstantiation And DoNotExtend JavaLangThrowable --- input/long-instantiation.xmi | 116 +++++++++++++++++++++++++++++++++ input/short-instantiation.xmi | 116 +++++++++++++++++++++++++++++++++ input/throwable.xmi | 43 ++++++++++++ src/main/atl/analysis.atl | 73 ++++++++++++++++++++- src/main/atl/bestPractices.atl | 14 +++- src/main/atl/design.atl | 24 +++++++ 6 files changed, 384 insertions(+), 2 deletions(-) create mode 100644 input/long-instantiation.xmi create mode 100644 input/short-instantiation.xmi create mode 100644 input/throwable.xmi diff --git a/input/long-instantiation.xmi b/input/long-instantiation.xmi new file mode 100644 index 0000000..d52be82 --- /dev/null +++ b/input/long-instantiation.xmi @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/short-instantiation.xmi b/input/short-instantiation.xmi new file mode 100644 index 0000000..17862ad --- /dev/null +++ b/input/short-instantiation.xmi @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/throwable.xmi b/input/throwable.xmi new file mode 100644 index 0000000..ee8e991 --- /dev/null +++ b/input/throwable.xmi @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index c042db7..46173ed 100644 --- a/src/main/atl/analysis.atl +++ b/src/main/atl/analysis.atl @@ -53,6 +53,8 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) = thisModule.tooManyFields(), thisModule.tooManyMethods(), thisModule.returnFromFinallyBlock(), + thisModule.longInstantiation(), + thisModule.shortInstantiation(), -- Performance rules -- @@ -67,7 +69,11 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) = -- Error prone rules -- - thisModule.missingBreakInSwitch() + thisModule.missingBreakInSwitch(), + + -- Best practicices rules + thisModule.doNotExtendJavaLangThrowable() + }; @@ -249,6 +255,71 @@ rule MeasureTooFewBranchesForASwitchStatement(switchStatement: java!SwitchStatem tooFewBranchesForASwitchStatement; } } + +--------------------------------------------- ShortInstantiation --------------------------------------------- +-- A Measure instance if the class violates the rule 'ShortInstantiation'. +rule MeasureShortInstantiation(variable : java!CompilationUnit) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ShortInstantiation', + shortDescription <- 'Calling new Short() causes memory allocation that can be avoided by the static Short.valueOf(). It makes use of an internal cache that recycles earlier instances making it more memory efficient. Note that new Short() is deprecated since JDK 9 for that reason.' + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' an instantiation of Short must be Short.ValueOf().' + ) + + do { + noc; + } +} + + +--------------------------------------------- LongInstantiation --------------------------------------------- +-- A Measure instance if the class violates the rule 'LongInstantiation'. +rule MeasureLongInstantiation(variable : java!CompilationUnit) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'LongInstantiation', + shortDescription <- 'Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf(). It makes use of an internal cache that recycles earlier instances making it more memory efficient. Note that new Long() is deprecated since JDK 9 for that reason.' + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' an instantiation of Long must be Long.ValueOf().' + ) + + do { + noc; + } +} + +--------------------------------------------- DoNotExtendJavaLangThrowable --------------------------------------------- +-- A Measure instance if the class violates the rule DoNotExtendJavaLangThrowable. +rule MeasureDoNotExtendJavaLangThrowable(variable : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'DoNotExtendJavaLangThrowable ', + shortDescription <- 'Extend Exception or RuntimeException instead of Throwable.' + + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' should not extend Throwable but RuntimeException or Exception .' + ) + + do { + noc; + } +} -- Returns the number of branches in the switch statement passed in parameter helper def: nbBranchesOfASwitchStatement(switchStatement:java!SwitchStatement) : Integer = diff --git a/src/main/atl/bestPractices.atl b/src/main/atl/bestPractices.atl index 15d0333..0f23253 100644 --- a/src/main/atl/bestPractices.atl +++ b/src/main/atl/bestPractices.atl @@ -46,4 +46,16 @@ helper def: avoidFieldNameMatchingMethodName() : Set(smm!Measure) = else res endif ) -); \ No newline at end of file +); + +--------------------------------------------- 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) + -- select all class who extend Throwable + ->select(it2| it2.superClass.type.name = 'Throwable') + -- collect all results and send an error message + ->collect(it3|thisModule.MeasureDoNotExtendJavaLangThrowable(it3)) + ; \ No newline at end of file diff --git a/src/main/atl/design.atl b/src/main/atl/design.atl index 17fe050..6b03182 100644 --- a/src/main/atl/design.atl +++ b/src/main/atl/design.atl @@ -48,3 +48,27 @@ helper def: returnFromFinallyBlock() : Set(smm!Measure) = endif ); +--------------------------------------------- ShortInstantiation --------------------------------------------- + +-- Rule for metrics ShortInstantiation : return the set of class Measures that violates the rule. +helper def: shortInstantiation() : Set(smm!Measure) = + -- Take all ClassInstanceCreation with allInstances() + java!ClassInstanceCreation.allInstances() + -- Select all class who declare a Short type + ->select(it| it.method.oclIsTypeOf(java!ConstructorDeclaration) and it.method.name = 'Short') + -- collect every results and send an error message + ->collect(it2|thisModule.MeasureLongInstantiation(it2.originalCompilationUnit)) + ; + +--------------------------------------------- LongInstantiation --------------------------------------------- + +-- Rule for metricsLongInstantiation : return the set of class Measures that violates the rule. +helper def: longInstantiation() : Set(smm!Measure) = + -- Take all ClassInstanceCreation with allInstances() + java!ClassInstanceCreation.allInstances() + -- Select all class who declare a Long type + ->select(it| it.method.oclIsTypeOf(java!ConstructorDeclaration) and it.method.name = 'Long') + -- collect every results and send an error message + ->collect(it2|thisModule.MeasureLongInstantiation(it2.originalCompilationUnit)) + ; + -- GitLab From a9dbbc7b857c46348baab62e46bf8ff8789eafa0 Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Wed, 27 Nov 2019 16:00:58 +0100 Subject: [PATCH 02/10] ShortInstantiation 1pts & LongInstantiation 1pts & DoNotExtendJavaLangThrowable 2pts --- src/main/atl/bestPractices.atl | 12 ------------ src/main/atl/design.atl | 23 ----------------------- src/main/atl/errorProne.atl | 12 +++++++++++- src/main/atl/performance.atl | 25 +++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/main/atl/bestPractices.atl b/src/main/atl/bestPractices.atl index b0e0d03..3af3f5c 100644 --- a/src/main/atl/bestPractices.atl +++ b/src/main/atl/bestPractices.atl @@ -40,15 +40,3 @@ helper def: avoidFieldNameMatchingMethodName() : Set(smm!Measure) = endif ) ); - ---------------------------------------------- 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) - -- select all class who extend Throwable - ->select(it2| it2.superClass.type.name = 'Throwable') - -- collect all results and send an error message - ->collect(it3|thisModule.MeasureDoNotExtendJavaLangThrowable(it3)) - ; \ No newline at end of file diff --git a/src/main/atl/design.atl b/src/main/atl/design.atl index 1d56b41..4e67f37 100644 --- a/src/main/atl/design.atl +++ b/src/main/atl/design.atl @@ -48,29 +48,6 @@ helper def: returnFromFinallyBlock() : Set(smm!Measure) = endif ); ---------------------------------------------- ShortInstantiation --------------------------------------------- - --- Rule for metrics ShortInstantiation : return the set of class Measures that violates the rule. -helper def: shortInstantiation() : Set(smm!Measure) = - -- Take all ClassInstanceCreation with allInstances() - java!ClassInstanceCreation.allInstances() - -- Select all class who declare a Short type - ->select(it| it.method.oclIsTypeOf(java!ConstructorDeclaration) and it.method.name = 'Short') - -- collect every results and send an error message - ->collect(it2|thisModule.MeasureLongInstantiation(it2.originalCompilationUnit)) - ; - ---------------------------------------------- LongInstantiation --------------------------------------------- - --- Rule for metricsLongInstantiation : return the set of class Measures that violates the rule. -helper def: longInstantiation() : Set(smm!Measure) = - -- Take all ClassInstanceCreation with allInstances() - java!ClassInstanceCreation.allInstances() - -- Select all class who declare a Long type - ->select(it| it.method.oclIsTypeOf(java!ConstructorDeclaration) and it.method.name = 'Long') - -- collect every results and send an error message - ->collect(it2|thisModule.MeasureLongInstantiation(it2.originalCompilationUnit)) - ; --------------------------------------------- returnEmptyArrayRatherThanNull --------------------------------------------- -- Helper for issue ReturnEmptyArrayRatherThanNull : returns a Measure for each method that returns an array rather than null helper def: returnEmptyArrayRatherThanNull() : Set(smm!Measure) = diff --git a/src/main/atl/errorProne.atl b/src/main/atl/errorProne.atl index 200a48c..f967a70 100644 --- a/src/main/atl/errorProne.atl +++ b/src/main/atl/errorProne.atl @@ -17,4 +17,14 @@ helper def: switchStatementContainsMissingBreaks(ss: java!SwitchStatement): Bool (thisModule.nbBranchesOfASwitchStatement(ss) - thisModule.nbIntentionalFallThroughOfASwitchStatement(ss)) > thisModule.nbBreakStatementOfASwitchStatement(ss) ; --------------------------------------------------------------------------------------------------------- \ No newline at end of file +--------------------------------------------- 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) + -- select all class who extend Throwable + ->select(it2| it2.superClass.type.name = 'Throwable') + -- collect all results and send an error message + ->collect(it3|thisModule.MeasureDoNotExtendJavaLangThrowable(it3)) + ; \ No newline at end of file diff --git a/src/main/atl/performance.atl b/src/main/atl/performance.atl index 774e956..4dde186 100644 --- a/src/main/atl/performance.atl +++ b/src/main/atl/performance.atl @@ -86,6 +86,7 @@ helper def: avoidThrowingNewInstanceOfSameException() : Set(smm!Measure) = endif ); + --------------------------------------------- AvoidUsingShortType--------------------------------------------- --Detects the use of the primitive type 'short' for rule AvoidUsingShortType. Prefer the use of 'int' instead of 'short'. @@ -93,3 +94,27 @@ helper def: avoidUsingShortType() : Set(smm!Measure) = java!VariableDeclarationStatement.allInstances() ->select(variable | variable.type.type.name = 'short') ->collect(variable | thisModule.MeasureAvoidUsingShortType(variable)); + +--------------------------------------------- ShortInstantiation --------------------------------------------- + +-- Rule for metrics ShortInstantiation : return the set of class Measures that violates the rule. +helper def: shortInstantiation() : Set(smm!Measure) = + -- Take all ClassInstanceCreation with allInstances() + java!ClassInstanceCreation.allInstances() + -- Select all class who declare a Short type + ->select(it| it.method.oclIsTypeOf(java!ConstructorDeclaration) and it.method.name = 'Short') + -- collect every results and send an error message + ->collect(it2|thisModule.MeasureLongInstantiation(it2.originalCompilationUnit)) + ; + +--------------------------------------------- LongInstantiation --------------------------------------------- + +-- Rule for metricsLongInstantiation : return the set of class Measures that violates the rule. +helper def: longInstantiation() : Set(smm!Measure) = + -- Take all ClassInstanceCreation with allInstances() + java!ClassInstanceCreation.allInstances() + -- Select all class who declare a Long type + ->select(it| it.method.oclIsTypeOf(java!ConstructorDeclaration) and it.method.name = 'Long') + -- collect every results and send an error message + ->collect(it2|thisModule.MeasureLongInstantiation(it2.originalCompilationUnit)) + ; -- GitLab From 7b23db9290987e631be6a142c5a32171cf3988bb Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Thu, 28 Nov 2019 14:38:31 +0100 Subject: [PATCH 03/10] fix #736 : DoNotExtendJavaLangError 2pts & optimisation on DoNotExtendJavaLangThrowable --- input/do-not-extend-java-lang-error.xmi | 47 +++++++++++++++++++++++++ input/throwable.xmi | 14 ++++++-- src/main/atl/analysis.atl | 25 ++++++++++++- src/main/atl/design.atl | 14 ++++++++ src/main/atl/errorProne.atl | 8 +++-- 5 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 input/do-not-extend-java-lang-error.xmi diff --git a/input/do-not-extend-java-lang-error.xmi b/input/do-not-extend-java-lang-error.xmi new file mode 100644 index 0000000..4436911 --- /dev/null +++ b/input/do-not-extend-java-lang-error.xmi @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/throwable.xmi b/input/throwable.xmi index ee8e991..2e18982 100644 --- a/input/throwable.xmi +++ b/input/throwable.xmi @@ -3,10 +3,11 @@ + - + @@ -14,14 +15,21 @@ + - - + + + + + + + + diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index 6c1f575..d4c58f5 100644 --- a/src/main/atl/analysis.atl +++ b/src/main/atl/analysis.atl @@ -73,9 +73,10 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) = -- Error prone rules -- thisModule.missingBreakInSwitch(), + thisModule.doNotExtendJavaLangThrowable(), + thisModule.doNotExtendJavaLangError(), -- Best practicices rules - thisModule.doNotExtendJavaLangThrowable(), thisModule.avoidThrowingNewInstanceOfSameException() }; @@ -643,3 +644,25 @@ rule MeasureEmptyStatementBlock(block : java!Block) { noc; } } + +--------------------------------------------- DoNotExtendJavaLangError --------------------------------------------- +-- A Measure instance if the class violates the rule DoNotExtendJavaLangError. +rule MeasureDoNotExtendJavaLangError(variable : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'DoNotExtendJavaLangError ', + shortDescription <- 'Errors are system exceptions. Do not extend them.' + + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' Do not extend Error, Errors are system exceptions.' + ) + + do { + noc; + } +} diff --git a/src/main/atl/design.atl b/src/main/atl/design.atl index 4e67f37..463b836 100644 --- a/src/main/atl/design.atl +++ b/src/main/atl/design.atl @@ -68,3 +68,17 @@ helper def: excessiveParameterList() : Set(smm!Measure) = -> select (method | method.body <> OclUndefined and method.parameters.size() > 10) -> collect(method | thisModule.MesureExcessiveParameterList(method)); + +--------------------------------------------- DoNotExtendJavaLangError --------------------------------------------- + +-- Rule for metrics DoNotExtendJavaLangDoNotExtendJavaLangError +helper def: doNotExtendJavaLangError() : Set(smm!Measure) = + -- select all class with a superTyper + java!ClassDeclaration.allInstances()->select(it | (it.superClass <> OclUndefined)) + -- select all class create by the user + ->select(it2| it2.proxy = false) + -- select all class who extend Error + ->select(it3| it3.superClass.type.name = 'Error') + -- collect all results and send an error message + ->collect(it4|thisModule.MeasureDoNotExtendJavaLangError(it4)) + ; diff --git a/src/main/atl/errorProne.atl b/src/main/atl/errorProne.atl index f967a70..fd96735 100644 --- a/src/main/atl/errorProne.atl +++ b/src/main/atl/errorProne.atl @@ -23,8 +23,10 @@ helper def: switchStatementContainsMissingBreaks(ss: java!SwitchStatement): Bool helper def: doNotExtendJavaLangThrowable() : Set(smm!Measure) = -- select all class with a superTyper java!ClassDeclaration.allInstances()->select(it | it.superClass <> OclUndefined) + -- select all class create by the user + ->select(it2| it2.proxy = false) -- select all class who extend Throwable - ->select(it2| it2.superClass.type.name = 'Throwable') + ->select(it3| it3.superClass.type.name = 'Throwable') -- collect all results and send an error message - ->collect(it3|thisModule.MeasureDoNotExtendJavaLangThrowable(it3)) - ; \ No newline at end of file + ->collect(it4|thisModule.MeasureDoNotExtendJavaLangThrowable(it4)) + ; -- GitLab From 42aa72335f4bac667bb3e2b826611e0fe0768cc5 Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Thu, 28 Nov 2019 15:01:20 +0100 Subject: [PATCH 04/10] fix #736 : DoNotExtendJavaLangError 2pts & optimisation on DoNotExtendJavaLangThrowable --- input/{throwable.xmi => do-not-extend-java-lang-lang.xmi} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename input/{throwable.xmi => do-not-extend-java-lang-lang.xmi} (100%) diff --git a/input/throwable.xmi b/input/do-not-extend-java-lang-lang.xmi similarity index 100% rename from input/throwable.xmi rename to input/do-not-extend-java-lang-lang.xmi -- GitLab From 5db88ed4823c975521c0b78a8e9bea1020988edf Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Thu, 28 Nov 2019 15:03:28 +0100 Subject: [PATCH 05/10] fix #736 : DoNotExtendJavaLangError 2pts & optimisation on DoNotExtendJavaLangThrowable --- ...d-java-lang-lang.xmi => do-not-extend-java-lang-throwable.xmi} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename input/{do-not-extend-java-lang-lang.xmi => do-not-extend-java-lang-throwable.xmi} (100%) diff --git a/input/do-not-extend-java-lang-lang.xmi b/input/do-not-extend-java-lang-throwable.xmi similarity index 100% rename from input/do-not-extend-java-lang-lang.xmi rename to input/do-not-extend-java-lang-throwable.xmi -- GitLab From 6857e51ac7160552b18a07e6aa23ab6d6d12d0fa Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Tue, 3 Dec 2019 16:58:54 +0100 Subject: [PATCH 06/10] fix #679, ExtendsObject 2pts --- input/extends-object.xmi | 28 ++++++++++++++++++++++++++++ src/main/atl/analysis.atl | 11 ++++++----- src/main/atl/codestyle.atl | 17 ++++++++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 input/extends-object.xmi diff --git a/input/extends-object.xmi b/input/extends-object.xmi new file mode 100644 index 0000000..b7af943 --- /dev/null +++ b/input/extends-object.xmi @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index d4c58f5..5bf27aa 100644 --- a/src/main/atl/analysis.atl +++ b/src/main/atl/analysis.atl @@ -46,6 +46,7 @@ helper def: allMeasures(project : java!Model): Set(smm!Measure) = thisModule.shortMethodName(), thisModule.tooManyStaticImports(), thisModule.AvoidDollarSigns() , + thisModule.extendsObject(), -- Design rules -- @@ -645,21 +646,21 @@ rule MeasureEmptyStatementBlock(block : java!Block) { } } ---------------------------------------------- DoNotExtendJavaLangError --------------------------------------------- +--------------------------------------------- ExtendsObject --------------------------------------------- -- A Measure instance if the class violates the rule DoNotExtendJavaLangError. -rule MeasureDoNotExtendJavaLangError(variable : java!ClassDeclaration) { +rule MeasureExtendsObject(variable : java!ClassDeclaration) { to om: smm!ObservedMeasure ( measure <- noc, measurements <- measurement ), noc: smm!DimensionalMeasure ( - name <- 'DoNotExtendJavaLangError ', - shortDescription <- 'Errors are system exceptions. Do not extend them.' + name <- 'ExtendsObject ', + shortDescription <- 'No need to explicitly extend Object.' ), measurement: smm!DirectMeasurement ( - error<-'In the Class '+ variable.name + ' Do not extend Error, Errors are system exceptions.' + error<-'In the Class '+ variable.name + ' No need to explicitly extend Object.' ) do { diff --git a/src/main/atl/codestyle.atl b/src/main/atl/codestyle.atl index 4db2163..8c28e93 100644 --- a/src/main/atl/codestyle.atl +++ b/src/main/atl/codestyle.atl @@ -20,4 +20,19 @@ helper def: tooManyStaticImports() : Set(smm!Measure) = java!CompilationUnit.allInstances() -- Add a new measurement if there are more than 4 static imports in the file. -> select (c | c.types.first().oclIsTypeOf(java!ClassDeclaration) and c.imports->select(i | i.static)->size() > 4) - -> collect(c | thisModule.MesureTooManyStaticImports(c)); \ No newline at end of file + -> collect(c | thisModule.MesureTooManyStaticImports(c)); + + +--------------------------------------------- ExtendsObject --------------------------------------------- + +-- Rule for metrics ExtendsObject +helper def: extendsObject() : Set(smm!Measure) = + -- select all class with a superTyper + java!ClassDeclaration.allInstances()->select(it | (it.superClass <> OclUndefined)) + -- select all class create by the user + ->select(it2| it2.proxy = false) + -- select all class who extend Error + ->select(it3| it3.superClass.type.name = 'Object') + -- collect all results and send an error message + ->collect(it4|thisModule.MeasureExtendsObject(it4)) + ; \ No newline at end of file -- GitLab From b639585007ea660238925d74c28d271bd2dc61cf Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Tue, 3 Dec 2019 17:08:46 +0100 Subject: [PATCH 07/10] fix #679, ExtendsObject 2pts --- input/extends-object.xmi | 19 ++++++++++++++----- src/main/atl/codestyle.atl | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/input/extends-object.xmi b/input/extends-object.xmi index b7af943..5e0dda4 100644 --- a/input/extends-object.xmi +++ b/input/extends-object.xmi @@ -2,16 +2,24 @@ + + + + + + + + - + - - - + + + @@ -24,5 +32,6 @@ - + + diff --git a/src/main/atl/codestyle.atl b/src/main/atl/codestyle.atl index 8c28e93..a7bea99 100644 --- a/src/main/atl/codestyle.atl +++ b/src/main/atl/codestyle.atl @@ -32,7 +32,7 @@ helper def: extendsObject() : Set(smm!Measure) = -- select all class create by the user ->select(it2| it2.proxy = false) -- select all class who extend Error - ->select(it3| it3.superClass.type.name = 'Object') + ->select(it3| it3.superClass.type.name = 'Object' or it3.superClass.type.name = 'java.lang.Object' ) -- collect all results and send an error message ->collect(it4|thisModule.MeasureExtendsObject(it4)) ; \ No newline at end of file -- GitLab From 5d6d6d0fc1f1754272df981ccd86d3ce10d37c23 Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Tue, 3 Dec 2019 17:38:45 +0100 Subject: [PATCH 08/10] fix #679, ExtendsObject 2pts --- src/main/atl/analysis.atl | 723 +++++++++++++++++++++++++++++++++++++ src/main/atl/codestyle.atl | 50 +++ src/main/atl/design.atl | 88 +++++ 3 files changed, 861 insertions(+) diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index e69de29..770d179 100644 --- a/src/main/atl/analysis.atl +++ b/src/main/atl/analysis.atl @@ -0,0 +1,723 @@ +-- @nsURI smm=http://www.eclipse.org/MoDisco/SMM/1.0.Beta2/smm +-- @nsURI java=http://www.eclipse.org/MoDisco/Java/0.2.incubation/java + + +module analysis; +create OUT: smm from IN: java; + +uses design; +uses codestyle; +uses multithreading; +uses performance; +uses bestPractices; +uses documentation; +uses errorProne; + + +rule Java2SMM { + from + project: java!Model + to + model: smm!SmmModel ( + name <- project.name, + librairies <- lib + ), + lib: smm!MeasureLibrary ( + name <- 'Java metrics', + measureElements <- thisModule.allMeasures(project) + ) +} + +helper def: allMeasures(project : java!Model): Set(smm!Measure) = + + Set{ + -- Example rule + -- + thisModule.numberOfClasses(), + + -- Multithreading rules + -- + thisModule.dontCallThreadRun(), + thisModule.avoidThreadGroup(), + thisModule.useNotifyAllInsteadOfNotify(), + + -- Code Style rules + -- + thisModule.shortMethodName(), + thisModule.tooManyStaticImports(), + thisModule.AvoidDollarSigns(), + thisModule.shortClassName(), + thisModule.extendsObject(), + + -- Design rules + -- + thisModule.tooManyFields(), + thisModule.tooManyMethods(), + thisModule.returnFromFinallyBlock(), + thisModule.longInstantiation(), + thisModule.shortInstantiation(), + thisModule.returnEmptyArrayRatherThanNull(), + thisModule.excessiveParameterList(), + thisModule.finalFieldCouldBeStatic(), + + -- Performance rules + -- + thisModule.uselessStringValueOf(), + thisModule.tooFewBranchesForASwitchStatement(), + thisModule.useIndexOfChar(), + thisModule.avoidPrintStackTrace(), + thisModule.avoidUsingShortType(), + thisModule.emptyStatementBlock(), + + -- Documentation rules + -- + thisModule.commentRequired(), + + -- Error prone rules + -- + thisModule.missingBreakInSwitch(), + thisModule.doNotExtendJavaLangThrowable(), + thisModule.doNotExtendJavaLangError(), + + -- Best practicices rules + thisModule.avoidThrowingNewInstanceOfSameException() +}; + + +-- creates a new Measure when String.valueOf is called to append its argument to a string +rule MeasureUselessStringValueOf(method : java!MethodInvocation) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Useless string value of', + shortDescription <- 'No need to call String.valueOf to append to a string; just use the valueOf() argument directly.' + ), + measurement: smm!DirectMeasurement ( + error<- 'The class '+ method.originalCompilationUnit.name + ' violates the rule useless string value of.' + ) + do { + noc; + } +} + +------------------------------------------------------------------------------------------ + +-- A Measure instance if the class violates the rule 'AvoidDollarSigns'. +rule MesureAvoidDollarSigns(node : java!ASTNode) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'AvoidDollarSigns', + shortDescription <- 'Avoid using dollar signs in variable/method/class/interface names.' + ), + measurement: smm!DirectMeasurement ( + error <- node.name + ' has dollar in ' + node.originalCompilationUnit.name + ) + do { + noc; + } +} + + + +-- A Measure instance if the class violates the rule 'ShortMethodName'. +rule MesureShortMethodName(method : java!MethodDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ShortMethodName', + shortDescription <- 'Method names names that are very short are not helpful to the reader.' + ), + measurement: smm!DirectMeasurement ( + error <- method.name + ' is too short in ' + method.originalCompilationUnit.name + ) + do { + noc; + } +} + + +-- A Measure instance if the class violates the rule 'ShortClassName'. +rule MesureShortClassName(class : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ShortClassName', + shortDescription <- 'Short Classnames with fewer than e.g. five characters are not recommended.' + ), + measurement: smm!DirectMeasurement ( + error <- 'The Class ' + class.name + ' is too short.' + ) + do { + noc; + } +} + + +-- creates a new Measure when Thread.run() is used instead of Thread.start() +rule MeasureDontCallThreadRun(method : java!MethodInvocation) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Don t call Thread.run()', + shortDescription <- 'Explicitly calling Thread.run() method will execute in the caller s thread of control. Instead, call Thread.start() for the intended behavior.' + ), + measurement: smm!DirectMeasurement ( + error<-'The class '+ method.originalCompilationUnit.name + ' violates the rule don t call Thread.run().' + ) + do { + noc; + } +} + + +-- A Measure instance if the class violates the rule 'TooManyFields'. +rule MesureTooManyFields(class : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'TooManyFields', + shortDescription <- 'Classes that have too many fields can become unwieldy and could be redesigned to have fewer field.' + ), + measurement: smm!DirectMeasurement ( + error <- class.originalCompilationUnit.name + ' have too many fields' + ) + do { + noc; + } +} + + +-- creates a new Measure when the parameter of String.indexOf(char) is not of type char when checking for the index of a single character +rule MeasureUseIndexOfChar(method : java!MethodInvocation) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Use index of char', + shortDescription <- 'Use String.indexOf(char) when checking for the index of a single character; it executes faster.' + ), + measurement: smm!DirectMeasurement ( + error<- 'The class '+ method.originalCompilationUnit.name + ' violates the rule index of char.' + ) + do { + noc; + } +} + +------------------------------------------------------------------------------------------ + +rule numberOfClasses() { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Number of Classes' + ), + measurement: smm!DirectMeasurement ( + value <- java!ClassDeclaration.allInstances() -> reject(each | each.isProxy()) -> size() + ) + + do { + noc; + } +} + + +-- Rule that creates an instance of Measure for the switch statement with too few branches passed in parameter +rule MeasureTooFewBranchesForASwitchStatement(switchStatement: java!SwitchStatement) { + to + om: smm!ObservedMeasure ( + measure <- tooFewBranchesForASwitchStatement, + measurements <- measurement + ), + tooFewBranchesForASwitchStatement: smm!DimensionalMeasure ( + name <- 'TooFewBranchesForASwitchStatement', + shortDescription <- 'Switch statements are intended to be used to support complex branching behaviour. Using a switch for only a few cases is ill-advised, since switches are not as easy to understand as if-then statements. In these cases use the if-then statement to increase code readability.' + ), + measurement: smm!DirectMeasurement ( + error <- 'Too few branches for SwitchStatement in Class : ' + switchStatement.originalCompilationUnit.name, + value <- thisModule.nbBranchesOfASwitchStatement(switchStatement) + ) + do { + tooFewBranchesForASwitchStatement; + } +} + +--------------------------------------------- ShortInstantiation --------------------------------------------- +-- A Measure instance if the class violates the rule 'ShortInstantiation'. +rule MeasureShortInstantiation(variable : java!CompilationUnit) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ShortInstantiation', + shortDescription <- 'Calling new Short() causes memory allocation that can be avoided by the static Short.valueOf(). It makes use of an internal cache that recycles earlier instances making it more memory efficient. Note that new Short() is deprecated since JDK 9 for that reason.' + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' an instantiation of Short must be Short.ValueOf().' + ) + + do { + noc; + } +} + + +--------------------------------------------- LongInstantiation --------------------------------------------- +-- A Measure instance if the class violates the rule 'LongInstantiation'. +rule MeasureLongInstantiation(variable : java!CompilationUnit) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'LongInstantiation', + shortDescription <- 'Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf(). It makes use of an internal cache that recycles earlier instances making it more memory efficient. Note that new Long() is deprecated since JDK 9 for that reason.' + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' an instantiation of Long must be Long.ValueOf().' + ) + + do { + noc; + } +} + +--------------------------------------------- DoNotExtendJavaLangThrowable --------------------------------------------- +-- A Measure instance if the class violates the rule DoNotExtendJavaLangThrowable. +rule MeasureDoNotExtendJavaLangThrowable(variable : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'DoNotExtendJavaLangThrowable ', + shortDescription <- 'Extend Exception or RuntimeException instead of Throwable.' + + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' should not extend Throwable but RuntimeException or Exception .' + ) + + do { + noc; + } +} + +-- Returns the number of branches in the switch statement passed in parameter +helper def: nbBranchesOfASwitchStatement(switchStatement:java!SwitchStatement) : Integer = + switchStatement.statements->select(each | each.oclIsTypeOf(java!SwitchCase)).size() +; + +-- Returns the number of Fields in Class +helper def: numberFieldsInClasse(s:java!ClassDeclaration) : Integer = + -- Return the number of FieldDeclaration in a class. + s.bodyDeclarations-> select(r | r.oclIsTypeOf(java!FieldDeclaration))->size(); + +-- Returns the number of breaks of the switch statement passed in parameter +helper def: nbBreakStatementOfASwitchStatement(ss:java!SwitchStatement) : Integer = + ss.statements->select(each | each.oclIsTypeOf(java!BreakStatement)).size() +; + +-- Returns the number of expressions of the switch statement passed in parameter +helper def: nbExpressionsStatementOfASwitchStatement(ss:java!SwitchStatement) : Integer = + ss.statements->select(each | each.oclIsTypeOf(java!ExpressionStatement)).size() +; + +-- Returns the number of intentional fall-through (empty switch cases) of a switchStatement +helper def: nbIntentionalFallThroughOfASwitchStatement(ss:java!SwitchStatement) : Integer = + thisModule.nbBranchesOfASwitchStatement(ss) - thisModule.nbExpressionsStatementOfASwitchStatement(ss) +; + +-- Creates an instance of Measure for the switch statement missing one or more break statements +-- To test, use input model : missing-break-in-switch.xmi +rule createMeasureForMissingBreakInSwitch(ss: java!SwitchStatement) { + to + om: smm!ObservedMeasure ( + measure <- missingBreakInSwitch, + measurements <- measurement + ), + missingBreakInSwitch: smm!DimensionalMeasure ( + name <- 'Missing break in Switch', + shortDescription <- 'Switch statements without break or return statements for each case option may indicate problematic behaviour. Empty cases are ignored as these indicate an intentional fall-through.' + ), + measurement: smm!DirectMeasurement ( + error <- 'Missing breaks in Switch in Class : ' + ss.originalCompilationUnit.name + '', + -- Indicates the number of breaks missing in the switch statement + value <- (thisModule.nbBranchesOfASwitchStatement(ss) - thisModule.nbIntentionalFallThroughOfASwitchStatement(ss)) - thisModule.nbBreakStatementOfASwitchStatement(ss) + ) + do { + missingBreakInSwitch; + } +} + +-- A Measure instance if the class violates the rule 'TooManyMethods'. +rule MesureTooManyMethods(class : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'TooManyMethods', + shortDescription <- 'A class with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to have more fine grained objects.' + ), + measurement: smm!DirectMeasurement ( + error <- class.originalCompilationUnit.name + ' has too many methods' + ) + + do { + noc; + } +} + +-- A Measure instance if the class violates the rule ReturnFromFinallyBlock. +rule MesureReturnFromFinallyBlock(method : java!MethodDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'returnFromFinallyBlock', + shortDescription <- 'Avoid returning from a finally block, this can discard exceptions.' + ), + measurement: smm!DirectMeasurement ( + error <- 'The method ' + method.name + ' in the class ' + method.originalCompilationUnit.name + ' has a return statement in a finally block.' + ) + do { + noc; + } +} + +-- A Measure instance if the class violates the rule 'TooManyStaticImports'. +rule MesureTooManyStaticImports(class : java!CompilationUnit) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'TooManyStaticImports', + shortDescription <- 'If you overuse the static import feature, it can make your program unreadable and unmaintainable, polluting its namespace with all the static members you import. Readers of your code (including you, a few months after you wrote it) will not know which class a static member comes from (Sun 1.5 Language Guide).' + ), + measurement: smm!DirectMeasurement ( + error <- class.name + ' has too many static imports' + ) + do { + noc; + } +} + +-- creates a new Measure when the method printStackTrace is used +rule MeasureAvoidPrintStackTrace(method : java!MethodInvocation) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Avoid print stack trace', + shortDescription <- 'Avoid printStackTrace(); use a logger call instead.' + ), + measurement: smm!DirectMeasurement ( + error<- 'The class '+ method.originalCompilationUnit.name + ' violates the rule avoid print stack trace.' + ) + do { + noc; + } +} + +-- creates a new Measure when Thread.run() is used instead of Thread.start() +rule measureAvoidThreadGroup(variable : java!VariableDeclarationFragment) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimnsionalMeasure ( + name <- 'Avoid Thread Group', + shortDescription <- 'Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environment it contains methods that are not thread-safe.' + ), + measurement: smm!DirectMeasurement ( + error<-'The class '+ variable.originalCompilationUnit.name + ' violates the rule avoid thread group.' + ) + + do { + noc; + } +} + +--------------------------------------------- AvoidThrowingNewInstanceOfSameException --------------------------------------------- +-- A Measure instance if the class violates the rule 'AvoidThrowingNewInstanceOfSameException'. +rule MeasureAvoidThrowingNewInstanceOfSameException(catch : java!CatchClause) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'AvoidThrowingNewInstanceOfSameException', + shortDescription <- 'Catch blocks that merely rethrow a caught exception wrapped inside a new instance of the same type only add to code size and runtime complexity.' + ), + measurement: smm!DirectMeasurement ( + error <-'The class '+ catch.originalCompilationUnit.name + ' has a method that rethrows a caught exception wrapped inside a new instance of the same type.' + ) + do { + noc; + } +} + +--------------------------------------------- ReturnEmptyArrayRatherThanNull --------------------------------------------- +-- creates a new Measure when a method returns an empty method rather than null +rule MesureReturnEmptyArrayRatherThanNull(method : java!ReturnStatement) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ReturnEmptyArrayRatherThanNull', + shortDescription <- 'For any method that returns an array, it is a better to return an empty array rather than a null reference.' + ), + measurement: smm!DirectMeasurement ( + error <- 'A method in the class ' + method.originalCompilationUnit.name + ' returns null instead of an empty array.' + ) + do { + noc; + } +} + +--------------------------------------------- ExcessiveParameterList --------------------------------------------- +-- creates a new Measure when a method has more than 10 parameters +rule MesureExcessiveParameterList(method : java!MethodDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ExcessiveParameterList', + shortDescription <- 'Methods with numerous parameters are a challenge to maintain, especially if most of them share the same datatype.' + ), + measurement: smm!DirectMeasurement ( + error<-'The method '+ method.name + ' in the class ' + method.originalCompilationUnit.name + ' has an excessive parameter list.' + ) + do { + noc; + } +} + +-- A Measure instance if the class violates the rule 'AvoidFieldNameMatchingMethodName'. +rule MesureAvoidFieldNameMatchingMethodName(class : java!ClassDeclaration, method : java!MethodDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'AvoidFieldNameMatchingMethodName', + shortDescription <- 'It can be confusing to have a field name with the same name as a method. While this is permitted, having information (field) and actions (method) is not clear naming. Developers versed in Smalltalk often prefer this approach as the methods denote accessor methods.' + ), + measurement: smm!DirectMeasurement ( + error <- 'In the ' + class.name + ' class you have an field and an method with the same name : '+ method.name + ) + do { + noc; + } +} + +-- Creates a measure instance when rule 'UseNotifyAllInsteadOfNotify' is violated +rule MeasureUseNotifyAllInsteadOfNotify(method: java!MethodInvocation) { + to + om: smm!ObservedMeasure ( + measure <- useNotifyAllInsteadOfNotify, + measurements <- measurement + ), + useNotifyAllInsteadOfNotify: smm!DimensionalMeasure ( + name <- 'UseNotifyAllInsteadOfNotify', + shortDescription <- 'Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead.' + ), + measurement: smm!DirectMeasurement ( + error <- 'Used notify() instead of notifyAll() in Class : ' + method.originalCompilationUnit.name + ) + do { + useNotifyAllInsteadOfNotify; + } +} + +-- Creates a measure instance for each element that violates the rule : CommentRequired +rule MeasureCommentRequired(element: java!BodyDeclaration, violatedProperties: Set(String)) { + to + om: smm!ObservedMeasure ( + measure <- commentRequired, + measurements <- measurement + ), + commentRequired: smm!DimensionalMeasure ( + name <- 'CommentRequired', + shortDescription <- 'Denotes whether comments are required (or unwanted) for specific language elements.' + ), + measurement: smm!DirectMeasurement ( + error <- 'Violated the properties {' + violatedProperties + '} in Class: ' + element.originalCompilationUnit.name + ' -> ' + element.oclType().name + ': ' + element.modifier.visibility + ' ' + thisModule.getBodyDeclarationName(element) + ) + do { + commentRequired; + } +} + +-- Returns the declaration name of a BodyDeclaration +-- In FielDeclaration, the attribute 'name' is NULL, this information is contained in "fragments" instead +helper def: getBodyDeclarationName(elem: java!BodyDeclaration): String = + if (elem.oclIsTypeOf(java!FieldDeclaration)) + then elem.fragments.first().name + else elem.name + endif +; + +-- Returns the AnnotationTypeDeclaration corresponding to the given BodyDeclaration +-- This is necessary because BodyDeclaration.annotations returns Sequence(!IN) +helper def: getAnnotationTypeDeclarationsFromBodyDeclaration(elem: java!BodyDeclaration): Set(java!AnnotationTypeDeclaration) = + java!AnnotationTypeDeclaration.allInstances() + ->select(annotTypeDec | + annotTypeDec.usagesInTypeAccess->exists(usage | + (usage.eContainer().eContainer().name = elem.name) + .and(usage.eContainer().eContainer().modifier.visibility = elem.modifier.visibility) + .and(usage.eContainer().eContainer().originalCompilationUnit.name = elem.originalCompilationUnit.name)) + ) +; + +-- A Measure instance if the class violates the rule 'AvoidUsingShortType' +rule MeasureAvoidUsingShortType(typeShort : java!VariableDeclarationStatement) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Avoid using short type', + shortDescription <- 'Java uses the \'short\' type to reduce memory usage, not to optimize calculation.' + + ' In fact, the JVM does not have any arithmetic capabilities for the short type:' + + ' the JVM must convert the short into an int, do the proper calculation and convert' + + ' the int back to a short. Thus any storage gains found through use of the \'short\' type' + + ' may be offset by adverse impacts on performance.' + ), + measurement: smm!DirectMeasurement ( + error <- 'The class ' + typeShort.originalCompilationUnit.name + ' has \'short\' type variable.' + ) + do { + noc; + } +} + +-- A Measure instance if the class violates the rule 'EmptyStatementBlock' +rule MeasureEmptyStatementBlock(block : java!Block) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'Empty Statement Block', + shortDescription <- 'Empty block statements serve no purpose and should be removed.' + ), + measurement: smm!DirectMeasurement ( + error <- 'The class ' + block.originalCompilationUnit.name + ' has an empty statement block.' + ) + do { + noc; + } +} + +--------------------------------------------- DoNotExtendJavaLangError --------------------------------------------- +-- A Measure instance if the class violates the rule DoNotExtendJavaLangError. +rule MeasureDoNotExtendJavaLangError(variable : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'DoNotExtendJavaLangError ', + shortDescription <- 'Errors are system exceptions. Do not extend them.' + + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' Do not extend Error, Errors are system exceptions.' + ) + + do { + noc; + } +} + +--------------------------------------------- MeasureFinalFieldCouldBeStatic --------------------------------------------- +-- A Measure instance if the class violates the rule MeasureFinalFieldCouldBeStatic. +rule MeasureFinalFieldCouldBeStatic(field : java!FieldDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'FinalFieldCouldBeStatic', + shortDescription <- 'If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead in each object at runtime.' + + ), + measurement: smm!DirectMeasurement ( + error<-'The field '+ field.fragments->collect(i | i.name)->first() + ' could be static in the class ' + field.originalCompilationUnit.name +'.' + ) + + do { + noc; + } +} + + +--------------------------------------------- MeasureExtendsObject --------------------------------------------- +-- A Measure instance if the class violates the rule ExtendsObject. +rule MeasureExtendsObject(variable : java!ClassDeclaration) { + to + om: smm!ObservedMeasure ( + measure <- noc, + measurements <- measurement + ), + noc: smm!DimensionalMeasure ( + name <- 'ExtendsObject', + shortDescription <- 'No need to explicitly extend Object.' + + ), + measurement: smm!DirectMeasurement ( + error<-'In the Class '+ variable.name + ' No need to explicitly extend Object.' + ) + + do { + noc; + } +} diff --git a/src/main/atl/codestyle.atl b/src/main/atl/codestyle.atl index e69de29..abe47be 100644 --- a/src/main/atl/codestyle.atl +++ b/src/main/atl/codestyle.atl @@ -0,0 +1,50 @@ +library codestyle; + +------------------------------------------------------------------------------------------ + +-- Rule for metrics AvoidDollarSigns : return the set of class Measures that violates the rule. +helper def: avoidDollarSigns() : Set(smm!Measure) = + -- Browse through all variable/method/class/interface. + java!TypeDeclaration.allInstances()->union(java!MethodDeclaration.allInstances())->union(java!VariableDeclaration.allInstances())->iterate(i; res : Set(smm!Measure) = Set{} | + -- Add a new measurement if the name contains a dollar. + if i.name.indexOf('$') <> -1 + then res->union(Set{thisModule.MesureAvoidDollarSigns(i)}) + else res + endif + ); + +-- Rule for metrics shortMethodName : return the set of method Measures that violates the rule. +helper def: shortMethodName() : Set(smm!Measure) = + -- Browse through all method, add a new measurement if the size of the method name is less than 3. + java!MethodDeclaration.allInstances() -> reject(each | each.isProxy())->select(i|i.name.size() < 3)->collect(j|thisModule.MesureShortMethodName(j)); + +--------------------------------------------- ShortClassName --------------------------------------------- +-- Rule for metrics shortClassName : return the set of class Measures that violates the rule. +helper def: shortClassName() : Set(smm!Measure) = + java!ClassDeclaration.allInstances() -> reject(each | each.isProxy()) + -> select( i | i.name.size() < 4) + -> collect (i | thisModule.MesureShortClassName(i)); + +--------------------------------------------- TooManyStaticImports --------------------------------------------- + +-- Rule for metrics TooManyStaticImports : return the set of class Measures that violates the rule. +helper def: tooManyStaticImports() : Set(smm!Measure) = + java!CompilationUnit.allInstances() + -- Add a new measurement if there are more than 4 static imports in the file. + -> select (c | c.types.first().oclIsTypeOf(java!ClassDeclaration) and c.imports->select(i | i.static)->size() > 4) + -> collect(c | thisModule.MesureTooManyStaticImports(c)); + + +--------------------------------------------- ExtendsObject --------------------------------------------- + +-- Rule for metrics ExtendsObject +helper def: extendsObject() : Set(smm!Measure) = + -- select all class with a superTyper + java!ClassDeclaration.allInstances()->select(it | (it.superClass <> OclUndefined)) + -- select all class create by the user + ->select(it2| it2.proxy = false) + -- select all class who extend Object + ->select(it3| it3.superClass.type.name = 'java.lang.Object' or it3.superClass.type.name = 'Object') + -- collect all results and send an error message + ->collect(it4|thisModule.MeasureDoNotExtendJavaLangError(it4)) + ; diff --git a/src/main/atl/design.atl b/src/main/atl/design.atl index e69de29..8fb8881 100644 --- a/src/main/atl/design.atl +++ b/src/main/atl/design.atl @@ -0,0 +1,88 @@ +library design; + + +--------------------------------------------- TooManyFields --------------------------------------------- + +-- Rule for metrics TooManyFields : return the set of class Measures that violates the rule. +helper def: tooManyFields() : Set(smm!Measure) = + -- Browse through all class + java!ClassDeclaration.allInstances() -> reject(each | each.isProxy())->select(i | thisModule.numberFieldsInClasse(i) >15)->collect(j | thisModule.MesureTooManyFields(j)); + +--------------------------------------------- TooManyMethods --------------------------------------------- + +-- Rule for metrics TooManyMethods : return the set of class Measures that violates the rule. +helper def: tooManyMethods() : Set(smm!Measure) = + -- Browse through all class + java!ClassDeclaration.allInstances()->reject(each | each.isProxy())->iterate(i; res : Set(smm!Measure) = Set{} | + -- Add a new measurement if there are more than 10 methods in the class. + if i.bodyDeclarations->select(r | r.oclIsTypeOf(java!MethodDeclaration))->reject(without | + without.name.startsWith('get') or without.name.startsWith('set') or without.name.startsWith('is'))->size() > 10 + then res->union(Set{thisModule.MesureTooManyMethods(i)}) + else res + endif + ); + +--------------------------------------------- ReturnFromFinallyBlock --------------------------------------------- +helper def: returnFromFinallyBlock() : Set(smm!Measure) = + java!MethodDeclaration.allInstances() ->iterate(method; methodRes : Set(smm!Measure) = Set{} | + if method.body <> OclUndefined + then method.body.statements -> iterate(methodStatement; methodStatementRes : Set(smm!Measure) = Set{} | + if methodStatement.oclIsTypeOf(java!TryStatement) + then if methodStatement.finally <> OclUndefined + then methodStatement.finally.statements ->iterate(finallyStatement; finallyStatementRes : Set(smm!Measure) = Set{} | + if finallyStatement.oclIsTypeOf(java!ReturnStatement) + then methodRes->union(Set{thisModule.MesureReturnFromFinallyBlock(method)}) + else methodRes + endif + ) + else methodRes + endif + else methodRes + endif + ) + else methodRes + endif + ); + +--------------------------------------------- returnEmptyArrayRatherThanNull --------------------------------------------- +-- Helper for issue ReturnEmptyArrayRatherThanNull : returns a Measure for each method that returns an array rather than null +helper def: returnEmptyArrayRatherThanNull() : Set(smm!Measure) = + java!MethodDeclaration.allInstances() + -> select (method | method.returnType <> OclUndefined and method.body <> OclUndefined) + -> select (method | method.returnType.type.oclIsTypeOf(java!ArrayType)) + -> collect(method | method.body.statements) + -> iterate(statements; statementRes : Set(smm!Measure) = Set{} | + statements + -> select (statement | statement.oclIsTypeOf(java!ReturnStatement) and statement.expression.oclIsTypeOf(java!NullLiteral)) + -> collect(statement | thisModule.MesureReturnEmptyArrayRatherThanNull(statement)) + ); + +--------------------------------------------- ExcessiveParameterList --------------------------------------------- +-- Helper for issue ExcessiveParameterList : return a Measure for each method that has too many parameters. +helper def: excessiveParameterList() : Set(smm!Measure) = + java!MethodDeclaration.allInstances() + -> select (method | method.body <> OclUndefined and method.parameters.size() > 10) + -> collect(method | thisModule.MesureExcessiveParameterList(method)); + + +--------------------------------------------- DoNotExtendJavaLangError --------------------------------------------- + +-- Rule for metrics DoNotExtendJavaLangDoNotExtendJavaLangError +helper def: doNotExtendJavaLangError() : Set(smm!Measure) = + -- select all class with a superTyper + java!ClassDeclaration.allInstances()->select(it | (it.superClass <> OclUndefined)) + -- select all class create by the user + ->select(it2| it2.proxy = false) + -- select all class who extend Error + ->select(it3| it3.superClass.type.name = 'Error') + -- collect all results and send an error message + ->collect(it4|thisModule.MeasureDoNotExtendJavaLangError(it4)) + ; + + +--------------------------------------------- FinalFieldCouldBeStatic --------------------------------------------- + +-- Rule for metrics FinalFieldCouldBeStatic : return the set of field Measures that violates the rule. +helper def: finalFieldCouldBeStatic() : Set(smm!Measure) = + -- Browse through all field that are final and not static + java!FieldDeclaration.allInstances()->select(i | i.modifier <> OclUndefined)->select(i | i.modifier.inheritance->toString() = 'final' and not i.modifier.static)->collect(j | thisModule.MeasureFinalFieldCouldBeStatic(j)); -- GitLab From c9dee0f0dc07346a821d09648cccf568c7fd1126 Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Thu, 5 Dec 2019 14:26:38 +0100 Subject: [PATCH 09/10] Adding bahaviors in rules DoNotExtendJavaLangThrowable and DoNotExtendJavaLangError --- input/do-not-extend-java-lang-error.xmi | 10 +++++++--- input/do-not-extend-java-lang-throwable.xmi | 15 ++++++++++++--- src/main/atl/analysis.atl | 2 +- src/main/atl/design.atl | 2 +- src/main/atl/errorProne.atl | 2 +- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/input/do-not-extend-java-lang-error.xmi b/input/do-not-extend-java-lang-error.xmi index 4436911..e33ef75 100644 --- a/input/do-not-extend-java-lang-error.xmi +++ b/input/do-not-extend-java-lang-error.xmi @@ -7,7 +7,11 @@ - + + + + + @@ -17,8 +21,8 @@ - - + + diff --git a/input/do-not-extend-java-lang-throwable.xmi b/input/do-not-extend-java-lang-throwable.xmi index 2e18982..f6095bd 100644 --- a/input/do-not-extend-java-lang-throwable.xmi +++ b/input/do-not-extend-java-lang-throwable.xmi @@ -17,13 +17,21 @@ + + + + + + + + - - + + - + @@ -48,4 +56,5 @@ + diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index 77b1a94..3b12454 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(), diff --git a/src/main/atl/design.atl b/src/main/atl/design.atl index 8fb8881..e844abf 100644 --- a/src/main/atl/design.atl +++ b/src/main/atl/design.atl @@ -74,7 +74,7 @@ helper def: doNotExtendJavaLangError() : Set(smm!Measure) = -- select all class create by the user ->select(it2| it2.proxy = false) -- select all class who extend Error - ->select(it3| it3.superClass.type.name = 'Error') + ->select(it3| it3.superClass.type.name = 'Error' or it3.superClass.type.name = 'java.lang.Error') -- collect all results and send an error message ->collect(it4|thisModule.MeasureDoNotExtendJavaLangError(it4)) ; diff --git a/src/main/atl/errorProne.atl b/src/main/atl/errorProne.atl index 45d2a7e..5c974bc 100644 --- a/src/main/atl/errorProne.atl +++ b/src/main/atl/errorProne.atl @@ -26,7 +26,7 @@ helper def: doNotExtendJavaLangThrowable() : Set(smm!Measure) = -- select all class create by the user ->select(it2| it2.proxy = false) -- select all class who extend Throwable - ->select(it3| it3.superClass.type.name = 'Throwable') + ->select(it3| it3.superClass.type.name = 'Throwable' or it3.superClass.type.name = 'java.lang.Throwable') -- collect all results and send an error message ->collect(it4|thisModule.MeasureDoNotExtendJavaLangThrowable(it4)) ; -- GitLab From aad2c18ddd1b42b7e3c26d36be4c6d147b774eca Mon Sep 17 00:00:00 2001 From: LouisQ56 <1questel.louis@gmail.com> Date: Thu, 5 Dec 2019 14:27:59 +0100 Subject: [PATCH 10/10] Adding bahaviors in rules DoNotExtendJavaLangThrowable and DoNotExtendJavaLangError --- src/main/atl/analysis.atl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/atl/analysis.atl b/src/main/atl/analysis.atl index 3b12454..77b1a94 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(), -- GitLab