https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/127569
>From 5bda5cb946e8056a43e487e66487632729d37aee Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Tue, 18 Feb 2025 11:23:30 +0530 Subject: [PATCH 1/3] Address error recovery fixing infinite loop while parsing --- clang/lib/Parse/ParseStmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index cd4504630f871..a044c8acab927 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1253,7 +1253,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { bool LastIsError = false; while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::eof)) { + Tok.isNot(tok::eof) && Tok.isNot(tok::annot_repl_input_end)) { if (Tok.is(tok::annot_pragma_unused)) { HandlePragmaUnused(); continue; >From 8d79608f78b99e3c1728dd2edd5fca0da4e60560 Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Wed, 11 Feb 2026 13:34:05 +0530 Subject: [PATCH 2/3] Apply discussed change as per https://github.com/llvm/llvm-project/pull/127569#issuecomment-3209476166 and added some tests --- clang/include/clang/Parse/Parser.h | 10 +++++++ clang/lib/Parse/ParseCXXInlineMethods.cpp | 16 +++++------ clang/lib/Parse/ParseDecl.cpp | 8 +++--- clang/lib/Parse/ParseDeclCXX.cpp | 8 +++--- clang/lib/Parse/ParseExprCXX.cpp | 2 +- clang/lib/Parse/ParseHLSL.cpp | 2 +- clang/lib/Parse/ParseObjc.cpp | 2 +- clang/lib/Parse/ParseOpenMP.cpp | 2 +- clang/lib/Parse/ParsePragma.cpp | 27 ++++++++++++------- clang/lib/Parse/ParseStmt.cpp | 2 +- clang/lib/Parse/Parser.cpp | 2 +- .../Interpreter/repl-input-end-recovery.cpp | 22 +++++++++++++++ 12 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 clang/test/Interpreter/repl-input-end-recovery.cpp diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 5ae02e2b4e8ad..27fba6161eecb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -721,6 +721,16 @@ class Parser : public CodeCompletionHandler { Kind == tok::annot_repl_input_end; } + /// Determine if the given token marks the end of the current parsing unit. + /// In incremental (REPL) mode, this checks for annot_repl_input_end. + /// In normal compilation, this checks for EOF. + bool isAtInputEnd(const Token &T) const { + if (T.is(tok::eof)) + return true; + return getLangOpts().IncrementalExtensions && + T.is(tok::annot_repl_input_end); + } + static void setTypeAnnotation(Token &Tok, TypeResult T) { assert((T.isInvalid() || T.get()) && "produced a valid-but-null type annotation?"); diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index bc18881e89110..a83568be93056 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -426,7 +426,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { Actions.ActOnParamDefaultArgumentError(Param, EqualLoc, /*DefaultArg=*/nullptr); } else { - if (Tok.isNot(tok::eof) || Tok.getEofData() != Param) { + if (!isAtInputEnd(Tok) || Tok.getEofData() != Param) { // The last two tokens are the terminator and the saved value of // Tok; the last token in the default argument is the one before // those. @@ -441,7 +441,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { // There could be leftover tokens (e.g. because of an error). // Skip through until we reach the 'end of default argument' token. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); if (Tok.is(tok::eof) && Tok.getEofData() == Param) @@ -534,7 +534,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens); - if (Tok.isNot(tok::eof) || Tok.getEofData() != LM.Method) + if (!isAtInputEnd(Tok) || Tok.getEofData() != LM.Method) Diag(Tok.getLocation(), diag::err_except_spec_unparsed); // Attach the exception-specification to the method. @@ -547,7 +547,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { // There could be leftover tokens (e.g. because of an error). // Skip through until we reach the original token position. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); // Clean up the remaining EOF token. @@ -604,7 +604,7 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) { Actions.ActOnStartOfFunctionDef(getCurScope(), LM.D); llvm::scope_exit _([&]() { - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); if (Tok.is(tok::eof) && Tok.getEofData() == LM.D) @@ -691,7 +691,7 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) { Actions.ActOnFinishCXXInClassMemberInitializer(MI.Field, EqualLoc, Init); // The next token should be our artificial terminating EOF token. - if (Tok.isNot(tok::eof)) { + if (!isAtInputEnd(Tok)) { if (!Init.isInvalid()) { SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation); if (!EndLoc.isValid()) @@ -701,7 +701,7 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) { } // Consume tokens until we hit the artificial EOF. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); } // Make sure this is *our* artificial EOF token. @@ -797,7 +797,7 @@ void Parser::ParseLexedAttribute(LateParsedAttribute &LA, // Due to a parsing error, we either went over the cached tokens or // there are still cached tokens left, so we skip the leftover tokens. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index df9e3878bffc0..8bea74976e312 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4832,7 +4832,7 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, // Due to a parsing error, we either went over the cached tokens or // there are still cached tokens left, so we skip the leftover tokens. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); // Consume the fake EOF token if it's there @@ -4864,7 +4864,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, // While we still have something to read, read the declarations in the struct. while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::eof)) { + !isAtInputEnd(Tok)) { // Each iteration of this loop reads one struct-declaration. // Check for extraneous top-level semicolon. @@ -8161,13 +8161,13 @@ TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context, // Check if we parsed the whole thing. if (Result.isUsable() && - (Tok.isNot(tok::eof) || Tok.getEofData() != TypeStr.data())) { + (!isAtInputEnd(Tok) || Tok.getEofData() != TypeStr.data())) { Diag(Tok.getLocation(), diag::err_type_unparsed); } // There could be leftover tokens (e.g. because of an error). // Skip through until we reach the 'end of directive' token. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); // Consume the end token. diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 9117a725843d9..749d13d7e81a0 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -229,7 +229,7 @@ void Parser::ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs, BalancedDelimiterTracker &Tracker) { if (index == InnerNSs.size()) { while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::eof)) { + !isAtInputEnd(Tok)) { ParsedAttributes DeclAttrs(AttrFactory); MaybeParseCXX11Attributes(DeclAttrs); ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); @@ -428,7 +428,7 @@ Decl *Parser::ParseExportDeclaration() { T.consumeOpen(); while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::eof)) { + !isAtInputEnd(Tok)) { ParsedAttributes DeclAttrs(AttrFactory); MaybeParseCXX11Attributes(DeclAttrs); ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); @@ -3671,7 +3671,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, if (TagDecl) { // While we still have something to read, read the member-declarations. while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::eof)) { + !isAtInputEnd(Tok)) { // Each iteration of this loop reads one member-declaration. ParseCXXClassMemberDeclarationWithPragmas( CurAS, AccessAttrs, static_cast<DeclSpec::TST>(TagType), TagDecl); @@ -4441,7 +4441,7 @@ bool Parser::ParseCXX11AttributeArgs( if (LO.CPlusPlus) { TentativeParsingAction TPA(*this); bool HasInvalidArgument = false; - while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) { + while (Tok.isNot(tok::r_paren) && !isAtInputEnd(Tok)) { if (Tok.isOneOf(tok::hash, tok::hashhash)) { Diag(Tok.getLocation(), diag::ext_invalid_attribute_argument) << PP.getSpelling(Tok); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 842b52375eb14..2d2d8e1dcac81 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -3698,7 +3698,7 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, // Match the ')'. if (Result.isInvalid()) { - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); assert(Tok.getEofData() == AttrEnd.getEofData()); ConsumeAnyToken(); diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp index c727ee3a1f1a6..a4a948ee10f9b 100644 --- a/clang/lib/Parse/ParseHLSL.cpp +++ b/clang/lib/Parse/ParseHLSL.cpp @@ -77,7 +77,7 @@ Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd, T.getOpenLocation()); Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs); - while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + while (Tok.isNot(tok::r_brace) && !isAtInputEnd(Tok)) { // FIXME: support attribute on constants inside cbuffer/tbuffer. ParsedAttributes DeclAttrs(AttrFactory); ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 0b9f113d9edc7..b59db1cb1f920 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -3324,7 +3324,7 @@ void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) { // expensive isBeforeInTranslationUnit call. if (PP.getSourceManager().isBeforeInTranslationUnit(Tok.getLocation(), OrigLoc)) - while (Tok.getLocation() != OrigLoc && Tok.isNot(tok::eof)) + while (Tok.getLocation() != OrigLoc && !isAtInputEnd(Tok)) ConsumeAnyToken(); } // Clean up the remaining EOF token, only if it's inserted by us. Otherwise diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index b41803d23cb25..08237e69524ff 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -1973,7 +1973,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( CachedTokens Toks; unsigned Cnt = 1; Toks.push_back(Tok); - while (Cnt && Tok.isNot(tok::eof)) { + while (Cnt && !isAtInputEnd(Tok)) { (void)ConsumeAnyToken(); if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp)) ++Cnt; diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index def2817c930b2..21dbd7adaa034 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -1063,7 +1063,7 @@ void Parser::HandlePragmaMSPragma() { if (!(this->*Handler)(PragmaName, PragmaLocation)) { // Pragma handling failed, and has been diagnosed. Slurp up the tokens // until eof (really end of line) to prevent follow-on errors. - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) PP.Lex(Tok); PP.Lex(Tok); } @@ -1140,7 +1140,7 @@ bool Parser::HandlePragmaMSSection(StringRef PragmaName, return false; } PP.Lex(Tok); // ) - if (Tok.isNot(tok::eof)) { + if (!isAtInputEnd(Tok)) { PP.Diag(PragmaLocation, diag::warn_pragma_extra_tokens_at_eol) << PragmaName; return false; @@ -1222,7 +1222,7 @@ bool Parser::HandlePragmaMSSegment(StringRef PragmaName, return false; } PP.Lex(Tok); // ) - if (Tok.isNot(tok::eof)) { + if (!isAtInputEnd(Tok)) { PP.Diag(PragmaLocation, diag::warn_pragma_extra_tokens_at_eol) << PragmaName; return false; @@ -1596,7 +1596,7 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { if (Toks.size() > 2) { Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) << PragmaLoopHintString(Info->PragmaName, Info->Option); - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); } @@ -1631,10 +1631,10 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { // Tokens following an error in an ill-formed constant expression will // remain in the token stream and must be removed. - if (Tok.isNot(tok::eof)) { + if (!isAtInputEnd(Tok)) { Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) << PragmaLoopHintString(Info->PragmaName, Info->Option); - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); } @@ -1657,10 +1657,10 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { // Tokens following an error in an ill-formed constant expression will // remain in the token stream and must be removed. - if (Tok.isNot(tok::eof)) { + if (!isAtInputEnd(Tok)) { Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) << PragmaLoopHintString(Info->PragmaName, Info->Option); - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); } @@ -2113,7 +2113,7 @@ void Parser::HandlePragmaAttribute() { // Tokens following an ill-formed attribute will remain in the token stream // and must be removed. - if (Tok.isNot(tok::eof)) { + if (!isAtInputEnd(Tok)) { Diag(Tok, diag::err_pragma_attribute_extra_tokens_after_attribute); SkipToEnd(); return; @@ -2823,7 +2823,14 @@ void PragmaSupportHandler<StartTok, EndTok, UnexpectedDiag>::HandlePragma( Tok.setKind(StartTok); Tok.setLocation(Introducer.Loc); - while (Tok.isNot(tok::eod) && Tok.isNot(tok::eof)) { + auto IsAtInputEnd = [&PP](const Token &T) { + if (T.is(tok::eof)) + return true; + return PP.getLangOpts().IncrementalExtensions && + T.is(tok::annot_repl_input_end); + }; + + while (Tok.isNot(tok::eod) && !IsAtInputEnd(Tok)) { Pragma.push_back(Tok); PP.Lex(Tok); if (Tok.is(StartTok)) { diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 1f0f927053e51..2a3906fd5ca5e 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1159,7 +1159,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { bool LastIsError = false; while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::eof) && Tok.isNot(tok::annot_repl_input_end)) { + !isAtInputEnd(Tok)) { if (Tok.is(tok::annot_pragma_unused)) { HandlePragmaUnused(); continue; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index be9e0a39a3781..4f5aed482e552 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -310,7 +310,7 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) { if (Toks.size() == 1 && Toks[0] == tok::eof && !HasFlagsSet(Flags, StopAtSemi) && !HasFlagsSet(Flags, StopAtCodeCompletion)) { - while (Tok.isNot(tok::eof)) + while (!isAtInputEnd(Tok)) ConsumeAnyToken(); return true; } diff --git a/clang/test/Interpreter/repl-input-end-recovery.cpp b/clang/test/Interpreter/repl-input-end-recovery.cpp new file mode 100644 index 0000000000000..a06cfad85a0c8 --- /dev/null +++ b/clang/test/Interpreter/repl-input-end-recovery.cpp @@ -0,0 +1,22 @@ +// REQUIRES: host-supports-jit +// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -verify -Xcc -Xclang -Xcc -verify-ignore-unexpected | FileCheck %s + +extern "C" int printf(const char *, ...); + +void foo() { int x = 5; // expected-error {{expected '}'}} +int g1 = 0; void foo() { g1 = 5; } foo(); printf("g1 = %d\n", g1); +// CHECK: g1 = 5 + +void (*test)() = [](){ if } // expected-error {{expected '(' after 'if'}} +int g2 = 0; void (*test)() = [](){ if (1) g2 = 7; }; test(); printf("g2 = %d\n", g2); +// CHECK: g2 = 7 + +namespace myspace { // expected-error {{expected '}'}} +namespace myspace { int v = 11; } printf("v = %d\n", myspace::v); +// CHECK: v = 11 + +struct X { using type = int }; // expected-error {{expected ';' after alias declaration}} +struct X { using type = int; }; X::type t = 3; printf("t = %d\n", t); +// CHECK: t = 3 + +%quit \ No newline at end of file >From 91975acd8e74ded2a98ca5b060d606368b83203d Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Wed, 11 Feb 2026 15:50:58 +0530 Subject: [PATCH 3/3] Use a static function --- clang/include/clang/Parse/Parser.h | 16 +++++++++------- clang/lib/Parse/ParsePragma.cpp | 10 ++-------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 27fba6161eecb..182a5f368a549 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -519,6 +519,14 @@ class Parser : public CodeCompletionHandler { bool SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags = static_cast<SkipUntilFlags>(0)); + /// Determine if the given token marks the end of the current partial translation unit. + /// In incremental (REPL) mode, this checks for annot_repl_input_end. + /// In normal compilation, this checks for EOF. + static bool isAtInputEnd(const Token &T, const LangOptions &LO) { + return T.is(tok::eof) || + (LO.IncrementalExtensions && T.is(tok::annot_repl_input_end)); + } + private: Preprocessor &PP; @@ -721,14 +729,8 @@ class Parser : public CodeCompletionHandler { Kind == tok::annot_repl_input_end; } - /// Determine if the given token marks the end of the current parsing unit. - /// In incremental (REPL) mode, this checks for annot_repl_input_end. - /// In normal compilation, this checks for EOF. bool isAtInputEnd(const Token &T) const { - if (T.is(tok::eof)) - return true; - return getLangOpts().IncrementalExtensions && - T.is(tok::annot_repl_input_end); + return isAtInputEnd(T, getLangOpts()); } static void setTypeAnnotation(Token &Tok, TypeResult T) { diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 21dbd7adaa034..2119a2935684c 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -2823,14 +2823,8 @@ void PragmaSupportHandler<StartTok, EndTok, UnexpectedDiag>::HandlePragma( Tok.setKind(StartTok); Tok.setLocation(Introducer.Loc); - auto IsAtInputEnd = [&PP](const Token &T) { - if (T.is(tok::eof)) - return true; - return PP.getLangOpts().IncrementalExtensions && - T.is(tok::annot_repl_input_end); - }; - - while (Tok.isNot(tok::eod) && !IsAtInputEnd(Tok)) { + while (Tok.isNot(tok::eod) && + !Parser::isAtInputEnd(Tok, PP.getLangOpts())) { Pragma.push_back(Tok); PP.Lex(Tok); if (Tok.is(StartTok)) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
