https://github.com/diohabara updated 
https://github.com/llvm/llvm-project/pull/187896

From 3349dabfc52cd6f440c3bc64ed5e453476d4dd47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?=
 <[email protected]>
Date: Sat, 21 Mar 2026 18:52:57 +0900
Subject: [PATCH] [clang][diagnostics] suggest extern template syntax for
 -Wundefined-var-template and -Wundefined-func-template

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 68 ++++++++++++++++++-
 ...antiation.diagnose_on_undefined_entity.cpp |  4 ++
 clang/test/SemaCXX/cxx1z-ast-print.cpp        |  4 +-
 .../instantiate-pure-virtual-function.cpp     |  6 +-
 .../test/SemaTemplate/undefined-template.cpp  | 13 ++++
 6 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d4d09a8ecef36..d8647f6d426c9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5954,6 +5954,8 @@ def note_unreachable_template_decl
 def note_inst_declaration_hint : Note<"add an explicit instantiation "
   "declaration to suppress this warning if %q0 is explicitly instantiated in "
   "another translation unit">;
+def note_inst_declaration_example : Note<
+  "e.g., '%0'">;
 def note_evaluating_exception_spec_here : Note<
   "in evaluation of exception specification for %q0 needed here">;
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index cc24e03e77c07..f221b180e2bdd 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5636,9 +5636,58 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
                diag::note_unreachable_template_decl);
         } else {
           Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
-          if (getLangOpts().CPlusPlus11)
+          if (getLangOpts().CPlusPlus11) {
             Diag(PointOfInstantiation, diag::note_inst_declaration_hint)
                 << Function;
+            // Suggest the exact extern template declaration syntax.
+            // Note: for functions with complex declarator return types
+            // (e.g., function pointers), the suggestion may not be
+            // perfectly formatted, but these cases are rare in practice.
+            std::string Suggestion;
+            {
+              llvm::raw_string_ostream OS(Suggestion);
+              OS << "extern template ";
+              // Constructors, destructors, and conversion operators have no
+              // separate return type in the declaration syntax.
+              if (!isa<CXXConstructorDecl>(Function) &&
+                  !isa<CXXDestructorDecl>(Function) &&
+                  !isa<CXXConversionDecl>(Function)) {
+                Function->getReturnType().print(OS, getPrintingPolicy());
+                OS << " ";
+              }
+              Function->getNameForDiagnostic(OS, getPrintingPolicy(),
+                                              /*Qualified=*/true);
+              OS << "(";
+              for (unsigned I = 0, N = Function->getNumParams(); I != N; ++I) {
+                if (I > 0)
+                  OS << ", ";
+                Function->getParamDecl(I)->getType().print(
+                    OS, getPrintingPolicy());
+              }
+              if (Function->isVariadic()) {
+                if (Function->getNumParams() > 0)
+                  OS << ", ";
+                OS << "...";
+              }
+              OS << ")";
+              if (const auto *MD = dyn_cast<CXXMethodDecl>(Function)) {
+                if (MD->getMethodQualifiers().hasConst())
+                  OS << " const";
+                if (MD->getMethodQualifiers().hasVolatile())
+                  OS << " volatile";
+                if (MD->getRefQualifier() == RQ_LValue)
+                  OS << " &";
+                else if (MD->getRefQualifier() == RQ_RValue)
+                  OS << " &&";
+              }
+              if (auto EST = Function->getExceptionSpecType();
+                  EST == EST_BasicNoexcept)
+                OS << " noexcept";
+              OS << ";";
+            }
+            Diag(PointOfInstantiation, diag::note_inst_declaration_example)
+                << Suggestion;
+          }
         }
       }
     }
