aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-duplications
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2011-09-26 22:16:29 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2011-09-27 02:07:56 +0400
commitc7c56a4d5ff5e0721f56c245ef11df52d2cc945c (patch)
tree4bfda245083a70b758ce141b9314c069472bee45 /sonar-duplications
parente01749f160a9bead858e17b37c2b1dc0be93b7bc (diff)
downloadsonarqube-c7c56a4d5ff5e0721f56c245ef11df52d2cc945c.tar.gz
sonarqube-c7c56a4d5ff5e0721f56c245ef11df52d2cc945c.zip
SONAR-2837 Fix detection of false-positive duplication in case of initialization of multidimensional array
Diffstat (limited to 'sonar-duplications')
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/java/BridgeWithExceptionTokenMatcher.java66
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java6
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java7
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java137
-rw-r--r--sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java17
5 files changed, 211 insertions, 22 deletions
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/java/BridgeWithExceptionTokenMatcher.java b/sonar-duplications/src/main/java/org/sonar/duplications/java/BridgeWithExceptionTokenMatcher.java
new file mode 100644
index 00000000000..27c4e3d9800
--- /dev/null
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/java/BridgeWithExceptionTokenMatcher.java
@@ -0,0 +1,66 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * 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.duplications.java;
+
+import java.util.List;
+
+import org.sonar.duplications.statement.matcher.TokenMatcher;
+import org.sonar.duplications.token.Token;
+import org.sonar.duplications.token.TokenQueue;
+
+public class BridgeWithExceptionTokenMatcher extends TokenMatcher {
+
+ private final String lToken;
+ private final String rToken;
+ private final String except;
+
+ public BridgeWithExceptionTokenMatcher(String lToken, String rToken, String except) {
+ if (lToken == null || rToken == null || except == null) {
+ throw new IllegalArgumentException();
+ }
+ this.lToken = lToken;
+ this.rToken = rToken;
+ this.except = except;
+ }
+
+ @Override
+ public boolean matchToken(TokenQueue tokenQueue, List<Token> matchedTokenList) {
+ if (!tokenQueue.isNextTokenValue(lToken)) {
+ return false;
+ }
+ int stack = 0;
+ while (tokenQueue.peek() != null) {
+ Token token = tokenQueue.poll();
+ matchedTokenList.add(token);
+ if (lToken.equals(token.getValue())) {
+ stack++;
+ } else if (rToken.equals(token.getValue())) {
+ stack--;
+ } else if (except.equals(token.getValue())) {
+ return false;
+ }
+ if (stack == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java b/sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java
index 99a74d26927..e783cd3c758 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java
@@ -19,10 +19,10 @@
*/
package org.sonar.duplications.java;
-import org.sonar.duplications.statement.StatementChunker;
-
import static org.sonar.duplications.statement.TokenMatcherFactory.*;
+import org.sonar.duplications.statement.StatementChunker;
+
public final class JavaStatementBuilder {
private JavaStatementBuilder() {
@@ -32,8 +32,10 @@ public final class JavaStatementBuilder {
return StatementChunker.builder()
.ignore(from("import"), to(";"))
.ignore(from("package"), to(";"))
+ .statement(new BridgeWithExceptionTokenMatcher("{", "}", ";"))
.ignore(token("}"))
.ignore(token("{"))
+ .ignore(token(";"))
.statement(from("@"), anyToken(), opt(bridge("(", ")")))
.statement(from("do"))
.statement(from("if"), bridge("(", ")"))
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java b/sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java
index 06a8e7d801e..6a36087bb4e 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java
@@ -19,13 +19,14 @@
*/
package org.sonar.duplications.statement.matcher;
+import java.util.Collections;
import java.util.List;
import org.sonar.duplications.token.Token;
import org.sonar.duplications.token.TokenQueue;
/**
- * Forgets token, which was consumed last.
+ * Last token would be returned to the queue.
*/
public class ForgetLastTokenMatcher extends TokenMatcher {
@@ -34,7 +35,9 @@ public class ForgetLastTokenMatcher extends TokenMatcher {
*/
@Override
public boolean matchToken(TokenQueue tokenQueue, List<Token> matchedTokenList) {
- matchedTokenList.remove(matchedTokenList.size() - 1);
+ int last = matchedTokenList.size() - 1;
+ tokenQueue.pushForward(Collections.singletonList(matchedTokenList.get(last)));
+ matchedTokenList.remove(last);
return true;
}
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java
index fa579e21b34..59aa6ee04e6 100644
--- a/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java
@@ -23,11 +23,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.number.OrderingComparisons.greaterThan;
import static org.junit.Assert.assertThat;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.io.*;
import java.nio.charset.Charset;
import java.util.List;
@@ -146,12 +142,135 @@ public class JavaStatementBuilderTest {
@Test
public void shouldHandleArray() {
+ List<Statement> statements = chunk("new Integer[] { 1, 2, 3, 4 };");
+ assertThat(statements.size(), is(2));
+ assertThat(statements.get(0).getValue(), is("newInteger[]"));
+ assertThat(statements.get(1).getValue(), is("{$NUMBER,$NUMBER,$NUMBER,$NUMBER}"));
+ }
+
+ /**
+ * See SONAR-2837
+ */
+ @Test
+ public void shouldHandleMultidimensionalArray() {
List<Statement> statements = chunk("new Integer[][] { { 1, 2 }, {3, 4} };");
- assertThat(statements.size(), is(4));
+ assertThat(statements.size(), is(2));
assertThat(statements.get(0).getValue(), is("newInteger[][]"));
- assertThat(statements.get(1).getValue(), is("$NUMBER,$NUMBER"));
- assertThat(statements.get(2).getValue(), is(","));
- assertThat(statements.get(3).getValue(), is("$NUMBER,$NUMBER"));
+ assertThat(statements.get(1).getValue(), is("{{$NUMBER,$NUMBER},{$NUMBER,$NUMBER}}"));
+
+ statements = chunk("new Integer[][] { null, {3, 4} };");
+ assertThat(statements.size(), is(2));
+ assertThat(statements.get(0).getValue(), is("newInteger[][]"));
+ assertThat(statements.get(1).getValue(), is("{null,{$NUMBER,$NUMBER}}"));
+ }
+
+ @Test
+ public void shouldHandleTryCatch() {
+ List<Statement> statements;
+ statements = chunk("try { } catch (Exception e) { }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("{}"));
+ assertThat(statements.get(2).getValue(), is("catch(Exceptione)"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } catch (Exception e) { }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("catch(Exceptione)"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } catch (Exception e) { onException(); }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("catch(Exceptione)"));
+ assertThat(statements.get(3).getValue(), is("onException()"));
+
+ statements = chunk("try { something(); } catch (Exception1 e) { onException1(); } catch (Exception2 e) { onException2(); }");
+ assertThat(statements.size(), is(6));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("catch(Exception1e)"));
+ assertThat(statements.get(3).getValue(), is("onException1()"));
+ assertThat(statements.get(4).getValue(), is("catch(Exception2e)"));
+ assertThat(statements.get(5).getValue(), is("onException2()"));
+ }
+
+ @Test
+ public void shouldHandleTryFinnaly() {
+ List<Statement> statements;
+ statements = chunk("try { } finally { }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("{}"));
+ assertThat(statements.get(2).getValue(), is("finally"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } finally { }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("finally"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } finally { somethingOther(); }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("finally"));
+ assertThat(statements.get(3).getValue(), is("somethingOther()"));
+ }
+
+ @Test
+ public void shouldHandleTryCatchFinally() {
+ List<Statement> statements;
+ statements = chunk("try { } catch (Exception e) {} finally { }");
+ assertThat(statements.size(), is(6));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("{}"));
+ assertThat(statements.get(2).getValue(), is("catch(Exceptione)"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+ assertThat(statements.get(4).getValue(), is("finally"));
+ assertThat(statements.get(5).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } catch (Exception e) { onException(); } finally { somethingOther(); }");
+ assertThat(statements.size(), is(6));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("catch(Exceptione)"));
+ assertThat(statements.get(3).getValue(), is("onException()"));
+ assertThat(statements.get(4).getValue(), is("finally"));
+ assertThat(statements.get(5).getValue(), is("somethingOther()"));
+ }
+
+ /**
+ * Java 7.
+ */
+ @Test
+ public void shouldHandleMultiCatch() {
+ List<Statement> statements;
+ statements = chunk("try { } catch (Exception1 | Exception2 e) { }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("{}"));
+ assertThat(statements.get(2).getValue(), is("catch(Exception1|Exception2e)"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } catch (Exception1 | Exception2 e) { }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("catch(Exception1|Exception2e)"));
+ assertThat(statements.get(3).getValue(), is("{}"));
+
+ statements = chunk("try { something(); } catch (Exception1 | Exception2 e) { onException(); }");
+ assertThat(statements.size(), is(4));
+ assertThat(statements.get(0).getValue(), is("try"));
+ assertThat(statements.get(1).getValue(), is("something()"));
+ assertThat(statements.get(2).getValue(), is("catch(Exception1|Exception2e)"));
+ assertThat(statements.get(3).getValue(), is("onException()"));
}
@Test
diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java
index 1050e7a10a3..7fc303f59c0 100644
--- a/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java
+++ b/sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java
@@ -21,32 +21,31 @@ package org.sonar.duplications.statement.matcher;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.sonar.duplications.token.Token;
import org.sonar.duplications.token.TokenQueue;
+import com.google.common.collect.Lists;
+
public class ForgetLastTokenMatcherTest {
@Test
public void shouldMatch() {
TokenQueue tokenQueue = spy(new TokenQueue());
- List<Token> output = mock(List.class);
- when(output.size()).thenReturn(4);
+ Token token = new Token("a", 0, 0);
+ List<Token> output = Lists.newArrayList(token);
ForgetLastTokenMatcher matcher = new ForgetLastTokenMatcher();
assertThat(matcher.matchToken(tokenQueue, output), is(true));
- verifyNoMoreInteractions(tokenQueue);
- verify(output).size();
- verify(output).remove(3);
- verifyNoMoreInteractions(output);
+ assertThat(output.size(), is(0));
+ verify(tokenQueue).pushForward(eq(Collections.singletonList(token)));
}
}