Skip to content

Conversation

@saideepaksana
Copy link

@saideepaksana saideepaksana commented Nov 9, 2025

Fixes #147324
Changes:

  • Updated CheckTemplateDeclScope in SemaTemplate.cpp to correctly detect abbreviated function templates and compute accurate source locations.

Tests Added:

  • template-local-class.cpp – templates and auto members inside local class
  • template-local-class-fn.cpp -
  • template-abbrev-local-class.cpp – abbreviated function templates across standards
  • template-nested-local-classes.cpp – nested local class templates
  • abbrev-top-level-fn.cpp – top-level functions with auto parameters

Result:
It will now show correct file, line, and column numbers, and handle auto parameter templates correctly

@github-actions
Copy link

github-actions bot commented Nov 9, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 9, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 9, 2025

@llvm/pr-subscribers-clang

Author: Sai Deepak Sana (saideepaksana)

Changes

Fixes #147324

Summary:
Fixes missing source location (filename, line, column) in template diagnostics when templates or abbreviated function templates (auto parameters) are declared inside local classes.

Changes:

  • Updated CheckTemplateDeclScope in SemaTemplate.cpp to correctly detect abbreviated function templates and compute accurate source locations.
  • Improved diagnostic messages for templates inside local classes, extern "C", and invalid scopes.
  • Added new test cases for C++17, C++20, and C++23.

Tests Added:

  • template-local-class.cpp – templates and auto members inside local class
  • template-local-class-fn.cpp -
  • template-abbrev-local-class.cpp – abbreviated function templates across standards
  • template-nested-local-classes.cpp – nested local class templates
  • abbrev-top-level-fn.cpp – top-level functions with auto parameters

Result:
Diagnostics now show correct file, line, and column numbers, and handle auto parameter templates accurately.


Full diff: https://github.com/llvm/llvm-project/pull/167201.diff

6 Files Affected:

  • (modified) clang/lib/Sema/SemaTemplate.cpp (+77-57)
  • (added) clang/test/SemaCXX/abbrev-top-level-fn.cpp (+5)
  • (added) clang/test/SemaCXX/template-abbrev-local-class.cpp (+12)
  • (added) clang/test/SemaCXX/template-local-class-fn.cpp (+12)
  • (added) clang/test/SemaCXX/template-local-class.cpp (+11)
  • (added) clang/test/SemaCXX/template-nested-local-classes.cpp (+17)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4a9e1bc93b918..e601b7cb11007 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -318,7 +318,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
     }
   }
 
-  if (isPackProducingBuiltinTemplateName(Template) && S &&
+  if (isPackProducingBuiltinTemplateName(Template) &&
       S->getTemplateParamParent() == nullptr)
     Diag(Name.getBeginLoc(), diag::err_builtin_pack_outside_template) << TName;
   // Recover by returning the template, even though we would never be able to
@@ -408,7 +408,9 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
     IsDependent = !LookupCtx && ObjectType->isDependentType();
     assert((IsDependent || !ObjectType->isIncompleteType() ||
             !ObjectType->getAs<TagType>() ||
-            ObjectType->castAs<TagType>()->getDecl()->isEntityBeingDefined()) &&
+            ObjectType->castAs<TagType>()
+                ->getOriginalDecl()
+                ->isEntityBeingDefined()) &&
            "Caller should have completed object type");
 
     // Template names cannot appear inside an Objective-C class or object type
@@ -949,11 +951,11 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
 
   switch (Arg.getKind()) {
   case ParsedTemplateArgument::Type: {
-    TypeSourceInfo *TSI;
-    QualType T = SemaRef.GetTypeFromParser(Arg.getAsType(), &TSI);
-    if (!TSI)
-      TSI = SemaRef.Context.getTrivialTypeSourceInfo(T, Arg.getNameLoc());
-    return TemplateArgumentLoc(TemplateArgument(T), TSI);
+    TypeSourceInfo *DI;
+    QualType T = SemaRef.GetTypeFromParser(Arg.getAsType(), &DI);
+    if (!DI)
+      DI = SemaRef.Context.getTrivialTypeSourceInfo(T, Arg.getNameLoc());
+    return TemplateArgumentLoc(TemplateArgument(T), DI);
   }
 
   case ParsedTemplateArgument::NonType: {
@@ -1817,7 +1819,7 @@ class ConstraintRefersToContainingTemplateChecker
   }
 
   bool VisitTagType(const TagType *T) override {
-    return TraverseDecl(T->getDecl());
+    return TraverseDecl(T->getOriginalDecl());
   }
 
   bool TraverseDecl(const Decl *D) override {
@@ -2788,7 +2790,7 @@ struct DependencyChecker : DynamicRecursiveASTVisitor {
     // An InjectedClassNameType will never have a dependent template name,
     // so no need to traverse it.
     return TraverseTemplateArguments(
-        T->getTemplateArgs(T->getDecl()->getASTContext()));
+        T->getTemplateArgs(T->getOriginalDecl()->getASTContext()));
   }
 };
 } // end anonymous namespace