@@ -6379,8 +6428,23 @@ void Sema::InstantiateVariableDefinition(SourceLocation 
PointOfInstantiation,
         Diag(PointOfInstantiation, diag::warn_var_template_missing)
           << Var;
         Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
-        if (getLangOpts().CPlusPlus11)
+        if (getLangOpts().CPlusPlus11) {
           Diag(PointOfInstantiation, diag::note_inst_declaration_hint) << Var;
+          // Suggest the exact extern template declaration syntax.
+          std::string Suggestion;
+          {
+            llvm::raw_string_ostream OS(Suggestion);
+            OS << "extern template ";
+            std::string QualName;
+            llvm::raw_string_ostream NameOS(QualName);
+            Var->getNameForDiagnostic(NameOS, getPrintingPolicy(),
+                                      /*Qualified=*/true);
+            Var->getType().print(OS, getPrintingPolicy(), QualName);
+            OS << ";";
+          }
+          Diag(PointOfInstantiation, diag::note_inst_declaration_example)
+              << Suggestion;
+        }
       }
       return;
     }
diff --git 
a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
 
b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
index 24e24221939fa..b79e5983fe6be 100644
--- 
a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
+++ 
b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -24,13 +24,17 @@ void use() {
 
   foo.non_static_member_function(); // expected-warning{{instantiation of 
function 'Foo<int>::non_static_member_function' required here, but no 
definition is available}}
   // expected-note@-1 {{add an explicit instantiation}}
+  // expected-note@-2 {{e.g., 'extern template}}
 
   Foo<int>::static_member_function(); // expected-warning{{instantiation of 
function 'Foo<int>::static_member_function' required here, but no definition is 
available}}
   // expected-note@-1 {{add an explicit instantiation}}
+  // expected-note@-2 {{e.g., 'extern template}}
 
   (void)Foo<int>::static_data_member; // expected-warning{{instantiation of 
variable 'Foo<int>::static_data_member' required here, but no definition is 
available}}
   // expected-note@-1 {{add an explicit instantiation}}
+  // expected-note@-2 {{e.g., 'extern template}}
 
   Foo<int>::nested::static_member_function(); // 
expected-warning{{instantiation of function 
'Foo<int>::nested::static_member_function' required here, but no definition is 
available}}
   // expected-note@-1 {{add an explicit instantiation}}
+  // expected-note@-2 {{e.g., 'extern template}}
 }
diff --git a/clang/test/SemaCXX/cxx1z-ast-print.cpp 
b/clang/test/SemaCXX/cxx1z-ast-print.cpp
index a6ba149e974ef..642b7ceff935e 100644
--- a/clang/test/SemaCXX/cxx1z-ast-print.cpp
+++ b/clang/test/SemaCXX/cxx1z-ast-print.cpp
@@ -7,5 +7,7 @@ struct TypeSuffix {
 // CHECK: int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>;
 int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>; // expected-warning 
{{instantiation of variable 'TypeSuffix::x<0>' required here, but no definition 
is available}} \
                                                  // expected-note {{add an 
explicit instantiation declaration to suppress this warning if 
'TypeSuffix::x<0>' is explicitly instantiated in another translation unit}} \
+                                                 // expected-note {{e.g., 
'extern template}} \
                                                  // expected-warning 
{{instantiation of variable 'TypeSuffix::y<0L>' required here, but no 
definition is available}} \
-                                                 // expected-note {{add an 
explicit instantiation declaration to suppress this warning if 
'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}}
+                                                 // expected-note {{add an 
explicit instantiation declaration to suppress this warning if 
'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}} \
+                                                 // expected-note {{e.g., 
'extern template}}
diff --git a/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp 
b/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
index caec42b6b77f9..548f7662b0af5 100644
--- a/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
+++ b/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
@@ -23,6 +23,7 @@ namespace call_pure_virtual_function_from_virtual {
   public:
     const void foo(const T &) { B::bar(1); } // expected-warning 
{{instantiation of function 
'call_pure_virtual_function_from_virtual::B<int>::bar' required here, but no 
definition is available}}
     // expected-note@-1 {{add an explicit instantiation declaration to 
suppress this warning if 'call_pure_virtual_function_from_virtual::B<int>::bar' 
is explicitly instantiated in another translation unit}}
+    // expected-note@-2 {{e.g., 'extern template}}
     virtual const void bar(unsigned int) = 0; // expected-note {{forward 
