Package com.google.errorprone

Source Code of com.google.errorprone.ErrorProneCompilerIntegrationTest$SuperCallMatcher

/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.errorprone;

import static com.google.errorprone.BugPattern.Category.ONE_OFF;
import static com.google.errorprone.BugPattern.MaturityLevel.EXPERIMENTAL;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.DiagnosticTestHelper.diagnosticMessage;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;

import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ExpressionStatementTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.bugpatterns.NonAtomicVolatileUpdate;
import com.google.errorprone.matchers.Description;

import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.main.Main.Result;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;

import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
* Integration tests for {@link ErrorProneCompiler}.
*
* @author alexeagle@google.com (Alex Eagle)
*/
@RunWith(JUnit4.class)
public class ErrorProneCompilerIntegrationTest {

  private DiagnosticTestHelper diagnosticHelper;
  private PrintWriter printWriter;
  private ByteArrayOutputStream outputStream;
  private ErrorProneTestCompiler.Builder compilerBuilder;
  ErrorProneTestCompiler compiler;

  @Before
  public void setUp() {
    diagnosticHelper = new DiagnosticTestHelper();
    outputStream = new ByteArrayOutputStream();
    printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
    compilerBuilder = new ErrorProneTestCompiler.Builder()
        .named("test")
        .redirectOutputTo(printWriter)
        .listenToDiagnostics(diagnosticHelper.collector);
    compiler = compilerBuilder.build();
  }

