diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3fe26f950ad51..da52f682d30a8 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; + /// 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> DiagnosedIncompleteTypeSet; + /// 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 ab83f625d2849..05a0c7cae6d66 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -209,10 +209,22 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS, 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(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())) { SS.SetInvalid(SS.getRange()); + return true; } 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@-2{{forward declaration of 'incomplete'}} + + // RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify -fblocks %s namespace A { struct C {