Move diagnostics into a namespace (#5173)

What this really does is avoids shadowing names, so that we can
comfortable have things like `Check::DiagnosticEmitter` or
`Check::DiagnosticLoc` without shadowing being a concern.

Note, down this path I'm also thinking about:

- Renaming misc DiagnosticConsumer/DiagnosticEmitter classes, possibly
just to DiagnosticConsumer/DiagnosticEmitter (so
`Check::DiagnosticEmitter` instead of `SemIRLocDiagnosticEmitter`).
- Dropping `Diagnostic` from `Emitter::DiagnosticBuilder`.
- But not for `Check::DiagnosticBuilder`, because `Check::Builder` would
be ambiguous.
- Renaming diagnostics/diagnostic_* to drop "diagnostic".

[Discussion about SemIRLoc ->
DiagnosticLoc](https://discord.com/channels/655572317891461132/655578254970716160/1353771570463768698)
reminded me of this (in particular the older [Check::DiagnosticBuilder
discussion](https://discord.com/channels/655572317891461132/655578254970716160/1344363562608627763)),
but I'd only do that rename if there's matching consensus about a path
forward where we keep SemIRLoc, and in a way that it's only ever used
for diagnostics (the divergence from which is at the root of current
LocId discussion).

I'm trying to keep that separate from a namespace addition for clarity.
This commit is contained in:
Jon Ross-Perkins
2025-03-26 19:12:10 +00:00
committed by GitHub
parent d317c56916
commit acbe6530c3
83 changed files with 560 additions and 526 deletions
+4 -3
View File
@@ -55,12 +55,13 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
"{0} argument{0:s} passed to "
"{1:=0:function|=1:generic class|=2:generic interface}"
" expecting {2} argument{2:s}",
IntAsSelect, IntAsSelect, IntAsSelect);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect,
Diagnostics::IntAsSelect);
CARBON_DIAGNOSTIC(
InCallToEntity, Note,
"calling {0:=0:function|=1:generic class|=2:generic interface}"
" declared here",
IntAsSelect);
Diagnostics::IntAsSelect);
context.emitter()
.Build(loc_id, CallArgCountMismatch, arg_ids.size(),
static_cast<int>(entity_kind_for_diagnostic), params.size())
@@ -218,7 +219,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
SemIR::InstId return_slot_arg_id = SemIR::InstId::None;
SemIR::ReturnTypeInfo return_info = [&] {
auto& function = context.functions().Get(callee_function.function_id);
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note,
"return type declared here");
+1 -1
View File
@@ -302,7 +302,7 @@ static auto BuildApiMapAndDiagnosePackaging(
CARBON_DIAGNOSTIC(
IncorrectExtension, Error,
"file extension of `{0:.impl|}.carbon` required for {0:`impl`|api}",
BoolAsSelect);
Diagnostics::BoolAsSelect);
auto diag = unit_info.emitter.Build(
packaging ? packaging->names.node_id : Parse::NodeId::None,
IncorrectExtension, is_impl);
+1 -1
View File
@@ -17,7 +17,7 @@ namespace Carbon::Check {
// Checking information that's tracked per file.
struct Unit {
DiagnosticConsumer* consumer;
Diagnostics::Consumer* consumer;
SharedValueStores* value_stores;
// The `timings` may be null if nothing is to be recorded.
Timings* timings;
+7 -7
View File
@@ -50,16 +50,16 @@ struct PackageImports {
// information), in addition to the `Unit` itself.
struct UnitAndImports {
// Converts a `NodeId` to a diagnostic location for `UnitAndImports`.
class Emitter : public DiagnosticEmitter<Parse::NodeId> {
class NodeEmitter : public Diagnostics::Emitter<Parse::NodeId> {
public:
explicit Emitter(DiagnosticConsumer* consumer,
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter)
: DiagnosticEmitter(consumer),
explicit NodeEmitter(Diagnostics::Consumer* consumer,
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter)
: Emitter(consumer),
tree_and_subtrees_getter_(tree_and_subtrees_getter) {}
protected:
auto ConvertLoc(Parse::NodeId node_id, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> Diagnostics::ConvertedLoc override {
return tree_and_subtrees_getter_().NodeToDiagnosticLoc(
node_id, /*token_only=*/false);
}
@@ -83,8 +83,8 @@ struct UnitAndImports {
Unit* unit;
// Emitter information.
ErrorTrackingDiagnosticConsumer err_tracker;
Emitter emitter;
Diagnostics::ErrorTrackingConsumer err_tracker;
NodeEmitter emitter;
// List of the outgoing imports. If a package includes unavailable library
// imports, it has an entry with has_load_error set. Invalid imports (for
+1 -1
View File
@@ -8,7 +8,7 @@
namespace Carbon::Check {
Context::Context(DiagnosticEmitter<SemIRLoc>* emitter,
Context::Context(Diagnostics::Emitter<SemIRLoc>* emitter,
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter,
SemIR::File* sem_ir, int imported_ir_count, int total_ir_count,
llvm::raw_ostream* vlog_stream)
+3 -3
View File
@@ -50,7 +50,7 @@ namespace Carbon::Check {
class Context {
public:
// Stores references for work.
explicit Context(DiagnosticEmitter<SemIRLoc>* emitter,
explicit Context(Diagnostics::Emitter<SemIRLoc>* emitter,
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter,
SemIR::File* sem_ir, int imported_ir_count,
int total_ir_count, llvm::raw_ostream* vlog_stream);
@@ -69,7 +69,7 @@ class Context {
return tokens().GetKind(parse_tree().node_token(node_id));
}
auto emitter() -> DiagnosticEmitter<SemIRLoc>& { return *emitter_; }
auto emitter() -> Diagnostics::Emitter<SemIRLoc>& { return *emitter_; }
auto parse_tree_and_subtrees() -> const Parse::TreeAndSubtrees& {
return tree_and_subtrees_getter_();
@@ -276,7 +276,7 @@ class Context {
private:
// Handles diagnostics.
DiagnosticEmitter<SemIRLoc>* emitter_;
Diagnostics::Emitter<SemIRLoc>* emitter_;
// Returns a lazily constructed TreeAndSubtrees.
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_;
+12 -9
View File
@@ -270,12 +270,12 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
CARBON_DIAGNOSTIC(ArrayInitFromLiteralArgCountMismatch, Error,
"cannot initialize array of {0} element{0:s} from {1} "
"initializer{1:s}",
IntAsSelect, IntAsSelect);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect);
CARBON_DIAGNOSTIC(
ArrayInitFromExprArgCountMismatch, Error,
"cannot initialize array of {0} element{0:s} from tuple "
"with {1} element{1:s}",
IntAsSelect, IntAsSelect);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect);
context.emitter().Emit(value_loc_id,
literal_elems.empty()
? ArrayInitFromExprArgCountMismatch
@@ -359,7 +359,7 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
TupleInitElementCountMismatch, Error,
"cannot initialize tuple of {0} element{0:s} from tuple "
"with {1} element{1:s}",
IntAsSelect, IntAsSelect);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect);
context.emitter().Emit(value_loc_id, TupleInitElementCountMismatch,
dest_elem_types.size(), src_elem_types.size());
}
@@ -459,7 +459,8 @@ static auto ConvertStructToStructOrClass(Context& context,
StructInitElementCountMismatch, Error,
"cannot initialize {0:class|struct} with {1} field{1:s} from struct "
"with {2} field{2:s}",
BoolAsSelect, IntAsSelect, IntAsSelect);
Diagnostics::BoolAsSelect, Diagnostics::IntAsSelect,
Diagnostics::IntAsSelect);
context.emitter().Emit(value_loc_id, StructInitElementCountMismatch,
ToClass, dest_elem_fields_size,
src_elem_fields.size());
@@ -869,7 +870,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
// While the types are the same, the conversion can still fail if it
// performs a copy while converting the value to another category, and
// the type (or some part of it) is not copyable.
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCopy, Note, "in copy of {0}", TypeOfInstId);
builder.Note(value_id, InCopy, value_id);
@@ -1135,7 +1136,7 @@ static auto DiagnoseConversionFailureToConstraintValue(Context& context,
ConversionFailureFacetToFacet, Error,
"cannot{0:| implicitly} convert type {1} that implements {2} "
"into type implementing {3}{0: with `as`|}",
BoolAsSelect, InstIdAsType, TypeOfInstId, SemIR::TypeId);
Diagnostics::BoolAsSelect, InstIdAsType, TypeOfInstId, SemIR::TypeId);
return context.emitter().Build(
loc_id, ConversionFailureFacetToFacet,
target.kind == ConversionTarget::ExplicitAs, expr_id,
@@ -1144,7 +1145,7 @@ static auto DiagnoseConversionFailureToConstraintValue(Context& context,
CARBON_DIAGNOSTIC(ConversionFailureTypeToFacet, Error,
"cannot{0:| implicitly} convert type {1} "
"into type implementing {2}{0: with `as`|}",
BoolAsSelect, InstIdAsType, SemIR::TypeId);
Diagnostics::BoolAsSelect, InstIdAsType, SemIR::TypeId);
return context.emitter().Build(
loc_id, ConversionFailureTypeToFacet,
target.kind == ConversionTarget::ExplicitAs, expr_id, target.type_id);
@@ -1154,7 +1155,8 @@ static auto DiagnoseConversionFailureToConstraintValue(Context& context,
ConversionFailureNonTypeToFacet, Error,
"cannot{0:| implicitly} convert non-type value of type {1} "
"{2:to|into type implementing} {3}{0: with `as`|}",
BoolAsSelect, TypeOfInstId, BoolAsSelect, SemIR::TypeId);
Diagnostics::BoolAsSelect, TypeOfInstId, Diagnostics::BoolAsSelect,
SemIR::TypeId);
return context.emitter().Build(
loc_id, ConversionFailureNonTypeToFacet,
target.kind == ConversionTarget::ExplicitAs, expr_id,
@@ -1286,7 +1288,8 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
CARBON_DIAGNOSTIC(ConversionFailure, Error,
"cannot{0:| implicitly} convert value of type {1} to "
"{2}{0: with `as`|}",
BoolAsSelect, TypeOfInstId, SemIR::TypeId);
Diagnostics::BoolAsSelect, TypeOfInstId,
SemIR::TypeId);
return context.emitter().Build(
loc_id, ConversionFailure,
target.kind == ConversionTarget::ExplicitAs, expr_id,
+2 -2
View File
@@ -332,7 +332,7 @@ auto DeductionContext::Deduce() -> bool {
// compile-time value (e.g. TupleType) that we can decompose further.
// So we do this conversion here, even though we will later try convert
// again when we have deduced all of the bindings.
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context().emitter(), [&](auto& builder) {
if (diagnose_) {
NoteInitializingParam(param_id, builder);
@@ -573,7 +573,7 @@ auto DeductionContext::CheckDeductionIsComplete() -> bool {
binding_type_id =
context().types().GetTypeIdForTypeConstantId(param_type_const_id);
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context().emitter(), [&](auto& builder) {
if (diagnose_) {
NoteInitializingParam(binding_id, builder);
+6 -6
View File
@@ -46,7 +46,7 @@ class SemIRLoc {
bool token_only_;
};
using DiagnosticBuilder = DiagnosticEmitter<SemIRLoc>::DiagnosticBuilder;
using DiagnosticBuilder = Diagnostics::Emitter<SemIRLoc>::DiagnosticBuilder;
// A function that forms a diagnostic for some kind of problem. The
// DiagnosticBuilder is returned rather than emitted so that the caller
@@ -69,7 +69,7 @@ inline auto TokenOnly(SemIR::LocId loc_id) -> SemIRLoc {
// 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 = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// NOLINTNEXTLINE(google-explicit-constructor)
TypeOfInstId(SemIR::InstId inst_id) : inst_id(inst_id) {}
@@ -86,7 +86,7 @@ struct TypeOfInstId {
// This should be used when the source expression used to construct a type is
// available.
struct InstIdAsType {
using DiagnosticType = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// NOLINTNEXTLINE(google-explicit-constructor)
InstIdAsType(SemIR::InstId inst_id) : inst_id(inst_id) {}
@@ -103,7 +103,7 @@ struct InstIdAsType {
// 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 = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// NOLINTNEXTLINE(google-explicit-constructor)
InstIdAsRawType(SemIR::InstId inst_id) : inst_id(inst_id) {}
@@ -118,7 +118,7 @@ struct InstIdAsRawType {
// possible, because it can't be formatted with syntactic sugar such as aliases
// that describe how the type was written.
struct TypeIdAsRawType {
using DiagnosticType = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// NOLINTNEXTLINE(google-explicit-constructor)
TypeIdAsRawType(SemIR::TypeId type_id) : type_id(type_id) {}
@@ -129,7 +129,7 @@ struct TypeIdAsRawType {
// An integer value together with its type. The type is used to determine how to
// format the value in diagnostics.
struct TypedInt {
using DiagnosticType = DiagnosticTypeInfo<llvm::APSInt>;
using DiagnosticType = Diagnostics::TypeInfo<llvm::APSInt>;
SemIR::TypeId type;
llvm::APInt value;
+6 -4
View File
@@ -187,7 +187,9 @@ class EvalContext {
auto sem_ir() -> SemIR::File& { return context().sem_ir(); }
auto emitter() -> DiagnosticEmitter<SemIRLoc>& { return context().emitter(); }
auto emitter() -> Diagnostics::Emitter<SemIRLoc>& {
return context().emitter();
}
private:
// The type-checking context in which we're performing evaluation.
@@ -1036,7 +1038,7 @@ static auto PerformBuiltinIntShiftOp(Context& context, SemIRLoc loc,
CARBON_DIAGNOSTIC(
CompileTimeShiftOutOfRange, Error,
"shift distance >= type width of {0} in `{1} {2:<<|>>} {3}`", unsigned,
TypedInt, BoolAsSelect, TypedInt);
TypedInt, Diagnostics::BoolAsSelect, TypedInt);
context.emitter().Emit(
loc, CompileTimeShiftOutOfRange, lhs_val.getBitWidth(),
{.type = lhs.type_id, .value = lhs_val},
@@ -1050,7 +1052,7 @@ static auto PerformBuiltinIntShiftOp(Context& context, SemIRLoc loc,
context.sem_ir().types().IsSignedInt(rhs.type_id)) {
CARBON_DIAGNOSTIC(CompileTimeShiftNegative, Error,
"shift distance negative in `{0} {1:<<|>>} {2}`",
TypedInt, BoolAsSelect, TypedInt);
TypedInt, Diagnostics::BoolAsSelect, TypedInt);
context.emitter().Emit(
loc, CompileTimeShiftNegative, {.type = lhs.type_id, .value = lhs_val},
builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift,
@@ -1888,7 +1890,7 @@ auto TryEvalBlockForSpecific(Context& context, SemIRLoc loc,
.values = result,
});
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(ResolvingSpecificHere, Note, "in {0} used here",
InstIdAsType);
+1 -1
View File
@@ -111,7 +111,7 @@ static auto HandleIntOrUnsignedIntTypeLiteral(Context& context,
CARBON_DIAGNOSTIC(IntWidthNotMultipleOf8, Error,
"bit width of integer type literal must be a multiple of "
"8; use `Core.{0:Int|UInt}({1})` instead",
BoolAsSelect, llvm::APSInt);
Diagnostics::BoolAsSelect, llvm::APSInt);
context.emitter().Emit(
node_id, IntWidthNotMultipleOf8, int_kind.is_signed(),
llvm::APSInt(context.ints().Get(size_id), /*isUnsigned=*/true));
+1 -1
View File
@@ -266,7 +266,7 @@ auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id)
// instead so we can generate a "name `.Self` implicitly referenced by
// designated expression, but not found" diagnostic instead of adding a
// note to the current "name `.Self` not found" message.
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(
NoPeriodSelfForDesignator, Note,
+1 -1
View File
@@ -87,7 +87,7 @@ static auto DiagnoseDuplicateNames(
CARBON_DIAGNOSTIC(StructNameDuplicate, Error,
"duplicated field name `{1}` in "
"{0:struct type literal|struct literal}",
BoolAsSelect, SemIR::NameId);
Diagnostics::BoolAsSelect, SemIR::NameId);
CARBON_DIAGNOSTIC(StructNamePrevious, Note,
"field with the same name here");
context.emitter()
+1 -1
View File
@@ -617,7 +617,7 @@ auto ImportNameFromOtherPackage(
}
// Annotate diagnostics as occurring during this name lookup.
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InNameLookup, Note, "in name lookup for `{0}`",
SemIR::NameId);
+5 -3
View File
@@ -83,13 +83,15 @@ static auto GenerateAst(Context& context, llvm::StringRef importing_file_path,
CARBON_DIAGNOSTIC(
CppInteropParseError, Error,
"{0} error{0:s} and {1} warning{1:s} in {2} `Cpp` import{2:s}:\n{3}",
IntAsSelect, IntAsSelect, IntAsSelect, std::string);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect,
Diagnostics::IntAsSelect, std::string);
context.emitter().Emit(loc, CppInteropParseError, num_errors, num_warnings,
num_imports, diagnostics_str);
} else if (num_warnings > 0) {
CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning,
"{0} warning{0:s} in `Cpp` {1} import{1:s}:\n{2}",
IntAsSelect, IntAsSelect, std::string);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect,
std::string);
context.emitter().Emit(loc, CppInteropParseWarning, num_warnings,
num_imports, diagnostics_str);
}
@@ -348,7 +350,7 @@ static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
-> SemIR::InstId {
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppNameLookup, Note,
"in `Cpp` name lookup for `{0}`", SemIR::NameId);
+8 -7
View File
@@ -209,14 +209,14 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
CARBON_DIAGNOSTIC(
RedeclParamPrevious, Note,
"previous declaration's corresponding {0:implicit |}parameter here",
BoolAsSelect);
Diagnostics::BoolAsSelect);
auto emit_diagnostic = [&]() {
if (!diagnose) {
return;
}
CARBON_DIAGNOSTIC(RedeclParamDiffers, Error,
"redeclaration differs at {0:implicit |}parameter {1}",
BoolAsSelect, int32_t);
Diagnostics::BoolAsSelect, int32_t);
context.emitter()
.Build(new_param_pattern_id, RedeclParamDiffers, is_implicit_param,
param_index + 1)
@@ -277,7 +277,8 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
CARBON_DIAGNOSTIC(RedeclParamDiffersType, Error,
"type {3} of {0:implicit |}parameter {1} in "
"redeclaration differs from previous parameter type {2}",
BoolAsSelect, int32_t, SemIR::TypeId, SemIR::TypeId);
Diagnostics::BoolAsSelect, int32_t, SemIR::TypeId,
SemIR::TypeId);
context.emitter()
.Build(new_param_pattern_id, RedeclParamDiffersType, is_implicit_param,
param_index + 1, prev_param_type_id, new_param_pattern.type_id())
@@ -315,11 +316,11 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc,
CARBON_DIAGNOSTIC(RedeclParamListDiffers, Error,
"redeclaration differs because of "
"{1:|missing }{0:implicit |}parameter list",
BoolAsSelect, BoolAsSelect);
Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect);
CARBON_DIAGNOSTIC(RedeclParamListPrevious, Note,
"previously declared "
"{1:with|without} {0:implicit |}parameter list",
BoolAsSelect, BoolAsSelect);
Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect);
context.emitter()
.Build(new_decl_loc, RedeclParamListDiffers, is_implicit_param,
new_param_patterns_id.has_value())
@@ -342,11 +343,11 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc,
CARBON_DIAGNOSTIC(
RedeclParamCountDiffers, Error,
"redeclaration differs because of {0:implicit |}parameter count of {1}",
BoolAsSelect, int32_t);
Diagnostics::BoolAsSelect, int32_t);
CARBON_DIAGNOSTIC(
RedeclParamCountPrevious, Note,
"previously declared with {0:implicit |}parameter count of {1}",
BoolAsSelect, int32_t);
Diagnostics::BoolAsSelect, int32_t);
context.emitter()
.Build(new_decl_loc, RedeclParamCountDiffers, is_implicit_param,
new_param_pattern_ids.size())
+5 -3
View File
@@ -11,9 +11,10 @@ namespace Carbon::Check {
// Builds the diagnostic for DiagnoseNotAllowed.
template <typename... TokenKinds>
static auto StartDiagnoseNotAllowed(
Context& context, const DiagnosticBase<TokenKinds...>& diagnostic_base,
Context& context,
const Diagnostics::DiagnosticBase<TokenKinds...>& diagnostic_base,
Parse::NodeId modifier_node, Lex::TokenKind declaration_kind)
-> DiagnosticEmitter<SemIRLoc>::DiagnosticBuilder {
-> Diagnostics::Emitter<SemIRLoc>::DiagnosticBuilder {
if constexpr (sizeof...(TokenKinds) == 0) {
return context.emitter().Build(modifier_node, diagnostic_base);
} else if constexpr (sizeof...(TokenKinds) == 1) {
@@ -34,7 +35,8 @@ static auto StartDiagnoseNotAllowed(
// declaration kind.
template <typename... TokenKinds>
static auto DiagnoseNotAllowed(
Context& context, const DiagnosticBase<TokenKinds...>& diagnostic_base,
Context& context,
const Diagnostics::DiagnosticBase<TokenKinds...>& diagnostic_base,
Parse::NodeId modifier_node, Lex::TokenKind decl_kind,
SemIR::LocId context_loc_id) -> void {
auto diag = StartDiagnoseNotAllowed(context, diagnostic_base, modifier_node,
+2 -2
View File
@@ -225,7 +225,7 @@ static auto DiagnoseInvalidQualifiedNameAccess(Context& context, SemIRLoc loc,
CARBON_DIAGNOSTIC(
ClassInvalidMemberAccess, Error,
"cannot access {0:private|protected} member `{1}` of type {2}",
BoolAsSelect, SemIR::NameId, SemIR::TypeId);
Diagnostics::BoolAsSelect, SemIR::NameId, SemIR::TypeId);
CARBON_DIAGNOSTIC(ClassMemberDeclaration, Note, "declared here");
context.emitter()
.Build(loc, ClassInvalidMemberAccess,
@@ -424,7 +424,7 @@ auto LookupQualifiedName(Context& context, SemIR::LocId loc_id,
SemIR::ConstantId const_id = GetConstantValueInSpecific(
context.sem_ir(), specific_id, extended_id);
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(FromExtendHere, Note,
"declared as an extended scope here");
+2 -2
View File
@@ -493,7 +493,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
CARBON_DIAGNOSTIC(TuplePatternSizeDoesntMatchLiteral, Error,
"tuple pattern expects {0} element{0:s}, but tuple "
"literal has {1}",
IntAsSelect, IntAsSelect);
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect);
context.emitter().Emit(pattern_loc_id, TuplePatternSizeDoesntMatchLiteral,
subpattern_ids.size(), subscrutinee_ids.size());
return;
@@ -531,7 +531,7 @@ auto MatchContext::EmitPatternMatch(Context& context,
if (entry.pattern_id == SemIR::ErrorInst::SingletonInstId) {
return;
}
DiagnosticAnnotationScope annotate_diagnostics(
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
if (kind_ == MatchKind::Caller) {
CARBON_DIAGNOSTIC(InCallToFunctionParam, Note,
@@ -12,7 +12,7 @@ namespace Carbon::Check {
auto SemIRLocDiagnosticEmitter::ConvertLoc(SemIRLoc loc,
ContextFnT context_fn) const
-> ConvertedDiagnosticLoc {
-> Diagnostics::ConvertedLoc {
auto converted = ConvertLocImpl(loc, context_fn);
// Use the token when possible, but -1 is the default value.
@@ -35,7 +35,7 @@ auto SemIRLocDiagnosticEmitter::ConvertLoc(SemIRLoc loc,
auto SemIRLocDiagnosticEmitter::ConvertLocImpl(SemIRLoc loc,
ContextFnT context_fn) const
-> ConvertedDiagnosticLoc {
-> Diagnostics::ConvertedLoc {
llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
loc.is_inst_id_ ? SemIR::GetAbsoluteNodeId(sem_ir_, loc.inst_id_)
: SemIR::GetAbsoluteNodeId(sem_ir_, loc.loc_id_);
@@ -59,7 +59,7 @@ auto SemIRLocDiagnosticEmitter::ConvertLocImpl(SemIRLoc loc,
auto SemIRLocDiagnosticEmitter::ConvertLocInFile(
SemIR::AbsoluteNodeId absolute_node_id, bool token_only,
ContextFnT /*context_fn*/) const -> ConvertedDiagnosticLoc {
ContextFnT /*context_fn*/) const -> Diagnostics::ConvertedLoc {
const auto& tree_and_subtrees =
tree_and_subtrees_getters_[absolute_node_id.check_ir_id.index]();
return tree_and_subtrees.NodeToDiagnosticLoc(absolute_node_id.node_id,
@@ -118,7 +118,7 @@ auto SemIRLocDiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
return llvm::APSInt(typed_int->value,
!sem_ir_->types().IsSignedInt(typed_int->type));
}
return DiagnosticEmitter<SemIRLoc>::ConvertArg(arg);
return Diagnostics::Emitter<SemIRLoc>::ConvertArg(arg);
}
} // namespace Carbon::Check
@@ -17,13 +17,13 @@
namespace Carbon::Check {
// Handles the transformation of a SemIRLoc to a DiagnosticLoc.
class SemIRLocDiagnosticEmitter : public DiagnosticEmitter<SemIRLoc> {
class SemIRLocDiagnosticEmitter : public Diagnostics::Emitter<SemIRLoc> {
public:
explicit SemIRLocDiagnosticEmitter(
DiagnosticConsumer* consumer,
Diagnostics::Consumer* consumer,
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
const SemIR::File* sem_ir)
: DiagnosticEmitter(consumer),
: Emitter(consumer),
tree_and_subtrees_getters_(tree_and_subtrees_getters),
sem_ir_(sem_ir) {}
@@ -44,17 +44,18 @@ class SemIRLocDiagnosticEmitter : public DiagnosticEmitter<SemIRLoc> {
// locations, or `loc` if it's in the same file and (for whatever reason)
// later.
auto ConvertLoc(SemIRLoc loc, ContextFnT context_fn) const
-> ConvertedDiagnosticLoc override;
-> Diagnostics::ConvertedLoc override;
private:
// Implements `ConvertLoc`, but without `last_token_` applied.
auto ConvertLocImpl(SemIRLoc loc, ContextFnT context_fn) const
-> ConvertedDiagnosticLoc;
-> Diagnostics::ConvertedLoc;
// Converts a node_id corresponding to a specific sem_ir to a diagnostic
// location.
auto ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id, bool token_only,
ContextFnT context_fn) const -> ConvertedDiagnosticLoc;
ContextFnT context_fn) const
-> Diagnostics::ConvertedLoc;
// Converters for each SemIR.
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters_;
+1 -1
View File
@@ -579,7 +579,7 @@ static auto NoteAbstractClass(Context& context, SemIR::ClassId class_id,
CARBON_DIAGNOSTIC(
ClassAbstractHere, Note,
"{0:=0:uses class that|=1:class} was declared abstract here",
IntAsSelect);
Diagnostics::IntAsSelect);
builder.Note(class_info.definition_id, ClassAbstractHere,
static_cast<int>(direct_use));
}
+20 -20
View File
@@ -11,37 +11,37 @@
ABSL_FLAG(std::string, testdata_manifest, "",
"A path to a file containing repo-relative names of test files.");
namespace Carbon {
namespace Carbon::Diagnostics {
namespace {
constexpr DiagnosticKind DiagnosticKinds[] = {
#define CARBON_DIAGNOSTIC_KIND(Name) DiagnosticKind::Name,
constexpr Kind Kinds[] = {
#define CARBON_DIAGNOSTIC_KIND(Name) Kind::Name,
#include "toolchain/diagnostics/diagnostic_kind.def"
};
constexpr DiagnosticKind UntestedDiagnosticKinds[] = {
constexpr Kind UntestedKinds[] = {
// These exist only for unit tests.
DiagnosticKind::TestDiagnostic,
DiagnosticKind::TestDiagnosticNote,
Kind::TestDiagnostic,
Kind::TestDiagnosticNote,
// Diagnosing erroneous install conditions, but test environments are
// typically correct.
DiagnosticKind::CompilePreludeManifestError,
DiagnosticKind::DriverInstallInvalid,
Kind::CompilePreludeManifestError,
Kind::DriverInstallInvalid,
// These diagnose filesystem issues that are hard to unit test.
DiagnosticKind::ErrorReadingFile,
DiagnosticKind::ErrorStattingFile,
DiagnosticKind::FileTooLarge,
Kind::ErrorReadingFile,
Kind::ErrorStattingFile,
Kind::FileTooLarge,
// These aren't feasible to test with a normal testcase, but are tested in
// lex/tokenized_buffer_test.cpp.
DiagnosticKind::TooManyTokens,
DiagnosticKind::UnsupportedCrLineEnding,
DiagnosticKind::UnsupportedLfCrLineEnding,
Kind::TooManyTokens,
Kind::UnsupportedCrLineEnding,
Kind::UnsupportedLfCrLineEnding,
// This is a little long but is tested in lex/numeric_literal_test.cpp.
DiagnosticKind::TooManyDigits,
Kind::TooManyDigits,
// TODO: This can only fire if the first message in a diagnostic is rooted
// in a file other than the file being compiled. The language server
@@ -50,16 +50,16 @@ constexpr DiagnosticKind UntestedDiagnosticKinds[] = {
// in the current file.
// - Require all diagnostics produced by compiling have their first location
// be in the file being compiled, never an import.
DiagnosticKind::LanguageServerDiagnosticInWrongFile,
Kind::LanguageServerDiagnosticInWrongFile,
};
// Looks for diagnostic kinds that aren't covered by a file_test.
TEST(Coverage, DiagnosticKind) {
TEST(Coverage, Kind) {
Testing::TestKindCoverage(absl::GetFlag(FLAGS_testdata_manifest),
R"(^ *// CHECK:STDERR: .* \[(\w+)\]$)",
llvm::ArrayRef(DiagnosticKinds),
llvm::ArrayRef(UntestedDiagnosticKinds));
llvm::ArrayRef(Kinds),
llvm::ArrayRef(UntestedKinds));
}
} // namespace
} // namespace Carbon
} // namespace Carbon::Diagnostics
+4 -5
View File
@@ -7,9 +7,9 @@
#include <algorithm>
#include <cstdint>
namespace Carbon {
namespace Carbon::Diagnostics {
auto DiagnosticLoc::FormatLocation(llvm::raw_ostream& out) const -> void {
auto Loc::FormatLocation(llvm::raw_ostream& out) const -> void {
if (filename.empty()) {
return;
}
@@ -23,8 +23,7 @@ auto DiagnosticLoc::FormatLocation(llvm::raw_ostream& out) const -> void {
out << ": ";
}
auto DiagnosticLoc::FormatSnippet(llvm::raw_ostream& out, int indent) const
-> void {
auto Loc::FormatSnippet(llvm::raw_ostream& out, int indent) const -> void {
if (column_number == -1) {
return;
}
@@ -48,4 +47,4 @@ auto DiagnosticLoc::FormatSnippet(llvm::raw_ostream& out, int indent) const
out << '\n';
}
} // namespace Carbon
} // namespace Carbon::Diagnostics
+23 -23
View File
@@ -15,9 +15,9 @@
#include "llvm/ADT/StringRef.h"
#include "toolchain/diagnostics/diagnostic_kind.h"
namespace Carbon {
namespace Carbon::Diagnostics {
enum class DiagnosticLevel : int8_t {
enum class Level : int8_t {
// Information about the location of another diagnostic, showing how we
// reached that location. This is currently only used for the "in import"
// message.
@@ -39,17 +39,17 @@ enum class DiagnosticLevel : int8_t {
// Arguments are passed to llvm::formatv; see:
// https://llvm.org/doxygen/FormatVariadic_8h_source.html
//
// See `DiagnosticEmitter::Emit` for comments about argument lifetimes.
#define CARBON_DIAGNOSTIC(DiagnosticName, Level, Format, ...) \
static constexpr auto DiagnosticName = \
::Carbon::DiagnosticBase<__VA_ARGS__>( \
::Carbon::DiagnosticKind::DiagnosticName, \
::Carbon::DiagnosticLevel::Level, Format)
// See `Diagnostics::Emitter::Emit` for comments about argument lifetimes.
#define CARBON_DIAGNOSTIC(DiagnosticName, LevelValue, Format, ...) \
static constexpr auto DiagnosticName = \
::Carbon::Diagnostics::DiagnosticBase<__VA_ARGS__>( \
::Carbon::Diagnostics::Kind::DiagnosticName, \
::Carbon::Diagnostics::Level::LevelValue, Format)
// A location for a diagnostic in a file. The lifetime of a DiagnosticLoc
// A location for a diagnostic in a file. The lifetime of a Loc
// is required to be less than SourceBuffer that it refers to due to the
// contained filename and line references.
struct DiagnosticLoc {
struct Loc {
// Writes the location to the given stream. It will be formatted as
// `<filename>:<line_number>:<column_number>: ` with parts dropped when
// unknown.
@@ -78,18 +78,18 @@ struct DiagnosticLoc {
// A message composing a diagnostic. This may be the main message, but can also
// be notes providing more information.
struct DiagnosticMessage {
struct Message {
// Helper for calling `format_fn`.
auto Format() const -> std::string { return format_fn(*this); }
// The diagnostic's kind.
DiagnosticKind kind;
Kind kind;
// The diagnostic's level.
DiagnosticLevel level;
Level level;
// The calculated location of the diagnostic.
DiagnosticLoc loc;
Loc loc;
// The diagnostic's format string. This, along with format_args, will be
// passed to format_fn.
@@ -104,18 +104,18 @@ struct DiagnosticMessage {
llvm::SmallVector<llvm::Any> format_args;
// Returns the formatted string. By default, this uses llvm::formatv.
std::function<auto(const DiagnosticMessage&)->std::string> format_fn;
std::function<auto(const Message&)->std::string> format_fn;
};
// An instance of a single error or warning. Information about the diagnostic
// can be recorded into it for more complex consumers.
struct Diagnostic {
// The diagnostic's level.
DiagnosticLevel level;
Level level;
// The byte offset of the final token which is associated with the diagnostic.
// This is used by `SortingDiagnosticConsumer`. This is separate from the
// `DiagnosticLoc` because it must refer to a position in the primary file
// This is used by `SortingConsumer`. This is separate from the
// `Loc` because it must refer to a position in the primary file
// being processed by a consumer, and has no use cross-file or in notes.
//
// This will usually be the start position (not end) of the last lexed token
@@ -127,14 +127,14 @@ struct Diagnostic {
// Messages related to the diagnostic. Only one should be a warning or error;
// other messages provide context.
llvm::SmallVector<DiagnosticMessage> messages;
llvm::SmallVector<Message> messages;
};
// Use the DIAGNOSTIC macro to instantiate this.
// This stores static information about a diagnostic category.
template <typename... Args>
struct DiagnosticBase {
explicit constexpr DiagnosticBase(DiagnosticKind kind, DiagnosticLevel level,
explicit constexpr DiagnosticBase(Kind kind, Level level,
llvm::StringLiteral format)
: Kind(kind), Level(level), Format(format) {
static_assert((... && !(std::is_same_v<Args, llvm::StringRef> ||
@@ -145,13 +145,13 @@ struct DiagnosticBase {
}
// The diagnostic's kind.
DiagnosticKind Kind;
Kind Kind;
// The diagnostic's level.
DiagnosticLevel Level;
Level Level;
// The diagnostic's format for llvm::formatv.
llvm::StringLiteral Format;
};
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_H_
+10 -10
View File
@@ -7,9 +7,9 @@
#include <algorithm>
#include <cstdint>
namespace Carbon {
namespace Carbon::Diagnostics {
auto StreamDiagnosticConsumer::HandleDiagnostic(Diagnostic diagnostic) -> void {
auto StreamConsumer::HandleDiagnostic(Diagnostic diagnostic) -> void {
if (printed_diagnostic_) {
*stream_ << "\n";
} else {
@@ -19,16 +19,16 @@ auto StreamDiagnosticConsumer::HandleDiagnostic(Diagnostic diagnostic) -> void {
for (const auto& message : diagnostic.messages) {
message.loc.FormatLocation(*stream_);
switch (message.level) {
case DiagnosticLevel::Error:
case Level::Error:
*stream_ << "error: ";
break;
case DiagnosticLevel::Warning:
case Level::Warning:
*stream_ << "warning: ";
break;
case DiagnosticLevel::Note:
case Level::Note:
*stream_ << "note: ";
break;
case DiagnosticLevel::LocationInfo:
case Level::LocationInfo:
break;
}
*stream_ << message.Format();
@@ -39,15 +39,15 @@ auto StreamDiagnosticConsumer::HandleDiagnostic(Diagnostic diagnostic) -> void {
// Don't include a snippet for location information to keep this diagnostic
// more visually associated with the following diagnostic that it describes
// and to better match C++ compilers.
if (message.level != DiagnosticLevel::LocationInfo) {
if (message.level != Level::LocationInfo) {
message.loc.FormatSnippet(*stream_);
}
}
}
auto ConsoleDiagnosticConsumer() -> DiagnosticConsumer& {
static auto* consumer = new StreamDiagnosticConsumer(&llvm::errs());
auto ConsoleConsumer() -> Consumer& {
static auto* consumer = new StreamConsumer(&llvm::errs());
return *consumer;
}
} // namespace Carbon
} // namespace Carbon::Diagnostics
+14 -15
View File
@@ -9,25 +9,25 @@
#include "llvm/ADT/StringRef.h"
#include "toolchain/diagnostics/diagnostic.h"
namespace Carbon {
namespace Carbon::Diagnostics {
// Receives diagnostics as they are emitted.
class DiagnosticConsumer {
class Consumer {
public:
virtual ~DiagnosticConsumer() = default;
virtual ~Consumer() = default;
// Handle a diagnostic.
//
// This relies on moves of the Diagnostic. At present, diagnostics are
// allocated on the stack, so their lifetime is that of HandleDiagnostic.
// However, SortingDiagnosticConsumer needs a longer lifetime, until all
// However, SortingConsumer needs a longer lifetime, until all
// diagnostics have been produced. As a consequence, it needs to either copy
// or move the Diagnostic, and right now we're moving due to the overhead of
// notes.
//
// At present, there is no persistent storage of diagnostics because IDEs
// would be fine with diagnostics being printed immediately and discarded,
// without SortingDiagnosticConsumer. If this becomes a performance issue, we
// without SortingConsumer. If this becomes a performance issue, we
// may want to investigate alternative ownership models that address both IDE
// and CLI user needs.
virtual auto HandleDiagnostic(Diagnostic diagnostic) -> void = 0;
@@ -37,10 +37,9 @@ class DiagnosticConsumer {
};
// A diagnostic consumer that prints to a stream.
class StreamDiagnosticConsumer : public DiagnosticConsumer {
class StreamConsumer : public Consumer {
public:
explicit StreamDiagnosticConsumer(llvm::raw_ostream* stream)
: stream_(stream) {}
explicit StreamConsumer(llvm::raw_ostream* stream) : stream_(stream) {}
auto HandleDiagnostic(Diagnostic diagnostic) -> void override;
auto Flush() -> void override { stream_->flush(); }
@@ -51,7 +50,7 @@ class StreamDiagnosticConsumer : public DiagnosticConsumer {
}
private:
auto Print(const DiagnosticMessage& message, llvm::StringRef prefix) -> void;
auto Print(const Message& message, llvm::StringRef prefix) -> void;
llvm::raw_ostream* stream_;
@@ -63,17 +62,17 @@ class StreamDiagnosticConsumer : public DiagnosticConsumer {
};
// Returns a diagnostic consumer instance that prints to stderr.
auto ConsoleDiagnosticConsumer() -> DiagnosticConsumer&;
auto ConsoleConsumer() -> Consumer&;
// Diagnostic consumer adaptor that tracks whether any errors have been
// produced.
class ErrorTrackingDiagnosticConsumer : public DiagnosticConsumer {
class ErrorTrackingConsumer : public Consumer {
public:
explicit ErrorTrackingDiagnosticConsumer(DiagnosticConsumer& next_consumer)
explicit ErrorTrackingConsumer(Consumer& next_consumer)
: next_consumer_(&next_consumer) {}
auto HandleDiagnostic(Diagnostic diagnostic) -> void override {
seen_error_ |= diagnostic.level == DiagnosticLevel::Error;
seen_error_ |= diagnostic.level == Level::Error;
next_consumer_->HandleDiagnostic(std::move(diagnostic));
}
@@ -84,10 +83,10 @@ class ErrorTrackingDiagnosticConsumer : public DiagnosticConsumer {
auto seen_error() const -> bool { return seen_error_; }
private:
DiagnosticConsumer* next_consumer_;
Consumer* next_consumer_;
bool seen_error_ = false;
};
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_CONSUMER_H_
+79 -80
View File
@@ -18,7 +18,7 @@
#include "toolchain/diagnostics/diagnostic_consumer.h"
#include "toolchain/diagnostics/diagnostic_kind.h"
namespace Carbon {
namespace Carbon::Diagnostics {
namespace Internal {
@@ -30,13 +30,13 @@ using NoTypeDeduction = std::type_identity_t<Arg>;
} // namespace Internal
template <typename LocT, typename AnnotateFn>
class DiagnosticAnnotationScope;
class AnnotationScope;
// The result of `DiagnosticConvert::ConvertLoc`. This is non-templated to allow
// sharing across converters.
struct ConvertedDiagnosticLoc {
// Becomes DiagnosticMessage::loc.
DiagnosticLoc loc;
struct ConvertedLoc {
// Becomes Message::loc.
Loc loc;
// Becomes Diagnostic::last_byte_offset.
int32_t last_byte_offset;
};
@@ -46,10 +46,10 @@ struct ConvertedDiagnosticLoc {
// write:
//
// struct NameId {
// using DiagnosticType = DiagnosticTypeInfo<std::string>;
// using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// };
template <typename StorageTypeT>
struct DiagnosticTypeInfo {
struct TypeInfo {
using StorageType = StorageTypeT;
};
@@ -62,10 +62,10 @@ struct DiagnosticTypeInfo {
// when parsing, or a parse tree node when type-checking, and to allow unit
// tests to be decoupled from any concrete location representation.
template <typename LocT>
class DiagnosticEmitter {
class Emitter {
public:
// A builder-pattern type to provide a fluent interface for constructing
// a more complex diagnostic. See `DiagnosticEmitter::Build` for the
// a more complex diagnostic. See `Emitter::Build` for the
// expected usage.
// This is nodiscard to protect against accidentally building a diagnostic
// without emitting it.
@@ -77,14 +77,14 @@ class DiagnosticEmitter {
-> DiagnosticBuilder& = default;
// Adds a note diagnostic attached to the main diagnostic being built.
// The API mirrors the main emission API: `DiagnosticEmitter::Emit`.
// For the expected usage see the builder API: `DiagnosticEmitter::Build`.
// The API mirrors the main emission API: `Emitter::Emit`.
// For the expected usage see the builder API: `Emitter::Build`.
template <typename... Args>
auto Note(LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder&;
// Emits the built diagnostic and its attached notes.
// For the expected usage see the builder API: `DiagnosticEmitter::Build`.
// For the expected usage see the builder API: `Emitter::Build`.
template <typename... Args>
auto Emit() & -> void;
@@ -98,10 +98,10 @@ class DiagnosticEmitter {
explicit operator bool() { return emitter_; }
private:
friend class DiagnosticEmitter<LocT>;
friend class Emitter<LocT>;
template <typename... Args>
explicit DiagnosticBuilder(DiagnosticEmitter<LocT>* emitter, LocT loc,
explicit DiagnosticBuilder(Emitter<LocT>* emitter, LocT loc,
const DiagnosticBase<Args...>& diagnostic_base,
llvm::SmallVector<llvm::Any> args);
@@ -116,30 +116,29 @@ class DiagnosticEmitter {
llvm::SmallVector<llvm::Any> args) -> void;
// Adds a message to the diagnostic, handling conversion of the arguments. A
// DiagnosticLoc must be provided instead of a LocT in order to
// Loc must be provided instead of a LocT in order to
// avoid potential recursion.
template <typename... Args>
auto AddMessageWithDiagnosticLoc(
DiagnosticLoc loc, const DiagnosticBase<Args...>& diagnostic_base,
llvm::SmallVector<llvm::Any> args) -> void;
auto AddMessageWithLoc(Loc loc,
const DiagnosticBase<Args...>& diagnostic_base,
llvm::SmallVector<llvm::Any> args) -> void;
// Handles the cast of llvm::Any to Args types for formatv.
// TODO: Custom formatting can be provided with an format_provider, but that
// affects all formatv calls. Consider replacing formatv with a custom call
// that allows diagnostic-specific formatting.
template <typename... Args, size_t... N>
static auto FormatFn(const DiagnosticMessage& message,
static auto FormatFn(const Message& message,
std::index_sequence<N...> /*indices*/) -> std::string;
DiagnosticEmitter<LocT>* emitter_;
Emitter<LocT>* emitter_;
Diagnostic diagnostic_;
};
// `consumer` is required to outlive the diagnostic emitter.
explicit DiagnosticEmitter(DiagnosticConsumer* consumer)
: consumer_(consumer) {}
explicit Emitter(Consumer* consumer) : consumer_(consumer) {}
virtual ~DiagnosticEmitter() = default;
virtual ~Emitter() = default;
// Emits an error.
//
@@ -165,16 +164,16 @@ class DiagnosticEmitter {
protected:
// Callback type used to report context messages from ConvertLoc.
// Note that the first parameter type is DiagnosticLoc rather than
// Note that the first parameter type is Loc rather than
// LocT, because ConvertLoc must not recurse.
using ContextFnT =
llvm::function_ref<auto(DiagnosticLoc, const DiagnosticBase<>&)->void>;
llvm::function_ref<auto(Loc, const DiagnosticBase<>&)->void>;
// Converts a LocT to a DiagnosticLoc and its `last_byte_offset` (see
// `DiagnosticMessage`). ConvertLoc may invoke context_fn to provide context
// Converts a LocT to a Loc and its `last_byte_offset` (see
// `Message`). ConvertLoc may invoke context_fn to provide context
// messages.
virtual auto ConvertLoc(LocT loc, ContextFnT context_fn) const
-> ConvertedDiagnosticLoc = 0;
-> ConvertedLoc = 0;
// Converts arg types as needed. Most children don't customize conversion, so
// the default returns the argument unchanged.
@@ -187,35 +186,35 @@ class DiagnosticEmitter {
auto MakeAny(Arg arg) -> llvm::Any;
template <typename OtherLocT, typename AnnotateFn>
friend class DiagnosticAnnotationScope;
friend class AnnotationScope;
DiagnosticConsumer* consumer_;
Consumer* consumer_;
llvm::SmallVector<llvm::function_ref<auto(DiagnosticBuilder& builder)->void>>
annotate_fns_;
};
// This relies on `void*` location handling on `DiagnosticEmitter`.
// This relies on `void*` location handling on `Emitter`.
//
// TODO: Based on how this ends up used or if we get more distinct emitters, it
// might be worth considering having diagnostics specify that they don't apply
// to source-location carrying emitters. For example, this might look like a
// `CARBON_NO_LOC_DIAGNOSTIC` macro, or some other factoring. But it might end
// up being more noise than it is worth.
class NoLocDiagnosticEmitter : public DiagnosticEmitter<void*> {
class NoLocEmitter : public Emitter<void*> {
public:
using DiagnosticEmitter::DiagnosticEmitter;
using Emitter::Emitter;
// Emits an error. This specialization only applies to
// `NoLocDiagnosticEmitter`.
// `NoLocEmitter`.
template <typename... Args>
auto Emit(const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args) -> void {
DiagnosticEmitter::Emit(nullptr, diagnostic_base, args...);
Emitter::Emit(nullptr, diagnostic_base, args...);
}
protected:
auto ConvertLoc(void* /*loc*/, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> ConvertedLoc override {
return {.loc = {.filename = ""}, .last_byte_offset = -1};
}
};
@@ -228,24 +227,23 @@ class NoLocDiagnosticEmitter : public DiagnosticEmitter<void*> {
// given emitter. That function can annotate the diagnostic by calling
// `builder.Note` to add notes.
template <typename LocT, typename AnnotateFn>
class DiagnosticAnnotationScope {
class AnnotationScope {
public:
DiagnosticAnnotationScope(DiagnosticEmitter<LocT>* emitter,
AnnotateFn annotate)
AnnotationScope(Emitter<LocT>* emitter, AnnotateFn annotate)
: emitter_(emitter), annotate_(std::move(annotate)) {
emitter_->annotate_fns_.push_back(annotate_);
}
~DiagnosticAnnotationScope() { emitter_->annotate_fns_.pop_back(); }
~AnnotationScope() { emitter_->annotate_fns_.pop_back(); }
private:
DiagnosticEmitter<LocT>* emitter_;
Emitter<LocT>* emitter_;
// Make a copy of the annotation function to ensure that it lives long enough.
AnnotateFn annotate_;
};
template <typename LocT, typename AnnotateFn>
DiagnosticAnnotationScope(DiagnosticEmitter<LocT>* emitter, AnnotateFn annotate)
-> DiagnosticAnnotationScope<LocT, AnnotateFn>;
AnnotationScope(Emitter<LocT>* emitter, AnnotateFn annotate)
-> AnnotationScope<LocT, AnnotateFn>;
// ============================================================================
// Only internal implementation details below this point.
@@ -254,13 +252,13 @@ DiagnosticAnnotationScope(DiagnosticEmitter<LocT>* emitter, AnnotateFn annotate)
namespace Internal {
// Determines whether there's a DiagnosticType member on Arg.
// Used by DiagnosticEmitter.
// Used by Emitter.
template <typename Arg>
concept HasDiagnosticType = requires { typename Arg::DiagnosticType; };
// The default implementation with no conversion.
template <typename Arg>
struct DiagnosticTypeForArg : public DiagnosticTypeInfo<Arg> {};
struct DiagnosticTypeForArg : public TypeInfo<Arg> {};
// Exposes a custom conversion for an argument type.
template <typename Arg>
@@ -271,14 +269,14 @@ struct DiagnosticTypeForArg<Arg> : public Arg::DiagnosticType {};
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Note(
auto Emitter<LocT>::DiagnosticBuilder::Note(
LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder& {
if (!emitter_) {
return *this;
}
CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note ||
diagnostic_base.Level == DiagnosticLevel::LocationInfo,
CARBON_CHECK(diagnostic_base.Level == Level::Note ||
diagnostic_base.Level == Level::LocationInfo,
"{0}", static_cast<int>(diagnostic_base.Level));
AddMessage(loc, diagnostic_base, {emitter_->MakeAny<Args>(args)...});
return *this;
@@ -286,7 +284,7 @@ auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Note(
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() & -> void {
auto Emitter<LocT>::DiagnosticBuilder::Emit() & -> void {
if (!emitter_) {
return;
}
@@ -303,7 +301,7 @@ concept AlwaysFalse = false;
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() && -> void {
auto Emitter<LocT>::DiagnosticBuilder::Emit() && -> void {
// TODO: This is required by clang-16, but `false` may work in newer clang
// versions. Replace when possible.
static_assert(Internal::AlwaysFalse<LocT>,
@@ -314,59 +312,59 @@ auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() && -> void {
template <typename LocT>
template <typename... Args>
DiagnosticEmitter<LocT>::DiagnosticBuilder::DiagnosticBuilder(
DiagnosticEmitter<LocT>* emitter, LocT loc,
Emitter<LocT>::DiagnosticBuilder::DiagnosticBuilder(
Emitter<LocT>* emitter, LocT loc,
const DiagnosticBase<Args...>& diagnostic_base,
llvm::SmallVector<llvm::Any> args)
: emitter_(emitter), diagnostic_({.level = diagnostic_base.Level}) {
AddMessage(loc, diagnostic_base, std::move(args));
CARBON_CHECK(diagnostic_base.Level != DiagnosticLevel::Note);
CARBON_CHECK(diagnostic_base.Level != Level::Note);
}
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::DiagnosticBuilder::AddMessage(
auto Emitter<LocT>::DiagnosticBuilder::AddMessage(
LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
llvm::SmallVector<llvm::Any> args) -> void {
if (!emitter_) {
return;
}
auto converted = emitter_->ConvertLoc(
loc, [&](DiagnosticLoc context_loc,
const DiagnosticBase<>& context_diagnostic_base) {
AddMessageWithDiagnosticLoc(context_loc, context_diagnostic_base, {});
loc,
[&](Loc context_loc, const DiagnosticBase<>& context_diagnostic_base) {
AddMessageWithLoc(context_loc, context_diagnostic_base, {});
});
// Use the last byte offset from the first message.
if (diagnostic_.messages.empty()) {
diagnostic_.last_byte_offset = converted.last_byte_offset;
}
AddMessageWithDiagnosticLoc(converted.loc, diagnostic_base, args);
AddMessageWithLoc(converted.loc, diagnostic_base, args);
}
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::DiagnosticBuilder::AddMessageWithDiagnosticLoc(
DiagnosticLoc loc, const DiagnosticBase<Args...>& diagnostic_base,
auto Emitter<LocT>::DiagnosticBuilder::AddMessageWithLoc(
Loc loc, const DiagnosticBase<Args...>& diagnostic_base,
llvm::SmallVector<llvm::Any> args) -> void {
if (!emitter_) {
return;
}
diagnostic_.messages.emplace_back(DiagnosticMessage{
.kind = diagnostic_base.Kind,
.level = diagnostic_base.Level,
.loc = loc,
.format = diagnostic_base.Format,
.format_args = std::move(args),
.format_fn = [](const DiagnosticMessage& message) -> std::string {
return FormatFn<Args...>(message,
std::make_index_sequence<sizeof...(Args)>());
}});
diagnostic_.messages.emplace_back(
Message{.kind = diagnostic_base.Kind,
.level = diagnostic_base.Level,
.loc = loc,
.format = diagnostic_base.Format,
.format_args = std::move(args),
.format_fn = [](const Message& message) -> std::string {
return FormatFn<Args...>(
message, std::make_index_sequence<sizeof...(Args)>());
}});
}
template <typename LocT>
template <typename... Args, size_t... N>
auto DiagnosticEmitter<LocT>::DiagnosticBuilder::FormatFn(
const DiagnosticMessage& message, std::index_sequence<N...> /*indices*/)
auto Emitter<LocT>::DiagnosticBuilder::FormatFn(
const Message& message, std::index_sequence<N...> /*indices*/)
-> std::string {
static_assert(sizeof...(Args) == sizeof...(N), "Invalid template args");
CARBON_CHECK(message.format_args.size() == sizeof...(Args),
@@ -381,9 +379,9 @@ auto DiagnosticEmitter<LocT>::DiagnosticBuilder::FormatFn(
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::Emit(
LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args) -> void {
auto Emitter<LocT>::Emit(LocT loc,
const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args) -> void {
DiagnosticBuilder builder(this, loc, diagnostic_base,
{MakeAny<Args>(args)...});
builder.Emit();
@@ -391,16 +389,17 @@ auto DiagnosticEmitter<LocT>::Emit(
template <typename LocT>
template <typename... Args>
auto DiagnosticEmitter<LocT>::Build(
LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder {
auto Emitter<LocT>::Build(LocT loc,
const DiagnosticBase<Args...>& diagnostic_base,
Internal::NoTypeDeduction<Args>... args)
-> DiagnosticBuilder {
return DiagnosticBuilder(this, loc, diagnostic_base,
{MakeAny<Args>(args)...});
}
template <typename LocT>
template <typename Arg>
auto DiagnosticEmitter<LocT>::MakeAny(Arg arg) -> llvm::Any {
auto Emitter<LocT>::MakeAny(Arg arg) -> llvm::Any {
llvm::Any converted = ConvertArg(arg);
using Storage = Internal::DiagnosticTypeForArg<Arg>::StorageType;
CARBON_CHECK(llvm::any_cast<Storage>(&converted),
@@ -409,6 +408,6 @@ auto DiagnosticEmitter<LocT>::MakeAny(Arg arg) -> llvm::Any {
return converted;
}
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_EMITTER_H_
@@ -13,72 +13,70 @@
namespace Carbon::Testing {
namespace {
using ::Carbon::Testing::IsDiagnostic;
using ::Carbon::Testing::IsSingleDiagnostic;
using testing::ElementsAre;
class FakeDiagnosticEmitter : public DiagnosticEmitter<int> {
class FakeEmitter : public Diagnostics::Emitter<int> {
public:
using DiagnosticEmitter::DiagnosticEmitter;
using Emitter::Emitter;
protected:
auto ConvertLoc(int n, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> Diagnostics::ConvertedLoc override {
return {.loc = {.line_number = 1, .column_number = n},
.last_byte_offset = -1};
}
};
class DiagnosticEmitterTest : public ::testing::Test {
class EmitterTest : public ::testing::Test {
public:
DiagnosticEmitterTest() : emitter_(&consumer_) {}
EmitterTest() : emitter_(&consumer_) {}
Testing::MockDiagnosticConsumer consumer_;
FakeDiagnosticEmitter emitter_;
FakeEmitter emitter_;
};
TEST_F(DiagnosticEmitterTest, EmitSimpleError) {
TEST_F(EmitterTest, EmitSimpleError) {
CARBON_DIAGNOSTIC(TestDiagnostic, Error, "simple error");
EXPECT_CALL(consumer_, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, 1, 1, "simple error")));
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Error, 1, 1, "simple error")));
EXPECT_CALL(consumer_, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, 1, 2, "simple error")));
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Error, 1, 2, "simple error")));
emitter_.Emit(1, TestDiagnostic);
emitter_.Emit(2, TestDiagnostic);
}
TEST_F(DiagnosticEmitterTest, EmitSimpleWarning) {
TEST_F(EmitterTest, EmitSimpleWarning) {
CARBON_DIAGNOSTIC(TestDiagnostic, Warning, "simple warning");
EXPECT_CALL(consumer_,
HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic, DiagnosticLevel::Warning, 1,
1, "simple warning")));
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Warning, 1, 1, "simple warning")));
emitter_.Emit(1, TestDiagnostic);
}
TEST_F(DiagnosticEmitterTest, EmitOneArgDiagnostic) {
TEST_F(EmitterTest, EmitOneArgDiagnostic) {
CARBON_DIAGNOSTIC(TestDiagnostic, Error, "arg: `{0}`", std::string);
EXPECT_CALL(consumer_, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, 1, 1, "arg: `str`")));
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Error, 1, 1, "arg: `str`")));
emitter_.Emit(1, TestDiagnostic, "str");
}
TEST_F(DiagnosticEmitterTest, EmitNote) {
TEST_F(EmitterTest, EmitNote) {
CARBON_DIAGNOSTIC(TestDiagnostic, Warning, "simple warning");
CARBON_DIAGNOSTIC(TestDiagnosticNote, Note, "note");
EXPECT_CALL(
consumer_,
HandleDiagnostic(IsDiagnostic(
DiagnosticLevel::Warning,
Diagnostics::Level::Warning,
ElementsAre(
IsDiagnosticMessage(DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Warning, 1, 1,
IsDiagnosticMessage(Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Warning, 1, 1,
"simple warning"),
IsDiagnosticMessage(DiagnosticKind::TestDiagnosticNote,
DiagnosticLevel::Note, 1, 2, "note")))));
IsDiagnosticMessage(Diagnostics::Kind::TestDiagnosticNote,
Diagnostics::Level::Note, 1, 2, "note")))));
emitter_.Build(1, TestDiagnostic).Note(2, TestDiagnosticNote).Emit();
}
+3 -3
View File
@@ -4,11 +4,11 @@
#include "toolchain/diagnostics/diagnostic_kind.h" // IWYU pragma: keep
namespace Carbon {
namespace Carbon::Diagnostics {
CARBON_DEFINE_ENUM_CLASS_NAMES(DiagnosticKind) = {
CARBON_DEFINE_ENUM_CLASS_NAMES(Kind) = {
#define CARBON_DIAGNOSTIC_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
#include "toolchain/diagnostics/diagnostic_kind.def"
};
} // namespace Carbon
} // namespace Carbon::Diagnostics
+7 -8
View File
@@ -9,12 +9,12 @@
#include "common/enum_base.h"
namespace Carbon {
namespace Carbon::Diagnostics {
// Although this currently fits into int8_t, it shouldn't be expected to
// long-term.
// NOLINTNEXTLINE(performance-enum-size)
CARBON_DEFINE_RAW_ENUM_CLASS(DiagnosticKind, uint16_t) {
CARBON_DEFINE_RAW_ENUM_CLASS(Kind, uint16_t) {
#define CARBON_DIAGNOSTIC_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
#include "toolchain/diagnostics/diagnostic_kind.def"
};
@@ -27,19 +27,18 @@ CARBON_DEFINE_RAW_ENUM_CLASS(DiagnosticKind, uint16_t) {
// definitions centrally is expected to create a compilation bottleneck
// long-term, and we also see value to keeping diagnostic format strings close
// to the consuming code.
class DiagnosticKind : public CARBON_ENUM_BASE(DiagnosticKind) {
class Kind : public CARBON_ENUM_BASE(Kind) {
public:
#define CARBON_DIAGNOSTIC_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name)
#include "toolchain/diagnostics/diagnostic_kind.def"
};
#define CARBON_DIAGNOSTIC_KIND(Name) \
CARBON_ENUM_CONSTANT_DEFINITION(DiagnosticKind, Name)
#define CARBON_DIAGNOSTIC_KIND(Name) CARBON_ENUM_CONSTANT_DEFINITION(Kind, Name)
#include "toolchain/diagnostics/diagnostic_kind.def"
// We expect DiagnosticKind to fit into 2 bits.
static_assert(sizeof(DiagnosticKind) == 2, "DiagnosticKind includes padding!");
// We expect Kind to fit into 2 bits.
static_assert(sizeof(Kind) == 2, "Kind includes padding!");
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_KIND_H_
+6 -6
View File
@@ -7,26 +7,26 @@
#include "toolchain/diagnostics/diagnostic_emitter.h"
namespace Carbon {
namespace Carbon::Diagnostics {
// We frequently want a `DiagnosticEmitter` that directly uses a filename. Note
// We frequently want a `Emitter` that directly uses a filename. Note
// that an empty string can be used for a diagnostic that has no particular
// location.
//
// Note this provides no way to set a line or column on diagnostics. More
// specific emitters must be used for that.
class FileDiagnosticEmitter : public DiagnosticEmitter<llvm::StringRef> {
class FileEmitter : public Emitter<llvm::StringRef> {
public:
using DiagnosticEmitter::DiagnosticEmitter;
using Emitter::Emitter;
protected:
// Converts a filename directly to the diagnostic location.
auto ConvertLoc(llvm::StringRef filename, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> ConvertedLoc override {
return {.loc = {.filename = filename}, .last_byte_offset = -1};
}
};
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_FILE_DIAGNOSTICS_H_
+6 -6
View File
@@ -7,9 +7,9 @@
#include "common/check.h"
#include "llvm/ADT/StringExtras.h"
auto llvm::format_provider<Carbon::BoolAsSelect>::format(
const Carbon::BoolAsSelect& wrapper, raw_ostream& out, StringRef style)
-> void {
auto llvm::format_provider<Carbon::Diagnostics::BoolAsSelect>::format(
const Carbon::Diagnostics::BoolAsSelect& wrapper, raw_ostream& out,
StringRef style) -> void {
if (style.empty()) {
llvm::format_provider<bool>::format(wrapper.value, out, style);
return;
@@ -30,9 +30,9 @@ auto llvm::format_provider<Carbon::BoolAsSelect>::format(
}
}
auto llvm::format_provider<Carbon::IntAsSelect>::format(
const Carbon::IntAsSelect& wrapper, raw_ostream& out, StringRef style)
-> void {
auto llvm::format_provider<Carbon::Diagnostics::IntAsSelect>::format(
const Carbon::Diagnostics::IntAsSelect& wrapper, raw_ostream& out,
StringRef style) -> void {
if (style == "s") {
if (wrapper.value != 1) {
out << "s";
+10 -10
View File
@@ -8,7 +8,7 @@
#include "common/ostream.h"
#include "llvm/Support/FormatVariadicDetails.h"
namespace Carbon {
namespace Carbon::Diagnostics {
// Selects a formatv string based on the value.
//
@@ -53,20 +53,20 @@ struct IntAsSelect {
int value;
};
} // namespace Carbon
} // namespace Carbon::Diagnostics
// See BoolAsSelect.
// See Diagnostics::BoolAsSelect.
template <>
struct llvm::format_provider<Carbon::BoolAsSelect> {
static auto format(const Carbon::BoolAsSelect& wrapper, raw_ostream& out,
StringRef style) -> void;
struct llvm::format_provider<Carbon::Diagnostics::BoolAsSelect> {
static auto format(const Carbon::Diagnostics::BoolAsSelect& wrapper,
raw_ostream& out, StringRef style) -> void;
};
// See IntAsSelect.
// See Diagnostics::IntAsSelect.
template <>
struct llvm::format_provider<Carbon::IntAsSelect> {
static auto format(const Carbon::IntAsSelect& wrapper, raw_ostream& out,
StringRef style) -> void;
struct llvm::format_provider<Carbon::Diagnostics::IntAsSelect> {
static auto format(const Carbon::Diagnostics::IntAsSelect& wrapper,
raw_ostream& out, StringRef style) -> void;
};
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_FORMAT_PROVIDERS_H_
+46 -24
View File
@@ -9,72 +9,94 @@
#include "llvm/Support/FormatVariadic.h"
namespace Carbon {
namespace Carbon::Diagnostics {
namespace {
using ::testing::Eq;
TEST(BoolAsSelect, Cases) {
constexpr char Format[] = "{0:a|b}";
EXPECT_THAT(llvm::formatv(Format, BoolAsSelect(true)).str(), Eq("a"));
EXPECT_THAT(llvm::formatv(Format, BoolAsSelect(false)).str(), Eq("b"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::BoolAsSelect(true)).str(),
Eq("a"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::BoolAsSelect(false)).str(),
Eq("b"));
}
TEST(BoolAsSelect, CasesWithNormalFormat) {
constexpr char Format[] = "{0} {0:a|b}";
EXPECT_THAT(llvm::formatv(Format, BoolAsSelect(true)).str(), Eq("true a"));
EXPECT_THAT(llvm::formatv(Format, BoolAsSelect(false)).str(), Eq("false b"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::BoolAsSelect(true)).str(),
Eq("true a"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::BoolAsSelect(false)).str(),
Eq("false b"));
}
TEST(BoolAsSelect, Spaces) {
constexpr char Format[] = "{0: a | b }";
EXPECT_THAT(llvm::formatv(Format, BoolAsSelect(true)).str(), Eq(" a "));
EXPECT_THAT(llvm::formatv(Format, BoolAsSelect(false)).str(), Eq(" b "));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::BoolAsSelect(true)).str(),
Eq(" a "));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::BoolAsSelect(false)).str(),
Eq(" b "));
}
TEST(IntAsSelect, OnlyDefault) {
constexpr char Format[] = "{0::default}";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq("default"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq("default"));
}
TEST(IntAsSelect, OneEquals) {
constexpr char Format[] = "{0:=0:zero}";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq("zero"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq("zero"));
}
TEST(IntAsSelect, TwoEquals) {
constexpr char Format[] = "{0:=0:zero|=1:one}";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq("zero"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(1)).str(), Eq("one"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq("zero"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(1)).str(),
Eq("one"));
}
TEST(IntAsSelect, TwoEqualsAndDefault) {
constexpr char Format[] = "{0:=0:zero|=1:one|:default}";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq("zero"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(1)).str(), Eq("one"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(2)).str(), Eq("default"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq("zero"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(1)).str(),
Eq("one"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(2)).str(),
Eq("default"));
}
TEST(IntAsSelect, Spaces) {
constexpr char Format[] = "{0:=0: zero |=1: one |: default }";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq(" zero "));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(1)).str(), Eq(" one "));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(2)).str(), Eq(" default "));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq(" zero "));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(1)).str(),
Eq(" one "));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(2)).str(),
Eq(" default "));
}
TEST(IntAsSelect, CasesWithNormalFormat) {
constexpr char Format[] = "{0} argument{0:=1:|:s}";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq("0 arguments"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(1)).str(), Eq("1 argument"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(2)).str(), Eq("2 arguments"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq("0 arguments"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(1)).str(),
Eq("1 argument"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(2)).str(),
Eq("2 arguments"));
}
TEST(IntAsSelect, PluralS) {
constexpr char Format[] = "{0} argument{0:s}";
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(0)).str(), Eq("0 arguments"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(1)).str(), Eq("1 argument"));
EXPECT_THAT(llvm::formatv(Format, IntAsSelect(2)).str(), Eq("2 arguments"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(0)).str(),
Eq("0 arguments"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(1)).str(),
Eq("1 argument"));
EXPECT_THAT(llvm::formatv(Format, Diagnostics::IntAsSelect(2)).str(),
Eq("2 arguments"));
}
} // namespace
} // namespace Carbon
} // namespace Carbon::Diagnostics
+7 -7
View File
@@ -4,7 +4,7 @@
#include "toolchain/diagnostics/mocks.h"
namespace Carbon {
namespace Carbon::Diagnostics {
auto PrintTo(const Diagnostic& diagnostic, std::ostream* os) -> void {
*os << "Diagnostic{";
@@ -17,21 +17,21 @@ auto PrintTo(const Diagnostic& diagnostic, std::ostream* os) -> void {
*os << "\"}";
}
auto PrintTo(DiagnosticLevel level, std::ostream* os) -> void {
auto PrintTo(Level level, std::ostream* os) -> void {
switch (level) {
case DiagnosticLevel::LocationInfo:
case Level::LocationInfo:
*os << "LocationInfo";
break;
case DiagnosticLevel::Note:
case Level::Note:
*os << "Note";
break;
case DiagnosticLevel::Warning:
case Level::Warning:
*os << "Warning";
break;
case DiagnosticLevel::Error:
case Level::Error:
*os << "Error";
break;
}
}
} // namespace Carbon
} // namespace Carbon::Diagnostics
+28 -26
View File
@@ -11,51 +11,53 @@
namespace Carbon::Testing {
class MockDiagnosticConsumer : public DiagnosticConsumer {
class MockDiagnosticConsumer : public Diagnostics::Consumer {
public:
MOCK_METHOD(void, HandleDiagnostic, (Diagnostic diagnostic), (override));
MOCK_METHOD(void, HandleDiagnostic, (Diagnostics::Diagnostic diagnostic),
(override));
};
// NOLINTNEXTLINE(modernize-use-trailing-return-type): From the macro.
MATCHER_P(IsDiagnosticMessageString, matcher, "") {
const DiagnosticMessage& message = arg;
MATCHER_P(IsDiagnosticsMessageString, matcher, "") {
const Diagnostics::Message& message = arg;
return testing::ExplainMatchResult(matcher, message.Format(),
result_listener);
}
inline auto IsDiagnosticMessage(testing::Matcher<DiagnosticKind> kind,
testing::Matcher<DiagnosticLevel> level,
inline auto IsDiagnosticMessage(testing::Matcher<Diagnostics::Kind> kind,
testing::Matcher<Diagnostics::Level> level,
testing::Matcher<int> line_number,
testing::Matcher<int> column_number,
testing::Matcher<std::string> message)
-> testing::Matcher<DiagnosticMessage> {
-> testing::Matcher<Diagnostics::Message> {
using testing::AllOf;
using testing::Field;
return AllOf(Field("kind", &DiagnosticMessage::kind, kind),
Field("level", &DiagnosticMessage::level, level),
Field(&DiagnosticMessage::loc,
AllOf(Field("line_number", &DiagnosticLoc::line_number,
line_number),
Field("column_number", &DiagnosticLoc::column_number,
column_number))),
IsDiagnosticMessageString(message));
return AllOf(
Field("kind", &Diagnostics::Message::kind, kind),
Field("level", &Diagnostics::Message::level, level),
Field(&Diagnostics::Message::loc,
AllOf(Field("line_number", &Diagnostics::Loc::line_number,
line_number),
Field("column_number", &Diagnostics::Loc::column_number,
column_number))),
IsDiagnosticsMessageString(message));
}
inline auto IsDiagnostic(
testing::Matcher<DiagnosticLevel> level,
testing::Matcher<llvm::SmallVector<DiagnosticMessage>> elements)
-> testing::Matcher<Diagnostic> {
testing::Matcher<Diagnostics::Level> level,
testing::Matcher<llvm::SmallVector<Diagnostics::Message>> elements)
-> testing::Matcher<Diagnostics::Diagnostic> {
return testing::AllOf(
testing::Field("level", &Diagnostic::level, level),
testing::Field("messages", &Diagnostic::messages, elements));
testing::Field("level", &Diagnostics::Diagnostic::level, level),
testing::Field("messages", &Diagnostics::Diagnostic::messages, elements));
}
inline auto IsSingleDiagnostic(testing::Matcher<DiagnosticKind> kind,
testing::Matcher<DiagnosticLevel> level,
inline auto IsSingleDiagnostic(testing::Matcher<Diagnostics::Kind> kind,
testing::Matcher<Diagnostics::Level> level,
testing::Matcher<int> line_number,
testing::Matcher<int> column_number,
testing::Matcher<std::string> message)
-> testing::Matcher<Diagnostic> {
-> testing::Matcher<Diagnostics::Diagnostic> {
return IsDiagnostic(
level, testing::ElementsAre(IsDiagnosticMessage(kind, level, line_number,
column_number, message)));
@@ -63,12 +65,12 @@ inline auto IsSingleDiagnostic(testing::Matcher<DiagnosticKind> kind,
} // namespace Carbon::Testing
namespace Carbon {
namespace Carbon::Diagnostics {
// Printing helpers for tests.
auto PrintTo(const Diagnostic& diagnostic, std::ostream* os) -> void;
auto PrintTo(DiagnosticLevel level, std::ostream* os) -> void;
auto PrintTo(Level level, std::ostream* os) -> void;
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_MOCKS_H_
+11 -11
View File
@@ -7,37 +7,37 @@
#include "toolchain/diagnostics/diagnostic_emitter.h"
namespace Carbon {
namespace Carbon::Diagnostics {
// Returns a singleton consumer that doesn't print its diagnostics.
inline auto NullDiagnosticConsumer() -> DiagnosticConsumer& {
struct Consumer : DiagnosticConsumer {
inline auto NullConsumer() -> Consumer& {
struct SingletonConsumer : Consumer {
auto HandleDiagnostic(Diagnostic /*d*/) -> void override {}
};
static auto* consumer = new Consumer;
static auto* consumer = new SingletonConsumer;
return *consumer;
}
// Returns a singleton emitter that doesn't print its diagnostics.
template <typename LocT>
inline auto NullDiagnosticEmitter() -> DiagnosticEmitter<LocT>& {
class Emitter : public DiagnosticEmitter<LocT> {
inline auto NullEmitter() -> Emitter<LocT>& {
class SingletonEmitter : public Emitter<LocT> {
public:
using DiagnosticEmitter<LocT>::DiagnosticEmitter;
using Emitter<LocT>::Emitter;
protected:
// Converts a filename directly to the diagnostic location.
auto ConvertLoc(LocT /*loc*/,
DiagnosticEmitter<LocT>::ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
Emitter<LocT>::ContextFnT /*context_fn*/) const
-> ConvertedLoc override {
return {.loc = {}, .last_byte_offset = -1};
}
};
static auto* emitter = new Emitter(&NullDiagnosticConsumer());
static auto* emitter = new SingletonEmitter(&NullConsumer());
return *emitter;
}
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_NULL_DIAGNOSTICS_H_
@@ -9,7 +9,7 @@
#include "llvm/ADT/STLExtras.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
namespace Carbon {
namespace Carbon::Diagnostics {
// Buffers incoming diagnostics for printing and sorting.
//
@@ -19,12 +19,12 @@ namespace Carbon {
// `Diagnostic::messages[0]` will always be a location in the consumer's primary
// file, but if it needs to correspond to a different file, the
// `last_byte_offset` must still indicate an offset within the primary file.
class SortingDiagnosticConsumer : public DiagnosticConsumer {
class SortingConsumer : public Consumer {
public:
explicit SortingDiagnosticConsumer(DiagnosticConsumer& next_consumer)
explicit SortingConsumer(Consumer& next_consumer)
: next_consumer_(&next_consumer) {}
~SortingDiagnosticConsumer() override {
~SortingConsumer() override {
// We choose not to automatically flush diagnostics here, because they are
// likely to refer to data that gets destroyed before the diagnostics
// consumer is destroyed, because the diagnostics consumer is typically
@@ -55,9 +55,9 @@ class SortingDiagnosticConsumer : public DiagnosticConsumer {
// specify 0.
llvm::SmallVector<Diagnostic, 0> diagnostics_;
DiagnosticConsumer* next_consumer_;
Consumer* next_consumer_;
};
} // namespace Carbon
} // namespace Carbon::Diagnostics
#endif // CARBON_TOOLCHAIN_DIAGNOSTICS_SORTING_DIAGNOSTIC_CONSUMER_H_
@@ -11,7 +11,7 @@
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/diagnostics/mocks.h"
namespace Carbon {
namespace Carbon::Diagnostics {
namespace {
using ::Carbon::Testing::IsSingleDiagnostic;
@@ -20,21 +20,21 @@ using ::testing::InSequence;
CARBON_DIAGNOSTIC(TestDiagnostic, Error, "M{0}", int);
class FakeDiagnosticEmitter : public DiagnosticEmitter<int32_t> {
class FakeEmitter : public Emitter<int32_t> {
public:
using DiagnosticEmitter::DiagnosticEmitter;
using Emitter::Emitter;
protected:
auto ConvertLoc(int32_t last_byte_offset, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> ConvertedLoc override {
return {.loc = {}, .last_byte_offset = last_byte_offset};
}
};
TEST(SortedDiagnosticEmitterTest, SortErrors) {
TEST(SortedEmitterTest, SortErrors) {
Testing::MockDiagnosticConsumer consumer;
SortingDiagnosticConsumer sorting_consumer(consumer);
FakeDiagnosticEmitter emitter(&sorting_consumer);
SortingConsumer sorting_consumer(consumer);
FakeEmitter emitter(&sorting_consumer);
emitter.Emit(1, TestDiagnostic, 1);
emitter.Emit(-1, TestDiagnostic, 2);
@@ -45,25 +45,19 @@ TEST(SortedDiagnosticEmitterTest, SortErrors) {
InSequence s;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, _, _, "M2")));
Kind::TestDiagnostic, Level::Error, _, _, "M2")));
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, _, _, "M3")));
Kind::TestDiagnostic, Level::Error, _, _, "M3")));
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, _, _, "M1")));
Kind::TestDiagnostic, Level::Error, _, _, "M1")));
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, _, _, "M5")));
Kind::TestDiagnostic, Level::Error, _, _, "M5")));
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, _, _, "M6")));
Kind::TestDiagnostic, Level::Error, _, _, "M6")));
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TestDiagnostic,
DiagnosticLevel::Error, _, _, "M4")));
Kind::TestDiagnostic, Level::Error, _, _, "M4")));
sorting_consumer.Flush();
}
} // namespace
} // namespace Carbon
} // namespace Carbon::Diagnostics
+7 -7
View File
@@ -299,8 +299,8 @@ static auto PhaseToString(CompileOptions::Phase phase) -> std::string {
}
}
auto CompileSubcommand::ValidateOptions(NoLocDiagnosticEmitter& emitter) const
-> bool {
auto CompileSubcommand::ValidateOptions(
Diagnostics::NoLocEmitter& emitter) const -> bool {
CARBON_DIAGNOSTIC(
CompilePhaseFlagConflict, Error,
"requested dumping {0} but compile phase is limited to `{1}`",
@@ -341,7 +341,7 @@ namespace {
class CompilationUnit {
public:
explicit CompilationUnit(DriverEnv& driver_env, const CompileOptions& options,
DiagnosticConsumer* consumer,
Diagnostics::Consumer* consumer,
llvm::StringRef input_filename);
// Loads source and lexes it. Returns true on success.
@@ -411,8 +411,8 @@ class CompilationUnit {
llvm::raw_pwrite_stream* vlog_stream_;
// Diagnostics are sent to consumer_, with optional sorting.
std::optional<SortingDiagnosticConsumer> sorting_consumer_;
DiagnosticConsumer* consumer_;
std::optional<Diagnostics::SortingConsumer> sorting_consumer_;
Diagnostics::Consumer* consumer_;
bool success_ = true;
@@ -436,7 +436,7 @@ class CompilationUnit {
CompilationUnit::CompilationUnit(DriverEnv& driver_env,
const CompileOptions& options,
DiagnosticConsumer* consumer,
Diagnostics::Consumer* consumer,
llvm::StringRef input_filename)
: driver_env_(&driver_env),
options_(options),
@@ -445,7 +445,7 @@ CompilationUnit::CompilationUnit(DriverEnv& driver_env,
if (vlog_stream_ != nullptr || options_.stream_errors) {
consumer_ = consumer;
} else {
sorting_consumer_ = SortingDiagnosticConsumer(*consumer);
sorting_consumer_ = Diagnostics::SortingConsumer(*consumer);
consumer_ = &*sorting_consumer_;
}
if (options_.dump_mem_usage && IncludeInDumps()) {
+1 -1
View File
@@ -77,7 +77,7 @@ class CompileSubcommand : public DriverSubcommand {
// Does custom validation of the compile-subcommand options structure beyond
// what the command line parsing library supports. Diagnoses and returns false
// on failure.
auto ValidateOptions(NoLocDiagnosticEmitter& emitter) const -> bool;
auto ValidateOptions(Diagnostics::NoLocEmitter& emitter) const -> bool;
CompileOptions options_;
};
+2 -2
View File
@@ -56,10 +56,10 @@ struct DriverEnv {
bool enable_leaking = false;
// A diagnostic consumer, to be able to connect output.
StreamDiagnosticConsumer consumer;
Diagnostics::StreamConsumer consumer;
// A diagnostic emitter that has no locations.
NoLocDiagnosticEmitter emitter;
Diagnostics::NoLocEmitter emitter;
// For CARBON_VLOG.
llvm::raw_pwrite_stream* vlog_stream = nullptr;
+7 -7
View File
@@ -22,7 +22,7 @@ namespace Carbon::LanguageServer {
// A consumer for turning diagnostics into a `textDocument/publishDiagnostics`
// notification.
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics
class PublishDiagnosticConsumer : public DiagnosticConsumer {
class PublishDiagnosticConsumer : public Diagnostics::Consumer {
public:
// Initializes params with the target file information.
explicit PublishDiagnosticConsumer(Context* context,
@@ -31,14 +31,14 @@ class PublishDiagnosticConsumer : public DiagnosticConsumer {
: context_(context), params_{.uri = uri, .version = version} {}
// Turns a diagnostic into an LSP diagnostic.
auto HandleDiagnostic(Diagnostic diagnostic) -> void override {
auto HandleDiagnostic(Diagnostics::Diagnostic diagnostic) -> void override {
const auto& message = diagnostic.messages[0];
if (message.loc.filename != params_.uri.file()) {
// `pushDiagnostic` requires diagnostics to be associated with a location
// in the current file. Suppress diagnostics rooted in other files.
// TODO: Consider if there's a better way to handle this.
RawStringOstream stream;
StreamDiagnosticConsumer consumer(&stream);
Diagnostics::StreamConsumer consumer(&stream);
consumer.HandleDiagnostic(diagnostic);
CARBON_DIAGNOSTIC(LanguageServerDiagnosticInWrongFile, Warning,
@@ -68,7 +68,7 @@ class PublishDiagnosticConsumer : public DiagnosticConsumer {
private:
// Returns the LSP range for a diagnostic. Note that Carbon uses 1-based
// numbers while LSP uses 0-based.
auto GetRange(const DiagnosticLoc& loc) -> clang::clangd::Range {
auto GetRange(const Diagnostics::Loc& loc) -> clang::clangd::Range {
return {.start = {.line = loc.line_number - 1,
.character = loc.column_number - 1},
.end = {.line = loc.line_number,
@@ -76,7 +76,7 @@ class PublishDiagnosticConsumer : public DiagnosticConsumer {
}
// Converts a diagnostic level to an LSP severity.
auto GetSeverity(DiagnosticLevel level) -> int {
auto GetSeverity(Diagnostics::Level level) -> int {
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticSeverity
enum class DiagnosticSeverity {
Error = 1,
@@ -86,9 +86,9 @@ class PublishDiagnosticConsumer : public DiagnosticConsumer {
};
switch (level) {
case DiagnosticLevel::Error:
case Diagnostics::Level::Error:
return static_cast<int>(DiagnosticSeverity::Error);
case DiagnosticLevel::Warning:
case Diagnostics::Level::Warning:
return static_cast<int>(DiagnosticSeverity::Warning);
default:
CARBON_FATAL("Unexpected diagnostic level: {0}", level);
+8 -5
View File
@@ -52,7 +52,8 @@ class Context {
};
// `vlog_stream` is optional; other parameters are required.
explicit Context(llvm::raw_ostream* vlog_stream, DiagnosticConsumer* consumer,
explicit Context(llvm::raw_ostream* vlog_stream,
Diagnostics::Consumer* consumer,
clang::clangd::LSPBinder::RawOutgoing* outgoing)
: vlog_stream_(vlog_stream),
file_emitter_(consumer),
@@ -70,16 +71,18 @@ class Context {
}
auto vlog_stream() -> llvm::raw_ostream* { return vlog_stream_; }
auto file_emitter() -> FileDiagnosticEmitter& { return file_emitter_; }
auto no_loc_emitter() -> NoLocDiagnosticEmitter& { return no_loc_emitter_; }
auto file_emitter() -> Diagnostics::FileEmitter& { return file_emitter_; }
auto no_loc_emitter() -> Diagnostics::NoLocEmitter& {
return no_loc_emitter_;
}
auto files() -> Map<std::string, File>& { return files_; }
private:
// Diagnostic and output streams.
llvm::raw_ostream* vlog_stream_;
FileDiagnosticEmitter file_emitter_;
NoLocDiagnosticEmitter no_loc_emitter_;
Diagnostics::FileEmitter file_emitter_;
Diagnostics::NoLocEmitter no_loc_emitter_;
clang::clangd::LSPBinder::RawOutgoing* outgoing_;
// Content of files managed by the language client.
@@ -44,7 +44,7 @@ class Logger : public clang::clangd::Logger {
auto Run(FILE* input_stream, llvm::raw_ostream& output_stream,
llvm::raw_ostream& error_stream, llvm::raw_ostream* vlog_stream,
DiagnosticConsumer& consumer) -> bool {
Diagnostics::Consumer& consumer) -> bool {
// The language server internally uses diagnostics for logging issues, but the
// clangd parts have their own logging system. We intercept that here.
Logger logger(&error_stream, vlog_stream);
@@ -65,7 +65,7 @@ auto Run(FILE* input_stream, llvm::raw_ostream& output_stream,
RawStringOstream out;
out << err;
CARBON_DIAGNOSTIC(LanguageServerTransportError, Error, "{0}", std::string);
NoLocDiagnosticEmitter emitter(&consumer);
Diagnostics::NoLocEmitter emitter(&consumer);
emitter.Emit(LanguageServerTransportError, out.TakeStr());
return false;
}
+1 -1
View File
@@ -17,7 +17,7 @@ namespace Carbon::LanguageServer {
// This is thread-hostile because `clangd::LoggingSession` relies on a global.
auto Run(FILE* input_stream, llvm::raw_ostream& output_stream,
llvm::raw_ostream& error_stream, llvm::raw_ostream* vlog_stream,
DiagnosticConsumer& consumer) -> bool;
Diagnostics::Consumer& consumer) -> bool;
} // namespace Carbon::LanguageServer
+1 -1
View File
@@ -6,7 +6,7 @@
namespace Carbon::Lex {
auto CanLexInt(DiagnosticEmitter<const char*>& emitter, llvm::StringRef text)
auto CanLexInt(Diagnostics::Emitter<const char*>& emitter, llvm::StringRef text)
-> bool {
// llvm::getAsInteger is used for parsing, but it's quadratic and visibly slow
// on large integer values. This limit exists to avoid hitting those limits.
+1 -1
View File
@@ -11,7 +11,7 @@ namespace Carbon::Lex {
// Should guard calls to getAsInteger due to performance issues with large
// integers. Emits an error if the text cannot be lexed.
auto CanLexInt(DiagnosticEmitter<const char*>& emitter, llvm::StringRef text)
auto CanLexInt(Diagnostics::Emitter<const char*>& emitter, llvm::StringRef text)
-> bool;
} // namespace Carbon::Lex
+4 -4
View File
@@ -81,7 +81,7 @@ class [[clang::internal_linkage]] Lexer {
};
Lexer(SharedValueStores& value_stores, SourceBuffer& source,
DiagnosticConsumer& consumer)
Diagnostics::Consumer& consumer)
: buffer_(value_stores, source),
consumer_(consumer),
emitter_(&consumer_, &buffer_),
@@ -220,7 +220,7 @@ class [[clang::internal_linkage]] Lexer {
llvm::SmallVector<TokenIndex> open_groups_;
bool has_mismatched_brackets_ = false;
ErrorTrackingDiagnosticConsumer consumer_;
Diagnostics::ErrorTrackingConsumer consumer_;
TokenizedBuffer::SourcePointerDiagnosticEmitter emitter_;
@@ -1526,7 +1526,7 @@ class Lexer::ErrorRecoveryBuffer {
};
// Issue an UnmatchedOpening diagnostic.
static auto DiagnoseUnmatchedOpening(DiagnosticEmitter<TokenIndex>& emitter,
static auto DiagnoseUnmatchedOpening(Diagnostics::Emitter<TokenIndex>& emitter,
TokenIndex opening_token) -> void {
CARBON_DIAGNOSTIC(UnmatchedOpening, Error,
"opening symbol without a corresponding closing symbol");
@@ -1610,7 +1610,7 @@ auto Lexer::DiagnoseAndFixMismatchedBrackets() -> void {
}
auto Lex(SharedValueStores& value_stores, SourceBuffer& source,
DiagnosticConsumer& consumer) -> TokenizedBuffer {
Diagnostics::Consumer& consumer) -> TokenizedBuffer {
return Lexer(value_stores, source, consumer).Lex();
}
+1 -1
View File
@@ -18,7 +18,7 @@ namespace Carbon::Lex {
// which will refer into the source.
auto Lex(SharedValueStores& value_stores,
SourceBuffer& source [[clang::lifetimebound]],
DiagnosticConsumer& consumer) -> TokenizedBuffer;
Diagnostics::Consumer& consumer) -> TokenizedBuffer;
} // namespace Carbon::Lex
+6 -6
View File
@@ -88,7 +88,7 @@ auto NumericLiteral::Lex(llvm::StringRef source_text,
// either diagnosing or extracting its meaning.
class NumericLiteral::Parser {
public:
Parser(DiagnosticEmitter<const char*>& emitter, NumericLiteral literal);
Parser(Diagnostics::Emitter<const char*>& emitter, NumericLiteral literal);
auto IsInt() -> bool {
return literal_.radix_point_ == static_cast<int>(literal_.text_.size());
@@ -124,7 +124,7 @@ class NumericLiteral::Parser {
auto CheckFractionalPart() -> bool;
auto CheckExponentPart() -> bool;
DiagnosticEmitter<const char*>& emitter_;
Diagnostics::Emitter<const char*>& emitter_;
NumericLiteral literal_;
// The radix of the literal: 2, 10, or 16, for a prefix of '0b', no prefix,
@@ -147,7 +147,7 @@ class NumericLiteral::Parser {
bool exponent_is_negative_ = false;
};
NumericLiteral::Parser::Parser(DiagnosticEmitter<const char*>& emitter,
NumericLiteral::Parser::Parser(Diagnostics::Emitter<const char*>& emitter,
NumericLiteral literal)
: emitter_(emitter), literal_(literal) {
int_part_ = literal.text_.substr(0, literal.radix_point_);
@@ -292,7 +292,7 @@ auto NumericLiteral::Parser::CheckDigitSequence(llvm::StringRef text,
InvalidDigit, Error,
"invalid digit '{0}' in {1:=2:binary|=10:decimal|=16:hexadecimal} "
"numeric literal",
char, IntAsSelect);
char, Diagnostics::IntAsSelect);
emitter_.Emit(text.begin() + i, InvalidDigit, c, static_cast<int>(radix));
return {.ok = false};
}
@@ -374,8 +374,8 @@ auto NumericLiteral::Parser::CheckExponentPart() -> bool {
}
// Parse the token and compute its value.
auto NumericLiteral::ComputeValue(DiagnosticEmitter<const char*>& emitter) const
-> Value {
auto NumericLiteral::ComputeValue(
Diagnostics::Emitter<const char*>& emitter) const -> Value {
Parser parser(emitter, *this);
if (!parser.Check()) {
+1 -1
View File
@@ -47,7 +47,7 @@ class NumericLiteral {
// Compute the value of the token, if possible. Emit diagnostics to the given
// emitter if the token is not valid.
auto ComputeValue(DiagnosticEmitter<const char*>& emitter) const -> Value;
auto ComputeValue(Diagnostics::Emitter<const char*>& emitter) const -> Value;
// Get the text corresponding to this literal.
auto text() const -> llvm::StringRef { return text_; }
+2 -2
View File
@@ -26,7 +26,7 @@ static void BM_Lex_Int(benchmark::State& state) {
static void BM_ComputeValue_Float(benchmark::State& state) {
auto val = NumericLiteral::Lex("0.000001", true);
CARBON_CHECK(val);
auto& emitter = NullDiagnosticEmitter<const char*>();
auto& emitter = Diagnostics::NullEmitter<const char*>();
for (auto _ : state) {
val->ComputeValue(emitter);
}
@@ -34,7 +34,7 @@ static void BM_ComputeValue_Float(benchmark::State& state) {
static void BM_ComputeValue_Int(benchmark::State& state) {
auto val = NumericLiteral::Lex("1_234_567_890", true);
auto& emitter = NullDiagnosticEmitter<const char*>();
auto& emitter = Diagnostics::NullEmitter<const char*>();
CARBON_CHECK(val);
for (auto _ : state) {
val->ComputeValue(emitter);
+1 -1
View File
@@ -21,7 +21,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
}
volatile auto value =
token->ComputeValue(NullDiagnosticEmitter<const char*>());
token->ComputeValue(Diagnostics::NullEmitter<const char*>());
(void)value;
return 0;
}
+2 -2
View File
@@ -23,7 +23,7 @@ using ::testing::VariantWith;
class NumericLiteralTest : public ::testing::Test {
public:
NumericLiteralTest() : error_tracker(ConsoleDiagnosticConsumer()) {}
NumericLiteralTest() : error_tracker(Diagnostics::ConsoleConsumer()) {}
auto Lex(llvm::StringRef text, bool can_form_real_literal) -> NumericLiteral {
std::optional<NumericLiteral> result =
@@ -41,7 +41,7 @@ class NumericLiteralTest : public ::testing::Test {
return Lex(text, can_form_real_literal).ComputeValue(emitter);
}
ErrorTrackingDiagnosticConsumer error_tracker;
Diagnostics::ErrorTrackingConsumer error_tracker;
};
// Matcher for signed llvm::APInt.
+1 -1
View File
@@ -14,7 +14,7 @@
namespace Carbon::Lex {
using LexerDiagnosticEmitter = DiagnosticEmitter<const char*>;
using LexerDiagnosticEmitter = Diagnostics::Emitter<const char*>;
static constexpr char MultiLineIndicator[] = R"(''')";
static constexpr char DoubleQuotedMultiLineIndicator[] = R"(""")";
+1 -1
View File
@@ -29,7 +29,7 @@ class StringLiteral {
// deal with, this can return the content directly. Otherwise, the allocator
// will be used for the StringRef.
auto ComputeValue(llvm::BumpPtrAllocator& allocator,
DiagnosticEmitter<const char*>& emitter) const
Diagnostics::Emitter<const char*>& emitter) const
-> llvm::StringRef;
// Get the text corresponding to this literal.
+2 -2
View File
@@ -94,8 +94,8 @@ static void BM_SimpleStringValue(benchmark::State& state, int size,
}
x.append(terminator);
for (auto _ : state) {
StringLiteral::Lex(x)->ComputeValue(allocator,
NullDiagnosticEmitter<const char*>());
StringLiteral::Lex(x)->ComputeValue(
allocator, Diagnostics::NullEmitter<const char*>());
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
llvm::BumpPtrAllocator allocator;
volatile auto value =
token->ComputeValue(allocator, NullDiagnosticEmitter<const char*>());
token->ComputeValue(allocator, Diagnostics::NullEmitter<const char*>());
(void)value;
return 0;
+2 -2
View File
@@ -16,7 +16,7 @@ namespace {
class StringLiteralTest : public ::testing::Test {
public:
StringLiteralTest() : error_tracker_(ConsoleDiagnosticConsumer()) {}
StringLiteralTest() : error_tracker_(Diagnostics::ConsoleConsumer()) {}
auto Lex(llvm::StringRef text) -> StringLiteral {
std::optional<StringLiteral> result = StringLiteral::Lex(text);
@@ -32,7 +32,7 @@ class StringLiteralTest : public ::testing::Test {
}
llvm::BumpPtrAllocator allocator_;
ErrorTrackingDiagnosticConsumer error_tracker_;
Diagnostics::ErrorTrackingConsumer error_tracker_;
};
TEST_F(StringLiteralTest, StringLiteralBounds) {
+4 -4
View File
@@ -17,18 +17,18 @@ namespace Carbon::Testing {
// A diagnostic converter for tests that lex a single token. Produces
// locations such as "`12.5`:1:3" to refer to the third character in the token.
class SingleTokenDiagnosticEmitter : public DiagnosticEmitter<const char*> {
class SingleTokenDiagnosticEmitter : public Diagnostics::Emitter<const char*> {
public:
// Form a converter for a given token. The string provided here must refer
// to the same character array that we are going to lex.
explicit SingleTokenDiagnosticEmitter(DiagnosticConsumer* consumer,
explicit SingleTokenDiagnosticEmitter(Diagnostics::Consumer* consumer,
llvm::StringRef token)
: DiagnosticEmitter(consumer), token_(token) {}
: Emitter(consumer), token_(token) {}
protected:
// Implements `DiagnosticConverter::ConvertLoc`.
auto ConvertLoc(const char* pos, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> Diagnostics::ConvertedLoc override {
CARBON_CHECK(StringRefContainsPointer(token_, pos),
"invalid diagnostic location");
llvm::StringRef prefix = token_.take_front(pos - token_.begin());
+2 -2
View File
@@ -378,7 +378,7 @@ auto TokenizedBuffer::CollectMemUsage(MemUsage& mem_usage,
}
auto TokenizedBuffer::SourcePointerToDiagnosticLoc(const char* loc) const
-> ConvertedDiagnosticLoc {
-> Diagnostics::ConvertedLoc {
CARBON_CHECK(StringRefContainsPointer(source_->text(), loc),
"location not within buffer");
int32_t offset = loc - source_->text().begin();
@@ -416,7 +416,7 @@ auto TokenizedBuffer::SourcePointerToDiagnosticLoc(const char* loc) const
}
auto TokenizedBuffer::TokenToDiagnosticLoc(TokenIndex token) const
-> ConvertedDiagnosticLoc {
-> Diagnostics::ConvertedLoc {
// Map the token location into a position within the source buffer.
const char* token_start =
source_->text().begin() + GetTokenInfo(token).byte_offset();
+12 -10
View File
@@ -177,7 +177,8 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
-> void;
// Converts a token to a diagnostic location.
auto TokenToDiagnosticLoc(TokenIndex token) const -> ConvertedDiagnosticLoc;
auto TokenToDiagnosticLoc(TokenIndex token) const
-> Diagnostics::ConvertedLoc;
// Returns true if the buffer has errors that were detected at lexing time.
auto has_errors() const -> bool { return has_errors_; }
@@ -207,15 +208,16 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
private:
friend class Lexer;
class SourcePointerDiagnosticEmitter : public DiagnosticEmitter<const char*> {
class SourcePointerDiagnosticEmitter
: public Diagnostics::Emitter<const char*> {
public:
explicit SourcePointerDiagnosticEmitter(DiagnosticConsumer* consumer,
explicit SourcePointerDiagnosticEmitter(Diagnostics::Consumer* consumer,
const TokenizedBuffer* tokens)
: DiagnosticEmitter(consumer), tokens_(tokens) {}
: Emitter(consumer), tokens_(tokens) {}
protected:
auto ConvertLoc(const char* loc, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> Diagnostics::ConvertedLoc override {
return tokens_->SourcePointerToDiagnosticLoc(loc);
}
@@ -223,15 +225,15 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
const TokenizedBuffer* tokens_;
};
class TokenDiagnosticEmitter : public DiagnosticEmitter<TokenIndex> {
class TokenDiagnosticEmitter : public Diagnostics::Emitter<TokenIndex> {
public:
explicit TokenDiagnosticEmitter(DiagnosticConsumer* consumer,
explicit TokenDiagnosticEmitter(Diagnostics::Consumer* consumer,
const TokenizedBuffer* tokens)
: DiagnosticEmitter(consumer), tokens_(tokens) {}
: Emitter(consumer), tokens_(tokens) {}
protected:
auto ConvertLoc(TokenIndex token, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> Diagnostics::ConvertedLoc override {
return tokens_->TokenToDiagnosticLoc(token);
}
@@ -241,7 +243,7 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
// Converts a pointer into the source to a diagnostic location.
auto SourcePointerToDiagnosticLoc(const char* loc) const
-> ConvertedDiagnosticLoc;
-> Diagnostics::ConvertedLoc;
// Specifies minimum widths to use when printing a token's fields via
// `printToken`.
+4 -4
View File
@@ -214,13 +214,13 @@ class LexerBenchHelper {
: source_(MakeSourceBuffer(text)) {}
auto Lex() -> TokenizedBuffer {
DiagnosticConsumer& consumer = NullDiagnosticConsumer();
Diagnostics::Consumer& consumer = Diagnostics::NullConsumer();
return Lex::Lex(value_stores_, source_, consumer);
}
auto DiagnoseErrors() -> std::string {
RawStringOstream result;
StreamDiagnosticConsumer consumer(&result);
Diagnostics::StreamConsumer consumer(&result);
auto buffer = Lex::Lex(value_stores_, source_, consumer);
consumer.Flush();
CARBON_CHECK(buffer.has_errors(),
@@ -234,8 +234,8 @@ class LexerBenchHelper {
auto MakeSourceBuffer(llvm::StringRef text) -> SourceBuffer {
CARBON_CHECK(fs_.addFile(filename_, /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBuffer(text)));
return std::move(*SourceBuffer::MakeFromFile(fs_, filename_,
ConsoleDiagnosticConsumer()));
return std::move(*SourceBuffer::MakeFromFile(
fs_, filename_, Diagnostics::ConsoleConsumer()));
}
SharedValueStores value_stores_;
+2 -2
View File
@@ -32,10 +32,10 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
llvm::MemoryBuffer::getMemBuffer(data_ref, /*BufferName=*/TestFileName,
/*RequiresNullTerminator=*/false)));
auto source =
SourceBuffer::MakeFromFile(fs, TestFileName, NullDiagnosticConsumer());
SourceBuffer::MakeFromFile(fs, TestFileName, Diagnostics::NullConsumer());
SharedValueStores value_stores;
auto buffer = Lex::Lex(value_stores, *source, NullDiagnosticConsumer());
auto buffer = Lex::Lex(value_stores, *source, Diagnostics::NullConsumer());
if (buffer.has_errors()) {
return 0;
}
+31 -28
View File
@@ -1028,8 +1028,8 @@ TEST_F(LexerTest, TypeLiteralTooManyDigits) {
// We increase the number of digits until the first one that is to large.
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TooManyTypeBitWidthDigits,
DiagnosticLevel::Error, 1, 2, _)));
Diagnostics::Kind::TooManyTypeBitWidthDigits,
Diagnostics::Level::Error, 1, 2, _)));
std::string code = "i";
// A 128-bit APInt should be plenty large, but if needed in the future it can
// be widened without issue.
@@ -1061,8 +1061,8 @@ TEST_F(LexerTest, TypeLiteralTooManyDigits) {
// crashing or hanging, and show the correct number.
constexpr int Count = 10000;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TooManyTypeBitWidthDigits,
DiagnosticLevel::Error, 1, 2,
Diagnostics::Kind::TooManyTypeBitWidthDigits,
Diagnostics::Level::Error, 1, 2,
HasSubstr(llvm::formatv(" {0} ", Count)))));
code = "i";
code.append(Count, '9');
@@ -1083,72 +1083,75 @@ TEST_F(LexerTest, DiagnosticTrailingComment) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::TrailingComment,
DiagnosticLevel::Error, 3, 19, _)));
Diagnostics::Kind::TrailingComment,
Diagnostics::Level::Error, 3, 19, _)));
compile_helper_.GetTokenizedBuffer(testcase, &consumer);
}
TEST_F(LexerTest, DiagnosticWhitespace) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::NoWhitespaceAfterCommentIntroducer,
DiagnosticLevel::Error, 1, 3, _)));
EXPECT_CALL(consumer,
HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::NoWhitespaceAfterCommentIntroducer,
Diagnostics::Level::Error, 1, 3, _)));
compile_helper_.GetTokenizedBuffer("//no space after comment", &consumer);
}
TEST_F(LexerTest, DiagnosticUnrecognizedEscape) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::UnknownEscapeSequence,
DiagnosticLevel::Error, 1, 8, HasSubstr("`b`"))));
EXPECT_CALL(consumer,
HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::UnknownEscapeSequence,
Diagnostics::Level::Error, 1, 8, HasSubstr("`b`"))));
compile_helper_.GetTokenizedBuffer(R"("hello\bworld")", &consumer);
}
TEST_F(LexerTest, DiagnosticBadHex) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::HexadecimalEscapeMissingDigits,
DiagnosticLevel::Error, 1, 9, _)));
Diagnostics::Kind::HexadecimalEscapeMissingDigits,
Diagnostics::Level::Error, 1, 9, _)));
compile_helper_.GetTokenizedBuffer(R"("hello\xabworld")", &consumer);
}
TEST_F(LexerTest, DiagnosticInvalidDigit) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::InvalidDigit,
DiagnosticLevel::Error, 1, 6, HasSubstr("'a'"))));
EXPECT_CALL(consumer,
HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::InvalidDigit, Diagnostics::Level::Error, 1,
6, HasSubstr("'a'"))));
compile_helper_.GetTokenizedBuffer("0x123abc", &consumer);
}
TEST_F(LexerTest, DiagnosticCR) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::UnsupportedCrLineEnding,
DiagnosticLevel::Error, 1, 1, _)));
Diagnostics::Kind::UnsupportedCrLineEnding,
Diagnostics::Level::Error, 1, 1, _)));
compile_helper_.GetTokenizedBuffer("\r", &consumer);
}
TEST_F(LexerTest, DiagnosticLfCr) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::UnsupportedLfCrLineEnding,
DiagnosticLevel::Error, 2, 1, _)));
Diagnostics::Kind::UnsupportedLfCrLineEnding,
Diagnostics::Level::Error, 2, 1, _)));
compile_helper_.GetTokenizedBuffer("\n\r", &consumer);
}
TEST_F(LexerTest, DiagnosticMissingTerminator) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::UnterminatedString,
DiagnosticLevel::Error, 1, 1, _)));
Diagnostics::Kind::UnterminatedString,
Diagnostics::Level::Error, 1, 1, _)));
compile_helper_.GetTokenizedBuffer(R"(#" ")", &consumer);
}
TEST_F(LexerTest, DiagnosticUnrecognizedChar) {
Testing::MockDiagnosticConsumer consumer;
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
DiagnosticKind::UnrecognizedCharacters,
DiagnosticLevel::Error, 1, 1, _)));
Diagnostics::Kind::UnrecognizedCharacters,
Diagnostics::Level::Error, 1, 1, _)));
compile_helper_.GetTokenizedBuffer("\b", &consumer);
}
@@ -1161,9 +1164,9 @@ TEST_F(LexerTest, DiagnosticFileTooLarge) {
input += "{}\n";
}
EXPECT_CALL(consumer,
HandleDiagnostic(IsSingleDiagnostic(DiagnosticKind::TooManyTokens,
DiagnosticLevel::Error,
TokenIndex::Max / 2, 1, _)));
HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::TooManyTokens, Diagnostics::Level::Error,
TokenIndex::Max / 2, 1, _)));
compile_helper_.GetTokenizedBuffer(input, &consumer);
}
+6 -5
View File
@@ -22,7 +22,8 @@
namespace Carbon::Parse {
Context::Context(Tree* tree, Lex::TokenizedBuffer* tokens,
DiagnosticConsumer* consumer, llvm::raw_ostream* vlog_stream)
Diagnostics::Consumer* consumer,
llvm::raw_ostream* vlog_stream)
: tree_(tree),
tokens_(tokens),
err_tracker_(*consumer),
@@ -283,8 +284,8 @@ auto Context::DiagnoseOperatorFixity(OperatorFixity fixity) -> void {
CARBON_DIAGNOSTIC(BinaryOperatorRequiresWhitespace, Error,
"whitespace missing {0:=-1:before|=0:around|=1:after} "
"binary operator",
IntAsSelect);
IntAsSelect pos(0);
Diagnostics::IntAsSelect);
Diagnostics::IntAsSelect pos(0);
if (tokens().HasLeadingWhitespace(*position_)) {
pos.value = 1;
} else if (tokens().HasTrailingWhitespace(*position_)) {
@@ -302,14 +303,14 @@ auto Context::DiagnoseOperatorFixity(OperatorFixity fixity) -> void {
CARBON_DIAGNOSTIC(
UnaryOperatorHasWhitespace, Error,
"whitespace is not allowed {0:after|before} this unary operator",
BoolAsSelect);
Diagnostics::BoolAsSelect);
emitter_.Emit(*position_, UnaryOperatorHasWhitespace, prefix);
} else if (IsLexicallyValidInfixOperator()) {
// Pre/postfix operators must not satisfy the infix operator rules.
CARBON_DIAGNOSTIC(
UnaryOperatorRequiresWhitespace, Error,
"whitespace is required {0:before|after} this unary operator",
BoolAsSelect);
Diagnostics::BoolAsSelect);
emitter_.Emit(*position_, UnaryOperatorRequiresWhitespace, prefix);
}
}
+8 -8
View File
@@ -35,16 +35,16 @@ enum class Lookahead : int32_t {
class Context {
public:
// A token-based emitter for use during parse.
class Emitter : public DiagnosticEmitter<Lex::TokenIndex> {
class TokenEmitter : public Diagnostics::Emitter<Lex::TokenIndex> {
public:
explicit Emitter(DiagnosticConsumer* consumer, Context* context)
: DiagnosticEmitter(consumer), context_(context) {}
explicit TokenEmitter(Diagnostics::Consumer* consumer, Context* context)
: Emitter(consumer), context_(context) {}
protected:
// Applies the `position_` to the `last_byte_offset` returned by
// `TokenToDiagnosticLoc`.
auto ConvertLoc(Lex::TokenIndex token, ContextFnT /*context_fn*/) const
-> ConvertedDiagnosticLoc override {
-> Diagnostics::ConvertedLoc override {
auto converted = context_->tokens().TokenToDiagnosticLoc(token);
converted.last_byte_offset =
std::max(converted.last_byte_offset,
@@ -122,7 +122,7 @@ class Context {
"StateStackEntry has unexpected size!");
explicit Context(Tree* tree, Lex::TokenizedBuffer* tokens,
DiagnosticConsumer* consumer,
Diagnostics::Consumer* consumer,
llvm::raw_ostream* vlog_stream);
// Adds a node to the parse tree that has no children (a leaf).
@@ -407,7 +407,7 @@ class Context {
auto has_errors() const -> bool { return err_tracker_.seen_error(); }
auto emitter() -> Emitter& { return emitter_; }
auto emitter() -> TokenEmitter& { return emitter_; }
auto position() -> Lex::TokenIterator& { return position_; }
auto position() const -> Lex::TokenIterator { return position_; }
@@ -440,8 +440,8 @@ class Context {
Tree* tree_;
Lex::TokenizedBuffer* tokens_;
ErrorTrackingDiagnosticConsumer err_tracker_;
Emitter emitter_;
Diagnostics::ErrorTrackingConsumer err_tracker_;
TokenEmitter emitter_;
// Whether to print verbose output.
llvm::raw_ostream* vlog_stream_;
+1 -1
View File
@@ -25,7 +25,7 @@ auto HandleBindingPattern(Context& context) -> void {
if (!state.has_error) {
CARBON_DIAGNOSTIC(ExpectedBindingPattern, Error,
"expected {0:name|`:` or `:!`} in binding pattern",
BoolAsSelect);
Diagnostics::BoolAsSelect);
context.emitter().Emit(*context.position(), ExpectedBindingPattern,
expected_name);
state.has_error = true;
+2 -2
View File
@@ -23,7 +23,7 @@ auto HandleBraceExpr(Context& context) -> void {
static auto HandleBraceExprParamError(Context& context,
Context::StateStackEntry state,
State param_finish_state) -> void {
IntAsSelect mode(0);
Diagnostics::IntAsSelect mode(0);
switch (param_finish_state) {
case State::BraceExprParamFinishAsType:
mode.value = 0;
@@ -41,7 +41,7 @@ static auto HandleBraceExprParamError(Context& context,
ExpectedStructLiteralField, Error,
"expected {0:=0:`.field: field_type`|"
"=1:`.field = value`|=2:`.field: field_type` or `.field = value`}",
IntAsSelect);
Diagnostics::IntAsSelect);
context.emitter().Emit(*context.position(), ExpectedStructLiteralField, mode);
state.has_error = true;
+2 -1
View File
@@ -38,7 +38,8 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind,
return;
} else {
CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPeriodOrArrow, Error,
"expected identifier after `{0:->|.}`", BoolAsSelect);
"expected identifier after `{0:->|.}`",
Diagnostics::BoolAsSelect);
context.emitter().Emit(*context.position(),
ExpectedIdentifierAfterPeriodOrArrow, is_arrow);
// If we see a keyword, assume it was intended to be a name.
+1 -1
View File
@@ -17,7 +17,7 @@ auto HandleInvalid(Context& context) -> void {
context.PopState());
}
auto Parse(Lex::TokenizedBuffer& tokens, DiagnosticConsumer& consumer,
auto Parse(Lex::TokenizedBuffer& tokens, Diagnostics::Consumer& consumer,
llvm::raw_ostream* vlog_stream) -> Tree {
// Delegate to the parser.
Tree tree(tokens);
+1 -1
View File
@@ -15,7 +15,7 @@ namespace Carbon::Parse {
// Parses the token buffer into a `Tree`.
//
// This is the factory function which is used to build parse trees.
auto Parse(Lex::TokenizedBuffer& tokens, DiagnosticConsumer& consumer,
auto Parse(Lex::TokenizedBuffer& tokens, Diagnostics::Consumer& consumer,
llvm::raw_ostream* vlog_stream) -> Tree;
} // namespace Carbon::Parse
+3 -3
View File
@@ -29,18 +29,18 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
llvm::MemoryBuffer::getMemBuffer(data_ref, /*BufferName=*/TestFileName,
/*RequiresNullTerminator=*/false)));
auto source =
SourceBuffer::MakeFromFile(fs, TestFileName, NullDiagnosticConsumer());
SourceBuffer::MakeFromFile(fs, TestFileName, Diagnostics::NullConsumer());
// Lex the input.
SharedValueStores value_stores;
auto tokens = Lex::Lex(value_stores, *source, NullDiagnosticConsumer());
auto tokens = Lex::Lex(value_stores, *source, Diagnostics::NullConsumer());
if (tokens.has_errors()) {
return 0;
}
// Now parse it into a tree. Note that parsing will (when asserts are enabled)
// walk the entire tree to verify it so we don't have to do that here.
Parse::Parse(tokens, NullDiagnosticConsumer(), /*vlog_stream=*/nullptr);
Parse::Parse(tokens, Diagnostics::NullConsumer(), /*vlog_stream=*/nullptr);
return 0;
}
+1 -1
View File
@@ -260,7 +260,7 @@ auto TreeAndSubtrees::GetSubtreeTokenRange(NodeId node_id) const -> TokenRange {
}
auto TreeAndSubtrees::NodeToDiagnosticLoc(NodeId node_id, bool token_only) const
-> ConvertedDiagnosticLoc {
-> Diagnostics::ConvertedLoc {
// Support the invalid token as a way to emit only the filename, when there
// is no line association.
if (!node_id.has_value()) {
+1 -1
View File
@@ -120,7 +120,7 @@ class TreeAndSubtrees {
// Converts the node to a diagnostic location, covering either the full
// subtree or only the token.
auto NodeToDiagnosticLoc(NodeId node_id, bool token_only) const
-> ConvertedDiagnosticLoc;
-> Diagnostics::ConvertedLoc;
// Returns an iterable range over the parse tree node and all of its
// descendants in depth-first postorder.
+3 -3
View File
@@ -479,7 +479,7 @@ struct NameId : public IdBase<NameId> {
static constexpr llvm::StringLiteral Label = "name";
// names().GetFormatted() is used for diagnostics.
using DiagnosticType = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// An enum of special names.
enum class SpecialNameId : uint8_t {
@@ -708,7 +708,7 @@ struct TypeId : public IdBase<TypeId> {
// `StringifyTypeExpr` is used for diagnostics. However, where possible, an
// `InstId` describing how the type was written should be preferred, using
// `InstIdAsType` or `TypeOfInstId` as the diagnostic argument type.
using DiagnosticType = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
using IdBase::IdBase;
@@ -755,7 +755,7 @@ struct ElementIndex : public IndexBase<ElementIndex> {
// The ID of a library name. This is either a string literal or `default`.
struct LibraryNameId : public IdBase<LibraryNameId> {
static constexpr llvm::StringLiteral Label = "library_name";
using DiagnosticType = DiagnosticTypeInfo<std::string>;
using DiagnosticType = Diagnostics::TypeInfo<std::string>;
// The name of `default`.
static const LibraryNameId Default;
+6 -6
View File
@@ -11,7 +11,7 @@
namespace Carbon {
auto SourceBuffer::MakeFromStdin(DiagnosticConsumer& consumer)
auto SourceBuffer::MakeFromStdin(Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer> {
return MakeFromMemoryBuffer(llvm::MemoryBuffer::getSTDIN(), "<stdin>",
/*is_regular_file=*/false, consumer);
@@ -19,9 +19,9 @@ auto SourceBuffer::MakeFromStdin(DiagnosticConsumer& consumer)
auto SourceBuffer::MakeFromFile(llvm::vfs::FileSystem& fs,
llvm::StringRef filename,
DiagnosticConsumer& consumer)
Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer> {
FileDiagnosticEmitter emitter(&consumer);
Diagnostics::FileEmitter emitter(&consumer);
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> file =
fs.openFileForRead(filename);
@@ -53,7 +53,7 @@ auto SourceBuffer::MakeFromFile(llvm::vfs::FileSystem& fs,
auto SourceBuffer::MakeFromStringCopy(llvm::StringRef filename,
llvm::StringRef text,
DiagnosticConsumer& consumer)
Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer> {
return MakeFromMemoryBuffer(
llvm::MemoryBuffer::getMemBufferCopy(text, filename), filename,
@@ -63,8 +63,8 @@ auto SourceBuffer::MakeFromStringCopy(llvm::StringRef filename,
auto SourceBuffer::MakeFromMemoryBuffer(
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer,
llvm::StringRef filename, bool is_regular_file,
DiagnosticConsumer& consumer) -> std::optional<SourceBuffer> {
FileDiagnosticEmitter emitter(&consumer);
Diagnostics::Consumer& consumer) -> std::optional<SourceBuffer> {
Diagnostics::FileEmitter emitter(&consumer);
if (buffer.getError()) {
CARBON_DIAGNOSTIC(ErrorReadingFile, Error, "error reading file: {0}",
+5 -5
View File
@@ -36,19 +36,19 @@ class SourceBuffer {
public:
// Opens and reads the contents of stdin. Returns a SourceBuffer on success.
// Prints an error and returns nullopt on failure.
static auto MakeFromStdin(DiagnosticConsumer& consumer)
static auto MakeFromStdin(Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer>;
// Opens the requested file. Returns a SourceBuffer on success. Prints an
// error and returns nullopt on failure.
static auto MakeFromFile(llvm::vfs::FileSystem& fs, llvm::StringRef filename,
DiagnosticConsumer& consumer)
Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer>;
// Handles conditional use of stdin based on the filename being "-".
static auto MakeFromFileOrStdin(llvm::vfs::FileSystem& fs,
llvm::StringRef filename,
DiagnosticConsumer& consumer)
Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer> {
if (filename == "-") {
return MakeFromStdin(consumer);
@@ -60,7 +60,7 @@ class SourceBuffer {
// Returns a source buffer with the provided text content. Copies `filename`
// and `text` to take ownership.
static auto MakeFromStringCopy(llvm::StringRef filename, llvm::StringRef text,
DiagnosticConsumer& consumer)
Diagnostics::Consumer& consumer)
-> std::optional<SourceBuffer>;
// Use one of the factory functions above to create a source buffer.
@@ -80,7 +80,7 @@ class SourceBuffer {
static auto MakeFromMemoryBuffer(
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer,
llvm::StringRef filename, bool is_regular_file,
DiagnosticConsumer& consumer) -> std::optional<SourceBuffer>;
Diagnostics::Consumer& consumer) -> std::optional<SourceBuffer>;
explicit SourceBuffer(std::string filename,
std::unique_ptr<llvm::MemoryBuffer> text,
+8 -8
View File
@@ -17,8 +17,8 @@ static constexpr llvm::StringLiteral TestFileName = "test.carbon";
TEST(SourceBufferTest, MissingFile) {
llvm::vfs::InMemoryFileSystem fs;
auto buffer =
SourceBuffer::MakeFromFile(fs, TestFileName, ConsoleDiagnosticConsumer());
auto buffer = SourceBuffer::MakeFromFile(fs, TestFileName,
Diagnostics::ConsoleConsumer());
EXPECT_FALSE(buffer);
}
@@ -27,8 +27,8 @@ TEST(SourceBufferTest, SimpleFile) {
CARBON_CHECK(fs.addFile(TestFileName, /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBuffer("Hello World")));
auto buffer =
SourceBuffer::MakeFromFile(fs, TestFileName, ConsoleDiagnosticConsumer());
auto buffer = SourceBuffer::MakeFromFile(fs, TestFileName,
Diagnostics::ConsoleConsumer());
ASSERT_TRUE(buffer);
EXPECT_EQ(TestFileName, buffer->filename());
@@ -44,8 +44,8 @@ TEST(SourceBufferTest, NoNull) {
/*BufferName=*/"",
/*RequiresNullTerminator=*/false)));
auto buffer =
SourceBuffer::MakeFromFile(fs, TestFileName, ConsoleDiagnosticConsumer());
auto buffer = SourceBuffer::MakeFromFile(fs, TestFileName,
Diagnostics::ConsoleConsumer());
ASSERT_TRUE(buffer);
EXPECT_EQ(TestFileName, buffer->filename());
@@ -57,8 +57,8 @@ TEST(SourceBufferTest, EmptyFile) {
CARBON_CHECK(fs.addFile(TestFileName, /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBuffer("")));
auto buffer =
SourceBuffer::MakeFromFile(fs, TestFileName, ConsoleDiagnosticConsumer());
auto buffer = SourceBuffer::MakeFromFile(fs, TestFileName,
Diagnostics::ConsoleConsumer());
ASSERT_TRUE(buffer);
EXPECT_EQ(TestFileName, buffer->filename());
+2 -2
View File
@@ -7,7 +7,7 @@
namespace Carbon::Testing {
auto CompileHelper::GetTokenizedBuffer(llvm::StringRef text,
DiagnosticConsumer* consumer)
Diagnostics::Consumer* consumer)
-> Lex::TokenizedBuffer& {
auto& source = GetSourceBuffer(text);
@@ -18,7 +18,7 @@ auto CompileHelper::GetTokenizedBuffer(llvm::StringRef text,
}
auto CompileHelper::GetTokenizedBufferWithSharedValueStore(
llvm::StringRef text, DiagnosticConsumer* consumer)
llvm::StringRef text, Diagnostics::Consumer* consumer)
-> std::pair<Lex::TokenizedBuffer&, SharedValueStores&> {
auto& tokens = GetTokenizedBuffer(text, consumer);
return {tokens, value_store_storage_.front()};
+3 -3
View File
@@ -21,12 +21,12 @@ class CompileHelper {
public:
// Returns the result of lex.
auto GetTokenizedBuffer(llvm::StringRef text,
DiagnosticConsumer* consumer = nullptr)
Diagnostics::Consumer* consumer = nullptr)
-> Lex::TokenizedBuffer&;
// Returns the result of lex along with shared values.
auto GetTokenizedBufferWithSharedValueStore(
llvm::StringRef text, DiagnosticConsumer* consumer = nullptr)
llvm::StringRef text, Diagnostics::Consumer* consumer = nullptr)
-> std::pair<Lex::TokenizedBuffer&, SharedValueStores&>;
// Returns the result of parse.
@@ -44,7 +44,7 @@ class CompileHelper {
auto GetSourceBuffer(llvm::StringRef text) -> SourceBuffer&;
// Diagnostics will be printed to console.
DiagnosticConsumer& consumer_ = ConsoleDiagnosticConsumer();
Diagnostics::Consumer& consumer_ = Diagnostics::ConsoleConsumer();
// An index to generate unique filenames.
int file_index_ = 0;