[clang] [clang][OpenMP] Fix code completion hang in var-list clauses (PR #234586)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 22 04:24:46 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Shivvv (ShivaGanesh265)
<details>
<summary>Changes</summary>
## Report
Fixes #<!-- -->233392, The issue was, Code completion inside OpenMP variable-list clauses were hanging in an infinite loop when the parser encountered an EOF.
Added EOF termination checks to the needed parsing loops in `ParseOpenMP.cpp` and added a regression test in `GH233392.cpp`.
## Testing
- `llvm-lit -v clang/test/CodeCompletion/GH233392.cpp` — **Passed**
- **Before the fix:** Original Clang reproducer timed out (exit code 124).
- **After the fix:** Code completion terminated successfully (exit code 0) and returned the expected completion result.
- Also investigated clangd. Its broad completion scan hit an assertion in both baseline and patched builds, while targeted LSP completion succeeded in both.
GenAI(Chatgpt) Assisted with understanding of the source code and in helping with edge case tests.
---
Full diff: https://github.com/llvm/llvm-project/pull/234586.diff
2 Files Affected:
- (modified) clang/lib/Parse/ParseOpenMP.cpp (+15-6)
- (added) clang/test/CodeCompletion/GH233392.cpp (+127)
``````````diff
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 95c26f35fff03a..de495716b19fe1 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -193,7 +193,8 @@ Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
return DeclGroupPtrTy();
SmallVector<std::pair<QualType, SourceLocation>, 8> ReductionTypes;
- // Parse list of types until ':' token.
+ // Parse list of types until ':' token. Stop at eof if code completion cut
+ // off parsing to avoid an infinite loop.
do {
ColonProtectionRAIIObject ColonRAII(*this);
SourceRange Range;
@@ -221,7 +222,7 @@ Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
return DeclGroupPtrTy();
}
}
- } while (Tok.isNot(tok::annot_pragma_openmp_end));
+ } while (Tok.isNot(tok::annot_pragma_openmp_end) && Tok.isNot(tok::eof));
if (ReductionTypes.empty()) {
SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
@@ -2963,8 +2964,10 @@ bool Parser::ParseOpenMPSimpleVarList(
bool IsCorrect = true;
bool NoIdentIsFound = true;
- // Read tokens while ')' or annot_pragma_openmp_end is not found.
- while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // Read tokens while ')' or annot_pragma_openmp_end is not found. Stop at
+ // eof if code completion cut off parsing to avoid an infinite loop.
+ while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end) &&
+ Tok.isNot(tok::eof)) {
CXXScopeSpec SS;
UnqualifiedId Name;
// Read var name.
@@ -5471,8 +5474,12 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
(Kind == OMPC_adjust_args &&
Data.ExtraModifier != OMPC_ADJUST_ARGS_unknown);
const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned);
+ // If code completion cut off parsing, the current token is morphed into
+ // eof; the loop body makes no progress then, so exit to avoid an infinite
+ // loop.
while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) &&
- Tok.isNot(tok::annot_pragma_openmp_end))) {
+ Tok.isNot(tok::annot_pragma_openmp_end) &&
+ Tok.isNot(tok::eof))) {
ParseScope OMPListScope(this, Scope::OpenMPDirectiveScope);
ColonProtectionRAIIObject ColonRAII(*this, MayHaveTail);
if (!ParseOpenMPReservedLocator(Kind, Data, getLangOpts())) {
@@ -5515,7 +5522,9 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
if (getLangOpts().OpenMP >= 52 && Kind == OMPC_linear) {
bool Malformed = false;
- while (Tok.isNot(tok::r_paren)) {
+ // Stop at eof if code completion cut off parsing to avoid an infinite
+ // loop.
+ while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) {
if (Tok.is(tok::identifier)) {
// identifier could be a linear kind (val, uval, ref) or step
// modifier or step size
diff --git a/clang/test/CodeCompletion/GH233392.cpp b/clang/test/CodeCompletion/GH233392.cpp
new file mode 100644
index 00000000000000..b1a6014f6b297d
--- /dev/null
+++ b/clang/test/CodeCompletion/GH233392.cpp
@@ -0,0 +1,127 @@
+// Check that code completion inside OpenMP variable-list clauses terminates.
+// https://github.com/llvm/llvm-project/issues/233392
+
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/reduction.cpp:3:39 %t/reduction.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/reduction.cpp:3:40 %t/reduction.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/reduction.cpp:3:41 %t/reduction.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/logical_reduction.cpp:3:41 %t/logical_reduction.cpp | FileCheck -check-prefix=CHECK-X %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/empty_reduction.cpp:3:41 %t/empty_reduction.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/private.cpp:3:34 %t/private.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/private_variable.cpp:3:34 %t/private_variable.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/shared.cpp:3:33 %t/shared.cpp | FileCheck -check-prefix=CHECK-X %s
+// RUN: %clang_cc1 -fsyntax-only -x c -fopenmp -code-completion-at=%t/reduction.c:3:40 %t/reduction.c | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp-simd -code-completion-at=%t/simd.cpp:3:32 %t/simd.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp-simd -code-completion-at=%t/reduction.cpp:3:40 %t/reduction.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/if.cpp:3:29 %t/if.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/num_threads.cpp:3:38 %t/num_threads.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/schedule.cpp:3:35 %t/schedule.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/reduction.cpp:4:32 %t/reduction.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%t/no_openmp.cpp:3:40 %t/no_openmp.cpp
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -fopenmp-version=52 -code-completion-at=%t/linear.cpp:3:42 %t/linear.cpp | FileCheck -check-prefix=CHECK-S %s
+// RUN: not %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/threadprivate.cpp:2:27 %t/threadprivate.cpp | FileCheck -check-prefix=CHECK-THREADPRIVATE %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -code-completion-at=%t/declare_reduction.cpp:1:35 %t/declare_reduction.cpp | FileCheck -check-prefix=CHECK-DECLRED %s
+
+//--- reduction.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for reduction(+ : s)
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- logical_reduction.cpp
+void f() {
+ int x = 0;
+#pragma omp parallel for reduction(&& : x)
+ for (int i = 0; i < 10; ++i) x += i;
+}
+
+//--- empty_reduction.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for reduction(&& : )
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- private.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for private()
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- private_variable.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for private(s)
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- shared.cpp
+void f() {
+ int x = 0;
+#pragma omp parallel for shared(x)
+ for (int i = 0; i < 10; ++i) x += i;
+}
+
+//--- reduction.c
+void f() {
+ int s = 0;
+#pragma omp parallel for reduction(+ : s)
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- simd.cpp
+void f() {
+ int s = 0;
+#pragma omp simd reduction(+ : s)
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- if.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for if()
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- num_threads.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for num_threads()
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- schedule.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for schedule()
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- no_openmp.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for reduction(+ : s)
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- linear.cpp
+void f() {
+ int s = 0;
+#pragma omp parallel for linear(s : step(2))
+ for (int i = 0; i < 10; ++i) s += i;
+}
+
+//--- threadprivate.cpp
+int tp;
+#pragma omp threadprivate(tp)
+
+//--- declare_reduction.cpp
+#pragma omp declare reduction(+ : int : omp_out = omp_in)
+void bar();
+
+// CHECK-S: COMPLETION: s : [#int#]s
+// CHECK-X: COMPLETION: x : [#int#]x
+// CHECK-THREADPRIVATE: COMPLETION: Pattern : alignas(<#expression#>)
+// CHECK-DECLRED: COMPLETION: int
``````````
</details>
https://github.com/llvm/llvm-project/pull/234586
More information about the cfe-commits
mailing list