Browse Source

SONAR-2837 Fix detection of false-positive duplication in case of initialization of multidimensional array

tags/2.12
Evgeny Mandrikov 12 years ago
parent
commit
c7c56a4d5f

+ 66
- 0
sonar-duplications/src/main/java/org/sonar/duplications/java/BridgeWithExceptionTokenMatcher.java View File

@@ -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;
}

}

+ 4
- 2
sonar-duplications/src/main/java/org/sonar/duplications/java/JavaStatementBuilder.java View File

@@ -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("(", ")"))

+ 5
- 2
sonar-duplications/src/main/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcher.java View File

@@ -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;
}


+ 128
- 9
sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java View 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

+ 8
- 9
sonar-duplications/src/test/java/org/sonar/duplications/statement/matcher/ForgetLastTokenMatcherTest.java View 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)));
}

}

Loading…
Cancel
Save