]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2837 Fix detection of false-positive duplication in case of initialization...
authorEvgeny Mandrikov <mandrikov@gmail.com>
Mon, 26 Sep 2011 18:16:29 +0000 (22:16 +0400)
committerEvgeny Mandrikov <mandrikov@gmail.com>
Mon, 26 Sep 2011 22:07:56 +0000 (02:07 +0400)
sonar-duplications/src/main/java/org/sonar/duplications/java/BridgeWithExceptionTokenMatcher.java [new file with mode: 0644]
sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java
sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java
sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java
sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java

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 (file)
index 0000000..27c4e3d
--- /dev/null
@@ -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;
+  }
+
+}
index 99a74d26927d5ab2a64b7fb7e739576a945e09ed..e783cd3c7582bf66e6f269e610815185440df3c1 100644 (file)
  */
 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("(", ")"))
index 06a8e7d801e5c3a729528b3217eff0083384fd64..6a36087bb4e8fe1cc78d4a81a081d2e8d445d64c 100644 (file)
  */
 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;
   }
 
index fa579e21b343bf4746b3c8153e46589a4f40f801..59aa6ee04e662dd7d277615fb65c5a3c469e3e82 100644 (file)
@@ -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
index 1050e7a10a3a19738dc06f7c866a007a874de6b3..7fc303f59c055992dd22301c07eecbf7e8ab0fe6 100644 (file)
@@ -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)));
   }
 
 }