declaration of template entity is here}}
   };
 
@@ -54,7 +55,10 @@ namespace non_pure_virtual_function {
 // expected-note@-3 {{add an explicit instantiation declaration to suppress 
this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly 
instantiated in another translation unit}}
 // expected-note@-4 {{add an explicit instantiation declaration to suppress 
this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly 
instantiated in another translation unit}}
 // expected-note@-5 {{add an explicit instantiation declaration to suppress 
this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly 
instantiated in another translation unit}}
-// expected-note@-6 {{used here}}
+// expected-note@-6 {{e.g., 'extern template}}
+// expected-note@-7 {{e.g., 'extern template}}
+// expected-note@-8 {{e.g., 'extern template}}
+// expected-note@-9 {{used here}}
 
   public:
     constexpr void bar(unsigned int) override { }
diff --git a/clang/test/SemaTemplate/undefined-template.cpp 
b/clang/test/SemaTemplate/undefined-template.cpp
index 52530e2e3909a..c26ab6c8ff03a 100644
--- a/clang/test/SemaTemplate/undefined-template.cpp
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -37,16 +37,19 @@ char func_01() {
 char func_02() {
   return C1<int>::s_var_1; // expected-warning{{instantiation of variable 
'C1<int>::s_var_1' required here, but no definition is available}}
                            // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<int>::s_var_1' is explicitly 
instantiated in another translation unit}}
+                           // expected-note@-2{{e.g., 'extern template char 
C1<int>::s_var_1;'}}
 }
 
 char func_03() {
   return C1<char>::s_var_2; // expected-warning{{instantiation of variable 
'C1<char>::s_var_2' required here, but no definition is available}}
                             // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<char>::s_var_2' is explicitly 
instantiated in another translation unit}}
+                            // expected-note@-2{{e.g., 'extern template char 
C1<char>::s_var_2;'}}
 }
 
 void func_04() {
   C1<int>::s_func_1(); // expected-warning{{instantiation of function 
'C1<int>::s_func_1' required here, but no definition is available}}
                        // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<int>::s_func_1' is explicitly 
instantiated in another translation unit}}
+                       // expected-note@-2{{e.g., 'extern template void 
C1<int>::s_func_1();'}}
 }
 
 void func_05() {
@@ -56,11 +59,13 @@ void func_05() {
 void func_06() {
   C1<char>::s_func_2(); // expected-warning{{instantiation of function 
'C1<char>::s_func_2' required here, but no definition is available}}
                         // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<char>::s_func_2' is explicitly 
instantiated in another translation unit}}
+                        // expected-note@-2{{e.g., 'extern template void 
C1<char>::s_func_2();'}}
 }
 
 void func_07(C1<int> *x) {
   x->meth_1();  // expected-warning{{instantiation of function 
'C1<int>::meth_1' required here, but no definition is available}}
                 // expected-note@-1{{add an explicit instantiation declaration 