  @Test
  public void fileWithError() throws Exception {
    Result exitCode = compiler.compile(compiler.fileManager().sources(getClass(),
        "bugpatterns/BadShiftAmountPositiveCases.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.ERROR));

    Matcher<Iterable<Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(containsString("[BadShiftAmount]")));
    assertTrue("Error should be found. " + diagnosticHelper.describe(),
        matcher.matches(diagnosticHelper.getDiagnostics()));
  }

  @Test
  public void fileWithWarning() throws Exception {
    compilerBuilder.report(new ErrorProneScanner(new NonAtomicVolatileUpdate()));
    compiler = compilerBuilder.build();
    Result exitCode = compiler.compile(compiler.fileManager().sources(getClass(),
        "bugpatterns/NonAtomicVolatileUpdatePositiveCases.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.OK));

    Matcher<Iterable<Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(containsString("[NonAtomicVolatileUpdate]")));
    assertTrue("Warning should be found. " + diagnosticHelper.describe(),
        matcher.matches(diagnosticHelper.getDiagnostics()));
  }

  @Test
  public void fileWithMultipleTopLevelClasses() throws Exception {
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(), "MultipleTopLevelClassesWithNoErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }

  @Test
  public void fileWithMultipleTopLevelClassesExtends() throws Exception {
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(), "MultipleTopLevelClassesWithNoErrors.java",
            "ExtendedMultipleTopLevelClassesWithNoErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }

  /**
   * Regression test for a bug in which multiple top-level classes may cause
   * NullPointerExceptions in the matchers.
   */
  @Test
  public void fileWithMultipleTopLevelClassesExtendsWithError()
      throws Exception {
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(), "MultipleTopLevelClassesWithErrors.java",
            "ExtendedMultipleTopLevelClassesWithErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.ERROR));

    Matcher<Iterable<Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(containsString("[SelfAssignment]")));
    assertTrue("Warning should be found. " + diagnosticHelper.describe(),
        matcher.matches(diagnosticHelper.getDiagnostics()));
    assertEquals(3, diagnosticHelper.getDiagnostics().size());
  }

  @Test
  public void unhandledExceptionsAreReportedWithoutBugParadeLink() throws Exception {
    @BugPattern(name = "", explanation = "", summary = "",
        maturity = EXPERIMENTAL, severity = ERROR, category = ONE_OFF)
    class Throwing extends BugChecker implements ExpressionStatementTreeMatcher {
      @Override
      public Description matchExpressionStatement(ExpressionStatementTree tree, VisitorState state)
      {
        throw new IllegalStateException("test123");
      }
    }
    compilerBuilder.report(new ErrorProneScanner(new Throwing()));
    compiler = compilerBuilder.build();
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(), "MultipleTopLevelClassesWithErrors.java",
            "ExtendedMultipleTopLevelClassesWithErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.ERROR));
    @SuppressWarnings("unchecked"// hamcrest should use @SafeVarargs
    Matcher<Iterable<Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(CoreMatchers.<String>allOf(
            containsString("IllegalStateException: test123"),
            containsString("unhandled exception was thrown by the Error Prone"))));
    assertTrue("Error should be reported. " + diagnosticHelper.describe(),
        matcher.matches(diagnosticHelper.getDiagnostics()));
  }

  /**
   * Regression test for Issue 188, error-prone doesn't work with annotation processors.
   */
  @Test
  public void annotationProcessingWorks() throws Exception {
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(), "UsesAnnotationProcessor.java"),
        List.of(new NullAnnotationProcessor()));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }

  /**
   * Test that if javac does dataflow on a class twice error-prone only analyses it once.
   */
  @Test
  public void reportReadyForAnalysisOnce() throws Exception {
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(),
            "FlowConstants.java",
            "FlowSub.java",
            // This order is important: the superclass needs to occur after the subclass in the
            // sources so it goes through flow twice (once so it can be used when the subclass
            // is desugared, once normally).
            "FlowSuper.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }

  @Test
  public void propagatesScannerThroughAnnotationProcessingRounds() throws Exception {
    ErrorProneScanner scanner = new ErrorProneScanner();
    compilerBuilder.report(scanner);
    compiler = compilerBuilder.build();
    Result exitCode = compiler.compile(
        compiler.fileManager().sources(getClass(), "UsesAnnotationProcessor.java"),
        Arrays.asList(new ScannerCheckingProcessor(scanner)));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }

  /**
   * Annotation processor that checks that the context always has the same {@link ErrorProneScanner}
   * instance at each stage of annotation processing.
   */
  @SupportedAnnotationTypes("*")
  public static final class ScannerCheckingProcessor extends AbstractProcessor {

    private final ErrorProneScanner expected;

    public ScannerCheckingProcessor(ErrorProneScanner expected) {
      this.expected = expected;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
      return SourceVersion.latest();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
      assertSame(expected, context.get(Scanner.class));
      return false;
    }
  }

  @BugPattern(name = "ConstructorMatcher", explanation = "",
      category = ONE_OFF, maturity = EXPERIMENTAL, severity = ERROR, summary = "")
  private static class ConstructorMatcher extends BugChecker implements MethodTreeMatcher {
    @Override
    public Description matchMethod(MethodTree tree, VisitorState state) {
      return describeMatch(tree);
    }
  }

  @Test
  public void ignoreGeneratedConstructors() throws Exception {
    compilerBuilder.report(new ErrorProneScanner(new ConstructorMatcher()));
    compiler = compilerBuilder.build();
    Result exitCode = compiler.compile(
        Arrays.asList(compiler.fileManager().forSourceLines("Test.java", "public class Test {}")));
    outputStream.flush();

    Matcher<Iterable<Diagnostic<JavaFileObject>>> matcher = not(hasItem(
        diagnosticMessage(containsString("[ConstructorMatcher]"))));
    assertTrue(
        "Warning should be found. " + diagnosticHelper.describe(),
        matcher.matches(diagnosticHelper.getDiagnostics()));

    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }

  @BugPattern(name = "SuperCallMatcher", explanation = "",
      category = ONE_OFF, maturity = EXPERIMENTAL, severity = ERROR, summary = "")
  private static class SuperCallMatcher extends BugChecker implements MethodInvocationTreeMatcher {
    @Override
    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
      Tree select = tree.getMethodSelect();
      Name name;
      if (select instanceof MemberSelectTree) {
        name = ((MemberSelectTree) select).getIdentifier();
      } else if (select instanceof IdentifierTree) {
        name = ((IdentifierTree) select).getName();
      } else {
        return Description.NO_MATCH;
      }
      return name.contentEquals("super")
          ? describeMatch(tree)
          : Description.NO_MATCH;
    }
  }

  // TODO(user) - how can we distinguish between synthetic super() calls and real ones?
  @Ignore
  @Test
  public void ignoreGeneratedSuperInvocations() throws Exception {
    compilerBuilder.report(new ErrorProneScanner(new SuperCallMatcher()));
    compiler = compilerBuilder.build();
    Result exitCode = compiler.compile(Arrays.asList(
        compiler.fileManager().forSourceLines("Test.java",
            "public class Test {",
            "  public Test() {}",
            "}")));
    outputStream.flush();

    Matcher<Iterable<Diagnostic<JavaFileObject>>> matcher = not(hasItem(
        diagnosticMessage(containsString("[SuperCallMatcher]"))));
    assertTrue(
        "Warning should be found. " + diagnosticHelper.describe(),
        matcher.matches(diagnosticHelper.getDiagnostics()));

    assertThat(outputStream.toString(), exitCode, is(Result.OK));
  }
}
TOP

Related Classes of com.google.errorprone.ErrorProneCompilerIntegrationTest$SuperCallMatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.