From: Godin Date: Sun, 14 Nov 2010 02:51:28 +0000 (+0000) Subject: SONAR-1845: Create a new Sonar rule to check method complexity X-Git-Tag: 2.6~583 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e410437db4ad779e779619ac3bea979c079b4e00;p=sonarqube.git SONAR-1845: Create a new Sonar rule to check method complexity --- diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/ClassComplexityCheck.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/ClassComplexityCheck.java new file mode 100644 index 00000000000..4025278c654 --- /dev/null +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/ClassComplexityCheck.java @@ -0,0 +1,75 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ + +package org.sonar.java.ast.check; + +import java.util.List; + +import org.sonar.check.IsoCategory; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonar.java.ast.visitor.ClassVisitor; +import org.sonar.squid.api.CheckMessage; +import org.sonar.squid.api.SourceCode; +import org.sonar.squid.api.SourceFile; +import org.sonar.squid.measures.Metric; + +import com.puppycrawl.tools.checkstyle.api.DetailAST; + +@Rule(key = "ClassComplexityCheck", isoCategory = IsoCategory.Maintainability) +public class ClassComplexityCheck extends JavaAstCheck { + + @RuleProperty + private Integer threshold; + + @Override + public List getWantedTokens() { + return ClassVisitor.wantedTokens; + } + + @Override + public void leaveToken(DetailAST ast) { + SourceCode currentResource = peekSourceCode(); + int complexity = calculateComplexity(currentResource); + if (complexity > threshold) { + CheckMessage message = new CheckMessage(this, "Class complexity exceeds " + threshold + "."); + message.setLine(ast.getLineNo()); + message.setCost(complexity - threshold); + SourceFile sourceFile = currentResource.getParent(SourceFile.class); + sourceFile.log(message); + } + } + + private int calculateComplexity(SourceCode sourceCode) { + int result = 0; + if (sourceCode.getChildren() != null) { + for (SourceCode child : sourceCode.getChildren()) { + result += calculateComplexity(child); + } + } + result += sourceCode.getInt(Metric.COMPLEXITY); + return result; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + +} diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/ComplexityCheck.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/ComplexityCheck.java deleted file mode 100644 index e2936cf0102..00000000000 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/ComplexityCheck.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2009 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ - -package org.sonar.java.ast.check; - -import java.util.List; - -import org.sonar.check.IsoCategory; -import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import org.sonar.java.ast.visitor.ClassVisitor; -import org.sonar.squid.api.CheckMessage; -import org.sonar.squid.api.SourceCode; -import org.sonar.squid.api.SourceFile; -import org.sonar.squid.measures.Metric; - -import com.puppycrawl.tools.checkstyle.api.DetailAST; - -@Rule(key = "ComplexityCheck", isoCategory = IsoCategory.Maintainability) -public class ComplexityCheck extends JavaAstCheck { - - @RuleProperty - private Integer threshold; - - @Override - public List getWantedTokens() { - return ClassVisitor.wantedTokens; - } - - @Override - public void leaveToken(DetailAST ast) { - SourceCode currentResource = peekSourceCode(); - int complexity = calculateComplexity(currentResource); - if (complexity > threshold) { - CheckMessage message = new CheckMessage(this, "Complexity exceeds " + threshold + "."); - message.setLine(ast.getLineNo()); - SourceFile sourceFile = currentResource.getParent(SourceFile.class); - sourceFile.log(message); - } - } - - private int calculateComplexity(SourceCode sourceCode) { - int result = 0; - if (sourceCode.getChildren() != null) { - for (SourceCode child : sourceCode.getChildren()) { - result += calculateComplexity(child); - } - } - result += sourceCode.getInt(Metric.COMPLEXITY); - return result; - } - - public void setThreshold(int threshold) { - this.threshold = threshold; - } - -} diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/MethodComplexityCheck.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/MethodComplexityCheck.java new file mode 100644 index 00000000000..072d19baf6d --- /dev/null +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/check/MethodComplexityCheck.java @@ -0,0 +1,44 @@ +package org.sonar.java.ast.check; + +import java.util.List; + +import org.sonar.check.IsoCategory; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonar.java.ast.visitor.MethodVisitor; +import org.sonar.squid.api.CheckMessage; +import org.sonar.squid.api.SourceCode; +import org.sonar.squid.api.SourceFile; +import org.sonar.squid.measures.Metric; + +import com.puppycrawl.tools.checkstyle.api.DetailAST; + +@Rule(key = "MethodComplexityCheck", isoCategory = IsoCategory.Maintainability) +public class MethodComplexityCheck extends JavaAstCheck { + + @RuleProperty + private Integer threshold; + + @Override + public List getWantedTokens() { + return MethodVisitor.wantedTokens; + } + + @Override + public void leaveToken(DetailAST ast) { + SourceCode currentResource = peekSourceCode(); + int complexity = currentResource.getInt(Metric.COMPLEXITY) + currentResource.getInt(Metric.BRANCHES) + 1; + if (complexity > threshold) { + CheckMessage message = new CheckMessage(this, "Method complexity exceeds " + threshold + "."); + message.setLine(ast.getLineNo()); + message.setCost(complexity - threshold); + SourceFile sourceFile = currentResource.getParent(SourceFile.class); + sourceFile.log(message); + } + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + +} diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/MethodVisitor.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/MethodVisitor.java index 480c346bf66..afe8e9d366c 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/MethodVisitor.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/MethodVisitor.java @@ -41,7 +41,7 @@ public class MethodVisitor extends JavaAstVisitor { private static final String CONSTRUCTOR = ""; - private static final List wantedTokens = Arrays.asList(TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF); + public static final List wantedTokens = Arrays.asList(TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF); private static final Map tokenJavaTypeMapping = new HashMap(); static { diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidRuleRepository.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidRuleRepository.java index b9c1473b86a..9c4ae500f29 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidRuleRepository.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidRuleRepository.java @@ -28,8 +28,9 @@ import org.sonar.api.rules.AnnotationRuleParser; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleRepository; import org.sonar.java.ast.check.BreakCheck; -import org.sonar.java.ast.check.ComplexityCheck; +import org.sonar.java.ast.check.ClassComplexityCheck; import org.sonar.java.ast.check.ContinueCheck; +import org.sonar.java.ast.check.MethodComplexityCheck; import org.sonar.java.ast.check.UndocumentedApiCheck; import org.sonar.java.bytecode.check.ArchitectureCheck; import org.sonar.java.bytecode.check.CallToDeprecatedMethodCheck; @@ -57,6 +58,6 @@ public final class SquidRuleRepository extends RuleRepository { (Class) CallToDeprecatedMethodCheck.class, UnusedPrivateMethodCheck.class, UnusedProtectedMethodCheck.class, ArchitectureCheck.class, DITCheck.class, // AST checks - UndocumentedApiCheck.class, ContinueCheck.class, BreakCheck.class, ComplexityCheck.class); + UndocumentedApiCheck.class, ContinueCheck.class, BreakCheck.class, ClassComplexityCheck.class, MethodComplexityCheck.class); } } diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/ChecksBridge.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/ChecksBridge.java index 0d083d2a77f..60530fc5fd9 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/ChecksBridge.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/ChecksBridge.java @@ -19,15 +19,15 @@ */ package org.sonar.plugins.squid.bridges; +import java.util.Locale; +import java.util.Set; + import org.sonar.api.resources.Resource; import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Violation; import org.sonar.squid.api.CheckMessage; import org.sonar.squid.api.SourceFile; -import java.util.Locale; -import java.util.Set; - public class ChecksBridge extends Bridge { protected ChecksBridge() { @@ -43,6 +43,7 @@ public class ChecksBridge extends Bridge { Violation violation = Violation.create(rule, sonarFile); violation.setLineId(checkMessage.getLine()); violation.setMessage(checkMessage.getText(Locale.ENGLISH)); + violation.setCost(checkMessage.getCost()); context.saveViolation(violation); } } diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/ClassComplexityCheckTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/ClassComplexityCheckTest.java new file mode 100644 index 00000000000..39692969332 --- /dev/null +++ b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/ClassComplexityCheckTest.java @@ -0,0 +1,64 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ + +package org.sonar.java.ast.check; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.sonar.java.ast.SquidTestUtils.getFile; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.java.ast.JavaAstScanner; +import org.sonar.java.squid.JavaSquidConfiguration; +import org.sonar.squid.Squid; +import org.sonar.squid.api.CheckMessage; +import org.sonar.squid.api.SourceFile; + +public class ClassComplexityCheckTest { + + private Squid squid; + + @Before + public void setUp() { + squid = new Squid(new JavaSquidConfiguration()); + ClassComplexityCheck check = new ClassComplexityCheck(); + check.setThreshold(5); + squid.registerVisitor(check); + JavaAstScanner scanner = squid.register(JavaAstScanner.class); + scanner.scanFile(getFile("/metrics/branches/NoBranches.java")); + scanner.scanFile(getFile("/metrics/branches/ComplexBranches.java")); + } + + @Test + public void testComplexityExceedsThreshold() { + SourceFile file = (SourceFile) squid.search("ComplexBranches.java"); + assertThat(file.getCheckMessages().size(), is(1)); + CheckMessage message = file.getCheckMessages().iterator().next(); + assertThat(message.getLine(), is(3)); + } + + @Test + public void testComplexityNotExceedsThreshold() { + SourceFile file = (SourceFile) squid.search("NoBranches.java"); + assertThat(file.getCheckMessages().size(), is(0)); + } + +} diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/ComplexityCheckTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/ComplexityCheckTest.java deleted file mode 100644 index 0c4c8a04d7d..00000000000 --- a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/ComplexityCheckTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2009 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ - -package org.sonar.java.ast.check; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.sonar.java.ast.SquidTestUtils.getFile; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.java.ast.JavaAstScanner; -import org.sonar.java.squid.JavaSquidConfiguration; -import org.sonar.squid.Squid; -import org.sonar.squid.api.CheckMessage; -import org.sonar.squid.api.SourceFile; - -public class ComplexityCheckTest { - - private Squid squid; - - @Before - public void setUp() { - squid = new Squid(new JavaSquidConfiguration()); - ComplexityCheck check = new ComplexityCheck(); - check.setThreshold(5); - squid.registerVisitor(check); - JavaAstScanner scanner = squid.register(JavaAstScanner.class); - scanner.scanFile(getFile("/metrics/branches/NoBranches.java")); - scanner.scanFile(getFile("/metrics/branches/ComplexBranches.java")); - } - - @Test - public void testComplexityExceedsThreshold() { - SourceFile file = (SourceFile) squid.search("ComplexBranches.java"); - assertThat(file.getCheckMessages().size(), is(1)); - CheckMessage message = file.getCheckMessages().iterator().next(); - assertThat(message.getLine(), is(3)); - } - - @Test - public void testComplexityNotExceedsThreshold() { - SourceFile file = (SourceFile) squid.search("NoBranches.java"); - assertThat(file.getCheckMessages().size(), is(0)); - } - -} diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/MethodComplexityCheckTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/MethodComplexityCheckTest.java new file mode 100644 index 00000000000..5f8a73396bd --- /dev/null +++ b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/check/MethodComplexityCheckTest.java @@ -0,0 +1,35 @@ +package org.sonar.java.ast.check; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.sonar.java.ast.SquidTestUtils.getFile; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.java.ast.JavaAstScanner; +import org.sonar.java.squid.JavaSquidConfiguration; +import org.sonar.squid.Squid; +import org.sonar.squid.api.CheckMessage; +import org.sonar.squid.api.SourceFile; + +public class MethodComplexityCheckTest { + private Squid squid; + + @Before + public void setUp() { + squid = new Squid(new JavaSquidConfiguration()); + MethodComplexityCheck check = new MethodComplexityCheck(); + check.setThreshold(5); + squid.registerVisitor(check); + JavaAstScanner scanner = squid.register(JavaAstScanner.class); + scanner.scanFile(getFile("/metrics/branches/ComplexBranches.java")); + } + + @Test + public void testMethodComplexityExceedsThreshold() { + SourceFile file = (SourceFile) squid.search("ComplexBranches.java"); + assertThat(file.getCheckMessages().size(), is(1)); + CheckMessage message = file.getCheckMessages().iterator().next(); + assertThat(message.getLine(), is(10)); + } +} diff --git a/sonar-squid/src/main/java/org/sonar/squid/api/CheckMessage.java b/sonar-squid/src/main/java/org/sonar/squid/api/CheckMessage.java index 425b26af8be..e9caeb3e2af 100644 --- a/sonar-squid/src/main/java/org/sonar/squid/api/CheckMessage.java +++ b/sonar-squid/src/main/java/org/sonar/squid/api/CheckMessage.java @@ -28,6 +28,7 @@ import org.sonar.check.Message; public class CheckMessage implements Message { private Integer line; + private Double cost; private SourceCode sourceCode; private CodeCheck codeCheck; private String defaultMessage; @@ -55,6 +56,14 @@ public class CheckMessage implements Message { return line; } + public void setCost(double cost) { + this.cost = cost; + } + + public Double getCost() { + return cost; + } + public CodeCheck getChecker() { return codeCheck; }