diff --git a/clang/include/clang/Sema/TypoCorrection.h b/clang/include/clang/Sema/TypoCorrection.h index 1d780c45efd55..0c882d3f796fe 100644 --- a/clang/include/clang/Sema/TypoCorrection.h +++ b/clang/include/clang/Sema/TypoCorrection.h @@ -350,6 +350,34 @@ class CorrectionCandidateCallback { NestedNameSpecifier TypoNNS; }; +/// Callback class to reject typo corrections that look like template parameters +/// when doing a qualified lookup. A template parameter (type or template) is +/// local to the current template scope and cannot be validly qualified by any +/// external scope (e.g. 'std::T' where T is a template parameter). +class QualifiedLookupValidatorCCC : public CorrectionCandidateCallback { +public: + explicit QualifiedLookupValidatorCCC(bool HasQualifier) + : HasQualifier(HasQualifier) {} + + bool ValidateCandidate(const TypoCorrection &Candidate) override { + if (HasQualifier) { + if (const NamedDecl *ND = Candidate.getCorrectionDecl()) { + // A template parameter can never be a member of any qualifier scope. + if (isa(ND) || isa(ND)) + return false; + } + } + return CorrectionCandidateCallback::ValidateCandidate(Candidate); + } + + std::unique_ptr clone() override { + return std::make_unique(*this); + } + +private: + bool HasQualifier; +}; + class DefaultFilterCCC final : public CorrectionCandidateCallback { public: explicit DefaultFilterCCC(const IdentifierInfo *Typo = nullptr, diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index a4a25a4f44602..9641395b68ae5 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -19,6 +19,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Template.h" +#include "clang/Sema/TypoCorrection.h" #include "llvm/ADT/STLExtras.h" using namespace clang; @@ -379,18 +380,17 @@ namespace { // Callback to only accept typo corrections that can be a valid C++ member // initializer: either a non-static field member or a base class. class NestedNameSpecifierValidatorCCC final - : public CorrectionCandidateCallback { + : public QualifiedLookupValidatorCCC { public: explicit NestedNameSpecifierValidatorCCC(Sema &SRef, bool HasQualifier) - : SRef(SRef), HasQualifier(HasQualifier) {} + : QualifiedLookupValidatorCCC(HasQualifier), SRef(SRef) {} bool ValidateCandidate(const TypoCorrection &candidate) override { + if (!QualifiedLookupValidatorCCC::ValidateCandidate(candidate)) + return false; const NamedDecl *ND = candidate.getCorrectionDecl(); if (!SRef.isAcceptableNestedNameSpecifier(ND)) return false; - // A template type parameter cannot have a nested name specifier. - if (HasQualifier && isa(ND)) - return false; return true; } @@ -399,10 +399,8 @@ class NestedNameSpecifierValidatorCCC final } private: - Sema &SRef; - bool HasQualifier; + Sema &SRef; }; - } [[nodiscard]] static bool ExtendNestedNameSpecifier(Sema &S, CXXScopeSpec &SS, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5d23660585ea5..aa72cb8fa2895 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -519,8 +519,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, // to correct any typos. DeclarationName Name = Found.getLookupName(); Found.clear(); - // Simple filter callback that, for keywords, only accepts the C++ *_cast - DefaultFilterCCC FilterCCC{}; + QualifiedLookupValidatorCCC FilterCCC(!SS.isEmpty()); FilterCCC.WantTypeSpecifiers = false; FilterCCC.WantExpressionKeywords = false; FilterCCC.WantRemainingKeywords = false; diff --git a/clang/test/SemaTemplate/gh183983.cpp b/clang/test/SemaTemplate/gh183983.cpp new file mode 100644 index 0000000000000..368176290dba1 --- /dev/null +++ b/clang/test/SemaTemplate/gh183983.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// expected-error@10 {{template template parameter requires 'class' or 'typename' after the parameter list}} +// expected-error@10 {{template template parameter must have its own template parameters}} +// expected-error@10 {{no template named 'a' in the global namespace}} +// expected-note@10 {{to match this '<'}} +// expected-error@10 {{expected expression}} +// expected-error@10 {{expected '>'}} +// expected-error@10 {{expected unqualified-id}} +template