Fix handling of alias templates

When an alias template was used, IWYU would resolve it to the underlying
aliased type rather than the template for the purposes of determining
what header was required.  This led to incorrect header removals in some
cases.

Fix that by splitting the TypeToDeclAsWritten into two functions:
TypeToDeclAsWritten and TypeToDeclForContent.  The former is used for
determining uses of the Decl itself, the latter is used for determining
uses of the arguments passed to the template.
This commit is contained in:
John Bytheway 2020-03-12 21:28:22 -04:00 committed by Kim Gräsman
parent f3bec0d16b
commit b8682077c7
6 changed files with 103 additions and 5 deletions

View File

@ -110,6 +110,7 @@ using clang::TemplateSpecializationKind;
using clang::TemplateSpecializationType;
using clang::TranslationUnitDecl;
using clang::Type;
using clang::TypeAliasTemplateDecl;
using clang::TypeDecl;
using clang::TypeLoc;
using clang::TypedefNameDecl;
@ -1207,7 +1208,7 @@ const Type* RemovePointersAndReferences(const Type* type) {
return type;
}
const NamedDecl* TypeToDeclAsWritten(const Type* type) {
static const NamedDecl* TypeToDeclImpl(const Type* type, bool as_written) {
// Get past all the 'class' and 'struct' prefixes, and namespaces.
type = RemoveElaboration(type);
@ -1222,20 +1223,29 @@ const NamedDecl* TypeToDeclAsWritten(const Type* type) {
// to keep typedefs as typedefs, so we do the record check last.
// We use getAs<> when we can -- unfortunately, it only exists
// for a few types so far.
const TemplateSpecializationType* template_spec_type = DynCastFrom(type);
const TemplateDecl* template_decl =
template_spec_type
? template_spec_type->getTemplateName().getAsTemplateDecl()
: nullptr;
if (const TypedefType* typedef_type = DynCastFrom(type)) {
return typedef_type->getDecl();
} else if (const InjectedClassNameType* icn_type
= type->getAs<InjectedClassNameType>()) {
return icn_type->getDecl();
} else if (as_written && template_decl &&
isa<TypeAliasTemplateDecl>(template_decl)) {
// A template type alias
return template_decl;
} else if (const RecordType* record_type
= type->getAs<RecordType>()) {
return record_type->getDecl();
} else if (const TagType* tag_type = DynCastFrom(type)) {
return tag_type->getDecl(); // probably just enums
} else if (const TemplateSpecializationType* template_spec_type
= DynCastFrom(type)) {
} else if (template_decl) {
// A non-concrete template class, such as 'Myclass<T>'
return template_spec_type->getTemplateName().getAsTemplateDecl();
return template_decl;
} else if (const FunctionType* function_type = DynCastFrom(type)) {
// TODO(csilvers): is it possible to map from fn type to fn decl?
(void)function_type;
@ -1245,6 +1255,14 @@ const NamedDecl* TypeToDeclAsWritten(const Type* type) {
}
}
const NamedDecl* TypeToDeclAsWritten(const Type* type) {
return TypeToDeclImpl(type, /*as_written=*/true);
}
const NamedDecl* TypeToDeclForContent(const Type* type) {
return TypeToDeclImpl(type, /*as_written=*/false);
}
const Type* RemoveReferenceAsWritten(const Type* type) {
if (const LValueReferenceType* ref_type = DynCastFrom(type))
return ref_type->getPointeeType().getTypePtr();
@ -1281,7 +1299,7 @@ GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type) {
return retval;
// Get the list of template args that apply to the decls.
const NamedDecl* decl = TypeToDeclAsWritten(tpl_spec_type);
const NamedDecl* decl = TypeToDeclForContent(tpl_spec_type);
const ClassTemplateSpecializationDecl* tpl_decl = DynCastFrom(decl);
if (!tpl_decl) // probably because tpl_spec_type is a dependent type
return retval;

View File

@ -744,6 +744,16 @@ const clang::Type* RemovePointersAndReferences(const clang::Type* type);
// this function returns nullptr.
const clang::NamedDecl* TypeToDeclAsWritten(const clang::Type* type);
// This is similar to TypeToDeclAsWritten, but in this case we are less
// interested in how the type was written; we want the Decl which we can
// explore the contents of, for example to determine which of its template
// arguments are used in a manner that constitutes a full use.
//
// The difference arises particularly for type aliases, where
// TypeToDeclAsWritten returns the Decl for the alias, whereas
// TypeToDeclForContent returns the underlying aliased Decl.
const clang::NamedDecl* TypeToDeclForContent(const clang::Type* type);
// Returns true if it's possible to implicitly convert a value of a
// different type to 'type' via an implicit constructor.
bool HasImplicitConversionConstructor(const clang::Type* type);

View File

@ -0,0 +1,10 @@
//===--- alias_template_use-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 "alias_template_use-i1.h"

View File

@ -0,0 +1,14 @@
//===--- alias_template_use-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.
//
//===----------------------------------------------------------------------===//
#include "alias_template_use-i2.h"
template<typename T>
using AliasTemplate = AliasedTemplate<T>;

View File

@ -0,0 +1,11 @@
//===--- alias_template_use-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.
//
//===----------------------------------------------------------------------===//
template<typename T>
class AliasedTemplate {};

View File

@ -0,0 +1,35 @@
//===--- alias_template_use.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.
//
//===----------------------------------------------------------------------===//
// Tests that use of template aliases is assigned to the header defining the
// alias, rather than the underlying type.
#include "alias_template_use-d1.h"
template<typename T>
class A {
};
class B {
// IWYU: AliasTemplate is...*alias_template_use-i1.h
A<AliasTemplate<int>> a;
};
/**** IWYU_SUMMARY
tests/cxx/alias_template_use.cc should add these lines:
#include "alias_template_use-i1.h"
tests/cxx/alias_template_use.cc should remove these lines:
- #include "alias_template_use-d1.h" // lines XX-XX
The full include-list for tests/cxx/alias_template_use.cc:
#include "alias_template_use-i1.h" // for AliasTemplate
***** IWYU_SUMMARY */