blob: 8f2f4b13a80c6845efbc0366a487abd98d2e40eb [file] [log] [blame]
// Copyright 2017 The Bazel Authors. 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.devtools.build.buildjar.javac;
import static java.util.Locale.ENGLISH;
import com.google.common.collect.ImmutableList;
import com.sun.tools.javac.api.ClientCodeWrapper.Trusted;
import com.sun.tools.javac.api.DiagnosticFormatter;
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JavacMessages;
import com.sun.tools.javac.util.Log;
import java.util.Locale;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
/**
* A {@link Diagnostic<JavaFileObject>} that includes the full formatted message produced by javac,
* which relies on compilation internals and can't be reproduced after the compilation is complete.
*/
public class FormattedDiagnostic implements Diagnostic<JavaFileObject> {
public final Diagnostic<? extends JavaFileObject> diagnostic;
public final String formatted;
public final String lintCategory;
public final boolean werror;
public FormattedDiagnostic(
Diagnostic<? extends JavaFileObject> diagnostic,
String formatted,
String lintCategory,
boolean werror) {
this.diagnostic = diagnostic;
this.formatted = formatted;
this.lintCategory = lintCategory;
this.werror = werror;
}
/** The formatted diagnostic message produced by javac's diagnostic formatter. */
public String getFormatted() {
return formatted;
}
public String getLintCategory() {
return lintCategory;
}
@Override
public String toString() {
return formatted;
}
@Override
public Kind getKind() {
return werror ? Kind.ERROR : diagnostic.getKind();
}
@Override
public JavaFileObject getSource() {
return diagnostic.getSource();
}
@Override
public long getPosition() {
return diagnostic.getPosition();
}
@Override
public long getStartPosition() {
return diagnostic.getStartPosition();
}
@Override
public long getEndPosition() {
return diagnostic.getEndPosition();
}
@Override
public long getLineNumber() {
return diagnostic.getLineNumber();
}
@Override
public long getColumnNumber() {
return diagnostic.getColumnNumber();
}
@Override
public String getCode() {
return diagnostic.getCode();
}
@Override
public String getMessage(Locale locale) {
return diagnostic.getMessage(locale);
}
/** A {@link DiagnosticListener<JavaFileObject>} that saves {@link FormattedDiagnostic}s. */
@Trusted
static class Listener implements DiagnosticListener<JavaFileObject> {
private final ImmutableList.Builder<FormattedDiagnostic> diagnostics = ImmutableList.builder();
private final boolean failFast;
private final Optional<WerrorCustomOption> werrorCustomOption;
private final Context context;
private boolean werror = false;
Listener(boolean failFast, Optional<WerrorCustomOption> werrorCustomOption, Context context) {
this.failFast = failFast;
this.werrorCustomOption = werrorCustomOption;
// retrieve context values later, in case it isn't initialized yet
this.context = context;
}
@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
JCDiagnostic jcDiagnostic = (JCDiagnostic) diagnostic;
boolean werror = isWerror(jcDiagnostic);
if (werror) {
this.werror = true;
}
DiagnosticFormatter<JCDiagnostic> formatter = Log.instance(context).getDiagnosticFormatter();
JavacMessages messages = JavacMessages.instance(context);
Locale locale = messages.getCurrentLocale();
String formatted = formatter.format(jcDiagnostic, locale);
if (werror) {
formatted =
formatted.replaceFirst(
formatter.formatKind(jcDiagnostic, locale),
messages.getLocalizedString(locale, "compiler.err.error"));
}
LintCategory lintCategory = jcDiagnostic.getLintCategory();
FormattedDiagnostic formattedDiagnostic =
new FormattedDiagnostic(
diagnostic, formatted, lintCategory != null ? lintCategory.option : null, werror);
diagnostics.add(formattedDiagnostic);
if (failFast && diagnostic.getKind().equals(Diagnostic.Kind.ERROR)) {
throw new FailFastException(formatted);
}
}
private boolean isWerror(JCDiagnostic diagnostic) {
if (werrorCustomOption.isEmpty()) {
return false;
}
String lintCategory = lintCategory(diagnostic);
if (lintCategory == null) {
return false;
}
switch (diagnostic.getKind()) {
case WARNING:
case MANDATORY_WARNING:
return werrorCustomOption.get().isEnabled(lintCategory);
default:
return false;
}
}
@Nullable
private static String lintCategory(JCDiagnostic diagnostic) {
if (diagnostic.getCode().equals("compiler.warn.sun.proprietary")) {
return "sunapi";
}
LintCategory lintCategory = diagnostic.getLintCategory();
if (lintCategory == null) {
return null;
}
return lintCategory.option;
}
ImmutableList<FormattedDiagnostic> build() {
return diagnostics.build();
}
boolean werror() {
return werror;
}
}
static class FailFastException extends RuntimeException {
FailFastException(String message) {
super(message);
}
}
public boolean isJSpecifyDiagnostic() {
return getKind().equals(Diagnostic.Kind.ERROR)
&& getCode().equals("compiler.err.proc.messager")
&& getMessage(ENGLISH).startsWith("[nullness] ");
}
}