diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-09-26 22:16:29 +0400 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-09-27 02:07:56 +0400 |
commit | c7c56a4d5ff5e0721f56c245ef11df52d2cc945c (patch) | |
tree | 4bfda245083a70b758ce141b9314c069472bee45 /sonar-duplications | |
parent | e01749f160a9bead858e17b37c2b1dc0be93b7bc (diff) | |
download | sonarqube-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')
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))); } } |