Commit 9c5f0532 authored by Gerson SUNYE's avatar Gerson SUNYE
Browse files

Merge branch 'master' into 'master'

Fixed issue #739 ExcessiveImports

Closes #739 and #757

See merge request naomod/mde/projet-2019!104
parents c4b50d05 cb84ec8c
-- @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.avoidThreadGroup(),
thisModule.useNotifyAllInsteadOfNotify(),
-- Code Style rules
--
thisModule.extendsObject(),
thisModule.genericsNaming(),
thisModule.longVariable(),
thisModule.misLeadingVariableName(),
thisModule.noPackageDeclaration(),
thisModule.shortClassName(),
thisModule.shortMethodName(),
thisModule.shortVariableName(),
thisModule.tooManyStaticImports(),
thisModule.unnecessaryReturn(),
-- Design rules
--
thisModule.ExcessiveClassLength(),
thisModule.IfBraces(),
thisModule.excessiveParameterList(),
thisModule.finalFieldCouldBeStatic(),
thisModule.longInstantiation(),
thisModule.returnFromFinallyBlock(),
thisModule.shortInstantiation(),
thisModule.signatureDeclareThrowsException(),
thisModule.tooManyFields(),
thisModule.tooManyMethods(),
thisModule.SimplifyBooleanAssertion(),
thisModule.abstractClassWithoutAnyMethod(),
-- Performance rules
--
thisModule.avoidPrintStackTrace(),
thisModule.avoidRethrowingException(),
thisModule.avoidUsingShortType(),
thisModule.emptyStatementBlock(),
thisModule.integerInstantiation(),
thisModule.useIndexOfChar(),
thisModule.uselessStringValueOf(),
thisModule.tooFewBranchesForASwitchStatement(),
-- Documentation rules
--
thisModule.commentContent(),
-- Error prone rules
--
thisModule.avoidCallingFinalize(),
thisModule.AvoidCatchingNPE(),
thisModule.avoidCatchingThrowable(),
thisModule.AvoidEnumAsIdentifier(),
thisModule.CloneMethodMustBePublic(),
thisModule.doNotCallGarbageCollectionExplicitly(),
thisModule.doNotExtendJavaLangError(),
thisModule.doNotExtendJavaLangThrowable(),
thisModule.dontImportSun(),
thisModule.emptyCatchBlock(),
thisModule.EmptyInitializer(),
thisModule.emptySwitchStatement(),
thisModule.emptySynchronizedBlock(),
thisModule.emptyTryBlock(),
thisModule.importFromSamePackage(),
thisModule.missingBreakInSwitch(),
thisModule.suspiciousEqualsMethodName(),
thisModule.UnconditionalIfStatement(),
thisModule.UseProperClassLoader(),
-- Best practices rules
thisModule.switchDensity(),
thisModule.useAssertSameInsteadOfAssertTrue(),
thisModule.useAssertTrueInsteadOfAssertEquals()
-- Bugged rules:
--
-- thisModule.avoidThrowingNullPointerException(),
-- thisModule.UseArrayListInsteadOfVector(),
-- thisModule.commentRequired(),
-- thisModule.compareObjectsWithEquals(),
-- thisModule.emptyWhileStmt(),
-- thisModule.emptyFinallyBlock(),
-- thisModule.emptyfinalizeMethod(),
-- thisModule.doNotThrowExceptionInFinally(),
-- thisModule.finalizeShouldBeProtected(),
-- thisModule.avoidDollarSigns(),
-- thisModule.returnEmptyArrayRatherThanNull(),
-- thisModule.replaceVectorToList()
-- thisModule.unusedPrivateMethod ()
-- thisModule.avoidStringBufferField()
-- thisModule.avoidThrowingNewInstanceOfSameException(),
-- thisModule.stringToString(),
-- thisModule.dontCallThreadRun(),
-- thisModule.methodWithSameNameAsEnclosingClass(),
-- thisModule.dontUseFloatTypeForLoopIndices(),
-- thisModule.methodWithSameNameAsEnclosingClass(),
};
-- 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;
}
}
-- A Measure instance if the variable violates the rule 'ShortVariableName'.
rule MesureShortVariableName(variable : java!VariableDeclaration) {
to
om: smm!ObservedMeasure (
measure <- noc,
measurements <- measurement
),
noc: smm!DimensionalMeasure (
name <- 'ShortVariableName',
shortDescription <- 'Short Variable names with fewer than e.g. three characters are not recommended.'
),
measurement: smm!DirectMeasurement (
error <- 'The Variable ' + variable.name + ' is too short.'
)
do {
noc;
}
}
-- A Measure instance if the method violates the rule 'StringToString'.
rule MeasureStringToString(method : java!MethodInvocation) {
to
om: smm!ObservedMeasure (
measure <- noc,
measurements <- measurement
),
noc: smm!DimensionalMeasure (
name <- 'StringToString',
shortDescription <- 'Avoid calling toString() on objects already known to be string instances; this is unnecessary.'
),
measurement: smm!DirectMeasurement (
error <- 'The Object ' + method.expression.variable.name + ' is already a String in ' + method.originalCompilationUnit.name
)
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!DimensionalMeasure (
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
),