@@ -2912,7 +2914,7 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier(
     if (const EnumType *EnumT = T->getAsCanonical<EnumType>()) {
       // FIXME: Forward-declared enums require a TSK_ExplicitSpecialization
       // check here.
-      EnumDecl *Enum = EnumT->getDecl();
+      EnumDecl *Enum = EnumT->getOriginalDecl();
 
       // Get to the parent type.
       if (TypeDecl *Parent = dyn_cast<TypeDecl>(Enum->getParent()))
@@ -3350,7 +3352,7 @@ static QualType builtinCommonTypeImpl(Sema &S, ElaboratedTypeKeyword Keyword,
 }
 
 static bool isInVkNamespace(const RecordType *RT) {
-  DeclContext *DC = RT->getDecl()->getDeclContext();
+  DeclContext *DC = RT->getOriginalDecl()->getDeclContext();
   if (!DC)
     return false;
 
@@ -3367,8 +3369,9 @@ static SpirvOperand checkHLSLSpirvTypeOperand(Sema &SemaRef,
   if (auto *RT = OperandArg->getAsCanonical<RecordType>()) {
     bool Literal = false;
     SourceLocation LiteralLoc;
-    if (isInVkNamespace(RT) && RT->getDecl()->getName() == "Literal") {
-      auto SpecDecl = dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
+    if (isInVkNamespace(RT) && RT->getOriginalDecl()->getName() == "Literal") {
+      auto SpecDecl =
+          dyn_cast<ClassTemplateSpecializationDecl>(RT->getOriginalDecl());
       assert(SpecDecl);
 
       const TemplateArgumentList &LiteralArgs = SpecDecl->getTemplateArgs();
@@ -3379,8 +3382,9 @@ static SpirvOperand checkHLSLSpirvTypeOperand(Sema &SemaRef,
     }
 
     if (RT && isInVkNamespace(RT) &&
-        RT->getDecl()->getName() == "integral_constant") {
-      auto SpecDecl = dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
+        RT->getOriginalDecl()->getName() == "integral_constant") {
+      auto SpecDecl =
+          dyn_cast<ClassTemplateSpecializationDecl>(RT->getOriginalDecl());
       assert(SpecDecl);
 
       const TemplateArgumentList &ConstantArgs = SpecDecl->getTemplateArgs();
@@ -3846,14 +3850,13 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
       // within enable_if in a SFINAE context, dig out the specific
       // enable_if condition that failed and present that instead.
       if (isEnableIfAliasTemplate(AliasTemplate)) {
-        if (SFINAETrap *Trap = getSFINAEContext();
-            TemplateDeductionInfo *DeductionInfo =
-                Trap ? Trap->getDeductionInfo() : nullptr) {
-          if (DeductionInfo->hasSFINAEDiagnostic() &&
-              DeductionInfo->peekSFINAEDiagnostic().second.getDiagID() ==
-                  diag::err_typename_nested_not_found_enable_if &&
-              TemplateArgs[0].getArgument().getKind() ==
-                  TemplateArgument::Expression) {
+        if (auto DeductionInfo = isSFINAEContext()) {
+          if (*DeductionInfo &&
+              (*DeductionInfo)->hasSFINAEDiagnostic() &&
+              (*DeductionInfo)->peekSFINAEDiagnostic().second.getDiagID() ==
+                diag::err_typename_nested_not_found_enable_if &&
+              TemplateArgs[0].getArgument().getKind()
+                == TemplateArgument::Expression) {
             Expr *FailedCond;
             std::string FailedDescription;
             std::tie(FailedCond, FailedDescription) =
@@ -3862,14 +3865,15 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
             // Remove the old SFINAE diagnostic.
             PartialDiagnosticAt OldDiag =
               {SourceLocation(), PartialDiagnostic::NullDiagnostic()};
-            DeductionInfo->takeSFINAEDiagnostic(OldDiag);
+            (*DeductionInfo)->takeSFINAEDiagnostic(OldDiag);
 
             // Add a new SFINAE diagnostic specifying which condition
             // failed.
-            DeductionInfo->addSFINAEDiagnostic(
-                OldDiag.first,
-                PDiag(diag::err_typename_nested_not_found_requirement)
-                    << FailedDescription << FailedCond->getSourceRange());
+            (*DeductionInfo)->addSFINAEDiagnostic(
+              OldDiag.first,
+              PDiag(diag::err_typename_nested_not_found_requirement)
+                << FailedDescription
+                << FailedCond->getSourceRange());
           }
         }
       }
@@ -3955,7 +3959,6 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
 
     if (Decl->getSpecializationKind() == TSK_Undeclared &&
         ClassTemplate->getTemplatedDecl()->hasAttrs()) {
-      NonSFINAEContext _(*this);
       InstantiatingTemplate Inst(*this, TemplateLoc, Decl);
       if (!Inst.isInvalid()) {
         MultiLevelTemplateArgumentList TemplateArgLists(Template,
@@ -4107,7 +4110,7 @@ TypeResult Sema::ActOnTagTemplateIdType(TagUseKind TUK,
 
   // Check the tag kind
   if (const RecordType *RT = Result->getAs<RecordType>()) {
-    RecordDecl *D = RT->getDecl();
+    RecordDecl *D = RT->getOriginalDecl();
 
     IdentifierInfo *Id = D->getIdentifier();
     assert(Id && "templated class must have an identifier");
@@ -4330,7 +4333,7 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) {
 }
 
 DeclResult Sema::ActOnVarTemplateSpecialization(
-    Scope *S, Declarator &D, TypeSourceInfo *TSI, LookupResult &Previous,
+    Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
     SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
     StorageClass SC, bool IsPartialSpecialization) {
   // D must be variable template id.
@@ -4456,8 +4459,8 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
     VarTemplatePartialSpecializationDecl *Partial =
         VarTemplatePartialSpecializationDecl::Create(
             Context, VarTemplate->getDeclContext(), TemplateKWLoc,
-            TemplateNameLoc, TemplateParams, VarTemplate, TSI->getType(), TSI,
-            SC, CTAI.CanonicalConverted);
+            TemplateNameLoc, TemplateParams, VarTemplate, DI->getType(), DI, SC,
+            CTAI.CanonicalConverted);
     Partial->setTemplateArgsAsWritten(TemplateArgs);
 
     if (!PrevPartial)
@@ -4475,7 +4478,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
     // this explicit specialization or friend declaration.
     Specialization = VarTemplateSpecializationDecl::Create(
         Context, VarTemplate->getDeclContext(), TemplateKWLoc, TemplateNameLoc,
-        VarTemplate, TSI->getType(), TSI, SC, CTAI.CanonicalConverted);
+        VarTemplate, DI->getType(), DI, SC, CTAI.CanonicalConverted);
     Specialization->setTemplateArgsAsWritten(TemplateArgs);
 
     if (!PrevDecl)
@@ -5566,11 +5569,12 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
 
     auto checkExpr = [&](Expr *E) -> Expr * {
       TemplateArgument SugaredResult, CanonicalResult;
+      unsigned CurSFINAEErrors = NumSFINAEErrors;
       ExprResult Res = CheckTemplateArgument(
           NTTP, NTTPType, E, SugaredResult, CanonicalResult,
           /*StrictCheck=*/CTAI.MatchingTTP || CTAI.PartialOrdering, CTAK);
       // If the current template argument causes an error, give up now.
-      if (Res.isInvalid())
+      if (Res.isInvalid() || CurSFINAEErrors < NumSFINAEErrors)
         return nullptr;
       CTAI.SugaredConverted.push_back(SugaredResult);
       CTAI.CanonicalConverted.push_back(CanonicalResult);
@@ -6379,11 +6383,11 @@ bool UnnamedLocalNoLinkageFinder::VisitDeducedTemplateSpecializationType(
 }
 
 bool UnnamedLocalNoLinkageFinder::VisitRecordType(const RecordType* T) {
-  return VisitTagDecl(T->getDecl()->getDefinitionOrSelf());
+  return VisitTagDecl(T->getOriginalDecl()->getDefinitionOrSelf());
 }
 
 bool UnnamedLocalNoLinkageFinder::VisitEnumType(const EnumType* T) {
-  return VisitTagDecl(T->getDecl()->getDefinitionOrSelf());
+  return VisitTagDecl(T->getOriginalDecl()->getDefinitionOrSelf());
 }
 
 bool UnnamedLocalNoLinkageFinder::VisitTemplateTypeParmType(
@@ -6408,7 +6412,7 @@ bool UnnamedLocalNoLinkageFinder::VisitTemplateSpecializationType(
 
 bool UnnamedLocalNoLinkageFinder::VisitInjectedClassNameType(
                                               const InjectedClassNameType* T) {
-  return VisitTagDecl(T->getDecl()->getDefinitionOrSelf());
+  return VisitTagDecl(T->getOriginalDecl()->getDefinitionOrSelf());
 }
 
 bool UnnamedLocalNoLinkageFinder::VisitDependentNameType(
@@ -8421,7 +8425,7 @@ bool Sema::TemplateParameterListsAreEqual(
   return true;
 }
 
-bool
+bool 
 Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
   if (!S)
     return false;
@@ -8445,33 +8449,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<Decl>(Ctx))
+      Loc = D->getBeginLoc();
+  }
+
+  // Try to extract class context if present.
+  CXXRecordDecl *RD = Ctx ? dyn_cast<CXXRecordDecl>(Ctx) : nullptr;
+  if (Loc.isInvalid() && RD)
+    Loc = RD->getLocation();
+
   if (Ctx) {
     if (Ctx->isFileContext())
       return false;
-    if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(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/abbrev-top-level-fn.cpp b/clang/test/SemaCXX/abbrev-top-level-fn.cpp
new file mode 100644
index 0000000000000..3f8bbb51e47f9
--- /dev/null
+++ b/clang/test/SemaCXX/abbrev-top-level-fn.cpp
@@ -0,0 +1,5 @@
+// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s
+
+void top_fun(auto x) { }
+// CHECK17: {{.+}}:[[@LINE-1]]:14: error: 'auto' not allowed in function prototype
\ No newline at end of file
diff --git a/clang/test/SemaCXX/template-abbrev-local-class.cpp b/clang/test/SemaCXX/template-abbrev-local-class.cpp
new file mode 100644
index 0000000000000..b8d3ac16a59d1
--- /dev/null
+++ b/clang/test/SemaCXX/template-abbrev-local-class.cpp
@@ -0,0 +1,12 @@
+// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK20
+// RUN: not %clang_cc1 -std=c++23 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK23
+// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
+
+int main() {
+  struct A {
+    void foo(auto x) {}
+// CHECK20: {{.+}}:[[@LINE-2]]:3: error: templates cannot be declared inside of a local class
+// CHECK23: {{.+}}:[[@LINE-3]]:3: error: templates cannot be declared inside of a local class
+// CHECK17: {{.+}}:[[@LINE-3]]:14: error: 'auto' not allowed in function prototype
+  };
+}
\ No newline at end of file
diff --git a/clang/test/SemaCXX/template-local-class-fn.cpp b/clang/test/SemaCXX/template-local-class-fn.cpp
new file mode 100644
index 0000000000000..de565ef12e4fc
--- /dev/null
+++ b/clang/test/SemaCXX/template-local-class-fn.cpp
@@ -0,0 +1,12 @@
+// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
+// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK20
+// RUN: not %clang_cc1 -std=c++23 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK23
+
+int main() {
+  struct Local {
+// CHECK20: {{.+}}:[[@LINE-1]]:3: error: templates cannot be declared inside of a local class
+// CHECK23: {{.+}}:[[@LINE-2]]:3: error: templates cannot be declared inside of a local class
+    void mem(auto x) {}
+// CHECK17: {{.+}}:[[@LINE-1]]:14: error: 'auto' not allowed in function prototype
+  };
+}
\ No newline at end of file
diff --git a/clang/test/SemaCXX/template-local-class.cpp b/clang/test/SemaCXX/template-local-class.cpp
new file mode 100644
index 0000000000000..65e3207af4f42
--- /dev/null
+++ b/clang/test/SemaCXX/template-local-class.cpp
@@ -0,0 +1,11 @@
+// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 | FileCheck %s
+
+int main() {
+  struct S {
+    auto L = [](auto x) { return x; };
+// CHECK: {{.+}}:[[@LINE-1]]:5: error: 'auto' not allowed in non-static struct member
+    template<typename T> void memb(T);
+// CHECK: {{.+}}:[[@LINE-1]]:5: error: templates cannot be declared inside of a local class
+  };
+  return 0;
+}
\ No newline at end of file
diff --git a/clang/test/SemaCXX/template-nested-local-classes.cpp b/clang/test/SemaCXX/template-nested-local-classes.cpp
new file mode 100644
index 0000000000000..94b895cfcfa9e
--- /dev/null
+++ b/clang/test/SemaCXX/template-nested-local-classes.cpp
@@ -0,0 +1,17 @@
+// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK20
+// RUN: not %clang_cc1 -std=c++17 -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK17
+
+int outer() {
+  struct Outer {
+    void f() {
+      struct Inner {
+        template<typename T> void m(T);
+// CHECK20: {{.+}}:[[@LINE-1]]:9: error: templates cannot be declared inside of a local class
+// CHECK20: {{.+}}:[[@LINE-3]]:7: error: templates cannot be declared inside of a local class
+        void bad(auto x) {}
+// CHECK17: {{.+}}:[[@LINE-1]]:18: error: 'auto' not allowed in function prototype
+      };
+    }
+  };
+  return 0;
+}
\ No newline at end of file

@saideepaksana
Copy link
Author

saideepaksana commented Nov 9, 2025

Hi Our main changes are in the file SemaTemplate.cpp and there are some changes from other people, which when we are trying to test, getting error's locally. So our main changes in the SemaTemplate.cpp are in the lines 8428 only, remaining changes are not from our side, they are the previous version of code present in the SemaTemplate.cpp from 3 days back

bool 
Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
  if (!S)
    return false;

  // Find the nearest enclosing declaration scope.
  S = S->getDeclParent();

  // C++ [temp.pre]p6: [P2096]
  //   A template, explicit specialization, or partial specialization shall not
  //   have C linkage.
  DeclContext *Ctx = S->getEntity();
  if (Ctx && Ctx->isExternCContext()) {
    SourceRange Range =
        TemplateParams->getTemplateLoc().isInvalid() && TemplateParams->size()
            ? TemplateParams->getParam(0)->getSourceRange()
            : TemplateParams->getSourceRange();
    Diag(Range.getBegin(), diag::err_template_linkage) << Range;
    if (const LinkageSpecDecl *LSD = Ctx->getExternCContext())
      Diag(LSD->getExternLoc(), diag::note_extern_c_begins_here);
    return true;
  }
  Ctx = Ctx ? Ctx->getRedeclContext() : nullptr;

  // 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<Decl>(Ctx))
      Loc = D->getBeginLoc();
  }

  // Try to extract class context if present.
  CXXRecordDecl *RD = Ctx ? dyn_cast<CXXRecordDecl>(Ctx) : nullptr;
  if (Loc.isInvalid() && RD)
    Loc = RD->getLocation();

  if (Ctx) {
    if (Ctx->isFileContext())
      return false;

    if (RD) {
      // C++ [temp.mem]p2:
      //   A local class shall not have member templates.
      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;
      }
    }
  }

  // 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();
}

Copy link
Contributor

@zwuis zwuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If AI was used to create PR, please disclose it. And to what extent it was used.

Please remove unrelated changes.

Please read https://clang.llvm.org/docs/InternalsManual.html#verifying-diagnostics.

Please remove duplicate test.

@saideepaksana saideepaksana deleted the fix/local-class-auto-diagnostic-147324 branch November 10, 2025 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clang: Missing line and file in message error when ‘auto’ is used in a not permitted context (and bad diagnostic)

3 participants