blob: 4786906eecd4d614fcb59ad3b1e600d5e86a09be [file] [log] [blame]
/*
* Copyright 2014-present Facebook, 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.facebook.buck.java;
import com.facebook.buck.io.ProjectFilesystem;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavacErrorParser {
private final ProjectFilesystem filesystem;
private final JavaPackageFinder javaPackageFinder;
private static ImmutableList<Pattern> onePartPatterns = ImmutableList.of(
Pattern.compile(
"error: cannot access (?<symbol>\\S+)"),
Pattern.compile(
"error: package \\S+ does not exist\nimport (?<symbol>\\S+);"),
Pattern.compile(
"error: package \\S+ does not exist\nimport static (?<symbol>\\S+)\\.[^.]+;"));
private static ImmutableList<Pattern> twoPartPatterns = ImmutableList.of(
Pattern.compile(
"\\s*symbol:\\s+class (?<class>\\S+)\n\\s*location:\\s+package (?<package>\\S+)"));
// These patterns match missing symbols that live in the current package. Usually, that means one
// java package that's split up into multiple java_library rules, which depend on each other.
// Classes in the same package can reference symbols without an import or fully qualified name,
// which means we have to infer the package name from the filepath in the error.
// NOTE: Regular missing symbols in other packages will often generate compiler errors that match
// these patterns, because imported symbols are used unqualified after the import. That means we
// might go looking for an imported symbol in the current package (wrong) in addition to the
// package it was imported from (right). That's ultimately fine, because the symbol can't exist in
// both places (without causing another compiler error).
private static ImmutableList<Pattern> localPackagePatterns = ImmutableList.of(
Pattern.compile(
"^(?<file>.+):[0-9]+: error: cannot find symbol\n" +
".*\n" +
".*\n" +
"\\s*symbol:\\s+(class|variable) (?<class>\\S+)"),
Pattern.compile(
"^(?<file>.+):[0-9]+: error: package (?<class>\\S+) does not exist"));
public JavacErrorParser(ProjectFilesystem filesystem, JavaPackageFinder javaPackageFinder) {
this.filesystem = filesystem;
this.javaPackageFinder = javaPackageFinder;
}
public Optional<String> getMissingSymbolFromCompilerError(String error) {
for (Pattern pattern: onePartPatterns) {
Matcher matcher = pattern.matcher(error);
if (matcher.find()) {
return Optional.of(matcher.group("symbol"));
}
}
for (Pattern pattern: twoPartPatterns) {
Matcher matcher = pattern.matcher(error);
if (matcher.find()) {
return Optional.of(matcher.group("package") + "." + matcher.group("class"));
}
}
for (Pattern pattern: localPackagePatterns) {
Matcher matcher = pattern.matcher(error);
if (matcher.find()) {
return getMissingSymbolInLocalPackage(matcher);
}
}
return Optional.absent();
}
private Optional<String> getMissingSymbolInLocalPackage(Matcher matcher) {
String fileName = matcher.group("file");
String className = matcher.group("class");
Path repoRoot = filesystem.getRootPath().toAbsolutePath().normalize();
Path relativePath = repoRoot.relativize(Paths.get(fileName));
String packageName = javaPackageFinder.findJavaPackageForPath(relativePath.toString());
return Optional.of(packageName + "." + className);
}
}