https://github.com/zyn0217 created 
https://github.com/llvm/llvm-project/pull/136295

It doesn't make sense that we only build a RecoveryExpr for expressions with 
invalid trailing commas. This patch extends it so that we now always build up a 
RecoveryExpr whenever the call contains anything invalid. As a result, we can 
back out HasTrailingComma.

There is only one diagnostic change as to concepts, where a RecoveryExpr than 
an ExprError is now used to model an invalud requires clause, for which we 
suggest adding parentheses around it. (This looks like what GCC diagnoses: 
https://gcc.godbolt.org/z/GT4TzbqTq)

>From 8efadc35d64f2724d5610a7ae66fa70a7c8e3d46 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 18 Apr 2025 19:20:49 +0800
Subject: [PATCH] [Clang] Improve error recovery for invalid calls

It doesn't make sense that we only build a RecoveryExpr for expressions
with invalid trailing commas. This patch extends it so that we now
always build up a RecoveryExpr whenever the call contains anything
invalid. As a result, we can back out HasTrailingComma.

There is only one diagnostic change as to concepts, where a
RecoveryExpr than an ExprError is used to model an invalud requires
clause, for which we now suggest adding parenthesis around it.
---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 clang/include/clang/Parse/Parser.h            |  3 +-
 clang/lib/Parse/ParseExpr.cpp                 | 31 +++++++------------
 clang/test/AST/ast-dump-recovery.cpp          | 11 ++++---
 clang/test/AST/new-unknown-type.cpp           |  8 ++++-
 .../Parser/cxx-concepts-requires-clause.cpp   |  3 +-
 6 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f5cd1fbeabcfe..613b79ca2c3d8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -395,6 +395,8 @@ Improvements to Clang's diagnostics
   constructors to initialize their non-modifiable members. The diagnostic is
   not new; being controlled via a warning group is what's new. Fixes #GH41104
 
+- Improved Clang's error recovery for invalid function calls.
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 662f54d0e8d8a..40dbe23434d04 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1942,8 +1942,7 @@ class Parser : public CodeCompletionHandler {
                            llvm::function_ref<void()> ExpressionStarts =
                                llvm::function_ref<void()>(),
                            bool FailImmediatelyOnInvalidExpr = false,
-                           bool EarlyTypoCorrection = false,
-                           bool *HasTrailingComma = nullptr);
+                           bool EarlyTypoCorrection = false);
 
   /// ParseSimpleExpressionList - A simple comma-separated list of expressions,
   /// used for misc language extensions.
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 3e3e7cbcd68b2..df015564849da 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2218,19 +2218,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
         CalledSignatureHelp = true;
         return PreferredType;
       };
