diff options
author | Kim Gräsman <kim.grasman@gmail.com> | 2023-02-26 20:14:25 +0100 |
---|---|---|
committer | Kim Gräsman <kim.grasman@gmail.com> | 2023-03-05 11:12:13 +0100 |
commit | 32bab56d3af67015ca83541ffb4c99fecf100350 (patch) | |
tree | 1f02a6048be69d997cd33871bd1798adf41806f9 | |
parent | 76d9e623014161894e9aeb78b0dda086ec187816 (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.cc | 58 | ||||
-rw-r--r-- | tests/cxx/expl_inst_select-d1.h | 12 | ||||
-rw-r--r-- | tests/cxx/expl_inst_select-i1.h | 11 | ||||
-rw-r--r-- | tests/cxx/expl_inst_select-i2.h | 16 | ||||
-rw-r--r-- | tests/cxx/expl_inst_select-i3.h | 15 | ||||
-rw-r--r-- | tests/cxx/expl_inst_select.cc | 62 | ||||
-rw-r--r-- | tests/cxx/explicit_instantiation2.cc | 3 |
7 files changed, 167 insertions, 10 deletions
@@ -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 |