Treat explicit template instantiations as full uses

Teach IsFowardDecl() to not confuse an explicit template instantiation
with a forward declaration.

Also make sure to report explicit instantiations (declaration or
definition) as full uses during visitation.

Fixes #558
This commit is contained in:
J.Ru 2018-09-12 09:42:53 +02:00
parent 2132e43fd1
commit 0d0832fee7
6 changed files with 108 additions and 3 deletions

11
iwyu.cc
View File

@ -3732,11 +3732,20 @@ class IwyuAstConsumer
// template <class T> struct Foo;
// template<> struct Foo<int> { ... };
// we don't want iwyu to recommend removing the 'forward declare' of Foo.
//
// Additionally, this type of decl is also used to represent explicit template
// instantiations, in which case we want the full type, not only a forward
// declaration.
bool VisitClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl* decl) {
if (CanIgnoreCurrentASTNode()) return true;
ClassTemplateDecl* specialized_decl = decl->getSpecializedTemplate();
ReportDeclForwardDeclareUse(CurrentLoc(), specialized_decl);
if (IsExplicitInstantiation(decl))
ReportDeclUse(CurrentLoc(), specialized_decl);
else
ReportDeclForwardDeclareUse(CurrentLoc(), specialized_decl);
return Base::VisitClassTemplateSpecializationDecl(decl);
}

View File

@ -105,6 +105,7 @@ using clang::TemplateArgumentLoc;
using clang::TemplateDecl;
using clang::TemplateName;
using clang::TemplateParameterList;
using clang::TemplateSpecializationKind;
using clang::TemplateSpecializationType;
using clang::TranslationUnitDecl;
using clang::Type;
@ -155,6 +156,13 @@ void DumpASTNode(llvm::raw_ostream& ostream, const ASTNode* node) {
}
}
TemplateSpecializationKind GetTemplateSpecializationKind(const Decl* decl) {
if (const auto* record = dyn_cast<CXXRecordDecl>(decl)) {
return record->getTemplateSpecializationKind();
}
return clang::TSK_Undeclared;
}
} // anonymous namespace
//------------------------------------------------------------
@ -942,12 +950,20 @@ bool IsFriendDecl(const Decl* decl) {
return decl->getFriendObjectKind() != Decl::FOK_None;
}
bool IsExplicitInstantiation(const clang::Decl* decl) {
TemplateSpecializationKind kind = GetTemplateSpecializationKind(decl);
return kind == clang::TSK_ExplicitInstantiationDeclaration ||
kind == clang::TSK_ExplicitInstantiationDefinition;
}
bool IsForwardDecl(const NamedDecl* decl) {
if (const auto* record_decl = dyn_cast<RecordDecl>(decl)) {
if (const auto* record_decl = dyn_cast<CXXRecordDecl>(decl)) {
return (!record_decl->getName().empty() &&
!record_decl->isCompleteDefinition() &&
!record_decl->isEmbeddedInDeclarator() &&
!IsFriendDecl(record_decl));
!IsFriendDecl(record_decl) &&
!IsExplicitInstantiation(record_decl));
}
return false;

View File

@ -594,6 +594,10 @@ bool IsDefaultNewOrDelete(const clang::FunctionDecl* decl,
// Returns true if this decl is part of a friend decl.
bool IsFriendDecl(const clang::Decl* decl);
// Returns true if this decl is an explicit template instantiation declaration
// or definition.
bool IsExplicitInstantiation(const clang::Decl* decl);
// Returns true if a named decl looks like a forward-declaration of a
// class (rather than a definition, a friend declaration, or an 'in
// place' declaration like 'struct Foo' in 'void MyFunc(struct Foo*);'

View File

@ -0,0 +1,14 @@
//===---- explicit_instantiation-template.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.
//
//===----------------------------------------------------------------------===//
#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPLICIT_INSTANTIATION_TEMPLATE_H_
#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPLICIT_INSTANTIATION_TEMPLATE_H_
template<typename T> class Template {};
#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPLICIT_INSTANTIATION_TEMPLATE_H_

View File

@ -0,0 +1,14 @@
//===- explicit_instantiation-template_direct.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.
//
//===----------------------------------------------------------------------===//
#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPLICIT_INSTANTIATION_TEMPLATE_DIRECT_H_
#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPLICIT_INSTANTIATION_TEMPLATE_DIRECT_H_
#include "explicit_instantiation-template.h"
#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPLICIT_INSTANTIATION_TEMPLATE_DIRECT_H_

View File

@ -0,0 +1,48 @@
//===-------- explicit_instantiation.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.
//
//===----------------------------------------------------------------------===//
#include "explicit_instantiation-template_direct.h"
// Test that all explicit instantiations variants of the base template
// require the full type:
// - Declaration and definition
// IWYU: Template is...*explicit_instantiation-template.h
extern template class Template<int>;
// IWYU: Template is...*explicit_instantiation-template.h
template class Template<int>;
// - Only declaration
// IWYU: Template is...*explicit_instantiation-template.h
extern template class Template<short>;
// - Only definition
// IWYU: Template is...*explicit_instantiation-template.h
template class Template<double>;
// The explicit instantiation of a specialization only needs a declaration
// of the base template
// IWYU: Template needs a declaration
template<> class Template<char> {};
extern template class Template<char>;
/**** IWYU_SUMMARY
tests/cxx/explicit_instantiation.cc should add these lines:
#include "explicit_instantiation-template.h"
tests/cxx/explicit_instantiation.cc should remove these lines:
- #include "explicit_instantiation-template_direct.h" // lines XX-XX
The full include-list for tests/cxx/explicit_instantiation.cc:
#include "explicit_instantiation-template.h" // for Template
***** IWYU_SUMMARY */