+      bool KnownInvalidCall = false;
       if (OpKind == tok::l_paren || !LHS.isInvalid()) {
         if (Tok.isNot(tok::r_paren)) {
-          bool HasTrailingComma = false;
-          bool HasError = ParseExpressionList(
-              ArgExprs,
-              [&] {
-                PreferredType.enterFunctionArgument(Tok.getLocation(),
-                                                    RunSignatureHelp);
-              },
-              /*FailImmediatelyOnInvalidExpr*/ false,
-              /*EarlyTypoCorrection*/ false, &HasTrailingComma);
-
-          if (HasError && !HasTrailingComma) {
+          if ((KnownInvalidCall = ParseExpressionList(ArgExprs, [&] {
+                 PreferredType.enterFunctionArgument(Tok.getLocation(),
+                                                     RunSignatureHelp);
+               }))) {
             (void)Actions.CorrectDelayedTyposInExpr(LHS);
             // If we got an error when parsing expression list, we don't call
             // the CodeCompleteCall handler inside the parser. So call it here
@@ -2238,7 +2232,6 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
             // middle of a parameter.
             if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
               RunSignatureHelp();
-            LHS = ExprError();
           } else if (LHS.isInvalid()) {
             for (auto &E : ArgExprs)
               Actions.CorrectDelayedTyposInExpr(E);
@@ -2249,6 +2242,12 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
       // Match the ')'.
       if (LHS.isInvalid()) {
         SkipUntil(tok::r_paren, StopAtSemi);
+      } else if (KnownInvalidCall) {
+        Expr *Fn = LHS.get();
+        ArgExprs.insert(ArgExprs.begin(), Fn);
+        LHS = Actions.CreateRecoveryExpr(Fn->getBeginLoc(), Tok.getLocation(),
+                                         ArgExprs);
+        SkipUntil(tok::r_paren, StopAtSemi);
       } else if (Tok.isNot(tok::r_paren)) {
         bool HadDelayedTypo = false;
         if (Actions.CorrectDelayedTyposInExpr(LHS).get() != LHS.get())
@@ -3700,8 +3699,7 @@ void Parser::injectEmbedTokens() {
 bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
                                  llvm::function_ref<void()> ExpressionStarts,
                                  bool FailImmediatelyOnInvalidExpr,
-                                 bool EarlyTypoCorrection,
-                                 bool *HasTrailingComma) {
+                                 bool EarlyTypoCorrection) {
   bool SawError = false;
   while (true) {
     if (ExpressionStarts)
@@ -3744,11 +3742,6 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> 
&Exprs,
     Token Comma = Tok;
     ConsumeToken();
     checkPotentialAngleBracketDelimiter(Comma);
-
-    if (Tok.is(tok::r_paren)) {
-      if (HasTrailingComma)
-        *HasTrailingComma = true;
-    }
   }
   if (SawError) {
     // Ensure typos get diagnosed when errors were encountered while parsing 
the
diff --git a/clang/test/AST/ast-dump-recovery.cpp 
b/clang/test/AST/ast-dump-recovery.cpp
index b59fa3778192f..b8195950f2fa1 100644
--- a/clang/test/AST/ast-dump-recovery.cpp
+++ b/clang/test/AST/ast-dump-recovery.cpp
@@ -34,21 +34,22 @@ void test_invalid_call_1(int s) {
 
 int some_func2(int a, int b);
 void test_invalid_call_2() {
-  // CHECK:   -RecoveryExpr {{.*}} 'int' contains-errors
+  // CHECK:   -RecoveryExpr {{.*}} '<dependent type>' contains-errors
   // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} '<overloaded function type>' 
lvalue (ADL) = 'some_func2'
   some_func2(,);
 
-  // CHECK:   -RecoveryExpr {{.*}} 'int' contains-errors
+  // CHECK:   -RecoveryExpr {{.*}} '<dependent type>' contains-errors
   // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} '<overloaded function type>' 
lvalue (ADL) = 'some_func2'
   some_func2(,,);
 
-  // CHECK:   `-RecoveryExpr {{.*}} 'int' contains-errors
+  // CHECK:   -RecoveryExpr {{.*}} '<dependent type>' contains-errors
   // CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} '<overloaded function type>' 
lvalue (ADL) = 'some_func2'
   // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
   some_func2(1,);
 
-  // FIXME: Handle invalid argument with recovery
-  // CHECK-NOT: `-RecoveryExpr
+  // CHECK:   -RecoveryExpr {{.*}} '<dependent type>' contains-errors
+  // CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} '<overloaded function type>' 
lvalue (ADL) = 'some_func2'
+  // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
   some_func2(,1);
 }
 
diff --git a/clang/test/AST/new-unknown-type.cpp 
b/clang/test/AST/new-unknown-type.cpp
index e7dd3c90145d2..9d6b19acba739 100644
--- a/clang/test/AST/new-unknown-type.cpp
+++ b/clang/test/AST/new-unknown-type.cpp
@@ -17,9 +17,15 @@ namespace b {
 // CHECK:      |-NamespaceDecl 0x{{[^ ]*}} <line:5:1, line:11:1> line:5:11 a
 // CHECK-NEXT: | `-FunctionDecl 0x{{[^ ]*}} <line:6:3, line:10:3> line:6:8 
computeSomething 'void ()'
 // CHECK-NEXT: |   `-CompoundStmt 0x{{[^ ]*}} <col:27, line:10:3>
+// CHECK-NEXT: |   |-RecoveryExpr {{.*}} '<dependent type>' contains-errors
+// CHECK-NEXT: |   | `-UnresolvedLookupExpr {{.*}} '<overloaded function 
type>' lvalue (ADL) = 'foo'
+// CHECK-NEXT: |   |-RecoveryExpr {{.*}} '<dependent type>' contains-errors
+// CHECK-NEXT: |   | `-UnresolvedLookupExpr {{.*}} '<overloaded function 
type>' lvalue (ADL) = 'foo'
+// CHECK-NEXT: |   `-RecoveryExpr {{.*}} '<dependent type>' contains-errors
+// CHECK-NEXT: |    `-UnresolvedLookupExpr {{.*}} '<overloaded function type>' 
lvalue (ADL) = 'foo'
 // CHECK-NEXT: |-NamespaceDecl 0x{{[^ ]*}} <line:13:1, line:15:1> line:13:11 b
 // CHECK-NEXT: | `-CXXRecordDecl 0x{{[^ ]*}} <line:14:3, col:14> col:10 
referenced struct Bar definition
 
 static b::Bar bar;
-// CHECK:      `-VarDecl 0x{{[^ ]*}} <line:23:1, col:15> col:15 bar 'b::Bar' 
static callinit
+// CHECK:      `-VarDecl 0x{{[^ ]*}} <line:29:1, col:15> col:15 bar 'b::Bar' 
static callinit
 // CHECK-NEXT:   `-CXXConstructExpr 0x{{[^ ]*}} <col:15> 'b::Bar' 'void () 
noexcept'
diff --git a/clang/test/Parser/cxx-concepts-requires-clause.cpp 
b/clang/test/Parser/cxx-concepts-requires-clause.cpp
index 13b4e8439882d..b921be5dbfd6d 100644
--- a/clang/test/Parser/cxx-concepts-requires-clause.cpp
+++ b/clang/test/Parser/cxx-concepts-requires-clause.cpp
@@ -119,7 +119,8 @@ template<typename T> requires 0
 
 template<typename T> requires foo<T>
 (int) bar() { };
-// expected-error@-1{{expected '(' for function-style cast or type 
construction}}
+// expected-error@-1{{expected '(' for function-style cast or type 
construction}} \
+// expected-error@-2{{parentheses are required around this expression in a 
requires clause}}
 
 template<typename T>
 void bar() requires foo<T>();

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to