to suppress this warning if 'C1<int>::meth_1' is explicitly instantiated in 
another translation unit}}
+                // expected-note@-2{{e.g., 'extern template void 
C1<int>::meth_1();'}}
 }
 
 void func_08(C1<int> *x) {
@@ -70,6 +75,7 @@ void func_08(C1<int> *x) {
 void func_09(C1<char> *x) {
   x->meth_1();  // expected-warning{{instantiation of function 
'C1<char>::meth_1' required here, but no definition is available}}
                 // expected-note@-1{{add an explicit instantiation declaration 
to suppress this warning if 'C1<char>::meth_1' is explicitly instantiated in 
another translation unit}}
+                // expected-note@-2{{e.g., 'extern template void 
C1<char>::meth_1();'}}
 }
 
 char func_10() {
@@ -79,6 +85,7 @@ char func_10() {
 char func_11() {
   return C1<int>::s_tvar_2<long>; // expected-warning{{instantiation of 
variable 'C1<int>::s_tvar_2<long>' required here, but no definition is 
available}}
                                   // expected-note@-1{{add an explicit 
instantiation declaration to suppress this warning if 'C1<int>::s_tvar_2<long>' 
is explicitly instantiated in another translation unit}}
+                                  // expected-note@-2{{e.g., 'extern template 
char C1<int>::s_tvar_2<long>;'}}
 }
 
 void func_12() {
@@ -88,6 +95,7 @@ void func_12() {
 void func_13() {
   C1<int>::s_tfunc_2<long>(); // expected-warning{{instantiation of function 
'C1<int>::s_tfunc_2<long>' required here, but no definition is available}}
                               // expected-note@-1{{add an explicit 
instantiation declaration to suppress this warning if 
'C1<int>::s_tfunc_2<long>' is explicitly instantiated in another translation 
unit}}
+                              // expected-note@-2{{e.g., 'extern template void 
C1<int>::s_tfunc_2<long>();'}}
 }
 
 char func_14() {
@@ -97,6 +105,7 @@ char func_14() {
 char func_15() {
   return C1<int>::C2<char>::s_var_2;  //expected-warning {{instantiation of 
variable 'C1<int>::C2<char>::s_var_2' required here, but no definition is 
available}}
                                       // expected-note@-1{{add an explicit 
instantiation declaration to suppress this warning if 
'C1<int>::C2<char>::s_var_2' is explicitly instantiated in another translation 
unit}}
+                                      // expected-note@-2{{e.g., 'extern 
template char C1<int>::C2<char>::s_var_2;'}}
 }
 
 void func_16() {
@@ -106,6 +115,7 @@ void func_16() {
 void func_17() {
   C1<int>::C2<char>::s_func_2(); // expected-warning{{instantiation of 
function 'C1<int>::C2<char>::s_func_2' required here, but no definition is 
available}}
                         // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<int>::C2<char>::s_func_2' is 
explicitly instantiated in another translation unit}}
+                        // expected-note@-2{{e.g., 'extern template void 
C1<int>::C2<char>::s_func_2();'}}
 }
 
 void func_18(C1<int>::C2<long> *x) {
@@ -115,6 +125,7 @@ void func_18(C1<int>::C2<long> *x) {
 void func_19(C1<int>::C2<char> *x) {
   x->meth_2();   // expected-warning{{instantiation of function 
'C1<int>::C2<char>::meth_2' required here, but no definition is available}}
                         // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<int>::C2<char>::meth_2' is 
explicitly instantiated in another translation unit}}
+                        // expected-note@-2{{e.g., 'extern template void 
C1<int>::C2<char>::meth_2();'}}
 }
 
 char func_20() {
@@ -124,6 +135,7 @@ char func_20() {
 char func_21() {
   return C1<int>::C2<long>::s_tvar_2<long>; // expected-warning{{instantiation 
of variable 'C1<int>::C2<long>::s_tvar_2<long>' required here, but no 
definition is available}}
                                   // expected-note@-1{{add an explicit 
instantiation declaration to suppress this warning if 
'C1<int>::C2<long>::s_tvar_2<long>' is explicitly instantiated in another 
translation unit}}
+                                  // expected-note@-2{{e.g., 'extern template 
char C1<int>::C2<long>::s_tvar_2<long>;'}}
 }
 
 void func_22(C1<int>::C2<long> *x) {
@@ -133,6 +145,7 @@ void func_22(C1<int>::C2<long> *x) {
 void func_23(C1<int>::C2<long> *x) {
   x->tmeth_2<int>();    // expected-warning{{instantiation of function 
'C1<int>::C2<long>::tmeth_2<int>' required here, but no definition is 
available}}
                         // expected-note@-1{{add an explicit instantiation 
declaration to suppress this warning if 'C1<int>::C2<long>::tmeth_2<int>' is 
explicitly instantiated in another translation unit}}
+                        // expected-note@-2{{e.g., 'extern template void 
C1<int>::C2<long>::tmeth_2<int>();'}}
 }
 
 namespace test_24 {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to