Skip to content

Conversation

spavloff
Copy link
Collaborator

Anaysis for noreturn attribute now misses the case when an assignment to a variable is made inside a lambda function call. This change fixes that case.

Anaysis for noreturn attribute now misses the case when an assignment to
a variable is made inside a lambda function call. This change fixes that
case.
@spavloff spavloff self-assigned this Aug 25, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 25, 2025

@llvm/pr-subscribers-clang

Author: Serge Pavlov (spavloff)

Changes

Anaysis for noreturn attribute now misses the case when an assignment to a variable is made inside a lambda function call. This change fixes that case.


Full diff: https://github.com/llvm/llvm-project/pull/155213.diff

2 Files Affected:

  • (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+35)
  • (modified) clang/test/SemaCXX/noreturn-vars.cpp (+13)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 0b94b1044f072..fe1498003e7da 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -467,6 +467,41 @@ struct TransferFunctions : public StmtVisitor<TransferFunctions> {
               AllValuesAreNoReturn = false;
     }
   }
+
+  void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *CE) {
+    if (CE->getOperator() == OO_Call && CE->getNumArgs() > 0) {
+      Expr *Obj = CE->getArg(0)->IgnoreParenCasts();
+      if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Obj))
+        Obj = MTE->getSubExpr();
+      if (auto *DRE = dyn_cast<DeclRefExpr>(Obj)) {
+        auto *D = dyn_cast<VarDecl>(DRE->getDecl());
+        if (D->hasInit())
+          Obj = D->getInit();
+      }
+      Visit(Obj);
+    }
+  }
+
+  void VisitLambdaExpr(LambdaExpr *LE) {
+    for (const LambdaCapture &Capture : LE->captures())
+      if (Capture.capturesVariable())
+        if (const VarDecl *VD = dyn_cast<VarDecl>(Capture.getCapturedVar()))
+          if (VD == Var)
+            if (Capture.getCaptureKind() == LCK_ByRef)
+              AllValuesAreNoReturn = false;
+  }
+
+  void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE) {
+    Visit(MTE->getSubExpr());
+  }
+
+  void VisitExprWithCleanups(FullExpr *FE) {
+    Visit(FE->getSubExpr());
+  }
+
+  void VisitCXXConstructExpr(CXXConstructExpr *CE) {
+    Visit(CE->getArg(0));
+  }
 };
 } // namespace
 
diff --git a/clang/test/SemaCXX/noreturn-vars.cpp b/clang/test/SemaCXX/noreturn-vars.cpp
index ca65fcf5ca31d..3b2473c1bc670 100644
--- a/clang/test/SemaCXX/noreturn-vars.cpp
+++ b/clang/test/SemaCXX/noreturn-vars.cpp
@@ -225,3 +225,16 @@ extern void abc_02(func_type *);
   abc_02(&func_ptr);
   func_ptr();
 } // expected-warning {{function declared 'noreturn' should not return}}
+
+[[noreturn]] void test_lambda() {
+  func_type func_ptr = noret;
+  [&func_ptr]() { func_ptr = ordinary; } ();
+  func_ptr();
+} // expected-warning {{function declared 'noreturn' should not return}}
+
+[[noreturn]] void test_lambda_var(int x) {
+  func_type func_ptr = noret;
+  auto LF = [&](){func_ptr = ordinary;};
+  LF();
+  func_ptr();
+} // expected-warning {{function declared 'noreturn' should not return}}

Copy link

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- clang/lib/Sema/AnalysisBasedWarnings.cpp clang/test/SemaCXX/noreturn-vars.cpp
View the diff from clang-format here.
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index fe1498003..e92536b56 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -495,13 +495,9 @@ struct TransferFunctions : public StmtVisitor<TransferFunctions> {
     Visit(MTE->getSubExpr());
   }
 
-  void VisitExprWithCleanups(FullExpr *FE) {
-    Visit(FE->getSubExpr());
-  }
+  void VisitExprWithCleanups(FullExpr *FE) { Visit(FE->getSubExpr()); }
 
-  void VisitCXXConstructExpr(CXXConstructExpr *CE) {
-    Visit(CE->getArg(0));
-  }
+  void VisitCXXConstructExpr(CXXConstructExpr *CE) { Visit(CE->getArg(0)); }
 };
 } // namespace
 


[[noreturn]] void test_lambda_var(int x) {
func_type func_ptr = noret;
auto LF = [&](){func_ptr = ordinary;};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to see a test for member functions here as well. Also, test that shows capture-by-value doesn't cause this diagnostic.

}

void VisitCXXConstructExpr(CXXConstructExpr *CE) {
Visit(CE->getArg(0));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right at all. the 'this' argument in a CXXConstructExpr does NOT get placed in the 0 arg (in fact, they are not present), so you can't assume 0 exists here. AND it isn't really clear why the 0 arg is important here, but others arent?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants