All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.testing.compile.Parser Maven / Gradle / Ivy

There is a newer version: 0.21.0
Show newest version
/*
 * Copyright (C) 2016 Google, Inc.
 *
 * 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.testing.compile;

import static com.google.common.base.MoreObjects.firstNonNull;
import static java.lang.Boolean.TRUE;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.function.Predicate.isEqual;
import static javax.tools.Diagnostic.Kind.ERROR;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimaps;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ErroneousTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.JavacTool;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

/** Methods to parse Java source files. */
public final class Parser {

  /**
   * Parses {@code sources} into {@linkplain CompilationUnitTree compilation units}. This method
   * does not compile the sources.
   */
  static ParseResult parse(Iterable sources) {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>();
    InMemoryJavaFileManager fileManager =
        new InMemoryJavaFileManager(
            compiler.getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8));
    JavacTask task =
        ((JavacTool) compiler)
            .getTask(
                null, // explicitly use the default because old javac logs some output on stderr
                fileManager,
                diagnosticCollector,
                ImmutableSet.of(),
                ImmutableSet.of(),
                sources);
    try {
      Iterable parsedCompilationUnits = task.parse();
      List> diagnostics = diagnosticCollector.getDiagnostics();
      if (foundParseErrors(parsedCompilationUnits, diagnostics)) {
        throw new IllegalStateException(
            "error while parsing:\n" + Joiner.on('\n').join(diagnostics));
      }
      return new ParseResult(
          sortDiagnosticsByKind(diagnostics), parsedCompilationUnits, Trees.instance(task));
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Returns {@code true} if errors were found while parsing source files.
   *
   * 

Normally, the parser reports error diagnostics, but in some cases there are no diagnostics; * instead the parse tree contains {@linkplain ErroneousTree "erroneous"} nodes. */ private static boolean foundParseErrors( Iterable parsedCompilationUnits, List> diagnostics) { return diagnostics.stream().map(Diagnostic::getKind).anyMatch(isEqual(ERROR)) || Iterables.any(parsedCompilationUnits, Parser::hasErrorNode); } /** * Returns {@code true} if the tree contains at least one {@linkplain ErroneousTree "erroneous"} * node. */ private static boolean hasErrorNode(Tree tree) { return isTrue(HAS_ERRONEOUS_NODE.scan(tree, false)); } private static final TreeScanner HAS_ERRONEOUS_NODE = new TreeScanner() { @Override public Boolean visitErroneous(ErroneousTree node, Boolean p) { return true; } @Override public Boolean scan(Iterable nodes, Boolean p) { for (Tree node : firstNonNull(nodes, ImmutableList.of())) { if (isTrue(scan(node, p))) { return true; } } return p; } @Override public Boolean scan(Tree tree, Boolean p) { return isTrue(p) ? p : super.scan(tree, p); } @Override public Boolean reduce(Boolean r1, Boolean r2) { return isTrue(r1) || isTrue(r2); } }; private static boolean isTrue(Boolean p) { return TRUE.equals(p); } private static ImmutableListMultimap> sortDiagnosticsByKind(Iterable> diagnostics) { return Multimaps.index(diagnostics, input -> input.getKind()); } /** * The diagnostic, parse trees, and {@link Trees} instance for a parse task. * *

Note: It is possible for the {@link Trees} instance contained within a {@code ParseResult} * to be invalidated by a call to {@link com.sun.tools.javac.api.JavacTaskImpl#cleanup()}. Though * we do not currently expose the {@link JavacTask} used to create a {@code ParseResult} to {@code * cleanup()} calls on its underlying implementation, this should be acknowledged as an * implementation detail that could cause unexpected behavior when making calls to methods in * {@link Trees}. */ static final class ParseResult { private final ImmutableListMultimap> diagnostics; private final ImmutableList compilationUnits; private final Trees trees; ParseResult( ImmutableListMultimap> diagnostics, Iterable compilationUnits, Trees trees) { this.trees = trees; this.compilationUnits = ImmutableList.copyOf(compilationUnits); this.diagnostics = diagnostics; } ImmutableListMultimap> diagnosticsByKind() { return diagnostics; } Iterable compilationUnits() { return compilationUnits; } Trees trees() { return trees; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy