diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 4a9e1bc93b918..ba91b25fc1843 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8421,7 +8421,7 @@ bool Sema::TemplateParameterListsAreEqual( return true; } -bool +bool Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { if (!S) return false; @@ -8445,33 +8445,49 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { } Ctx = Ctx ? Ctx->getRedeclContext() : nullptr; - // C++ [temp]p2: - // A template-declaration can appear only as a namespace scope or - // class scope declaration. - // C++ [temp.expl.spec]p3: - // An explicit specialization may be declared in any scope in which the - // corresponding primary template may be defined. - // C++ [temp.class.spec]p6: [P2096] - // A partial specialization may be declared in any scope in which the - // corresponding primary template may be defined. + // Compute a SourceLocation to use for diagnostics. Prefer the explicit + // template location, but fall back to nearby Decl locations when needed. + SourceLocation Loc = TemplateParams->getTemplateLoc(); + if (Loc.isInvalid()) + Loc = TemplateParams->getSourceRange().getBegin(); + + if (Loc.isInvalid() && Ctx) { + if (const Decl *D = dyn_cast(Ctx)) + Loc = D->getBeginLoc(); + } + + // Try to extract class context if present. + CXXRecordDecl *RD = Ctx ? dyn_cast(Ctx) : nullptr; + if (Loc.isInvalid() && RD) + Loc = RD->getLocation(); + if (Ctx) { if (Ctx->isFileContext()) return false; - if (CXXRecordDecl *RD = dyn_cast(Ctx)) { + + if (RD) { // C++ [temp.mem]p2: // A local class shall not have member templates. - if (RD->isLocalClass()) - return Diag(TemplateParams->getTemplateLoc(), - diag::err_template_inside_local_class) - << TemplateParams->getSourceRange(); - else + if (RD->isLocalClass()) { + // when the template location is not valid we are trying to use fallback SourceLocation such that diagnostic prints a usable file:line:col location + if (Loc.isInvalid()) + Loc = TemplateParams->getSourceRange().getBegin(); + + return Diag(Loc, diag::err_template_inside_local_class) + << TemplateParams->getSourceRange(); + } + else { return false; + } } } - return Diag(TemplateParams->getTemplateLoc(), - diag::err_template_outside_namespace_or_class_scope) - << TemplateParams->getSourceRange(); + // when teplate declared outside the namspace or class scope it Fallbacks and it give valid SourceLocation with file:line info. + if (Loc.isInvalid()) + Loc = TemplateParams->getSourceRange().getBegin(); + + return Diag(Loc, diag::err_template_outside_namespace_or_class_scope) + << TemplateParams->getSourceRange(); } /// Determine what kind of template specialization the given declaration diff --git a/clang/test/SemaCXX/lambda-local-c++17.cpp b/clang/test/SemaCXX/lambda-local-c++17.cpp new file mode 100644 index 0000000000000..0d23849fa5cab --- /dev/null +++ b/clang/test/SemaCXX/lambda-local-c++17.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int main() { + auto L = []() { + struct LocalInLambda { + void qux(auto x) {} // expected-error {{'auto' not allowed in function prototype}} + }; + (void)sizeof(LocalInLambda); + }; + L(); +} \ No newline at end of file diff --git a/clang/test/SemaCXX/lambda-local-c++20.cpp b/clang/test/SemaCXX/lambda-local-c++20.cpp new file mode 100644 index 0000000000000..1fc82d928a6b3 --- /dev/null +++ b/clang/test/SemaCXX/lambda-local-c++20.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s + +int main() { + auto L = []() { + struct LocalInLambda { // expected-error {{templates cannot be declared inside of a local class}} + void qux(auto x) {} + }; + (void)sizeof(LocalInLambda); + }; + L(); +} diff --git a/clang/test/SemaCXX/nested-local.cpp b/clang/test/SemaCXX/nested-local.cpp new file mode 100644 index 0000000000000..e776979b4582b --- /dev/null +++ b/clang/test/SemaCXX/nested-local.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s + +void fn() { + struct Outer { + struct Inner { + void foo(auto x) {} // expected-error {{'auto' not allowed in function prototype}} + }; + }; +} diff --git a/clang/test/SemaCXX/static-local.cpp b/clang/test/SemaCXX/static-local.cpp new file mode 100644 index 0000000000000..7c8d378e0e247 --- /dev/null +++ b/clang/test/SemaCXX/static-local.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s + +int main() { + static struct StaticLocal { // expected-error {{templates cannot be declared inside of a local class}} + void bar(auto x) {} + } s; + (void)s; +} diff --git a/clang/test/SemaCXX/template-member-local-c++17.cpp b/clang/test/SemaCXX/template-member-local-c++17.cpp new file mode 100644 index 0000000000000..237d400118888 --- /dev/null +++ b/clang/test/SemaCXX/template-member-local-c++17.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +struct Outer { + void member() { + struct Local { + void baz(auto x) {} // expected-error {{'auto' not allowed in function prototype}} + }; + (void)sizeof(Local); + } +}; + +int main() { + Outer o; + o.member(); +} diff --git a/clang/test/SemaCXX/template-member-local-c++20.cpp b/clang/test/SemaCXX/template-member-local-c++20.cpp new file mode 100644 index 0000000000000..86ade46eebbf4 --- /dev/null +++ b/clang/test/SemaCXX/template-member-local-c++20.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s + +template +struct Outer { + void member() { + struct Local { // expected-error {{templates cannot be declared inside of a local class}} + void baz(auto x) {} + }; + (void)sizeof(Local); + } +}; + +int main() { + Outer o; + o.member(); +}