Files
carbon-lang/toolchain/parse/tree_node_diagnostic_converter.h
T
Jon Ross-Perkins b079acd86f Replace NodeId with a hybrid LocationId in SemIR diagnostics. (#3810)
The purpose of this change is to allow something such as a FunctionDecl
instruction to note an imported instruction as the "loc_id". Note that
doesn't occur here: this change is already very sweeping in edits. There
is no testdata affected, intended to show equivalent behavior.

We might want to consolidate NodeId references towards LocationId, but
if that's preferred, I'd still like to split it out. A lot of this just
piping through LocationId where it's a build error otherwise, enough
that imports should be able to start using it for diagnostics.
ValueStores are added but still unused -- just flushing out structure
for review.

Restructuring SemIRLocation is necessary to use LocationId this way. For
TokenOnly, it's not getting used in Parse, so I migrated it to Check and
it's now specific to SemIRLocation.

I also considered making LocationId reference an InstId (which would
need to be an ImportRef) instead of an ImportIRInstId. However, that
would've required import.cpp to add instructions for decls which are
reached during resolution -- we typically don't have an inst ready for
use. An extra inst is essentially 16 bytes in InstId's ValueStore + 4
bytes in LocationId's ValueStore, whereas this is 8 bytes per.
2024-03-27 22:55:22 +00:00

101 lines
3.5 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_TOOLCHAIN_PARSE_TREE_NODE_DIAGNOSTIC_CONVERTER_H_
#define CARBON_TOOLCHAIN_PARSE_TREE_NODE_DIAGNOSTIC_CONVERTER_H_
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/lex/tokenized_buffer.h"
#include "toolchain/parse/tree.h"
namespace Carbon::Parse {
class NodeLocation {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
NodeLocation(NodeId node_id) : NodeLocation(node_id, false) {}
NodeLocation(NodeId node_id, bool token_only)
: node_id_(node_id), token_only_(token_only) {}
// TODO: Have some other way of representing diagnostic that applies to a file
// as a whole.
// NOLINTNEXTLINE(google-explicit-constructor)
NodeLocation(InvalidNodeId node_id) : NodeLocation(node_id, false) {}
auto node_id() const -> NodeId { return node_id_; }
auto token_only() const -> bool { return token_only_; }
private:
NodeId node_id_;
bool token_only_;
};
class NodeLocationConverter : public DiagnosticConverter<NodeLocation> {
public:
explicit NodeLocationConverter(const Lex::TokenizedBuffer* tokens,
llvm::StringRef filename,
const Tree* parse_tree)
: token_converter_(tokens),
filename_(filename),
parse_tree_(parse_tree) {}
// Map the given token into a diagnostic location.
auto ConvertLocation(NodeLocation node_location, ContextFnT context_fn) const
-> DiagnosticLocation override {
// Support the invalid token as a way to emit only the filename, when there
// is no line association.
if (!node_location.node_id().is_valid()) {
return {.filename = filename_};
}
if (node_location.token_only()) {
return token_converter_.ConvertLocation(
parse_tree_->node_token(node_location.node_id()), context_fn);
}
// Construct a location that encompasses all tokens that descend from this
// node (including the root).
Lex::TokenIndex start_token =
parse_tree_->node_token(node_location.node_id());
Lex::TokenIndex end_token = start_token;
for (NodeId desc : parse_tree_->postorder(node_location.node_id())) {
Lex::TokenIndex desc_token = parse_tree_->node_token(desc);
if (!desc_token.is_valid()) {
continue;
}
if (desc_token < start_token) {
start_token = desc_token;
} else if (desc_token > end_token) {
end_token = desc_token;
}
}
DiagnosticLocation start_loc =
token_converter_.ConvertLocation(start_token, context_fn);
if (start_token == end_token) {
return start_loc;
}
DiagnosticLocation end_loc =
token_converter_.ConvertLocation(end_token, context_fn);
// For multiline locations we simply return the rest of the line for now
// since true multiline locations are not yet supported.
if (start_loc.line_number != end_loc.line_number) {
start_loc.length = start_loc.line.size() - start_loc.column_number + 1;
} else {
if (start_loc.column_number != end_loc.column_number) {
start_loc.length =
end_loc.column_number + end_loc.length - start_loc.column_number;
}
}
return start_loc;
}
private:
Lex::TokenDiagnosticConverter token_converter_;
llvm::StringRef filename_;
const Tree* parse_tree_;
};
} // namespace Carbon::Parse
#endif // CARBON_TOOLCHAIN_PARSE_TREE_NODE_DIAGNOSTIC_CONVERTER_H_