| // Copyright 2014 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.lib.events; | |
| import com.google.common.base.Preconditions; | |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; | |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; | |
| import java.io.Serializable; | |
| import java.util.Objects; | |
| /** | |
| * A Location denotes a position within a Starlark file. | |
| * | |
| * <p>A location is a triple {@code (file, line column)}, where {@code file} is the apparent name of | |
| * the file, {@code line} is the optional 1-based line number, and {@code column} is the optional | |
| * 1-based column number measured in UTF-16 code units. If the column is zero it is not displayed. | |
| * If the line number is also zero, it too is not displayed; in this case, the location denotes the | |
| * file as a whole. | |
| */ | |
| // TODO(adonovan): invert the dependency syntax -> events dependency. | |
| // That is, make this package depend on the Starlark syntax package, | |
| // not the other way around. | |
| // This Location type belongs in lib.syntax. | |
| // | |
| // Due to recent lib.syntax clean-ups, Location construction is | |
| // infrequent during normal execution; Locations are constructed only | |
| // on error. | |
| // | |
| // The syntax package also depends on Event, both for print events | |
| // (which can be eliminated by defining a PrintHandler interface | |
| // and defining an adaptor for it in lib.events) and for syntax errors, | |
| // for which lib.syntax will need a new String+Location error type, | |
| // which again this package will adapt to events.Event. | |
| public abstract class Location implements Serializable, Comparable<Location> { | |
| // TODO(adonovan): merge with Lexer.LexerLocation and make this the only implementation of | |
| // Location, once the parser no longer eagerly creates Locations but instead | |
| // records token offsets and the LineNumberTable in the tree. | |
| @AutoCodec | |
| @Immutable | |
| static class FileLineColumn extends Location { | |
| final String file; | |
| final LineAndColumn linecol; | |
| FileLineColumn(String file, LineAndColumn linecol) { | |
| this.file = Preconditions.checkNotNull(file); | |
| this.linecol = linecol; | |
| } | |
| @Override | |
| public String file() { | |
| return file; | |
| } | |
| @Override | |
| protected LineAndColumn getLineAndColumn() { | |
| return linecol; | |
| } | |
| } | |
| /** | |
| * Returns a Location for the given file, line and column. If {@code column} is non-zero, {@code | |
| * line} too must be non-zero. | |
| */ | |
| public static Location fromFileLineColumn(String file, int line, int column) { | |
| Preconditions.checkArgument(line != 0 || column == 0, "non-zero column but no line number"); | |
| return new FileLineColumn(file, new LineAndColumn(line, column)); | |
| } | |
| /** Returns a Location for the file as a whole. */ | |
| public static Location fromFile(String file) { | |
| return new FileLineColumn(file, ZERO); | |
| } | |
| private static final LineAndColumn ZERO = new LineAndColumn(0, 0); | |
| /** Returns the name of the file containing this location. */ | |
| public abstract String file(); | |
| protected abstract LineAndColumn getLineAndColumn(); | |
| /** Returns the line number of this location. */ | |
| public final int line() { | |
| return getLineAndColumn().line; | |
| } | |
| /** Returns the column number of this location. */ | |
| public final int column() { | |
| return getLineAndColumn().column; | |
| } | |
| /** | |
| * Formats the location as {@code "file:line:col"}. If the column is zero, it is omitted. If the | |
| * line is also zero, it too is omitted. | |
| */ | |
| @Override | |
| public String toString() { | |
| StringBuilder buf = new StringBuilder(); | |
| buf.append(file()); | |
| LineAndColumn linecol = getLineAndColumn(); | |
| if (linecol.line != 0) { | |
| buf.append(':').append(linecol.line); | |
| if (linecol.column != 0) { | |
| buf.append(':').append(linecol.column); | |
| } | |
| } | |
| return buf.toString(); | |
| } | |
| /** A value class that describes the line and column of a location. */ | |
| // TODO(adonovan): make private when we combine with LexerLocation. | |
| @AutoCodec | |
| @Immutable | |
| public static final class LineAndColumn { | |
| public final int line; | |
| public final int column; | |
| public LineAndColumn(int line, int column) { | |
| this.line = line; | |
| this.column = column; | |
| } | |
| @Override | |
| public boolean equals(Object o) { | |
| if (o == this) { | |
| return true; | |
| } | |
| if (!(o instanceof LineAndColumn)) { | |
| return false; | |
| } | |
| LineAndColumn lac = (LineAndColumn) o; | |
| return lac.line == line && lac.column == column; | |
| } | |
| @Override | |
| public int hashCode() { | |
| return line * 41 + column; | |
| } | |
| @Override | |
| public String toString() { | |
| return line + ":" + column; | |
| } | |
| } | |
| /** Returns a three-valued lexicographical comparison of two Locations. */ | |
| @Override | |
| public final int compareTo(Location that) { | |
| int cmp = this.file().compareTo(that.file()); | |
| if (cmp != 0) { | |
| return cmp; | |
| } | |
| LineAndColumn x = this.getLineAndColumn(); | |
| LineAndColumn y = that.getLineAndColumn(); | |
| return Long.compare(((long) x.line << 32) | x.column, ((long) y.line << 32) | y.column); | |
| } | |
| @Override | |
| public final int hashCode() { | |
| return Objects.hash(file(), getLineAndColumn()); | |
| } | |
| @Override | |
| public final boolean equals(Object that) { | |
| return this == that | |
| || that instanceof Location | |
| && this.file().equals(((Location) that).file()) | |
| && this.getLineAndColumn().equals(((Location) that).getLineAndColumn()); | |
| } | |
| /** A location for built-in functions. */ | |
| @AutoCodec public static final Location BUILTIN = fromFile("<builtin>"); | |
| } |