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

## Background

When `-Wundefined-var-template` or `-Wundefined-func-template` fires, users 
want to know the concrete syntax to add. However, the current diagnostic only 
says "add an explicit instantiation declaration" without showing what to write, 
and the syntax is hard to guess.
This new diagnostic would be especially helpful for users migrating from GCC or 
MSVC, where the explicit instantiation syntax differs.

Fixes: https://github.com/llvm/llvm-project/issues/62121

## Changes

- [x] extern template declaration (`extern template int S<int>::value;`)
- [ ] explicit specialization (`template <> int S<int>::value;`)
- [ ] generic out-of-class definition (`template <typename T> int S<T>::value;`)

## Before/After

Before:

```
# TODO
```

After:

```
# TODO
```



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