[clang] [Clang][Sema] Avoid duplicate diagnostics for incomplete types in nested name specifier (C++20+) (PR #147036)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 4 09:51:23 PDT 2025
https://github.com/zhy-tju updated https://github.com/llvm/llvm-project/pull/147036
>From 4b098e63f1c1aa8bb4c7d4d1966a22c236a9ff17 Mon Sep 17 00:00:00 2001
From: zhy <2697737506 at qq.com>
Date: Fri, 4 Jul 2025 16:50:03 +0800
Subject: [PATCH 1/4] [Clang] Duplicate diagnostics in C++20+ mode
---
clang/include/clang/Sema/DeclSpec.h | 10 ++++++++++
clang/lib/Sema/SemaCXXScopeSpec.cpp | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 567ad2d5934d4..8721b836002e2 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -76,6 +76,10 @@ class CXXScopeSpec {
NestedNameSpecifierLocBuilder Builder;
ArrayRef<TemplateParameterList *> TemplateParamLists;
+ /// Flag indicating whether an incomplete-type diagnostic
+ /// has already been emitted for this scope specifier.
+ bool HadIncompleteTypeError = false;
+
public:
SourceRange getRange() const { return Range; }
void setRange(SourceRange R) { Range = R; }
@@ -83,6 +87,12 @@ class CXXScopeSpec {
void setEndLoc(SourceLocation Loc) { Range.setEnd(Loc); }
SourceLocation getBeginLoc() const { return Range.getBegin(); }
SourceLocation getEndLoc() const { return Range.getEnd(); }
+
+ /// Return true if an incomplete-type diagnostic has already been emitted.
+ bool hasIncompleteTypeError() const { return HadIncompleteTypeError; }
+
+ /// Mark that an incomplete-type error was emitted for this scope.
+ void setIncompleteTypeError(bool v = true) { HadIncompleteTypeError = v; }
void setTemplateParamLists(ArrayRef<TemplateParameterList *> L) {
TemplateParamLists = L;
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index ab83f625d2849..a95d2e83768e2 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -209,10 +209,20 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
SourceLocation loc = SS.getLastQualifierNameLoc();
if (loc.isInvalid()) loc = SS.getRange().getBegin();
+ // If an incomplete-type error has already been emitted for this scope,
+ // suppress duplicate diagnostics to avoid noisy repeated messages.
+ if (SS.hasIncompleteTypeError())
+ return true;
+
// The type must be complete.
if (RequireCompleteType(loc, type, diag::err_incomplete_nested_name_spec,
SS.getRange())) {
SS.SetInvalid(SS.getRange());
+
+ // Remember that we've already diagnosed this incomplete type,
+ // so later checks won't emit redundant diagnostics.
+ SS.setIncompleteTypeError();
+
return true;
}
>From 7a111a6b45f43b84be4a33d20763fd002e501deb Mon Sep 17 00:00:00 2001
From: zhy <2697737506 at qq.com>
Date: Fri, 4 Jul 2025 17:19:50 +0800
Subject: [PATCH 2/4] [Clang] Duplicate diagnostics in C++20+ mode
---
clang/test/SemaCXX/nested-name-spec.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clang/test/SemaCXX/nested-name-spec.cpp b/clang/test/SemaCXX/nested-name-spec.cpp
index abeaba9d8dde2..df82d7a8dcf70 100644
--- a/clang/test/SemaCXX/nested-name-spec.cpp
+++ b/clang/test/SemaCXX/nested-name-spec.cpp
@@ -1,3 +1,10 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+struct incomplete;
+incomplete::type var; // expected-error{{incomplete type 'incomplete' named in nested name specifier}}
+// expected-note at -2{{forward declaration of 'incomplete'}}
+
+
// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify -fblocks %s
namespace A {
struct C {
>From 9d650488a66ae47878ca5d13c3353da7bb4dac38 Mon Sep 17 00:00:00 2001
From: zhy <2697737506 at qq.com>
Date: Fri, 4 Jul 2025 18:27:28 +0800
Subject: [PATCH 3/4] [Clang] Duplicate diagnostics
---
clang/include/clang/Sema/DeclSpec.h | 10 ----------
clang/include/clang/Sema/Sema.h | 6 ++++++
clang/lib/Sema/SemaCXXScopeSpec.cpp | 17 ++++++++---------
3 files changed, 14 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 8721b836002e2..567ad2d5934d4 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -76,10 +76,6 @@ class CXXScopeSpec {
NestedNameSpecifierLocBuilder Builder;
ArrayRef<TemplateParameterList *> TemplateParamLists;
- /// Flag indicating whether an incomplete-type diagnostic
- /// has already been emitted for this scope specifier.
- bool HadIncompleteTypeError = false;
-
public:
SourceRange getRange() const { return Range; }
void setRange(SourceRange R) { Range = R; }
@@ -87,12 +83,6 @@ class CXXScopeSpec {
void setEndLoc(SourceLocation Loc) { Range.setEnd(Loc); }
SourceLocation getBeginLoc() const { return Range.getBegin(); }
SourceLocation getEndLoc() const { return Range.getEnd(); }
-
- /// Return true if an incomplete-type diagnostic has already been emitted.
- bool hasIncompleteTypeError() const { return HadIncompleteTypeError; }
-
- /// Mark that an incomplete-type error was emitted for this scope.
- void setIncompleteTypeError(bool v = true) { HadIncompleteTypeError = v; }
void setTemplateParamLists(ArrayRef<TemplateParameterList *> L) {
TemplateParamLists = L;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3fe26f950ad51..1c7a67d32cf72 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1555,6 +1555,12 @@ class Sema final : public SemaBase {
Sema(const Sema &) = delete;
void operator=(const Sema &) = delete;
+ /// Used to suppress duplicate diagnostics for incomplete types
+ /// in nested name specifiers (e.g. `incomplete::type`).
+ /// Without this, Clang may emit the same error multiple times
+ /// in C++20 or later, due to multiple semantic passes over the scope.
+ llvm::DenseSet<const TagDecl *> IncompleteDiagSet;
+
/// The handler for the FileChanged preprocessor events.
///
/// Used for diagnostics that implement custom semantic analysis for #include
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index a95d2e83768e2..8731f3cbbb8cd 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -206,22 +206,21 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
if (tag->isBeingDefined())
return false;
+ // Avoid emitting duplicate diagnostics for the same tag.
+ // This happens in C++20+ due to more aggressive semantic analysis.
+ if (IncompleteDiagSet.contains(tag))
+ return true;
+
SourceLocation loc = SS.getLastQualifierNameLoc();
if (loc.isInvalid()) loc = SS.getRange().getBegin();
- // If an incomplete-type error has already been emitted for this scope,
- // suppress duplicate diagnostics to avoid noisy repeated messages.
- if (SS.hasIncompleteTypeError())
- return true;
-
// The type must be complete.
if (RequireCompleteType(loc, type, diag::err_incomplete_nested_name_spec,
SS.getRange())) {
- SS.SetInvalid(SS.getRange());
+ // mark as diagnosed
+ IncompleteDiagSet.insert(tag);
- // Remember that we've already diagnosed this incomplete type,
- // so later checks won't emit redundant diagnostics.
- SS.setIncompleteTypeError();
+ SS.SetInvalid(SS.getRange());
return true;
}
>From cdfecb9998ec0e26f556926d0179333b6b75f8ba Mon Sep 17 00:00:00 2001
From: zhy <2697737506 at qq.com>
Date: Sat, 5 Jul 2025 00:50:45 +0800
Subject: [PATCH 4/4] new_change
---
clang/include/clang/Sema/Sema.h | 10 +++++-----
clang/lib/Sema/SemaCXXScopeSpec.cpp | 19 +++++++++++--------
2 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1c7a67d32cf72..da52f682d30a8 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1555,11 +1555,11 @@ class Sema final : public SemaBase {
Sema(const Sema &) = delete;
void operator=(const Sema &) = delete;
- /// Used to suppress duplicate diagnostics for incomplete types
- /// in nested name specifiers (e.g. `incomplete::type`).
- /// Without this, Clang may emit the same error multiple times
- /// in C++20 or later, due to multiple semantic passes over the scope.
- llvm::DenseSet<const TagDecl *> IncompleteDiagSet;
+ /// Tracks (TagDecl, SourceLocation) pairs that have already triggered
+ /// an "incomplete type in nested name specifier" diagnostic,
+ /// to prevent emitting duplicate errors in C++20 and later,
+ /// where the same scope may be processed multiple times.
+ llvm::DenseSet<std::pair<const clang::TagDecl *, clang::SourceLocation>> DiagnosedIncompleteTypeSet;
/// The handler for the FileChanged preprocessor events.
///
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 8731f3cbbb8cd..05a0c7cae6d66 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -206,20 +206,23 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
if (tag->isBeingDefined())
return false;
- // Avoid emitting duplicate diagnostics for the same tag.
- // This happens in C++20+ due to more aggressive semantic analysis.
- if (IncompleteDiagSet.contains(tag))
- return true;
-
SourceLocation loc = SS.getLastQualifierNameLoc();
if (loc.isInvalid()) loc = SS.getRange().getBegin();
+ // If the DeclContext is a tag declaration, form a unique key
+ // from the TagDecl and the source location where the scope starts.
+ // If this key has already been diagnosed, skip emitting the error again.
+ const TagDecl *Tag = dyn_cast_or_null<TagDecl>(DC);
+ if (Tag) {
+ auto Key = std::make_pair(Tag, SS.getBeginLoc());
+ if (!DiagnosedIncompleteTypeSet.insert(Key).second)
+ return true; // Already diagnosed
+ }
+
+
// The type must be complete.
if (RequireCompleteType(loc, type, diag::err_incomplete_nested_name_spec,
SS.getRange())) {
- // mark as diagnosed
- IncompleteDiagSet.insert(tag);
-
SS.SetInvalid(SS.getRange());
return true;
More information about the cfe-commits
mailing list