diff options
9 files changed, 208 insertions, 1 deletions
diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/ClassVisitor.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/ClassVisitor.java index 993af7f8468..4995f90c4b0 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/ClassVisitor.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/ClassVisitor.java @@ -57,6 +57,7 @@ public class ClassVisitor extends JavaAstVisitor { if (isAbstract(ast)) { unit.setMeasure(Metric.ABSTRACT_CLASSES, 1); } + unit.setSuppressWarnings(SuppressWarningsAnnotationUtils.isSuppressAllWarnings(ast)); } @Override @@ -86,4 +87,5 @@ public class ClassVisitor extends JavaAstVisitor { static SourceClass createSourceClass(SourceClass parentClass, String innerClassName) { return new SourceClass(parentClass.getKey() + "$" + innerClassName, innerClassName); } + } 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 afe8e9d366c..e1e08dcccab 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 @@ -70,6 +70,7 @@ public class MethodVisitor extends JavaAstVisitor { if (isConstructor) { sourceMethod.setMeasure(Metric.CONSTRUCTORS, 1); } + sourceMethod.setSuppressWarnings(SuppressWarningsAnnotationUtils.isSuppressAllWarnings(ast)); addSourceCode(sourceMethod); } diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/SuppressWarningsAnnotationUtils.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/SuppressWarningsAnnotationUtils.java new file mode 100644 index 00000000000..f35441b34a1 --- /dev/null +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/SuppressWarningsAnnotationUtils.java @@ -0,0 +1,80 @@ +/* + * 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.visitor; + +import com.puppycrawl.tools.checkstyle.api.AnnotationUtility; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; + +/** + * Inspired by {@link com.puppycrawl.tools.checkstyle.checks.annotation.SuppressWarningsCheck} + */ +public final class SuppressWarningsAnnotationUtils { + + private final static String SUPPRESS_WARNINGS_ANNOTATION_NAME = "SuppressWarnings"; + private final static String SUPPRESS_WARNINGS_ANNOTATION_FQ_NAME = "java.lang." + SUPPRESS_WARNINGS_ANNOTATION_NAME; + private final static String VALUE = "\"all\""; + + public static boolean isSuppressAllWarnings(DetailAST ast) { + DetailAST suppressWarningsAnnotation = getSuppressWarningsAnnotation(ast); + if (suppressWarningsAnnotation != null) { + DetailAST warningHolder = findWarningsHolder(suppressWarningsAnnotation); + for (DetailAST warning = warningHolder.findFirstToken(TokenTypes.EXPR); warning != null; warning = warning.getNextSibling()) { + if (warning.getType() == TokenTypes.EXPR) { + DetailAST fChild = warning.getFirstChild(); + if (fChild.getType() == TokenTypes.STRING_LITERAL) { + String text = warning.getFirstChild().getText(); + if (VALUE.equals(text)) { + return true; + } + } + } + } + } + return false; + } + + private static DetailAST findWarningsHolder(DetailAST aAnnotation) { + DetailAST annValuePair = aAnnotation.findFirstToken(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR); + DetailAST annArrayInit; + if (annValuePair != null) { + annArrayInit = annValuePair.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT); + } else { + annArrayInit = aAnnotation.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT); + } + if (annArrayInit != null) { + return annArrayInit; + } + return aAnnotation; + } + + private static DetailAST getSuppressWarningsAnnotation(DetailAST ast) { + DetailAST annotation = AnnotationUtility.getAnnotation(ast, SUPPRESS_WARNINGS_ANNOTATION_NAME); + if (annotation == null) { + annotation = AnnotationUtility.getAnnotation(ast, SUPPRESS_WARNINGS_ANNOTATION_FQ_NAME); + } + return annotation; + } + + private SuppressWarningsAnnotationUtils() { + } + +} diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/NoSonarFilterLoader.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/NoSonarFilterLoader.java index c727a8db0b0..1fd831fc4c2 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/NoSonarFilterLoader.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/NoSonarFilterLoader.java @@ -19,9 +19,16 @@ */ package org.sonar.plugins.squid.bridges; +import java.util.HashSet; +import java.util.Set; + import org.sonar.api.checks.NoSonarFilter; import org.sonar.api.resources.Resource; +import org.sonar.squid.api.SourceClass; +import org.sonar.squid.api.SourceCode; import org.sonar.squid.api.SourceFile; +import org.sonar.squid.api.SourceMethod; +import org.sonar.squid.indexer.QueryByParent; public class NoSonarFilterLoader extends Bridge { private NoSonarFilter noSonarFilter; @@ -34,7 +41,25 @@ public class NoSonarFilterLoader extends Bridge { @Override public void onFile(SourceFile squidFile, Resource sonarFile) { if (noSonarFilter != null) { - noSonarFilter.addResource(sonarFile, squidFile.getNoSonarTagLines()); + // lines with NOSONAR tag + Set<Integer> ignoredLines = new HashSet<Integer>(squidFile.getNoSonarTagLines()); + // classes and methods with annotaion SuppressWarnings + for (SourceCode sourceCode : squid.search(new QueryByParent(squidFile))) { + if (sourceCode instanceof SourceMethod && ((SourceMethod) sourceCode).isSuppressWarnings()) { + visitLines(sourceCode, ignoredLines); + } + if (sourceCode instanceof SourceClass && ((SourceClass) sourceCode).isSuppressWarnings()) { + visitLines(sourceCode, ignoredLines); + } + } + + noSonarFilter.addResource(sonarFile, ignoredLines); + } + } + + private static void visitLines(SourceCode sourceCode, Set<Integer> ignoredLines) { + for (int line = sourceCode.getStartAtLine(); line <= sourceCode.getEndAtLine(); line++) { + ignoredLines.add(line); } } } diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/ClassVisitorTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/ClassVisitorTest.java index bef90bb87fa..421fae7069f 100644 --- a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/ClassVisitorTest.java +++ b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/ClassVisitorTest.java @@ -19,8 +19,10 @@ */ package org.sonar.java.ast.visitor; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.sonar.java.ast.SquidTestUtils.getFile; @@ -121,6 +123,13 @@ public class ClassVisitorTest { } @Test + public void detectSuppressWarningsAnnotation() { + squid.register(JavaAstScanner.class).scanFile(getFile("/rules/ClassWithSuppressWarningsAnnotation.java")); + SourceClass sourceClass = (SourceClass) squid.search("ClassWithSuppressWarningsAnnotation"); + assertThat(sourceClass.isSuppressWarnings(), is(true)); + } + + @Test public void testCreateSquidClassFromPackage() { SourceClass squidClass = ClassVisitor.createSourceClass(new SourcePackage("org/sonar"), "Squid"); assertEquals("org/sonar/Squid", squidClass.getKey()); diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/MethodVisitorTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/MethodVisitorTest.java index 622c4afdf71..52f955842f4 100644 --- a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/MethodVisitorTest.java +++ b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/MethodVisitorTest.java @@ -19,8 +19,10 @@ */ package org.sonar.java.ast.visitor; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.sonar.java.ast.SquidTestUtils.getFile; import org.junit.Before; @@ -89,4 +91,24 @@ public class MethodVisitorTest { SourceCode source = squid.decorateSourceCodeTreeWith(Metric.values()); assertEquals(2, source.getInt(Metric.CONSTRUCTORS)); } + + @Test + public void detectSuppressWarningsAnnotation() { + squid.register(JavaAstScanner.class).scanFile(getFile("/rules/ClassWithSuppressWarningsAnnotation.java")); + + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#fullyQualifiedName()V").isSuppressWarnings(), is(true)); + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#singleValue()V").isSuppressWarnings(), is(true)); + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#arrayWithSingleValue()V").isSuppressWarnings(), is(true)); + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#arrayWithMultipleValues()V").isSuppressWarnings(), is(true)); + assertThat(getMethod("ClassWithSuppressWarningsAnnotation$1#methodInAnonymousInnerClass()V").isSuppressWarnings(), is(true)); + + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#notHandled()V").isSuppressWarnings(), is(false)); + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#notHandled2()V").isSuppressWarnings(), is(false)); + assertThat(getMethod("ClassWithSuppressWarningsAnnotation#notHandled3()V").isSuppressWarnings(), is(false)); + } + + private SourceMethod getMethod(String key) { + return (SourceMethod) squid.search(key); + } + } diff --git a/plugins/sonar-squid-java-plugin/test-resources/rules/ClassWithSuppressWarningsAnnotation.java b/plugins/sonar-squid-java-plugin/test-resources/rules/ClassWithSuppressWarningsAnnotation.java new file mode 100644 index 00000000000..b91bb86843b --- /dev/null +++ b/plugins/sonar-squid-java-plugin/test-resources/rules/ClassWithSuppressWarningsAnnotation.java @@ -0,0 +1,42 @@ +@SuppressWarnings("all") +class ClassWithSuppressWarningsAnnotation { + + @java.lang.SuppressWarnings("all") + public void fullyQualifiedName() { + } + + @SuppressWarnings("all") + public void singleValue() { + } + + @SuppressWarnings(value = { "all" }) + public void arrayWithSingleValue() { + } + + @SuppressWarnings(value = { "null", "all" }) + public void arrayWithMultipleValues() { + } + + public void doJob() { + Object o = new Object() { + @SuppressWarnings("all") + public void methodInAnonymousInnerClass() { + } + }; + } + + // Currently Sonar is unable to properly handle following cases + + @SuppressWarnings("a" + "ll") + public void notHandled() { + } + + @SuppressWarnings(false ? "null" : "all") + public void notHandled2() { + } + + private static final String SUPPRESS = "all"; + @SuppressWarnings(SUPPRESS) + public void notHandled3() { + } +} diff --git a/sonar-squid/src/main/java/org/sonar/squid/api/SourceClass.java b/sonar-squid/src/main/java/org/sonar/squid/api/SourceClass.java index 20287f283f1..1be1ceef863 100644 --- a/sonar-squid/src/main/java/org/sonar/squid/api/SourceClass.java +++ b/sonar-squid/src/main/java/org/sonar/squid/api/SourceClass.java @@ -21,6 +21,11 @@ package org.sonar.squid.api; public class SourceClass extends SourceCode { + /** + * This is used only for Java for now, but can be used for other languages. So maybe we should push it down to SourceCode. + */ + private boolean suppressWarnings = false; + public SourceClass(String key) { super(key); } @@ -28,4 +33,12 @@ public class SourceClass extends SourceCode { public SourceClass(String key, String className) { super(key, className); } + + public void setSuppressWarnings(boolean suppressWarnings) { + this.suppressWarnings = suppressWarnings; + } + + public boolean isSuppressWarnings() { + return suppressWarnings; + } } diff --git a/sonar-squid/src/main/java/org/sonar/squid/api/SourceMethod.java b/sonar-squid/src/main/java/org/sonar/squid/api/SourceMethod.java index 0378f655da4..60eab9faf2a 100644 --- a/sonar-squid/src/main/java/org/sonar/squid/api/SourceMethod.java +++ b/sonar-squid/src/main/java/org/sonar/squid/api/SourceMethod.java @@ -23,6 +23,11 @@ import org.sonar.squid.measures.Metric; public class SourceMethod extends SourceCode { + /** + * This is used only for Java for now, but can be used for other languages. So maybe we should push it down to SourceCode. + */ + private boolean suppressWarnings = false; + public SourceMethod(String key) { super(key); } @@ -35,4 +40,12 @@ public class SourceMethod extends SourceCode { public boolean isAccessor() { return getInt(Metric.ACCESSORS) != 0; } + + public void setSuppressWarnings(boolean suppressWarnings) { + this.suppressWarnings = suppressWarnings; + } + + public boolean isSuppressWarnings() { + return suppressWarnings; + } } |