[clang] [Sema] Diagnose use of if/else-if condition variable inside else-if/else branch(s) (PR #156436)

via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 4 03:23:05 PDT 2025


https://github.com/arrowten updated https://github.com/llvm/llvm-project/pull/156436

>From e5ded955c4005ab52dd75c87550034fe34f6f479 Mon Sep 17 00:00:00 2001
From: arrowten <ajaywakodikarsocial at gmail.com>
Date: Tue, 2 Sep 2025 15:33:52 +0530
Subject: [PATCH] [Sema] Diagnose use of if/else-if condition variable inside
 else-if/else branch(s)

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +
 clang/lib/Sema/SemaStmt.cpp                   | 29 ++++++++
 clang/test/Sema/warn-conditional-scope.cpp    | 74 +++++++++++++++++++
 3 files changed, 106 insertions(+)
 create mode 100644 clang/test/Sema/warn-conditional-scope.cpp

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c934fed2c7462..daabb3cd188d8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13594,6 +13594,9 @@ def warn_acc_var_referenced_lacks_op
               "reference has no effect">,
       InGroup<DiagGroup<"openacc-var-lacks-operation">>,
       DefaultError;
+def warn_out_of_scope_var_usage
+    : Warning<"variable %0 declared in 'if' block is either false or null">,
+      InGroup<DiagGroup<"conditional-scope">>;
 
 // AMDGCN builtins diagnostics
 def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 5625fb359807a..93795db87427e 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -971,6 +971,35 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
   if (!ConstevalOrNegatedConsteval && !elseStmt)
     DiagnoseEmptyStmtBody(RParenLoc, thenStmt, diag::warn_empty_if_body);
 
+  // Checks for if condition variable usage in else scope
+  if (elseStmt) {
+    if (auto* CondVar = dyn_cast_or_null<VarDecl>(Cond.get().first)) {
+      bool usedInElse = false;
+      std::function<bool(Stmt*)> checkForUsage = [&](Stmt *S) -> bool {
+	if (!S) return false;
+	if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) {
+	  if (DRE->getDecl() == CondVar) {
+	    return true;
+	  }
+	}
+
+	for (Stmt *Child: S->children()) {
+	  if (checkForUsage(Child)) {
+	    return true;
+	  }
+	}
+
+	return false;
+      };
+      usedInElse = checkForUsage(elseStmt);
+
+      if (usedInElse) {
+	Diag(elseStmt->getBeginLoc(), diag::warn_out_of_scope_var_usage)
+	  << CondVar->getName();
+      }
+    }
+  }
+
   if (ConstevalOrNegatedConsteval ||
       StatementKind == IfStatementKind::Constexpr) {
     auto DiagnoseLikelihood = [&](const Stmt *S) {
diff --git a/clang/test/Sema/warn-conditional-scope.cpp b/clang/test/Sema/warn-conditional-scope.cpp
new file mode 100644
index 0000000000000..b4b4e2e163c2f
--- /dev/null
+++ b/clang/test/Sema/warn-conditional-scope.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconditional-scope -DTEST_1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconditional-scope -DTEST_2 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconditional-scope -DTEST_3 %s
+
+int *get_something();
+int *get_something_else();
+int *get_something_else_again();
+int *get_something_else_again_now();
+
+#ifdef TEST_1
+
+int test() {
+  if (int *ptr = get_something()) {
+    return ptr[0] * ptr[0];
+  }
+  // expected-warning at +1{{variable ptr declared in 'if' block is either false or null}}
+  else if (int *ptr2 = get_something_else()) {
+    return ptr[0] * ptr2[0];
+  }
+  // expected-warning at +1{{variable ptr2 declared in 'if' block is either false or null}}
+  else if (int* ptr3 = get_something_else_again()) {
+    return ptr[0] * ptr2[0] * ptr3[0];	
+  }
+  // expected-warning at +1{{variable ptr3 declared in 'if' block is either false or null}}
+  else if (int *ptr4 = get_something_else_again_now()) {
+    return ptr[0] * ptr2[0] * ptr3[0] * ptr4[0];
+  }
+  else {
+    return -1;
+  }
+}
+
+#endif
+
+#ifdef TEST_2
+
+int test() {
+  if (int *ptr = get_something()) {
+    return ptr[0] * ptr[0];
+  }
+  // expected-warning at +1{{variable ptr declared in 'if' block is either false or null}}
+  else if (int *ptr2 = get_something_else()) {
+    return ptr2[0] * ptr2[0];
+  }
+  // expected-warning at +1{{variable ptr2 declared in 'if' block is either false or null}}
+  else if (int* ptr3 = get_something_else_again()) {
+    return ptr[0] * ptr2[0] * ptr3[0];
+  }
+  else {
+    return -1;
+  }
+}
+
+#endif
+
+#ifdef TEST_3
+
+int test() {
+  if (int *ptr = get_something()) {
+    return ptr[0] * ptr[0];
+  }
+  else if (int *ptr2 = get_something_else()) {
+    return ptr2[0] * ptr2[0];
+  }
+  // expected-warning at +1{{variable ptr2 declared in 'if' block is either false or null}}
+  else if (int* ptr3 = get_something_else_again()) {
+    return ptr2[0] * ptr2[0] * ptr3[0];
+  }
+  else {
+    return -1;
+  }
+}
+
+#endif



More information about the cfe-commits mailing list