/* * Copyright 2016 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.turbine.diag; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import com.google.common.base.CharMatcher; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.diag.TurbineError.ErrorKind; import java.util.Objects; import javax.tools.Diagnostic; import org.checkerframework.checker.nullness.qual.Nullable; /** A compilation error. */ public class TurbineDiagnostic { private final Diagnostic.Kind severity; private final ErrorKind kind; private final ImmutableList args; private final @Nullable SourceFile source; private final int position; private TurbineDiagnostic( Diagnostic.Kind severity, ErrorKind kind, ImmutableList args, @Nullable SourceFile source, int position) { this.severity = requireNonNull(severity); this.kind = requireNonNull(kind); this.args = requireNonNull(args); this.source = source; this.position = position; } /** The diagnostic kind. */ public ErrorKind kind() { return kind; } /** * The diagnostic severity (error, warning, ...). Turbine only produces errors, non-error * diagnostics are only ever created by annotation processors. */ public Diagnostic.Kind severity() { return severity; } boolean isError() { return severity.equals(Diagnostic.Kind.ERROR); } /** The diagnostic message. */ public String diagnostic() { StringBuilder sb = new StringBuilder(path()); if (line() != -1) { sb.append(':').append(line()); } sb.append(": error: "); sb.append(message()).append(System.lineSeparator()); if (line() != -1 && column() != -1) { requireNonNull(source); // line and column imply source is non-null sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(source.lineMap().line(position))) .append(System.lineSeparator()); sb.append(Strings.repeat(" ", column() - 1)).append('^'); } return sb.toString(); } /** The diagnostic arguments. */ public ImmutableList args() { return args; } private static TurbineDiagnostic create( Diagnostic.Kind severity, ErrorKind kind, ImmutableList args, SourceFile source, int position) { switch (kind) { case SYMBOL_NOT_FOUND: { checkArgument( args.size() == 1 && getOnlyElement(args) instanceof ClassSymbol, "diagnostic (%s) has invalid argument %s", kind, args); break; } default: // fall out } return new TurbineDiagnostic(severity, kind, args, source, position); } public static TurbineDiagnostic format(Diagnostic.Kind severity, ErrorKind kind, String message) { return create(severity, kind, ImmutableList.of(message), null, -1); } /** * Formats a diagnostic. * * @param source the current source file * @param kind the error kind * @param args format args */ public static TurbineDiagnostic format(SourceFile source, ErrorKind kind, Object... args) { return create(Diagnostic.Kind.ERROR, kind, ImmutableList.copyOf(args), source, -1); } /** * Formats a diagnostic. * * @param position the diagnostic position * @param kind the error kind * @param args format args */ public static TurbineDiagnostic format( Diagnostic.Kind severity, SourceFile source, int position, ErrorKind kind, Object... args) { return create(severity, kind, ImmutableList.copyOf(args), source, position); } @Override public int hashCode() { return Objects.hash(kind, source, position); } @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof TurbineDiagnostic)) { return false; } TurbineDiagnostic that = (TurbineDiagnostic) obj; return severity.equals(that.severity) && kind.equals(that.kind) && args.equals(that.args) && Objects.equals(source, that.source) && position == that.position; } public String path() { return source != null && source.path() != null ? source.path() : "<>"; } @SuppressWarnings("nullness") // position != -1 implies source is non-null public int line() { return position != -1 ? source.lineMap().lineNumber(position) : -1; } @SuppressWarnings("nullness") // position != -1 implies source is non-null public int column() { return position != -1 ? source.lineMap().column(position) + 1 : -1; } public String message() { return kind.format(args.toArray()); } }