mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 19:51:09 +01:00
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.
101 lines
3.5 KiB
C++
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_
|