mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Introduces `Context` and `SoftContext` messages, which can be introduced
through a `ContextBuilder`:
- The `Context` messages come before the diagnostic in the output.
- The first `Context` message steals the diagnostic level from the main
diagnostic, and turns the main diagnostic into a Note attached to the
context.
- A `SoftContext` message works similarly, but if it's preceeded by a
`Context` or `SoftContext` message, then it is dropped. This can be used
as a default/backup scope when nothing more interesting is provided up
the stack, such as in `TryEvalBlockForSpecific`.
The `ContextBuilder` is provided to a callback through
`Diagnostics::ContextScope`, an RAII type `AnnotationScope` but for
context messages.
This allows a high level operation to provide a context message like
"failed to identify facet type {0}" which will then be used as the error
if a diagnostic is produced during identification, with the latter
diagnostic attached as a note to explain why the contextual operation
failed.
In particular, this allows monomorphization errors (such as an array
bound being negative) to be attached to a higher lever operation instead
of being top-level diagnostics themselves, with the monomorphization
site being a note. This inverts the source code locations that appear in
the diagnostic, so that the top-level diagnostic points to the "user
code" which causes the monomorphization.
This is presented as an alternative strategy to #6753, which plumbed
diagnoser callbacks around to achieve the same goals.
We replace the diagnoser callbacks in type completion and operators with
ContextScope callbacks instead, which now provide better diagnostics for
monomorphization errors. Other callers to MakeSpecific do not yet have
ContextScopes introduced in order to turn monomorphization errors into
more interesting diagnostics.
153 lines
5.5 KiB
C++
153 lines
5.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_CHECK_DIAGNOSTIC_HELPERS_H_
|
|
#define CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_HELPERS_H_
|
|
|
|
#include <concepts>
|
|
|
|
#include "llvm/ADT/APSInt.h"
|
|
#include "toolchain/parse/node_ids.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// The `DiagnosticEmitterBase` is templated on this type so that
|
|
// diagnostics can be passed an `InstId` as a location, without having to
|
|
// explicitly construct a `LocId` from it first.
|
|
class LocIdForDiagnostics {
|
|
public:
|
|
// Constructs a token-only location for a diagnostic.
|
|
//
|
|
// This means the displayed location will include only the location's specific
|
|
// parse node, instead of also including its descendants.
|
|
static auto TokenOnly(Parse::NodeId node_id) -> LocIdForDiagnostics {
|
|
return LocIdForDiagnostics(SemIR::LocId(node_id), true);
|
|
}
|
|
|
|
template <typename LocT>
|
|
requires std::constructible_from<SemIR::LocId, LocT>
|
|
explicit(false) LocIdForDiagnostics(LocT loc_id)
|
|
: LocIdForDiagnostics(SemIR::LocId(loc_id), false) {}
|
|
|
|
auto loc_id() const -> SemIR::LocId { return loc_id_; }
|
|
|
|
auto is_token_only() const -> bool { return is_token_only_; }
|
|
|
|
private:
|
|
explicit LocIdForDiagnostics(SemIR::LocId loc_id, bool is_token_only)
|
|
: loc_id_(loc_id), is_token_only_(is_token_only) {}
|
|
|
|
SemIR::LocId loc_id_;
|
|
bool is_token_only_;
|
|
};
|
|
|
|
// We define the emitter separately for dependencies, so only provide a base
|
|
// here.
|
|
using DiagnosticEmitterBase = Diagnostics::Emitter<LocIdForDiagnostics>;
|
|
|
|
using DiagnosticBuilder = DiagnosticEmitterBase::Builder;
|
|
using DiagnosticContextBuilder = DiagnosticEmitterBase::ContextBuilder;
|
|
|
|
// A function that adds a Context message for a diagnostic.
|
|
using DiagnosticContextFn =
|
|
llvm::function_ref<auto(DiagnosticContextBuilder&)->void>;
|
|
|
|
// An expression with a constant value, for rendering in a diagnostic. The
|
|
// diagnostic rendering will include enclosing "`"s.
|
|
struct InstIdAsConstant {
|
|
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
|
|
|
|
explicit(false) InstIdAsConstant(SemIR::InstId inst_id) : inst_id(inst_id) {}
|
|
|
|
SemIR::InstId inst_id;
|
|
};
|
|
|
|
// An expression whose type should be rendered in a diagnostic. The diagnostic
|
|
// rendering will include enclosing "`"s, and may also include extra information
|
|
// about the type if it might otherwise be ambiguous or context-dependent, such
|
|
// as the targets of aliases used in the type.
|
|
//
|
|
// TODO: Include such additional information where relevant. For example:
|
|
// "`StdString` (aka `Cpp.std.basic_string(Char)`)".
|
|
//
|
|
// This should be used instead of `TypeId` as a diagnostic argument wherever
|
|
// possible, because we should eventually be able to produce a sugared type name
|
|
// in this case, whereas a `TypeId` will render as a canonical type.
|
|
struct TypeOfInstId {
|
|
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
|
|
|
|
explicit(false) TypeOfInstId(SemIR::InstId inst_id) : inst_id(inst_id) {}
|
|
|
|
SemIR::InstId inst_id;
|
|
};
|
|
|
|
// A type expression, for rendering in a diagnostic. The diagnostic rendering
|
|
// will include enclosing "`"s, and may also include extra information about the
|
|
// type if it would otherwise be ambiguous.
|
|
//
|
|
// TODO: Include such additional information where relevant.
|
|
//
|
|
// This should be used when the source expression used to construct a type is
|
|
// available.
|
|
//
|
|
// Note that this is currently an alias for InstIdAsConstant. However, using
|
|
// InstIdAsType is clearer when defining CARBON_DIAGNOSTICs, and we may wish to
|
|
// distinguish type arguments in diagnostics from more general constants in some
|
|
// way in the future.
|
|
using InstIdAsType = InstIdAsConstant;
|
|
|
|
// A type expression, for rendering in a diagnostic as a raw type. When
|
|
// formatting as a raw type in a diagnostic, the type will be formatted as a
|
|
// simple Carbon expression, without enclosing "`"s. Once we start including
|
|
// extra information about types, such annotations will also not be included for
|
|
// raw types.
|
|
//
|
|
// This is intended for cases where the type is part of a larger syntactic
|
|
// construct in a diagnostic, such as "redefinition of `impl {0} as {1}`".
|
|
struct InstIdAsRawType {
|
|
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
|
|
|
|
explicit(false) InstIdAsRawType(SemIR::InstId inst_id) : inst_id(inst_id) {}
|
|
|
|
SemIR::InstId inst_id;
|
|
};
|
|
|
|
// A type value for rendering in a diagnostic without enclosing "`"s. See
|
|
// `InstIdAsRawType` for details on raw type formatting.
|
|
//
|
|
// As with `TypeId`, this should be avoided as a diagnostic argument where
|
|
// possible, because it can't be formatted with syntactic sugar such as aliases
|
|
// that describe how the type was written.
|
|
struct TypeIdAsRawType {
|
|
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
|
|
|
|
explicit(false) TypeIdAsRawType(SemIR::TypeId type_id) : type_id(type_id) {}
|
|
|
|
SemIR::TypeId type_id;
|
|
};
|
|
|
|
// An integer value together with its type. The type is used to determine how to
|
|
// format the value in diagnostics.
|
|
struct TypedInt {
|
|
using DiagnosticType = Diagnostics::TypeInfo<llvm::APSInt>;
|
|
|
|
SemIR::TypeId type;
|
|
llvm::APInt value;
|
|
};
|
|
|
|
struct SpecificInterfaceIdAsRawType {
|
|
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
|
|
|
|
explicit(false) SpecificInterfaceIdAsRawType(
|
|
SemIR::SpecificInterfaceId specific_interface_id)
|
|
: specific_interface_id(specific_interface_id) {}
|
|
|
|
SemIR::SpecificInterfaceId specific_interface_id;
|
|
};
|
|
|
|
} // namespace Carbon::Check
|
|
|
|
#endif // CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_HELPERS_H_
|