diff --git a/toolchain/diagnostics/diagnostic_emitter.h b/toolchain/diagnostics/diagnostic_emitter.h index b3dbb6a70dfe..98ced89a5936 100644 --- a/toolchain/diagnostics/diagnostic_emitter.h +++ b/toolchain/diagnostics/diagnostic_emitter.h @@ -68,6 +68,33 @@ class DiagnosticLocationTranslator { -> Diagnostic::Location = 0; }; +// CRTP base class for diagnostics. `DiagnosticEmitter` requires `ShortName` and +// `Format`; `Message` is used by the default `Format` implementation. A simple +// child will look like: +// +// struct MySimpleError : DiagnosticBase { +// static constexpr llvm::StringLiteral ShortName = "short-name"; +// static constexpr llvm::StringLiteral Message = "A message."; +// }; +// +// emitter.EmitError(location); +// +// A complex child may provide an alternate `Format` implementation: +// +// struct MyComplexError : DiagnosticBase { +// static constexpr llvm::StringLiteral ShortName = "short-name"; +// +// auto Format() -> std::string { return llvm::formatv("See {0}.", ref); } +// +// std::string ref; +// }; +// +// emitter.EmitError(location, {.ref = "ref"; }); +template +struct DiagnosticBase { + static auto Format() -> std::string { return Derived::Message.str(); } +}; + // Manages the creation of reports, the testing if diagnostics are enabled, and // the collection of reports. // @@ -88,7 +115,9 @@ class DiagnosticEmitter { ~DiagnosticEmitter() = default; // Emits an error unconditionally. - template + template , DiagnosticT>>> auto EmitError(LocationT location, DiagnosticT diag) -> void { // TODO: Encode the diagnostic kind in the Diagnostic object rather than // hardcoding an "error: " prefix. @@ -143,12 +172,6 @@ inline auto ConsoleDiagnosticConsumer() -> DiagnosticConsumer& { return *consumer; } -// CRTP base class for diagnostics with no substitutions. -template -struct SimpleDiagnostic { - static auto Format() -> std::string { return Derived::Message.str(); } -}; - // Diagnostic consumer adaptor that tracks whether any errors have been // produced. class ErrorTrackingDiagnosticConsumer : public DiagnosticConsumer { diff --git a/toolchain/diagnostics/diagnostic_emitter_test.cpp b/toolchain/diagnostics/diagnostic_emitter_test.cpp index 664d9f7a0bd8..1b36d2305b52 100644 --- a/toolchain/diagnostics/diagnostic_emitter_test.cpp +++ b/toolchain/diagnostics/diagnostic_emitter_test.cpp @@ -14,7 +14,7 @@ namespace Carbon::Testing { namespace { -struct FakeDiagnostic { +struct FakeDiagnostic : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "fake-diagnostic"; // TODO: consider ways to put the Message into `format` to allow dynamic // selection of the message. diff --git a/toolchain/lexer/numeric_literal.cpp b/toolchain/lexer/numeric_literal.cpp index 8e2751fb1ccf..1789549777ba 100644 --- a/toolchain/lexer/numeric_literal.cpp +++ b/toolchain/lexer/numeric_literal.cpp @@ -14,13 +14,13 @@ namespace Carbon { namespace { -struct EmptyDigitSequence : SimpleDiagnostic { +struct EmptyDigitSequence : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; static constexpr llvm::StringLiteral Message = "Empty digit sequence in numeric literal."; }; -struct InvalidDigit { +struct InvalidDigit : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; auto Format() -> std::string { @@ -35,13 +35,13 @@ struct InvalidDigit { int radix; }; -struct InvalidDigitSeparator : SimpleDiagnostic { +struct InvalidDigitSeparator : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; static constexpr llvm::StringLiteral Message = "Misplaced digit separator in numeric literal."; }; -struct IrregularDigitSeparators { +struct IrregularDigitSeparators : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-irregular-digit-separators"; @@ -58,19 +58,19 @@ struct IrregularDigitSeparators { int radix; }; -struct UnknownBaseSpecifier : SimpleDiagnostic { +struct UnknownBaseSpecifier : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; static constexpr llvm::StringLiteral Message = "Unknown base specifier in numeric literal."; }; -struct BinaryRealLiteral : SimpleDiagnostic { +struct BinaryRealLiteral : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; static constexpr llvm::StringLiteral Message = "Binary real number literals are not supported."; }; -struct WrongRealLiteralExponent { +struct WrongRealLiteralExponent : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; auto Format() -> std::string { @@ -81,7 +81,7 @@ struct WrongRealLiteralExponent { char expected; }; -struct TooManyDigits { +struct TooManyDigits : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-number"; auto Format() -> std::string { diff --git a/toolchain/lexer/string_literal.cpp b/toolchain/lexer/string_literal.cpp index 7a81d0f484e1..3eb8b09dc2c6 100644 --- a/toolchain/lexer/string_literal.cpp +++ b/toolchain/lexer/string_literal.cpp @@ -17,27 +17,27 @@ namespace Carbon { using LexerDiagnosticEmitter = DiagnosticEmitter; struct ContentBeforeStringTerminator - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Only whitespace is permitted before the closing `\"\"\"` of a " "multi-line string."; }; -struct UnicodeEscapeTooLarge : SimpleDiagnostic { +struct UnicodeEscapeTooLarge : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Code point specified by `\\u{...}` escape is greater than 0x10FFFF."; }; -struct UnicodeEscapeSurrogate : SimpleDiagnostic { +struct UnicodeEscapeSurrogate : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Code point specified by `\\u{...}` escape is a surrogate character."; }; struct UnicodeEscapeMissingBracedDigits - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Escape sequence `\\u` must be followed by a braced sequence of " @@ -45,30 +45,30 @@ struct UnicodeEscapeMissingBracedDigits }; struct HexadecimalEscapeMissingDigits - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Escape sequence `\\x` must be followed by two " "uppercase hexadecimal digits, for example `\\x0F`."; }; -struct DecimalEscapeSequence : SimpleDiagnostic { +struct DecimalEscapeSequence : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Decimal digit follows `\\0` escape sequence. Use `\\x00` instead of " "`\\0` if the next character is a digit."; }; -struct UnknownEscapeSequence { +struct UnknownEscapeSequence : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr const char* Message = "Unrecognized escape sequence `{0}`."; - char first; - auto Format() -> std::string { return llvm::formatv(Message, first).str(); } + + char first; }; -struct MismatchedIndentInString : SimpleDiagnostic { +struct MismatchedIndentInString : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Indentation does not match that of the closing \"\"\" in multi-line " @@ -76,7 +76,7 @@ struct MismatchedIndentInString : SimpleDiagnostic { }; struct InvalidHorizontalWhitespaceInString - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-invalid-string"; static constexpr llvm::StringLiteral Message = "Whitespace other than plain space must be expressed with an escape " diff --git a/toolchain/lexer/tokenized_buffer.cpp b/toolchain/lexer/tokenized_buffer.cpp index 668eb81f9465..a4e27ad4e2d9 100644 --- a/toolchain/lexer/tokenized_buffer.cpp +++ b/toolchain/lexer/tokenized_buffer.cpp @@ -25,32 +25,32 @@ namespace Carbon { -struct TrailingComment : SimpleDiagnostic { +struct TrailingComment : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-comments"; static constexpr llvm::StringLiteral Message = "Trailing comments are not permitted."; }; struct NoWhitespaceAfterCommentIntroducer - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-comments"; static constexpr llvm::StringLiteral Message = "Whitespace is required after '//'."; }; -struct UnmatchedClosing : SimpleDiagnostic { +struct UnmatchedClosing : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-balanced-delimiters"; static constexpr llvm::StringLiteral Message = "Closing symbol without a corresponding opening symbol."; }; -struct MismatchedClosing : SimpleDiagnostic { +struct MismatchedClosing : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-balanced-delimiters"; static constexpr llvm::StringLiteral Message = "Closing symbol does not match most recent opening symbol."; }; -struct UnrecognizedCharacters : SimpleDiagnostic { +struct UnrecognizedCharacters : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-unrecognized-characters"; static constexpr llvm::StringLiteral Message = diff --git a/toolchain/parser/parser_impl.cpp b/toolchain/parser/parser_impl.cpp index 27b3013c410d..05d6c0932b62 100644 --- a/toolchain/parser/parser_impl.cpp +++ b/toolchain/parser/parser_impl.cpp @@ -17,10 +17,10 @@ namespace Carbon { -struct StackLimitExceeded { +struct StackLimitExceeded : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; - auto Format() -> std::string { + static auto Format() -> std::string { return llvm::formatv("Exceeded recursion limit ({0})", ParseTree::StackDepthLimit); } @@ -55,46 +55,43 @@ class ParseTree::Parser::ScopedStackStep { return (error_return_expr); \ } -struct UnexpectedTokenInCodeBlock - : SimpleDiagnostic { +struct UnexpectedTokenInCodeBlock : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Unexpected token in code block."; }; -struct ExpectedFunctionName : SimpleDiagnostic { +struct ExpectedFunctionName : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected function name after `fn` keyword."; }; -struct ExpectedFunctionParams : SimpleDiagnostic { +struct ExpectedFunctionParams : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected `(` after function name."; }; -struct ExpectedFunctionBodyOrSemi - : SimpleDiagnostic { +struct ExpectedFunctionBodyOrSemi : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected function definition or `;` after function declaration."; }; -struct ExpectedVariableName : SimpleDiagnostic { +struct ExpectedVariableName : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected pattern in `var` declaration."; }; -struct ExpectedParameterName : SimpleDiagnostic { +struct ExpectedParameterName : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected parameter declaration."; }; -struct ExpectedStructLiteralField - : SimpleDiagnostic { +struct ExpectedStructLiteralField : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; auto Format() -> std::string { @@ -116,23 +113,23 @@ struct ExpectedStructLiteralField bool can_be_value; }; -struct UnrecognizedDeclaration : SimpleDiagnostic { +struct UnrecognizedDeclaration : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Unrecognized declaration introducer."; }; -struct ExpectedCodeBlock : SimpleDiagnostic { +struct ExpectedCodeBlock : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected braced code block."; }; -struct ExpectedExpression : SimpleDiagnostic { +struct ExpectedExpression : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected expression."; }; -struct ExpectedParenAfter : SimpleDiagnostic { +struct ExpectedParenAfter : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr const char* Message = "Expected `(` after `{0}`."; @@ -143,7 +140,7 @@ struct ExpectedParenAfter : SimpleDiagnostic { TokenKind introducer; }; -struct ExpectedCloseParen : SimpleDiagnostic { +struct ExpectedCloseParen : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Unexpected tokens before `)`."; @@ -153,13 +150,13 @@ struct ExpectedCloseParen : SimpleDiagnostic { }; struct ExpectedSemiAfterExpression - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected `;` after expression."; }; -struct ExpectedSemiAfter : SimpleDiagnostic { +struct ExpectedSemiAfter : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr const char* Message = "Expected `;` after `{0}`."; @@ -170,15 +167,14 @@ struct ExpectedSemiAfter : SimpleDiagnostic { TokenKind preceding; }; -struct ExpectedIdentifierAfterDot - : SimpleDiagnostic { +struct ExpectedIdentifierAfterDot : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Expected identifier after `.`."; }; struct UnexpectedTokenAfterListElement - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr const char* Message = "Expected `,` or `{0}`."; @@ -190,7 +186,7 @@ struct UnexpectedTokenAfterListElement }; struct BinaryOperatorRequiresWhitespace - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr const char* Message = "Whitespace missing {0} binary operator."; @@ -209,8 +205,7 @@ struct BinaryOperatorRequiresWhitespace bool has_trailing_space; }; -struct UnaryOperatorHasWhitespace - : SimpleDiagnostic { +struct UnaryOperatorHasWhitespace : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr const char* Message = "Whitespace is not allowed {0} this unary operator."; @@ -223,7 +218,7 @@ struct UnaryOperatorHasWhitespace }; struct UnaryOperatorRequiresWhitespace - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr const char* Message = "Whitespace is required {0} this unary operator."; @@ -236,7 +231,7 @@ struct UnaryOperatorRequiresWhitespace }; struct OperatorRequiresParentheses - : SimpleDiagnostic { + : DiagnosticBase { static constexpr llvm::StringLiteral ShortName = "syntax-error"; static constexpr llvm::StringLiteral Message = "Parentheses are required to disambiguate operator precedence.";