summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Gräsman <kim.grasman@gmail.com>2023-02-26 20:14:25 +0100
committerKim Gräsman <kim.grasman@gmail.com>2023-03-05 11:12:13 +0100
commit32bab56d3af67015ca83541ffb4c99fecf100350 (patch)
tree1f02a6048be69d997cd33871bd1798adf41806f9
parent76d9e623014161894e9aeb78b0dda086ec187816 (diff)
Report explicit instantions with more discretion
Whenever we ran into an explicit instantiation declaration/definition, or the use of one, we would report all previously seen declarations of it. If the same explicit instantiation declaration showed up in multiple headers, we might unnecessarily keep otherwise-unused #includes. Use more discretion when reporting these uses: * If there is a decl/defn in the same file, use that. * If not, but one of the prior redecl is a declaration, not a definition, prefer that, because explicit instantiation declarations should be cheaper than their definitions * If there are no declarations, use whatever definition is first in the redecl chain This should minimize the includes necessary for explicit instantiation declarations/definitions. Add a testcase to explicitly capture selection of explicit instantiations, and update explicit_instantiation2 testcase for new policy.
-rw-r--r--iwyu.cc58
-rw-r--r--tests/cxx/expl_inst_select-d1.h12
-rw-r--r--tests/cxx/expl_inst_select-i1.h11
-rw-r--r--tests/cxx/expl_inst_select-i2.h16
-rw-r--r--tests/cxx/expl_inst_select-i3.h15
-rw-r--r--tests/cxx/expl_inst_select.cc62
-rw-r--r--tests/cxx/explicit_instantiation2.cc3
7 files changed, 167 insertions, 10 deletions
diff --git a/iwyu.cc b/iwyu.cc
index 965720f..d8c201c 100644
--- a/iwyu.cc
+++ b/iwyu.cc
@@ -3257,23 +3257,63 @@ class InstantiatedTemplateVisitor
void ReportExplicitInstantiations(const Type* type) {
const auto* decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
TypeToDeclAsWritten(type));
-
if (decl == nullptr)
return;
+ if (IsProvidedByTemplate(decl))
+ return;
+
// Go through all previous redecls and filter out those that are not
// explicit template instantiations or already provided by the template.
+ std::vector<const CXXRecordDecl*> explicit_inst_decls;
for (const NamedDecl* redecl : decl->redecls()) {
- if (!IsExplicitInstantiation(redecl) ||
- !GlobalSourceManager()->isBeforeInTranslationUnit(
- redecl->getLocation(), caller_loc()) ||
- IsProvidedByTemplate(decl))
- continue;
+ if (IsExplicitInstantiation(redecl) &&
+ GlobalSourceManager()->isBeforeInTranslationUnit(
+ redecl->getLocation(), caller_loc())) {
+ // Earlier checks imply that this is a CXXRecordDecl.
+ explicit_inst_decls.push_back(cast<CXXRecordDecl>(redecl));
+ }
+ }
+
+ if (explicit_inst_decls.empty())
+ return;
+
+ // If there is a redecl in the same file, prefer that.
+ for (const CXXRecordDecl* redecl : explicit_inst_decls) {
+ if (GetFileEntry(redecl->getLocation()) == GetFileEntry(caller_loc())) {
+ VERRS(6) << "Found explicit instantiation declaration or definition in "
+ "same file\n";
+ Base::ReportDeclUse(caller_loc(), redecl,
+ "(for explicit instantiation)",
+ UF_ExplicitInstantiation);
+ return;
+ }
+ }
+
+ // Otherwise, if there is any redecl that is an explicit instantiation
+ // _declaration_, prefer that, because a declaration should be cheaper to
+ // process than a definition. The language guarantees that there is a
+ // definition available somewhere in the program, or the linker will
+ // complain.
+ for (const CXXRecordDecl* redecl : explicit_inst_decls) {
+ if (redecl->getTemplateSpecializationKind() ==
+ clang::TSK_ExplicitInstantiationDeclaration) {
+ VERRS(6) << "Found explicit instantiation declaration\n";
+ Base::ReportDeclUse(caller_loc(), redecl,
+ "(for explicit instantiation)",
+ UF_ExplicitInstantiation);
+ return;
+ }
+ }
- // Report the specific decl that points to the explicit instantiation
- Base::ReportDeclUse(caller_loc(), redecl, "(for explicit instantiation)",
- UF_ExplicitInstantiation);
+ // If nothing more specific was found, arbitrarily pick the first one.
+ if (explicit_inst_decls.size() > 1) {
+ VERRS(6) << "Found " << explicit_inst_decls.size() << " "
+ << "explicit instantiation decls; reporting the first one\n";
}
+ Base::ReportDeclUse(caller_loc(), explicit_inst_decls[0],
+ "(for explicit instantiation)",
+ UF_ExplicitInstantiation);
}
// If constructing an object, check the type we're constructing.
diff --git a/tests/cxx/expl_inst_select-d1.h b/tests/cxx/expl_inst_select-d1.h
new file mode 100644
index 0000000..32179f0
--- /dev/null
+++ b/tests/cxx/expl_inst_select-d1.h
@@ -0,0 +1,12 @@
+//===--- expl_inst_select-d1.h - test input file for iwyu -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tests/cxx/expl_inst_select-i1.h"
+#include "tests/cxx/expl_inst_select-i2.h"
+#include "tests/cxx/expl_inst_select-i3.h"
diff --git a/tests/cxx/expl_inst_select-i1.h b/tests/cxx/expl_inst_select-i1.h
new file mode 100644
index 0000000..7d76c2f
--- /dev/null
+++ b/tests/cxx/expl_inst_select-i1.h
@@ -0,0 +1,11 @@
+//===--- expl_inst_select-i1.h - test input file for iwyu -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+template<class T>
+struct Template {};
diff --git a/tests/cxx/expl_inst_select-i2.h b/tests/cxx/expl_inst_select-i2.h
new file mode 100644
index 0000000..3148a07
--- /dev/null
+++ b/tests/cxx/expl_inst_select-i2.h
@@ -0,0 +1,16 @@
+//===--- expl_inst_select-i2.h - test input file for iwyu -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Explicit instantiation declaration whose definition is in the main file
+// (this declaration is unused).
+extern template class Template<char>;
+
+// Explicit instantiation declaration with a definition in -i3.h
+// (this declaration should be preferred).
+extern template class Template<short>;
diff --git a/tests/cxx/expl_inst_select-i3.h b/tests/cxx/expl_inst_select-i3.h
new file mode 100644
index 0000000..3a1ef2a
--- /dev/null
+++ b/tests/cxx/expl_inst_select-i3.h
@@ -0,0 +1,15 @@
+//===--- expl_inst_select-i3.h - test input file for iwyu -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Explicit instantiation definition used in main file.
+template class Template<double>;
+
+// Explicit instantiation definition with declaration in -i2.h (which is
+// preferred to this definition).
+template class Template<short>;
diff --git a/tests/cxx/expl_inst_select.cc b/tests/cxx/expl_inst_select.cc
new file mode 100644
index 0000000..33cfddf
--- /dev/null
+++ b/tests/cxx/expl_inst_select.cc
@@ -0,0 +1,62 @@
+//===--- expl_inst_select.cc - test input file for iwyu -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This checks deduplication/selection of explicit template instantiations.
+
+// IWYU_ARGS: -I .
+
+#include "tests/cxx/expl_inst_select-d1.h"
+
+// An explicit instantiation definition anchors a prior declaration.
+// IWYU: Template is...*expl_inst_select-i1.h
+// IWYU: Template is...*expl_inst_select-i2.h.*for explicit instantiation
+template class Template<char>;
+
+// An explicit instantiation declaration for later use.
+// IWYU: Template needs a declaration...*
+// IWYU: Template is...*expl_inst_select-i1.h
+extern template class Template<int>;
+
+// Use of an explicit instantiation for which there is both a declaration and
+// definition in the include closure should prefer a declaration.
+// IWYU: Template is...*expl_inst_select-i1.h
+// IWYU: Template is...*expl_inst_select-i2.h.*for explicit instantiation
+Template<short> ts;
+
+// Use of an explicit instantiation for which there is only a definition.
+// IWYU: Template is...*expl_inst_select-i1.h
+// IWYU: Template is...*expl_inst_select-i3.h.*for explicit instantiation
+Template<double> td;
+
+// No 'for explicit instantiation' diagnostic for use of an instantiation
+// definition available in the same file.
+// IWYU: Template is...*expl_inst_select-i1.h
+Template<char> tc;
+
+// No 'for explicit instantiation' diagnostic for use of an instantiation
+// declaration available in the same file.
+// IWYU: Template is...*expl_inst_select-i1.h
+Template<int> ti;
+
+/**** IWYU_SUMMARY
+
+tests/cxx/expl_inst_select.cc should add these lines:
+#include "tests/cxx/expl_inst_select-i1.h"
+#include "tests/cxx/expl_inst_select-i2.h"
+#include "tests/cxx/expl_inst_select-i3.h"
+
+tests/cxx/expl_inst_select.cc should remove these lines:
+- #include "tests/cxx/expl_inst_select-d1.h" // lines XX-XX
+
+The full include-list for tests/cxx/expl_inst_select.cc:
+#include "tests/cxx/expl_inst_select-i1.h" // for Template
+#include "tests/cxx/expl_inst_select-i2.h" // for Template
+#include "tests/cxx/expl_inst_select-i3.h" // for Template
+
+***** IWYU_SUMMARY */
diff --git a/tests/cxx/explicit_instantiation2.cc b/tests/cxx/explicit_instantiation2.cc
index d32a316..e47d151 100644
--- a/tests/cxx/explicit_instantiation2.cc
+++ b/tests/cxx/explicit_instantiation2.cc
@@ -41,8 +41,9 @@ Template<bool> t1a; // 1a
// IWYU: Template is...*template_bool.h.*for explicit instantiation
template class Template<bool>; // 2
+// Included explicit instantiation no longer reported here as a local definition
+// is available.
// IWYU: Template is...*explicit_instantiation-template.h
-// IWYU: Template is...*template_bool.h.*for explicit instantiation
Template<bool> t1b; // 1b
// IWYU: Template is...*explicit_instantiation-template.h