From a24963ea6b05447f982e1df829dd3981136b730a Mon Sep 17 00:00:00 2001 From: Jon Meow Date: Mon, 4 Apr 2022 15:14:43 -0700 Subject: [PATCH] Move diagnostic definitions to be closer to where they emit (#1169) --- toolchain/lexer/numeric_literal.cpp | 37 ++++++------ toolchain/lexer/string_literal.cpp | 62 ++++++++++---------- toolchain/lexer/tokenized_buffer.cpp | 27 +++++---- toolchain/parser/parser_impl.cpp | 87 ++++++++++++++-------------- 4 files changed, 109 insertions(+), 104 deletions(-) diff --git a/toolchain/lexer/numeric_literal.cpp b/toolchain/lexer/numeric_literal.cpp index 45297aa0f8e3..855ef28efd9a 100644 --- a/toolchain/lexer/numeric_literal.cpp +++ b/toolchain/lexer/numeric_literal.cpp @@ -31,25 +31,6 @@ static auto operator<<(llvm::raw_ostream& out, LexedNumericLiteral::Radix radix) return out; } -CARBON_DIAGNOSTIC(InvalidDigitSeparator, Error, - "Misplaced digit separator in numeric literal."); -CARBON_DIAGNOSTIC(InvalidDigit, Error, - "Invalid digit '{0}' in {1} numeric literal.", char, - LexedNumericLiteral::Radix); -CARBON_DIAGNOSTIC(EmptyDigitSequence, Error, - "Empty digit sequence in numeric literal."); -CARBON_DIAGNOSTIC( - IrregularDigitSeparators, Error, - "Digit separators in {0} number should appear every {1} characters " - "from the right.", - LexedNumericLiteral::Radix, int); -CARBON_DIAGNOSTIC(UnknownBaseSpecifier, Error, - "Unknown base specifier in numeric literal."); -CARBON_DIAGNOSTIC(BinaryRealLiteral, Error, - "Binary real number literals are not supported."); -CARBON_DIAGNOSTIC(WrongRealLiteralExponent, Error, - "Expected '{0}' to introduce exponent.", char); - auto LexedNumericLiteral::Lex(llvm::StringRef source_text) -> llvm::Optional { LexedNumericLiteral result; @@ -317,17 +298,24 @@ auto LexedNumericLiteral::Parser::CheckDigitSequence( // next to another digit separator, or at the end. if (!allow_digit_separators || i == 0 || text[i - 1] == '_' || i + 1 == n) { + CARBON_DIAGNOSTIC(InvalidDigitSeparator, Error, + "Misplaced digit separator in numeric literal."); emitter_.Emit(text.begin() + 1, InvalidDigitSeparator); } ++num_digit_separators; continue; } + CARBON_DIAGNOSTIC(InvalidDigit, Error, + "Invalid digit '{0}' in {1} numeric literal.", char, + LexedNumericLiteral::Radix); emitter_.Emit(text.begin() + i, InvalidDigit, c, radix); return {.ok = false}; } if (num_digit_separators == static_cast(text.size())) { + CARBON_DIAGNOSTIC(EmptyDigitSequence, Error, + "Empty digit sequence in numeric literal."); emitter_.Emit(text.begin(), EmptyDigitSequence); return {.ok = false}; } @@ -358,6 +346,11 @@ auto LexedNumericLiteral::Parser::CheckDigitSeparatorPlacement( } auto diagnose_irregular_digit_separators = [&]() { + CARBON_DIAGNOSTIC( + IrregularDigitSeparators, Error, + "Digit separators in {0} number should appear every {1} characters " + "from the right.", + LexedNumericLiteral::Radix, int); emitter_.Emit(text.begin(), IrregularDigitSeparators, radix, radix == Radix::Decimal ? 3 : 4); }; @@ -387,6 +380,8 @@ auto LexedNumericLiteral::Parser::CheckDigitSeparatorPlacement( auto LexedNumericLiteral::Parser::CheckLeadingZero() -> bool { if (radix_ == Radix::Decimal && int_part_.startswith("0") && int_part_ != "0") { + CARBON_DIAGNOSTIC(UnknownBaseSpecifier, Error, + "Unknown base specifier in numeric literal."); emitter_.Emit(int_part_.begin(), UnknownBaseSpecifier); return false; } @@ -408,6 +403,8 @@ auto LexedNumericLiteral::Parser::CheckFractionalPart() -> bool { } if (radix_ == Radix::Binary) { + CARBON_DIAGNOSTIC(BinaryRealLiteral, Error, + "Binary real number literals are not supported."); emitter_.Emit(literal_.text_.begin() + literal_.radix_point_, BinaryRealLiteral); // Carry on and parse the binary real literal anyway. @@ -429,6 +426,8 @@ auto LexedNumericLiteral::Parser::CheckExponentPart() -> bool { char expected_exponent_kind = (radix_ == Radix::Decimal ? 'e' : 'p'); if (literal_.text_[literal_.exponent_] != expected_exponent_kind) { + CARBON_DIAGNOSTIC(WrongRealLiteralExponent, Error, + "Expected '{0}' to introduce exponent.", char); emitter_.Emit(literal_.text_.begin() + literal_.exponent_, WrongRealLiteralExponent, expected_exponent_kind); return false; diff --git a/toolchain/lexer/string_literal.cpp b/toolchain/lexer/string_literal.cpp index a9818c87d854..07df9b06f285 100644 --- a/toolchain/lexer/string_literal.cpp +++ b/toolchain/lexer/string_literal.cpp @@ -17,37 +17,6 @@ namespace Carbon { using LexerDiagnosticEmitter = DiagnosticEmitter; -CARBON_DIAGNOSTIC( - ContentBeforeStringTerminator, Error, - "Only whitespace is permitted before the closing `\"\"\"` of a " - "multi-line string."); -CARBON_DIAGNOSTIC( - UnicodeEscapeTooLarge, Error, - "Code point specified by `\\u{{...}}` escape is greater than 0x10FFFF."); -CARBON_DIAGNOSTIC( - UnicodeEscapeSurrogate, Error, - "Code point specified by `\\u{{...}}` escape is a surrogate character."); -CARBON_DIAGNOSTIC( - UnicodeEscapeMissingBracedDigits, Error, - "Escape sequence `\\u` must be followed by a braced sequence of " - "uppercase hexadecimal digits, for example `\\u{{70AD}}`."); -CARBON_DIAGNOSTIC(HexadecimalEscapeMissingDigits, Error, - "Escape sequence `\\x` must be followed by two " - "uppercase hexadecimal digits, for example `\\x0F`."); -CARBON_DIAGNOSTIC( - DecimalEscapeSequence, Error, - "Decimal digit follows `\\0` escape sequence. Use `\\x00` instead " - "of `\\0` if the next character is a digit."); -CARBON_DIAGNOSTIC(UnknownEscapeSequence, Error, - "Unrecognized escape sequence `{0}`.", char); -CARBON_DIAGNOSTIC(MismatchedIndentInString, Error, - "Indentation does not match that of the closing \"\"\" in " - "multi-line string literal."); -CARBON_DIAGNOSTIC( - InvalidHorizontalWhitespaceInString, Error, - "Whitespace other than plain space must be expressed with an escape " - "sequence in a string literal."); - static constexpr char MultiLineIndicator[] = R"(""")"; // Return the number of opening characters of a multi-line string literal, @@ -182,6 +151,10 @@ static auto CheckIndent(LexerDiagnosticEmitter& emitter, llvm::StringRef text, // The last line is not permitted to contain any content after its // indentation. if (indent.end() != content.end()) { + CARBON_DIAGNOSTIC( + ContentBeforeStringTerminator, Error, + "Only whitespace is permitted before the closing `\"\"\"` of a " + "multi-line string."); emitter.Emit(indent.end(), ContentBeforeStringTerminator); } @@ -197,11 +170,17 @@ static auto ExpandUnicodeEscapeSequence(LexerDiagnosticEmitter& emitter, return false; } if (digits.getAsInteger(16, code_point) || code_point > 0x10FFFF) { + CARBON_DIAGNOSTIC(UnicodeEscapeTooLarge, Error, + "Code point specified by `\\u{{...}}` escape is greater " + "than 0x10FFFF."); emitter.Emit(digits.begin(), UnicodeEscapeTooLarge); return false; } if (code_point >= 0xD800 && code_point < 0xE000) { + CARBON_DIAGNOSTIC(UnicodeEscapeSurrogate, Error, + "Code point specified by `\\u{{...}}` escape is a " + "surrogate character."); emitter.Emit(digits.begin(), UnicodeEscapeSurrogate); return false; } @@ -255,6 +234,10 @@ static auto ExpandAndConsumeEscapeSequence(LexerDiagnosticEmitter& emitter, case '0': result += '\0'; if (!content.empty() && IsDecimalDigit(content.front())) { + CARBON_DIAGNOSTIC( + DecimalEscapeSequence, Error, + "Decimal digit follows `\\0` escape sequence. Use `\\x00` instead " + "of `\\0` if the next character is a digit."); emitter.Emit(content.begin(), DecimalEscapeSequence); return; } @@ -267,6 +250,9 @@ static auto ExpandAndConsumeEscapeSequence(LexerDiagnosticEmitter& emitter, content = content.drop_front(2); return; } + CARBON_DIAGNOSTIC(HexadecimalEscapeMissingDigits, Error, + "Escape sequence `\\x` must be followed by two " + "uppercase hexadecimal digits, for example `\\x0F`."); emitter.Emit(content.begin(), HexadecimalEscapeMissingDigits); break; case 'u': { @@ -282,10 +268,16 @@ static auto ExpandAndConsumeEscapeSequence(LexerDiagnosticEmitter& emitter, return; } } + CARBON_DIAGNOSTIC( + UnicodeEscapeMissingBracedDigits, Error, + "Escape sequence `\\u` must be followed by a braced sequence of " + "uppercase hexadecimal digits, for example `\\u{{70AD}}`."); emitter.Emit(content.begin(), UnicodeEscapeMissingBracedDigits); break; } default: + CARBON_DIAGNOSTIC(UnknownEscapeSequence, Error, + "Unrecognized escape sequence `{0}`.", char); emitter.Emit(content.begin() - 1, UnknownEscapeSequence, first); break; } @@ -315,6 +307,10 @@ static auto ExpandEscapeSequencesAndRemoveIndent( const char* line_start = contents.begin(); contents = contents.drop_while(IsHorizontalWhitespace); if (!contents.startswith("\n")) { + CARBON_DIAGNOSTIC( + MismatchedIndentInString, Error, + "Indentation does not match that of the closing \"\"\" in " + "multi-line string literal."); emitter.Emit(line_start, MismatchedIndentInString); } } @@ -354,6 +350,10 @@ static auto ExpandEscapeSequencesAndRemoveIndent( contents[after_space] != '\n') { // TODO: Include the source range of the whitespace up to // `contents.begin() + after_space` in the diagnostic. + CARBON_DIAGNOSTIC( + InvalidHorizontalWhitespaceInString, Error, + "Whitespace other than plain space must be expressed with an " + "escape sequence in a string literal."); emitter.Emit(contents.begin(), InvalidHorizontalWhitespaceInString); // Include the whitespace in the string contents for error recovery. result += contents.substr(0, after_space); diff --git a/toolchain/lexer/tokenized_buffer.cpp b/toolchain/lexer/tokenized_buffer.cpp index 8c777e84e9a1..2fc98802546c 100644 --- a/toolchain/lexer/tokenized_buffer.cpp +++ b/toolchain/lexer/tokenized_buffer.cpp @@ -27,18 +27,6 @@ namespace Carbon { -CARBON_DIAGNOSTIC(TrailingComment, Error, - "Trailing comments are not permitted."); -CARBON_DIAGNOSTIC(NoWhitespaceAfterCommentIntroducer, Error, - "Whitespace is required after '//'."); -CARBON_DIAGNOSTIC(UnmatchedClosing, Error, - "Closing symbol without a corresponding opening symbol."); -CARBON_DIAGNOSTIC(MismatchedClosing, Error, - "Closing symbol does not match most recent opening symbol."); -CARBON_DIAGNOSTIC(UnrecognizedCharacters, Error, - "Encountered unrecognized characters while parsing."); -CARBON_DIAGNOSTIC(UnterminatedString, Error, "String is missing a terminator."); - // TODO: Move Overload and VariantMatch somewhere more central. // Form an overload set from a list of functions. For example: @@ -131,10 +119,15 @@ class TokenizedBuffer::Lexer { if (source_text.startswith("//")) { // Any comment must be the only non-whitespace on the line. if (set_indent_) { + CARBON_DIAGNOSTIC(TrailingComment, Error, + "Trailing comments are not permitted."); + emitter_.Emit(source_text.begin(), TrailingComment); } // The introducer '//' must be followed by whitespace or EOF. if (source_text.size() > 2 && !IsSpace(source_text[2])) { + CARBON_DIAGNOSTIC(NoWhitespaceAfterCommentIntroducer, Error, + "Whitespace is required after '//'."); emitter_.Emit(source_text.begin() + 2, NoWhitespaceAfterCommentIntroducer); } @@ -285,6 +278,8 @@ class TokenizedBuffer::Lexer { literal->ComputeValue(emitter_)); return token; } else { + CARBON_DIAGNOSTIC(UnterminatedString, Error, + "String is missing a terminator."); emitter_.Emit(literal->text().begin(), UnterminatedString); return buffer_.AddToken({.kind = TokenKind::Error(), .token_line = string_line, @@ -335,6 +330,9 @@ class TokenizedBuffer::Lexer { closing_token_info.kind = TokenKind::Error(); closing_token_info.error_length = kind.GetFixedSpelling().size(); + CARBON_DIAGNOSTIC( + UnmatchedClosing, Error, + "Closing symbol without a corresponding opening symbol."); emitter_.Emit(location, UnmatchedClosing); // Note that this still returns true as we do consume a symbol. return token; @@ -412,6 +410,9 @@ class TokenizedBuffer::Lexer { } open_groups_.pop_back(); + CARBON_DIAGNOSTIC( + MismatchedClosing, Error, + "Closing symbol does not match most recent opening symbol."); token_emitter_.Emit(opening_token, MismatchedClosing); CHECK(!buffer_.tokens().empty()) << "Must have a prior opening token!"; @@ -510,6 +511,8 @@ class TokenizedBuffer::Lexer { .token_line = current_line_, .column = current_column_, .error_length = static_cast(error_text.size())}); + CARBON_DIAGNOSTIC(UnrecognizedCharacters, Error, + "Encountered unrecognized characters while parsing."); emitter_.Emit(error_text.begin(), UnrecognizedCharacters); current_column_ += error_text.size(); diff --git a/toolchain/parser/parser_impl.cpp b/toolchain/parser/parser_impl.cpp index 4c98fe4d3388..6a1637d99ad1 100644 --- a/toolchain/parser/parser_impl.cpp +++ b/toolchain/parser/parser_impl.cpp @@ -17,8 +17,8 @@ namespace Carbon { -CARBON_DIAGNOSTIC(StackLimitExceeded, Error, "Exceeded recursion limit ({0})", - int); +CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error, + "Expected `;` after expression."); // Manages the parser's stack depth, particularly decrementing on destruction. // This should only be instantiated through RETURN_IF_STACK_LIMITED. @@ -31,6 +31,8 @@ class ParseTree::Parser::ScopedStackStep { auto VerifyUnderLimit() -> bool { if (parser_->stack_depth_ >= StackDepthLimit) { + CARBON_DIAGNOSTIC(StackLimitExceeded, Error, + "Exceeded recursion limit ({0})", int); parser_->emitter_.Emit(*parser_->position_, StackLimitExceeded, ParseTree::StackDepthLimit); return false; @@ -74,46 +76,6 @@ static auto operator<<(llvm::raw_ostream& out, RelativeLocation loc) return out; } -CARBON_DIAGNOSTIC(ExpectedFunctionName, Error, - "Expected function name after `fn` keyword."); -CARBON_DIAGNOSTIC(ExpectedFunctionParams, Error, - "Expected `(` after function name."); -CARBON_DIAGNOSTIC( - ExpectedFunctionBodyOrSemi, Error, - "Expected function definition or `;` after function declaration."); -CARBON_DIAGNOSTIC(ExpectedVariableName, Error, - "Expected pattern in `var` declaration."); -CARBON_DIAGNOSTIC(ExpectedParameterName, Error, - "Expected parameter declaration."); -CARBON_DIAGNOSTIC(ExpectedStructLiteralField, Error, "Expected {0}{1}{2}.", - llvm::StringRef, llvm::StringRef, llvm::StringRef); -CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error, - "Unrecognized declaration introducer."); -CARBON_DIAGNOSTIC(ExpectedCodeBlock, Error, "Expected braced code block."); -CARBON_DIAGNOSTIC(ExpectedExpression, Error, "Expected expression."); -CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.", - TokenKind); -CARBON_DIAGNOSTIC(ExpectedCloseParen, Error, "Unexpected tokens before `)`."); -CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error, - "Expected `;` after expression."); -CARBON_DIAGNOSTIC(ExpectedSemiAfter, Error, "Expected `;` after `{0}`.", - TokenKind); -CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDot, Error, - "Expected identifier after `.`."); -CARBON_DIAGNOSTIC(UnexpectedTokenAfterListElement, Error, - "Expected `,` or `{0}`.", TokenKind); -CARBON_DIAGNOSTIC(BinaryOperatorRequiresWhitespace, Error, - "Whitespace missing {0} binary operator.", RelativeLocation); -CARBON_DIAGNOSTIC(UnaryOperatorHasWhitespace, Error, - "Whitespace is not allowed {0} this unary operator.", - RelativeLocation); -CARBON_DIAGNOSTIC(UnaryOperatorRequiresWhitespace, Error, - "Whitespace is required {0} this unary operator.", - RelativeLocation); -CARBON_DIAGNOSTIC( - OperatorRequiresParentheses, Error, - "Parentheses are required to disambiguate operator precedence."); - ParseTree::Parser::Parser(ParseTree& tree_arg, TokenizedBuffer& tokens_arg, TokenDiagnosticEmitter& emitter) : tree_(tree_arg), @@ -322,6 +284,7 @@ auto ParseTree::Parser::ParseCloseParen(TokenizedBuffer::Token open_paren, } // TODO: Include the location of the matching open_paren in the diagnostic. + CARBON_DIAGNOSTIC(ExpectedCloseParen, Error, "Unexpected tokens before `)`."); emitter_.Emit(*position_, ExpectedCloseParen); SkipTo(tokens_.GetMatchedClosingToken(open_paren)); AddLeafNode(kind, Consume(TokenKind::CloseParen())); @@ -354,6 +317,8 @@ auto ParseTree::Parser::ParseList(TokenKind open, TokenKind close, if (!NextTokenIsOneOf({close, TokenKind::Comma()})) { if (!element_error) { + CARBON_DIAGNOSTIC(UnexpectedTokenAfterListElement, Error, + "Expected `,` or `{0}`.", TokenKind); emitter_.Emit(*position_, UnexpectedTokenAfterListElement, close); } has_errors = true; @@ -397,10 +362,14 @@ auto ParseTree::Parser::ParsePattern(PatternKind kind) -> llvm::Optional { switch (kind) { case PatternKind::Parameter: + CARBON_DIAGNOSTIC(ExpectedParameterName, Error, + "Expected parameter declaration."); emitter_.Emit(*position_, ExpectedParameterName); break; case PatternKind::Variable: + CARBON_DIAGNOSTIC(ExpectedVariableName, Error, + "Expected pattern in `var` declaration."); emitter_.Emit(*position_, ExpectedVariableName); break; } @@ -446,6 +415,7 @@ auto ParseTree::Parser::ParseCodeBlock() -> llvm::Optional { ConsumeIf(TokenKind::OpenCurlyBrace()); if (!maybe_open_curly) { // Recover by parsing a single statement. + CARBON_DIAGNOSTIC(ExpectedCodeBlock, Error, "Expected braced code block."); emitter_.Emit(*position_, ExpectedCodeBlock); return ParseStatement(); } @@ -493,6 +463,8 @@ auto ParseTree::Parser::ParseFunctionDeclaration() -> Node { auto name_n = ConsumeAndAddLeafNodeIf(TokenKind::Identifier(), ParseNodeKind::DeclaredName()); if (!name_n) { + CARBON_DIAGNOSTIC(ExpectedFunctionName, Error, + "Expected function name after `fn` keyword."); emitter_.Emit(*position_, ExpectedFunctionName); // FIXME: We could change the lexer to allow us to synthesize certain // kinds of tokens and try to "recover" here, but unclear that this is @@ -503,6 +475,8 @@ auto ParseTree::Parser::ParseFunctionDeclaration() -> Node { TokenizedBuffer::Token open_paren = *position_; if (tokens_.GetKind(open_paren) != TokenKind::OpenParen()) { + CARBON_DIAGNOSTIC(ExpectedFunctionParams, Error, + "Expected `(` after function name."); emitter_.Emit(open_paren, ExpectedFunctionParams); SkipPastLikelyEnd(function_intro_token, handle_semi_in_error_recovery); return add_error_function_node(); @@ -524,6 +498,9 @@ auto ParseTree::Parser::ParseFunctionDeclaration() -> Node { } } else if (!ConsumeAndAddLeafNodeIf(TokenKind::Semi(), ParseNodeKind::DeclarationEnd())) { + CARBON_DIAGNOSTIC( + ExpectedFunctionBodyOrSemi, Error, + "Expected function definition or `;` after function declaration."); emitter_.Emit(*position_, ExpectedFunctionBodyOrSemi); if (tokens_.GetLine(*position_) == tokens_.GetLine(close_paren)) { // Only need to skip if we've not already found a new line. @@ -596,6 +573,8 @@ auto ParseTree::Parser::ParseDeclaration() -> llvm::Optional { } // We didn't recognize an introducer for a valid declaration. + CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error, + "Unrecognized declaration introducer."); emitter_.Emit(*position_, UnrecognizedDeclaration); // Skip forward past any end of a declaration we simply didn't understand so @@ -657,6 +636,9 @@ auto ParseTree::Parser::ParseBraceExpression() -> llvm::Optional { auto start_elem = GetSubtreeStartPosition(); auto diagnose_invalid_syntax = [&] { + CARBON_DIAGNOSTIC(ExpectedStructLiteralField, Error, + "Expected {0}{1}{2}.", llvm::StringRef, + llvm::StringRef, llvm::StringRef); bool can_be_type = kind != Value; bool can_be_value = kind != Type; emitter_.Emit(*position_, ExpectedStructLiteralField, @@ -736,6 +718,7 @@ auto ParseTree::Parser::ParsePrimaryExpression() -> llvm::Optional { return ParseBraceExpression(); default: + CARBON_DIAGNOSTIC(ExpectedExpression, Error, "Expected expression."); emitter_.Emit(*position_, ExpectedExpression); return llvm::None; } @@ -753,6 +736,8 @@ auto ParseTree::Parser::ParseDesignatorExpression(SubtreeStart start, if (name) { AddLeafNode(ParseNodeKind::DesignatedName(), *name); } else { + CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDot, Error, + "Expected identifier after `.`."); emitter_.Emit(*position_, ExpectedIdentifierAfterDot); // If we see a keyword, assume it was intended to be the designated name. // TODO: Should keywords be valid in designators? @@ -876,6 +861,9 @@ auto ParseTree::Parser::DiagnoseOperatorFixity(OperatorFixity fixity) -> void { if (fixity == OperatorFixity::Infix) { // Infix operators must satisfy the infix operator rules. if (!is_valid_as_infix) { + CARBON_DIAGNOSTIC(BinaryOperatorRequiresWhitespace, Error, + "Whitespace missing {0} binary operator.", + RelativeLocation); emitter_.Emit(*position_, BinaryOperatorRequiresWhitespace, tokens_.HasLeadingWhitespace(*position_) ? RelativeLocation::After @@ -891,12 +879,18 @@ auto ParseTree::Parser::DiagnoseOperatorFixity(OperatorFixity fixity) -> void { if (NextTokenKind().IsSymbol() && (prefix ? tokens_.HasTrailingWhitespace(*position_) : tokens_.HasLeadingWhitespace(*position_))) { + CARBON_DIAGNOSTIC(UnaryOperatorHasWhitespace, Error, + "Whitespace is not allowed {0} this unary operator.", + RelativeLocation); emitter_.Emit( *position_, UnaryOperatorHasWhitespace, prefix ? RelativeLocation::After : RelativeLocation::Before); } // Pre/postfix operators must not satisfy the infix operator rules. if (is_valid_as_infix) { + CARBON_DIAGNOSTIC(UnaryOperatorRequiresWhitespace, Error, + "Whitespace is required {0} this unary operator.", + RelativeLocation); emitter_.Emit( *position_, UnaryOperatorRequiresWhitespace, prefix ? RelativeLocation::Before : RelativeLocation::After); @@ -930,6 +924,11 @@ auto ParseTree::Parser::IsTrailingOperatorInfix() -> bool { auto ParseTree::Parser::ParseOperatorExpression( PrecedenceGroup ambient_precedence) -> llvm::Optional { + // May be omitted a couple different ways here. + CARBON_DIAGNOSTIC( + OperatorRequiresParentheses, Error, + "Parentheses are required to disambiguate operator precedence."); + RETURN_IF_STACK_LIMITED(llvm::None); auto start = GetSubtreeStartPosition(); @@ -1047,6 +1046,8 @@ auto ParseTree::Parser::ParseParenCondition(TokenKind introducer) auto start = GetSubtreeStartPosition(); auto open_paren = ConsumeIf(TokenKind::OpenParen()); if (!open_paren) { + CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.", + TokenKind); emitter_.Emit(*position_, ExpectedParenAfter, introducer); } @@ -1113,6 +1114,8 @@ auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind, auto semi = ConsumeAndAddLeafNodeIf(TokenKind::Semi(), ParseNodeKind::StatementEnd()); if (!semi) { + CARBON_DIAGNOSTIC(ExpectedSemiAfter, Error, "Expected `;` after `{0}`.", + TokenKind); emitter_.Emit(*position_, ExpectedSemiAfter, keyword_kind); // FIXME: Try to skip to a semicolon to recover. }