include-what-you-use/iwyu_ast_util.cc

1516 lines
54 KiB
C++
Raw Normal View History

//===--- iwyu_ast_util.cc - clang-AST utilities for include-what-you-use --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Utilities that make it easier to work with Clang's AST.
#include "iwyu_ast_util.h"
#include <set> // for set
#include <string> // for string, operator+, etc
#include <utility> // for pair
#include "iwyu_globals.h"
#include "iwyu_location_util.h"
#include "iwyu_path_util.h"
2019-12-26 12:18:47 +00:00
#include "iwyu_port.h" // for CHECK_
#include "iwyu_stl_util.h"
#include "iwyu_verrs.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDumper.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
namespace clang {
class FileEntry;
} // namespace clang
using clang::ASTDumper;
using clang::BlockPointerType;
using clang::CXXConstructExpr;
using clang::CXXConstructorDecl;
using clang::CXXDeleteExpr;
using clang::CXXDependentScopeMemberExpr;
using clang::CXXDestructorDecl;
using clang::CXXMethodDecl;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
using clang::CXXNewExpr;
using clang::CXXRecordDecl;
using clang::CallExpr;
using clang::ClassTemplateDecl;
using clang::ClassTemplatePartialSpecializationDecl;
using clang::ClassTemplateSpecializationDecl;
using clang::Decl;
using clang::DeclContext;
using clang::DeclRefExpr;
using clang::DeclaratorDecl;
using clang::DependentNameType;
using clang::DependentScopeDeclRefExpr;
using clang::DependentTemplateName;
using clang::DependentTemplateSpecializationType;
using clang::ElaboratedType;
using clang::EnumDecl;
using clang::Expr;
using clang::ExprWithCleanups;
using clang::FileEntry;
using clang::FullSourceLoc;
using clang::FunctionDecl;
using clang::FunctionType;
using clang::ImplicitCastExpr;
using clang::InjectedClassNameType;
using clang::LValueReferenceType;
using clang::MemberExpr;
using clang::MemberPointerType;
using clang::NamedDecl;
using clang::NestedNameSpecifier;
using clang::ObjCObjectType;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
using clang::OverloadExpr;
using clang::PointerType;
using clang::QualType;
using clang::QualifiedTemplateName;
using clang::RecordDecl;
using clang::RecordType;
using clang::RecursiveASTVisitor;
using clang::SourceLocation;
using clang::SourceRange;
using clang::Stmt;
using clang::SubstTemplateTypeParmType;
using clang::TagDecl;
using clang::TagType;
using clang::TemplateArgument;
using clang::TemplateArgumentList;
using clang::TemplateArgumentListInfo;
using clang::TemplateArgumentLoc;
using clang::TemplateDecl;
using clang::TemplateName;
using clang::TemplateParameterList;
using clang::TemplateSpecializationKind;
using clang::TemplateSpecializationType;
using clang::TranslationUnitDecl;
using clang::Type;
using clang::TypeAliasTemplateDecl;
using clang::TypeDecl;
using clang::TypeLoc;
using clang::TypedefNameDecl;
using clang::TypedefType;
using clang::UnaryOperator;
using clang::UsingDirectiveDecl;
using clang::ValueDecl;
using clang::VarDecl;
using llvm::ArrayRef;
using llvm::PointerUnion;
using llvm::cast;
using llvm::dyn_cast;
using llvm::dyn_cast_or_null;
using llvm::errs;
using llvm::isa;
using llvm::raw_string_ostream;
namespace include_what_you_use {
namespace {
void DumpASTNode(llvm::raw_ostream& ostream, const ASTNode* node) {
if (const Decl *decl = node->GetAs<Decl>()) {
ostream << "[" << GetKindName(decl) << "] " << PrintableDecl(decl);
} else if (const Stmt *stmt = node->GetAs<Stmt>()) {
ostream << "[" << GetKindName(stmt) << "] " << PrintableStmt(stmt);
} else if (const TypeLoc* typeloc = node->GetAs<TypeLoc>()) {
ostream << "[" << GetKindName(*typeloc) << "] "
<< PrintableTypeLoc(*typeloc);
} else if (const Type* type = node->GetAs<Type>()) { // +typeloc
ostream << "[" << GetKindName(type) << "] " << PrintableType(type);
} else if (const NestedNameSpecifier* nns =
node->GetAs<NestedNameSpecifier>()) {
ostream << "[NestedNameSpecifier] " << PrintableNestedNameSpecifier(nns);
} else if (const TemplateName* tpl_name = node->GetAs<TemplateName>()) {
ostream << "[TemplateName] " << PrintableTemplateName(*tpl_name);
} else if (const TemplateArgumentLoc* tpl_argloc =
node->GetAs<TemplateArgumentLoc>()) {
ostream << "[TemplateArgumentLoc] "
<< PrintableTemplateArgumentLoc(*tpl_argloc);
} else if (const TemplateArgument* tpl_arg =
node->GetAs<TemplateArgument>()) {
ostream << "[TemplateArgument] " << PrintableTemplateArgument(*tpl_arg);
} else {
CHECK_UNREACHABLE_("Unknown kind for ASTNode");
}
}
TemplateSpecializationKind GetTemplateSpecializationKind(const Decl* decl) {
if (const auto* record = dyn_cast<CXXRecordDecl>(decl)) {
return record->getTemplateSpecializationKind();
}
return clang::TSK_Undeclared;
}
} // anonymous namespace
//------------------------------------------------------------
// ASTNode and associated utilities.
SourceLocation ASTNode::GetLocation() const {
SourceLocation retval;
if (FillLocationIfKnown(&retval))
return retval;
// OK, let's ask a parent node.
for (const ASTNode* node = parent_; node != nullptr; node = node->parent_) {
if (node->FillLocationIfKnown(&retval))
break;
}
// If the parent node shows the spelling and instantiation
// locations are in a different file, then we're uncertain of our
// own location. Return an invalid location.
if (retval.isValid()) {
clang::SourceManager& sm = *GlobalSourceManager();
FullSourceLoc full_loc(retval, sm);
const FileEntry* spelling_file =
sm.getFileEntryForID(sm.getFileID(full_loc.getSpellingLoc()));
const FileEntry* instantiation_file =
sm.getFileEntryForID(sm.getFileID(full_loc.getExpansionLoc()));
if (spelling_file != instantiation_file)
return SourceLocation();
}
return retval;
}
bool ASTNode::FillLocationIfKnown(SourceLocation* loc) const {
using include_what_you_use::GetLocation;
switch (kind_) {
case kDeclKind:
*loc = GetLocation(as_decl_); // in iwyu_location_util.h
return true;
case kStmtKind:
*loc = GetLocation(as_stmt_);
return true;
case kTypelocKind:
*loc = GetLocation(as_typeloc_);
return true;
case kNNSLocKind:
*loc = GetLocation(as_nnsloc_);
return true;
case kTemplateArgumentLocKind:
*loc = GetLocation(as_template_argloc_);
return true;
case kTypeKind:
case kNNSKind:
case kTemplateNameKind:
case kTemplateArgumentKind:
return false;
}
CHECK_UNREACHABLE_("Unexpected kind of ASTNode");
}
// --- Utilities for ASTNode.
bool IsElaboratedTypeSpecifier(const ASTNode* ast_node) {
if (ast_node == nullptr)
return false;
const ElaboratedType* elaborated_type = ast_node->GetAs<ElaboratedType>();
return elaborated_type && elaborated_type->getKeyword() != clang::ETK_None;
}
const ASTNode* MostElaboratedAncestor(const ASTNode* ast_node) {
// Read past elaborations like 'class' keyword or namespaces.
while (ast_node->ParentIsA<ElaboratedType>()) {
ast_node = ast_node->parent();
}
return ast_node;
}
bool IsQualifiedNameNode(const ASTNode* ast_node) {
if (ast_node == nullptr)
return false;
const ElaboratedType* elaborated_type = ast_node->GetAs<ElaboratedType>();
if (elaborated_type == nullptr)
return false;
return elaborated_type->getQualifier() != nullptr;
}
bool IsNodeInsideCXXMethodBody(const ASTNode* ast_node) {
// If we're a destructor, we're definitely part of a method body;
// destructors don't have any other parts to them. This case is
// triggered when we see implicit destruction of member vars.
if (ast_node && ast_node->IsA<CXXDestructorDecl>())
return true;
for (; ast_node != nullptr; ast_node = ast_node->parent()) {
// If we're a constructor, check if we're part of the
// initializers, which also count as 'the body' of the method.
if (const CXXConstructorDecl* ctor =
ast_node->GetParentAs<CXXConstructorDecl>()) {
for (CXXConstructorDecl::init_const_iterator
it = ctor->init_begin(); it != ctor->init_end(); ++it) {
if (ast_node->ContentIs((*it)->getInit()))
return true;
}
// Now fall through to see if we're the body of the constructor.
}
if (const CXXMethodDecl* method_decl =
ast_node->GetParentAs<CXXMethodDecl>()) {
if (ast_node->ContentIs(method_decl->getBody())) {
return true;
}
}
}
return false;
}
UseFlags ComputeUseFlags(const ASTNode* ast_node) {
UseFlags flags = UF_None;
if (IsNodeInsideCXXMethodBody(ast_node))
flags |= UF_InCxxMethodBody;
// Definitions of free functions are a little special, because they themselves
// count as uses of all prior declarations (ideally we should probably just
2019-12-26 15:41:52 +00:00
// require one but it's hard to say which, so we pick all previously seen).
// Later IWYU analysis phases do some canonicalization that isn't
// necessary/valid for this case, so mark it up for later.
if (const auto* fd = ast_node->GetAs<FunctionDecl>()) {
if (fd->getKind() == Decl::Function && fd->isThisDeclarationADefinition())
flags |= UF_FunctionDfn;
}
return flags;
}
bool IsNestedTagAsWritten(const ASTNode* ast_node) {
return (ast_node->IsA<TagDecl>() &&
(ast_node->ParentIsA<CXXRecordDecl>() ||
// For templated nested-classes, a ClassTemplateDecl is interposed.
(ast_node->ParentIsA<ClassTemplateDecl>() &&
ast_node->AncestorIsA<CXXRecordDecl>(2))));
}
bool IsDefaultTemplateTemplateArg(const ASTNode* ast_node) {
// Is ast_node the 'D' in the following:
// template<template <typename A> class T = D> class C { ... }
// ('D' might be something like 'vector').
// D is a TemplateName, since it's a template, and its parent
// is a TemplateArgument, since D is inside a template argument.
// The only way a template name can be in a template argument
// is if it's a default parameter.
return (ast_node->IsA<TemplateName>() &&
ast_node->ParentIsA<TemplateArgument>());
}
bool IsCXXConstructExprInInitializer(const ASTNode* ast_node) {
if (!ast_node->IsA<CXXConstructExpr>())
return false;
CHECK_(ast_node->parent() && "Constructor should not be a top-level node!");
// Typically, you can tell an initializer because its parent is a
// constructor decl. But sometimes -- I'm not exactly sure when --
// there can be an ExprWithCleanups in the middle.
return ((ast_node->ParentIsA<CXXConstructorDecl>()) ||
(ast_node->ParentIsA<ExprWithCleanups>() &&
ast_node->AncestorIsA<CXXConstructorDecl>(2)));
}
bool IsCXXConstructExprInNewExpr(const ASTNode* ast_node) {
if (!ast_node->IsA<CXXConstructExpr>())
return false;
CHECK_(ast_node->parent() && "Constructor should not be a top-level node!");
return ast_node->ParentIsA<CXXNewExpr>();
}
template<typename T>
NestedNameSpecifier* TryGetQualifier(const ASTNode* ast_node) {
if (ast_node->IsA<T>())
return ast_node->GetAs<T>()->getQualifier();
return nullptr;
}
const NestedNameSpecifier* GetQualifier(const ASTNode* ast_node) {
const NestedNameSpecifier* nns = nullptr;
if (ast_node->IsA<TemplateName>()) {
const TemplateName* tn = ast_node->GetAs<TemplateName>();
if (const DependentTemplateName* dtn = tn->getAsDependentTemplateName())
nns = dtn->getQualifier();
else if (const QualifiedTemplateName* qtn =
tn->getAsQualifiedTemplateName())
nns = qtn->getQualifier();
}
if (!nns) nns = TryGetQualifier<ElaboratedType>(ast_node);
if (!nns) nns = TryGetQualifier<DependentNameType>(ast_node);
if (!nns)
nns = TryGetQualifier<DependentTemplateSpecializationType>(ast_node);
if (!nns) nns = TryGetQualifier<UsingDirectiveDecl>(ast_node);
if (!nns) nns = TryGetQualifier<EnumDecl>(ast_node);
if (!nns) nns = TryGetQualifier<RecordDecl>(ast_node);
if (!nns) nns = TryGetQualifier<DeclaratorDecl>(ast_node);
if (!nns) nns = TryGetQualifier<FunctionDecl>(ast_node);
if (!nns) nns = TryGetQualifier<CXXDependentScopeMemberExpr>(ast_node);
if (!nns) nns = TryGetQualifier<DeclRefExpr>(ast_node);
if (!nns) nns = TryGetQualifier<DependentScopeDeclRefExpr>(ast_node);
if (!nns) nns = TryGetQualifier<MemberExpr>(ast_node);
return nns;
}
const DeclContext* GetDeclContext(const ASTNode* ast_node) {
for (; ast_node != nullptr; ast_node = ast_node->parent()) {
if (ast_node->IsA<Decl>())
return ast_node->GetAs<Decl>()->getDeclContext();
}
return nullptr;
}
//------------------------------------------------------------
// Helper functions for working with raw Clang AST nodes.
// --- Printers.
string PrintableLoc(SourceLocation loc) {
return NormalizeFilePath(loc.printToString(*GlobalSourceManager()));
}
string PrintableDecl(const Decl* decl, bool terse/*=true*/) {
if (!decl)
return "<null decl>";
// Use the terse flag to limit the level of output to one line.
clang::PrintingPolicy policy = decl->getASTContext().getPrintingPolicy();
policy.TerseOutput = terse;
policy.SuppressInitializers = terse;
policy.PolishForDeclaration = terse;
std::string buffer;
raw_string_ostream ostream(buffer);
decl->print(ostream, policy);
return ostream.str();
}
string PrintableStmt(const Stmt* stmt) {
if (!stmt)
return "<null stmt>";
std::string buffer;
raw_string_ostream ostream(buffer);
ASTDumper dumper(ostream, /*ShowColors=*/false);
dumper.Visit(stmt);
return ostream.str();
}
string PrintableType(const Type* type) {
if (!type)
return "<null type>";
string typestr = QualType(type, 0).getAsString();
if (GlobalFlags().HasDebugFlag("printtypeclass")) {
typestr = GetKindName(type) + ":" + typestr;
}
return typestr;
}
string PrintableTypeLoc(const TypeLoc& typeloc) {
return PrintableType(typeloc.getTypePtr());
}
string PrintableNestedNameSpecifier(const NestedNameSpecifier* nns) {
if (!nns)
return "<null nns>";
std::string buffer; // llvm wants regular string, not our versa-string
raw_string_ostream ostream(buffer);
nns->print(ostream, DefaultPrintPolicy());
return ostream.str();
}
string PrintableTemplateName(const TemplateName& tpl_name) {
std::string buffer; // llvm wants regular string, not our versa-string
raw_string_ostream ostream(buffer);
tpl_name.print(ostream, DefaultPrintPolicy());
return ostream.str();
}
string PrintableTemplateArgument(const TemplateArgument& arg) {
std::string buffer;
raw_string_ostream ostream(buffer);
printTemplateArgumentList(ostream, ArrayRef<TemplateArgument>(arg),
DefaultPrintPolicy());
return ostream.str();
}
string PrintableTemplateArgumentLoc(const TemplateArgumentLoc& arg) {
std::string buffer;
raw_string_ostream ostream(buffer);
printTemplateArgumentList(ostream, ArrayRef<TemplateArgumentLoc>(arg),
DefaultPrintPolicy());
return ostream.str();
}
string PrintableASTNode(const ASTNode* node) {
if (!node)
return "<null ast node>";
std::string buffer;
raw_string_ostream ostream(buffer);
DumpASTNode(ostream, node);
return ostream.str();
}
// These print to stderr. They're useful for debugging (e.g. inside gdb).
void PrintASTNode(const ASTNode* node) {
DumpASTNode(errs(), node);
errs() << "\n";
}
void PrintStmt(const Stmt* stmt) {
ASTDumper dumper(llvm::errs(), /*ShowColors=*/false);
dumper.Visit(stmt);
}
string GetWrittenQualifiedNameAsString(const NamedDecl* named_decl) {
std::string retval;
llvm::raw_string_ostream ostream(retval);
clang::PrintingPolicy printing_policy =
named_decl->getASTContext().getPrintingPolicy();
printing_policy.SuppressUnwrittenScope = true;
named_decl->printQualifiedName(ostream, printing_policy);
return ostream.str();
}
// --- Utilities for Template Arguments.
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// If the TemplateArgument is a type (and not an expression such as
// 'true', or a template such as 'vector', etc), return it. Otherwise
// return nullptr.
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
static const Type* GetTemplateArgAsType(const TemplateArgument& tpl_arg) {
if (tpl_arg.getKind() == TemplateArgument::Type)
return tpl_arg.getAsType().getTypePtr();
return nullptr;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
// These utilities figure out the template arguments that are
// specified in various contexts: TemplateSpecializationType (for
// template classes) and FunctionDecl (for template functions).
//
// For classes, we care only about explicitly specified template args,
// not implicit, default args. For functions, we care about all
// template args, since if not specified they're derived from the
// function arguments. In either case, we only care about template
// arguments that are types (including template types), not other
// kinds of arguments such as built-in types.
// This helper class visits a given AST node and finds all the types
// beneath it, which it returns as a set. For example, if you have a
// VarDecl 'vector<int(*)(const MyClass&)> x', it would return
// (vector<int(*)(const MyClass&)>, int(*)(const MyClass&),
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// int(const MyClass&), int, const MyClass&, MyClass). Note that
// this function only returns types-as-written, so it does *not* return
// alloc<int(*)(const MyClass&)>, even though it's part of vector.
class TypeEnumerator : public RecursiveASTVisitor<TypeEnumerator> {
public:
typedef RecursiveASTVisitor<TypeEnumerator> Base;
// --- Public interface
// We can add more entry points as needed.
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
set<const Type*> Enumerate(const Type* type) {
seen_types_.clear();
if (!type)
return seen_types_;
TraverseType(QualType(type, 0));
return seen_types_;
}
set<const Type*> Enumerate(const TemplateArgument& tpl_arg) {
seen_types_.clear();
TraverseTemplateArgument(tpl_arg);
return seen_types_;
}
// --- Methods on RecursiveASTVisitor
bool TraverseType(QualType type) {
TraverseArgumentsOfSugaredTemplates(type);
return Base::TraverseType(type);
}
bool TraverseTypeLoc(TypeLoc type_loc) {
TraverseArgumentsOfSugaredTemplates(type_loc.getType());
return Base::TraverseTypeLoc(type_loc);
}
bool VisitType(Type* type) {
seen_types_.insert(type);
return true;
}
private:
// Clang doesn't traverse underlying type for most of the sugar types.
// If a TemplateSpecializationType occurs in a sugaring chain, traverse its
// arguments explicitly, otherwise they aren't put into resugar_map.
void TraverseArgumentsOfSugaredTemplates(QualType type) {
if (type.isNull())
return;
if (const auto* template_spec = type->getAs<TemplateSpecializationType>()) {
for (const TemplateArgument& arg : template_spec->template_arguments())
TraverseTemplateArgument(arg);
}
}
set<const Type*> seen_types_;
};
iwyu was egregiously wrong in how it handled template arguments using the 'precomputed cache'. In such situations, it totally ignored the currently active resugar_map, replacing it with one of its own. That worked fine for types outside of templates, but not fine for types inside (such as a 'hash_map<T>' inside a templated class). I "fixed" this. "Fixed" is in quotes because this turned up a whole slew of other problems I don't even attempt to resolve here (though I spent a few hours trying). One is that it's possible to have a type like hash_map that has some arguments that are dependent and some that aren't; in theory, for these types, we can correctly attribute the use to the template author or template instantiator depending on which type it is. But I can't figure out how to get clang to do any meaningful analysis of incomplete (dependent) types, so I've punted on that for now. The second thing wrong is I jumped through all sorts of hoops to handle default template arguments correctly, so if a class has a hash_map<T> and you instantiate T with string, you're also made responsible for hash<string>. This *should* work, but clang is giving hash<string> a type I don't expect (RecordType, not TemplateSpecializationType), and I don't know how to deal with that -- I don't know how to extract the 'string' part of this RecordType. Ugh. I punt on this now, as well. Even in this incomplete form, it's enough to resolve a P1 bug, so I figure it's worth putting in. R=dsturtevant DELTA=141 (97 added, 7 deleted, 37 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=3147
2011-08-31 12:42:23 +01:00
// A 'component' of a type is a type beneath it in the AST tree.
// So 'Foo*' has component 'Foo', as does 'vector<Foo>', while
// vector<pair<Foo, Bar>> has components pair<Foo,Bar>, Foo, and Bar.
iwyu was egregiously wrong in how it handled template arguments using the 'precomputed cache'. In such situations, it totally ignored the currently active resugar_map, replacing it with one of its own. That worked fine for types outside of templates, but not fine for types inside (such as a 'hash_map<T>' inside a templated class). I "fixed" this. "Fixed" is in quotes because this turned up a whole slew of other problems I don't even attempt to resolve here (though I spent a few hours trying). One is that it's possible to have a type like hash_map that has some arguments that are dependent and some that aren't; in theory, for these types, we can correctly attribute the use to the template author or template instantiator depending on which type it is. But I can't figure out how to get clang to do any meaningful analysis of incomplete (dependent) types, so I've punted on that for now. The second thing wrong is I jumped through all sorts of hoops to handle default template arguments correctly, so if a class has a hash_map<T> and you instantiate T with string, you're also made responsible for hash<string>. This *should* work, but clang is giving hash<string> a type I don't expect (RecordType, not TemplateSpecializationType), and I don't know how to deal with that -- I don't know how to extract the 'string' part of this RecordType. Ugh. I punt on this now, as well. Even in this incomplete form, it's enough to resolve a P1 bug, so I figure it's worth putting in. R=dsturtevant DELTA=141 (97 added, 7 deleted, 37 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=3147
2011-08-31 12:42:23 +01:00
set<const Type*> GetComponentsOfType(const Type* type) {
TypeEnumerator type_enumerator;
return type_enumerator.Enumerate(type);
}
// --- Utilities for Decl.
bool IsTemplatizedFunctionDecl(const FunctionDecl* decl) {
return decl && decl->getTemplateSpecializationArgs() != nullptr;
}
bool HasImplicitConversionCtor(const CXXRecordDecl* cxx_class) {
for (CXXRecordDecl::ctor_iterator ctor = cxx_class->ctor_begin();
ctor != cxx_class->ctor_end(); ++ctor) {
if (ctor->isExplicit() || ctor->getNumParams() != 1 ||
ctor->isCopyConstructor() || ctor->isMoveConstructor())
continue;
return true;
}
return false;
}
// C++ [class.virtual]p8:
// If the return type of D::f differs from the return type of B::f, the
// class type in the return type of D::f shall be complete at the point of
// declaration of D::f or shall be the class type D.
bool HasCovariantReturnType(const CXXMethodDecl* method_decl) {
QualType derived_return_type = method_decl->getReturnType();
for (CXXMethodDecl::method_iterator
it = method_decl->begin_overridden_methods();
it != method_decl->end_overridden_methods(); ++it) {
// There are further constraints on covariant return types as such
// (e.g. parents must be related, derived override must have return type
// derived from base override, etc.) but the only _valid_ case I can think
// of where return type differs is when they're actually covariant.
// That is, if Clang can already compile this code without errors, and
// return types differ, it can only be due to covariance.
if ((*it)->getReturnType() != derived_return_type)
return true;
}
return false;
}
const TagDecl* GetTagDefinition(const Decl* decl) {
const TagDecl* as_tag = DynCastFrom(decl);
const ClassTemplateDecl* as_tpl = DynCastFrom(decl);
if (as_tpl) { // Convert the template to its underlying class defn.
as_tag = DynCastFrom(as_tpl->getTemplatedDecl());
}
if (as_tag) {
if (const TagDecl* tag_dfn = as_tag->getDefinition()) {
return tag_dfn;
}
// If we're a templated class that was never instantiated (because
// we were never "used"), then getDefinition() will return nullptr.
if (const ClassTemplateSpecializationDecl* spec_decl = DynCastFrom(decl)) {
PointerUnion<ClassTemplateDecl*,
ClassTemplatePartialSpecializationDecl*>
specialized_decl = spec_decl->getSpecializedTemplateOrPartial();
if (const ClassTemplatePartialSpecializationDecl*
partial_spec_decl =
specialized_decl.dyn_cast<
ClassTemplatePartialSpecializationDecl*>()) {
// decl would be instantiated from a template partial
// specialization.
CHECK_(partial_spec_decl->hasDefinition());
return partial_spec_decl->getDefinition();
} else if (const ClassTemplateDecl* tpl_decl =
specialized_decl.dyn_cast<ClassTemplateDecl*>()) {
// decl would be instantiated from a non-specialized
// template.
if (tpl_decl->getTemplatedDecl()->hasDefinition())
return tpl_decl->getTemplatedDecl()->getDefinition();
}
}
}
return nullptr;
}
SourceRange GetSourceRangeOfClassDecl(const Decl* decl) {
// If we're a templatized class, go 'up' a level to get the
// template<...> prefix as well.
if (const CXXRecordDecl* cxx_decl = DynCastFrom(decl)) {
if (cxx_decl->getDescribedClassTemplate())
return cxx_decl->getDescribedClassTemplate()->getSourceRange();
}
// We can get source ranges of classes and template classes.
if (const TagDecl* tag_decl = DynCastFrom(decl))
return tag_decl->getSourceRange();
if (const TemplateDecl* tpl_decl = DynCastFrom(decl))
return tpl_decl->getSourceRange();
CHECK_UNREACHABLE_("Cannot get source range for this decl type");
}
// Use a local RAV implementation to simply collect all FunctionDecls marked for
// late template parsing. This happens with the flag -fdelayed-template-parsing,
// which is on by default in MSVC-compatible mode.
set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) {
struct Visitor : public RecursiveASTVisitor<Visitor> {
bool VisitFunctionDecl(FunctionDecl* function_decl) {
if (function_decl->isLateTemplateParsed())
late_parsed_decls.insert(function_decl);
return true;
}
set<FunctionDecl*> late_parsed_decls;
};
Visitor v;
v.TraverseDecl(decl);
return v.late_parsed_decls;
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// Helper for the Get*ResugarMap*() functions. Given a map from
// desugared->resugared types, looks at each component of the
// resugared type (eg, both hash_set<Foo>* and vector<hash_set<Foo>>
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// have two components: hash_set<Foo> and Foo), and returns a map that
// contains the original map elements plus mapping for the components.
// This is because when a type is 'owned' by the template
// instantiator, all parts of the type are owned. We only consider
// type-components as written.
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
static map<const Type*, const Type*> ResugarTypeComponents(
const map<const Type*, const Type*>& resugar_map) {
map<const Type*, const Type*> retval = resugar_map;
for (const auto& types : resugar_map) {
const set<const Type*>& components = GetComponentsOfType(types.second);
for (const Type* component_type : components) {
const Type* desugared_type = GetCanonicalType(component_type);
if (!ContainsKey(retval, desugared_type)) {
retval[desugared_type] = component_type;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
VERRS(6) << "Adding a type-components of interest: "
<< PrintableType(component_type) << "\n";
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
}
}
return retval;
}
// Helpers for GetTplTypeResugarMapForFunction().
static map<const Type*, const Type*> GetTplTypeResugarMapForFunctionNoCallExpr(
const FunctionDecl* decl, unsigned start_arg) {
map<const Type*, const Type*> retval;
if (!decl) // can be nullptr if the function call is via a function pointer
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
return retval;
if (const TemplateArgumentList* tpl_list =
decl->getTemplateSpecializationArgs()) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
for (unsigned i = start_arg; i < tpl_list->size(); ++i) {
if (const Type* arg_type = GetTemplateArgAsType(tpl_list->get(i))) {
retval[GetCanonicalType(arg_type)] = arg_type;
VERRS(6) << "Adding an implicit tpl-function type of interest: "
<< PrintableType(arg_type) << "\n";
}
}
}
return retval;
}
static map<const Type*, const Type*>
GetTplTypeResugarMapForFunctionExplicitTplArgs(
const FunctionDecl* decl,
const TemplateArgumentListInfo& explicit_tpl_list) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
map<const Type*, const Type*> retval;
for (const TemplateArgumentLoc& loc : explicit_tpl_list.arguments()) {
if (const Type* arg_type = GetTemplateArgAsType(loc.getArgument())) {
retval[GetCanonicalType(arg_type)] = arg_type;
VERRS(6) << "Adding an explicit template-function type of interest: "
<< PrintableType(arg_type) << "\n";
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
}
return retval;
}
// Get the type of an expression while preserving as much type sugar as
// possible. This was originally designed for use with function argument
// expressions, and so might not work in a more general context.
static const Type* GetSugaredTypeOf(const Expr* expr) {
// Search the expression subtree for better sugar; stop as soon as a type
// different from expr's type is found.
struct Visitor : public RecursiveASTVisitor<Visitor> {
Visitor(QualType origtype) : sugared(origtype.getLocalUnqualifiedType()) {
}
bool VisitDeclRefExpr(DeclRefExpr* e) {
return !CollectSugar(e);
}
bool VisitImplicitCastExpr(ImplicitCastExpr* e) {
return !CollectSugar(e->getSubExpr());
}
bool CollectSugar(const Expr* e) {
QualType exprtype = e->getType().getLocalUnqualifiedType();
if (!exprtype.isNull() && exprtype != sugared) {
sugared = exprtype;
return true;
}
return false;
}
QualType sugared;
};
// Default to the expr's type.
Visitor v(expr->getType());
v.TraverseStmt(const_cast<Expr*>(expr));
return v.sugared.getTypePtr();
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
map<const Type*, const Type*> GetTplTypeResugarMapForFunction(
const FunctionDecl* decl, const Expr* calling_expr) {
map<const Type*, const Type*> retval;
// If calling_expr is nullptr, then we can't find any explicit template
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// arguments, if they were specified (e.g. 'Fn<int>()'), and we
// won't be able to get the function arguments as written. So we
// can't resugar at all. We just have to hope that the types happen
// to be already sugared, because the actual-type is already canonical.
if (calling_expr == nullptr) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
retval = GetTplTypeResugarMapForFunctionNoCallExpr(decl, 0);
retval = ResugarTypeComponents(retval); // add in retval's decomposition
return retval;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
// If calling_expr is a CXXConstructExpr of CXXNewExpr, then it's
// impossible to explicitly specify template arguments; all we have
// to go on is function arguments. If it's a CallExpr, and some
// arguments might be explicit, and others implicit. Otherwise,
// it's a type that doesn't take function template args at all (like
// CXXDeleteExpr) or only takes explicit args (like DeclRefExpr).
const Expr* const* fn_args = nullptr;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
unsigned num_args = 0;
unsigned start_of_implicit_args = 0;
if (const CXXConstructExpr* ctor_expr = DynCastFrom(calling_expr)) {
fn_args = ctor_expr->getArgs();
num_args = ctor_expr->getNumArgs();
} else if (const CallExpr* call_expr = DynCastFrom(calling_expr)) {
fn_args = call_expr->getArgs();
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
num_args = call_expr->getNumArgs();
const Expr* callee_expr = call_expr->getCallee()->IgnoreParenCasts();
const TemplateArgumentListInfo& explicit_tpl_args =
GetExplicitTplArgs(callee_expr);
if (explicit_tpl_args.size() > 0) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
retval = GetTplTypeResugarMapForFunctionExplicitTplArgs(
decl, explicit_tpl_args);
start_of_implicit_args = explicit_tpl_args.size();
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
} else {
// If calling_expr has explicit template args, then consider them.
const TemplateArgumentListInfo& explicit_tpl_args =
GetExplicitTplArgs(calling_expr);
if (explicit_tpl_args.size() > 0) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
retval = GetTplTypeResugarMapForFunctionExplicitTplArgs(
decl, explicit_tpl_args);
retval = ResugarTypeComponents(retval);
}
return retval;
}
// Now we have to figure out, as best we can, the sugar-mappings for
// compiler-deduced template args. We do this by looking at every
// type specified in any part of the function arguments as written.
// If any of these types matches a template type, then we take that
// to be the resugar mapping. If none of the types match, then we
// assume that the template is matching some desugared part of the
// type, and we ignore it. For instance:
// operator<<(basic_ostream<char, T>& o, int i);
// If I pass in an ostream as the first argument, then no part
// of the (sugared) argument types match T, so we ignore it.
const map<const Type*, const Type*>& desugared_types =
GetTplTypeResugarMapForFunctionNoCallExpr(decl, start_of_implicit_args);
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// TODO(csilvers): SubstTemplateTypeParms are always desugared,
// making this less useful than it should be.
// TODO(csilvers): if the GetArg(i) expr has an implicit cast
// under it, take the pre-cast type instead?
set<const Type*> fn_arg_types;
for (unsigned i = 0; i < num_args; ++i) {
const Type* argtype = GetSugaredTypeOf(fn_args[i]);
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// TODO(csilvers): handle RecordTypes that are a TemplateSpecializationDecl
iwyu was egregiously wrong in how it handled template arguments using the 'precomputed cache'. In such situations, it totally ignored the currently active resugar_map, replacing it with one of its own. That worked fine for types outside of templates, but not fine for types inside (such as a 'hash_map<T>' inside a templated class). I "fixed" this. "Fixed" is in quotes because this turned up a whole slew of other problems I don't even attempt to resolve here (though I spent a few hours trying). One is that it's possible to have a type like hash_map that has some arguments that are dependent and some that aren't; in theory, for these types, we can correctly attribute the use to the template author or template instantiator depending on which type it is. But I can't figure out how to get clang to do any meaningful analysis of incomplete (dependent) types, so I've punted on that for now. The second thing wrong is I jumped through all sorts of hoops to handle default template arguments correctly, so if a class has a hash_map<T> and you instantiate T with string, you're also made responsible for hash<string>. This *should* work, but clang is giving hash<string> a type I don't expect (RecordType, not TemplateSpecializationType), and I don't know how to deal with that -- I don't know how to extract the 'string' part of this RecordType. Ugh. I punt on this now, as well. Even in this incomplete form, it's enough to resolve a P1 bug, so I figure it's worth putting in. R=dsturtevant DELTA=141 (97 added, 7 deleted, 37 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=3147
2011-08-31 12:42:23 +01:00
InsertAllInto(GetComponentsOfType(argtype), &fn_arg_types);
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
for (const Type* type : fn_arg_types) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// See if any of the template args in retval are the desugared form of us.
const Type* desugared_type = GetCanonicalType(type);
if (ContainsKey(desugared_types, desugared_type)) {
retval[desugared_type] = type;
if (desugared_type != type) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
VERRS(6) << "Remapping template arg of interest: "
<< PrintableType(desugared_type) << " -> "
<< PrintableType(type) << "\n";
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
}
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// Log the types we never mapped.
for (const auto& types : desugared_types) {
if (!ContainsKey(retval, types.first)) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
VERRS(6) << "Ignoring unseen-in-fn-args template arg of interest: "
<< PrintableType(types.first) << "\n";
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
retval = ResugarTypeComponents(retval); // add in the decomposition of retval
return retval;
}
const NamedDecl* GetInstantiatedFromDecl(const CXXRecordDecl* class_decl) {
if (const ClassTemplateSpecializationDecl* tpl_sp_decl =
DynCastFrom(class_decl)) { // an instantiated class template
PointerUnion<ClassTemplateDecl*, ClassTemplatePartialSpecializationDecl*>
instantiated_from = tpl_sp_decl->getInstantiatedFrom();
if (const ClassTemplateDecl* tpl_decl =
instantiated_from.dyn_cast<ClassTemplateDecl*>()) {
// class_decl is instantiated from a non-specialized template.
return tpl_decl;
} else if (const ClassTemplatePartialSpecializationDecl*
partial_spec_decl =
instantiated_from.dyn_cast<
ClassTemplatePartialSpecializationDecl*>()) {
// class_decl is instantiated from a template partial specialization.
return partial_spec_decl;
}
}
// class_decl is not instantiated from a template.
return class_decl;
}
const NamedDecl* GetDefinitionAsWritten(const NamedDecl* decl) {
// First, get to decl-as-written.
if (const CXXRecordDecl* class_decl = DynCastFrom(decl)) {
decl = GetInstantiatedFromDecl(class_decl);
if (const ClassTemplateDecl* tpl_decl = DynCastFrom(decl))
decl = tpl_decl->getTemplatedDecl(); // convert back to CXXRecordDecl
} else if (const FunctionDecl* func_decl = DynCastFrom(decl)) {
// If we're instantiated from a template, use the template pattern as the
// decl-as-written.
// But avoid friend declarations in templates, something happened in Clang
// r283207 that caused them to form a dedicated redecl chain, separate
// from all other redecls.
const FunctionDecl* tp_decl = func_decl->getTemplateInstantiationPattern();
if (tp_decl && tp_decl->getFriendObjectKind() == Decl::FOK_None)
decl = tp_decl;
}
// Then, get to definition.
if (const NamedDecl* class_dfn = GetTagDefinition(decl)) {
return class_dfn;
} else if (const FunctionDecl* fn_decl = DynCastFrom(decl)) {
for (FunctionDecl::redecl_iterator it = fn_decl->redecls_begin();
it != fn_decl->redecls_end(); ++it) {
if ((*it)->isThisDeclarationADefinition())
return *it;
}
}
// Couldn't find a definition, just return the original declaration.
return decl;
}
bool IsFriendDecl(const Decl* decl) {
// For 'template<...> friend class T', the decl will just be 'class T'.
// We need to go 'up' a level to check friendship in the right place.
if (const CXXRecordDecl* cxx_decl = DynCastFrom(decl))
if (cxx_decl->getDescribedClassTemplate())
decl = cxx_decl->getDescribedClassTemplate();
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 IsInInlineNamespace(const Decl* decl) {
const DeclContext* dc = decl->getDeclContext();
for (; dc; dc = dc->getParent()) {
if (dc->isInlineNamespace())
return true;
}
return false;
}
bool IsForwardDecl(const NamedDecl* decl) {
if (const auto* tag_decl = dyn_cast<TagDecl>(decl)) {
// clang-format off
return (!tag_decl->getName().empty() &&
!tag_decl->isCompleteDefinition() &&
!tag_decl->isEmbeddedInDeclarator() &&
!IsFriendDecl(tag_decl) &&
!IsExplicitInstantiation(tag_decl));
} // clang-format on
return false;
}
// Two possibilities: it's written as a nested class (that is, with a
// qualifier) or it's actually living inside another class.
bool IsNestedClass(const TagDecl* decl) {
if (decl->getQualifier() &&
decl->getQualifier()->getKind() == NestedNameSpecifier::TypeSpec) {
return true;
}
return isa<RecordDecl>(decl->getDeclContext());
}
bool HasDefaultTemplateParameters(const TemplateDecl* decl) {
TemplateParameterList* tpl_params = decl->getTemplateParameters();
return tpl_params->getMinRequiredArguments() < tpl_params->size();
}
template <class T> inline set<const clang::NamedDecl*> GetRedeclsOfRedeclarable(
const clang::Redeclarable<T>* decl) {
return set<const clang::NamedDecl*>(decl->redecls_begin(),
decl->redecls_end());
}
// The only way to find out whether a decl can be dyn_cast to a
// Redeclarable<T> and what T is is to enumerate the possibilities.
// Hence we hard-code the list.
set<const clang::NamedDecl*> GetNonTagRedecls(const clang::NamedDecl* decl) {
CHECK_(!isa<TagDecl>(decl) && "For tag types, call GetTagRedecls()");
CHECK_(!isa<ClassTemplateDecl>(decl) && "For tpls, call GetTagRedecls()");
// TODO(wan): go through iwyu to replace TypedefDecl with
// TypedefNameDecl as needed.
if (const TypedefNameDecl* specific_decl = DynCastFrom(decl))
return GetRedeclsOfRedeclarable(specific_decl);
if (const FunctionDecl* specific_decl = DynCastFrom(decl))
return GetRedeclsOfRedeclarable(specific_decl);
if (const VarDecl* specific_decl = DynCastFrom(decl))
return GetRedeclsOfRedeclarable(specific_decl);
// Not redeclarable, so the output is just the input.
set<const clang::NamedDecl*> retval;
retval.insert(decl);
return retval;
}
set<const NamedDecl*> GetTagRedecls(const NamedDecl* decl) {
const TagDecl* tag_decl = DynCastFrom(decl);
const ClassTemplateDecl* tpl_decl = DynCastFrom(decl);
if (tpl_decl)
tag_decl = tpl_decl->getTemplatedDecl();
if (!tag_decl)
return set<const NamedDecl*>();
set<const NamedDecl*> redecls;
for (TagDecl::redecl_iterator it = tag_decl->redecls_begin();
it != tag_decl->redecls_end(); ++it) {
const auto* redecl = cast<TagDecl>(*it);
// If this decl is a friend decl, don't count it: friend decls
// don't serve as forward-declarations. (This should never
// happen, I think, but it sometimes does due to a clang bug:
// http://llvm.org/bugs/show_bug.cgi?id=8669). The only exception
// is made because every decl is a redecl of itself.
if (IsFriendDecl(redecl) && redecl != decl)
continue;
if (tpl_decl) { // need to convert back to a ClassTemplateDecl
CHECK_(isa<CXXRecordDecl>(redecl) &&
cast<CXXRecordDecl>(redecl)->getDescribedClassTemplate());
const CXXRecordDecl* cxx_redecl = cast<CXXRecordDecl>(redecl);
redecls.insert(cxx_redecl->getDescribedClassTemplate());
} else {
redecls.insert(redecl);
}
}
return redecls;
}
const NamedDecl* GetFirstRedecl(const NamedDecl* decl) {
const NamedDecl* first_decl = decl;
FullSourceLoc first_decl_loc(GetLocation(first_decl), *GlobalSourceManager());
set<const NamedDecl*> all_redecls = GetTagRedecls(decl);
if (all_redecls.empty()) // input is not a class or class template
return nullptr;
for (const NamedDecl* redecl : all_redecls) {
const FullSourceLoc redecl_loc(GetLocation(redecl), *GlobalSourceManager());
if (redecl_loc.isBeforeInTranslationUnitThan(first_decl_loc)) {
first_decl = redecl;
first_decl_loc = redecl_loc;
}
}
return first_decl;
}
const NamedDecl* GetNonfriendClassRedecl(const NamedDecl* decl) {
const RecordDecl* record_decl = DynCastFrom(decl);
const ClassTemplateDecl* tpl_decl = DynCastFrom(decl);
if (tpl_decl)
record_decl = tpl_decl->getTemplatedDecl();
// This check is so we return the input decl whenever possible.
if (!record_decl || !IsFriendDecl(record_decl))
return decl;
set<const NamedDecl*> all_redecls = GetTagRedecls(decl);
CHECK_(!all_redecls.empty() && "Uncaught non-class decl");
return *all_redecls.begin(); // arbitrary choice
}
bool DeclsAreInSameClass(const Decl* decl1, const Decl* decl2) {
if (!decl1 || !decl2)
return false;
if (decl1->getDeclContext() != decl2->getDeclContext())
return false;
return decl1->getDeclContext()->isRecord();
}
bool IsBuiltinFunction(const clang::NamedDecl* decl) {
if (const clang::IdentifierInfo* iden = decl->getIdentifier()) {
unsigned builtin_id = iden->getBuiltinID();
if (builtin_id != 0) {
const clang::Builtin::Context& ctx = decl->getASTContext().BuiltinInfo;
return !ctx.isPredefinedLibFunction(builtin_id) &&
!ctx.isHeaderDependentFunction(builtin_id);
}
}
return false;
}
// --- Utilities for Type.
const Type* GetTypeOf(const Expr* expr) {
return expr->getType().getTypePtr();
}
const Type* GetTypeOf(const CXXConstructExpr* expr) {
const Type* type = expr->getType().getTypePtr();
if (const clang::ArrayType* array_type = type->getAsArrayTypeUnsafe()) {
type = array_type->getElementType().getTypePtr();
}
return type;
}
const Type* GetTypeOf(const ValueDecl* decl) {
return decl->getType().getTypePtr();
}
const Type* GetTypeOf(const TypeDecl* decl) {
return decl->getTypeForDecl();
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
const Type* GetCanonicalType(const Type* type) {
QualType canonical_type = type->getCanonicalTypeUnqualified();
return canonical_type.getTypePtr();
}
// Based on Type::getUnqualifiedDesugaredType.
const Type* Desugar(const Type* type) {
// Null types happen sometimes in IWYU.
if (!type) {
return type;
}
const Type* cur = type;
while (true) {
// Don't desugar types that (potentially) add a name.
if (cur->getTypeClass() == Type::Typedef ||
cur->getTypeClass() == Type::TemplateSpecialization) {
return cur;
}
switch (cur->getTypeClass()) {
#define ABSTRACT_TYPE(Class, Parent)
#define TYPE(Class, Parent) \
case Type::Class: { \
const auto* derived = cast<clang::Class##Type>(cur); \
if (!derived->isSugared()) { \
return cur; \
} \
cur = derived->desugar().getTypePtr(); \
break; \
}
#include "clang/AST/TypeNodes.inc"
}
}
}
bool IsTemplatizedType(const Type* type) {
return (type && isa<TemplateSpecializationType>(Desugar(type)));
}
bool IsClassType(const clang::Type* type) {
type = Desugar(type);
return (type &&
(isa<TemplateSpecializationType>(type) || isa<RecordType>(type)));
}
bool InvolvesTypeForWhich(const Type* type,
std::function<bool(const Type*)> pred) {
type = Desugar(type);
if (pred(type))
return true;
const Decl* decl = TypeToDeclAsWritten(type);
if (const auto* cts_decl =
dyn_cast_or_null<ClassTemplateSpecializationDecl>(decl)) {
const TemplateArgumentList& tpl_args = cts_decl->getTemplateArgs();
for (const TemplateArgument& tpl_arg : tpl_args.asArray()) {
if (const Type* arg_type = GetTemplateArgAsType(tpl_arg)) {
if (InvolvesTypeForWhich(arg_type, pred)) {
return true;
}
}
}
}
return false;
}
bool IsPointerOrReferenceAsWritten(const Type* type) {
type = Desugar(type);
return isa<PointerType>(type) || isa<LValueReferenceType>(type);
}
const Type* RemovePointersAndReferencesAsWritten(const Type* type) {
type = Desugar(type);
while (isa<PointerType>(type) ||
isa<LValueReferenceType>(type)) {
type = type->getPointeeType().getTypePtr();
}
return type;
}
const Type* RemovePointerFromType(const Type* type) {
if (!IsPointerOrReferenceAsWritten(type)) { // ah well, have to desugar
type = type->getUnqualifiedDesugaredType();
}
if (!IsPointerOrReferenceAsWritten(type)) {
return nullptr;
}
type = Desugar(type);
type = type->getPointeeType().getTypePtr();
return type;
}
// This follows typedefs/etc to remove pointers, if necessary.
const Type* RemovePointersAndReferences(const Type* type) {
while (true) {
const Type* deref_type = RemovePointerFromType(type);
if (deref_type == nullptr) // type wasn't a pointer (or reference) type
break; // removed all pointers
type = deref_type;
}
return type;
}
static const NamedDecl* TypeToDeclImpl(const Type* type, bool as_written) {
// Read past SubstTemplateTypeParmType (this can happen if a function
// template returns the tpl-arg type: e.g. for 'T MyFn<T>() {...}; MyFn<X>.a',
// the type of MyFn<X> will be a substitution) as well as any elaboration.
type = Desugar(type);
CHECK_(!isa<ObjCObjectType>(type) && "IWYU doesn't support Objective-C");
// We have to be a bit careful about the order, because we want
// 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 (template_decl) {
// A non-concrete template class, such as 'Myclass<T>'
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;
return nullptr;
} else {
return nullptr;
}
}
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();
else
return type;
}
bool HasImplicitConversionConstructor(const Type* type) {
type = Desugar(type);
if (isa<PointerType>(type))
return false; // can't implicitly convert to a pointer
if (isa<LValueReferenceType>(type) &&
!type->getPointeeType().isConstQualified())
return false; // can't implicitly convert to a non-const reference
type = RemoveReferenceAsWritten(type);
const NamedDecl* decl = TypeToDeclAsWritten(type);
if (!decl) // not the kind of type that has a decl (e.g. built-in)
return false;
const CXXRecordDecl* cxx_class = DynCastFrom(decl);
if (!cxx_class)
return false; // can't implicitly convert to a non-class type
return HasImplicitConversionCtor(cxx_class);
}
map<const clang::Type*, const clang::Type*>
GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
map<const Type*, const Type*> retval;
const auto* tpl_spec_type = type->getAs<TemplateSpecializationType>();
if (!tpl_spec_type) {
return retval;
}
// Pull the template arguments out of the specialization type. If this is
// a ClassTemplateSpecializationDecl specifically, we want to
// get the arguments therefrom to correctly handle default arguments.
llvm::ArrayRef<TemplateArgument> tpl_args = tpl_spec_type->template_arguments();
unsigned num_args = tpl_args.size();
const NamedDecl* decl = TypeToDeclAsWritten(tpl_spec_type);
const auto* cls_tpl_decl = dyn_cast<ClassTemplateSpecializationDecl>(decl);
if (cls_tpl_decl) {
const TemplateArgumentList& tpl_arg_list =
cls_tpl_decl->getTemplateInstantiationArgs();
tpl_args = tpl_arg_list.asArray();
num_args = tpl_arg_list.size();
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// TemplateSpecializationType only includes explicitly specified
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// types in its args list, so we start with that. Note that an
// explicitly specified type may fulfill multiple template args:
// template <typename R, typename A1> struct Foo<R(A1)> { ... }
set<unsigned> explicit_args; // indices into tpl_args we've filled
TypeEnumerator type_enumerator;
for (unsigned i = 0; i < tpl_spec_type->template_arguments().size(); ++i) {
set<const Type*> arg_components =
type_enumerator.Enumerate(tpl_spec_type->template_arguments()[i]);
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// Go through all template types mentioned in the arg-as-written,
// and compare it against each of the types in the template decl
// (the latter are all desugared). If there's a match, update
// the mapping.
for (const Type* type : arg_components) {
for (unsigned i = 0; i < num_args; ++i) {
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
if (const Type* arg_type = GetTemplateArgAsType(tpl_args[i])) {
if (GetCanonicalType(type) == GetCanonicalType(arg_type)) {
retval[arg_type] = type;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
VERRS(6) << "Adding a template-class type of interest: "
<< PrintableType(arg_type) << " -> " << PrintableType(type)
<< "\n";
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
explicit_args.insert(i);
}
}
}
}
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
// Now take a look at the args that were not filled explicitly.
for (unsigned i = 0; i < num_args; ++i) {
if (ContainsKey(explicit_args, i))
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
continue;
if (const Type* arg_type = GetTemplateArgAsType(tpl_args[i])) {
retval[arg_type] = nullptr;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
VERRS(6) << "Adding a template-class default type of interest: "
<< PrintableType(arg_type) << "\n";
}
}
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
return retval;
}
map<const clang::Type*, const clang::Type*> GetTplTypeResugarMapForClass(
const clang::Type* type) {
return ResugarTypeComponents( // add in the decomposition of retval
GetTplTypeResugarMapForClassNoComponentTypes(type));
}
bool CanBeOpaqueDeclared(const clang::EnumType* type) {
return type->getDecl()->isFixed();
}
// --- Utilities for Stmt.
bool IsAddressOf(const Expr* expr) {
if (const UnaryOperator* unary = DynCastFrom(expr->IgnoreParens()))
return unary->getOpcode() == clang::UO_AddrOf;
return false;
}
const Type* TypeOfParentIfMethod(const CallExpr* expr) {
// callee_expr is a MemberExpr if we're a normal class method, or
// DeclRefExpr if we're a static class method or an overloaded operator.
const Expr* callee_expr = expr->getCallee()->IgnoreParenCasts();
if (const MemberExpr* member_expr = DynCastFrom(callee_expr)) {
const Type* class_type = GetTypeOf(member_expr->getBase());
// For class->member(), class_type is a pointer.
return RemovePointersAndReferencesAsWritten(class_type);
} else if (const DeclRefExpr* ref_expr = DynCastFrom(callee_expr)) {
if (ref_expr->getQualifier()) { // static methods like C<T>::a()
return ref_expr->getQualifier()->getAsType();
}
}
return nullptr;
}
const Expr* GetFirstClassArgument(CallExpr* expr) {
if (const FunctionDecl* callee_decl = expr->getDirectCallee()) {
if (isa<CXXMethodDecl>(callee_decl)) {
// If a method is called, return 'this'.
return expr->getArg(0);
}
// Handle free functions.
CHECK_(callee_decl->getNumParams() == expr->getNumArgs() &&
"Require one-to-one match between call arguments and decl parameters");
int params_count = callee_decl->getNumParams();
for (int i = 0; i < params_count; i++) {
// View argument types from the perspective of function declaration,
// not from the caller's perspective. For example, function parameter
// can have template type but function argument is not necessarily
// a template when the function is called.
const Type* param_type = GetTypeOf(callee_decl->getParamDecl(i));
param_type = RemovePointersAndReferencesAsWritten(param_type);
// Make sure we do the right thing given a function like
// template <typename T> void operator>>(const T& x, ostream& os);
// In this case ('myclass >> os'), we want to be returning the
// type of os, not of myclass, and we do, because myclass will be
// a SubstTemplateTypeParmType, not a RecordType.
if (isa<SubstTemplateTypeParmType>(param_type))
continue;
// See through typedefs.
param_type = param_type->getUnqualifiedDesugaredType();
if (isa<RecordType>(param_type) ||
isa<TemplateSpecializationType>(param_type)) {
return expr->getArg(i);
}
}
}
return nullptr;
}
const CXXDestructorDecl* GetDestructorForDeleteExpr(const CXXDeleteExpr* expr) {
const Type* type = expr->getDestroyedType().getTypePtrOrNull();
// type is nullptr when deleting a dependent type: 'T foo; delete foo'
if (type == nullptr)
return nullptr;
const NamedDecl* decl = TypeToDeclAsWritten(type);
if (const CXXRecordDecl* cxx_record = DynCastFrom(decl))
return cxx_record->getDestructor();
return nullptr;
}
const CXXDestructorDecl* GetSiblingDestructorFor(
const CXXConstructorDecl* ctor) {
return ctor ? ctor->getParent()->getDestructor() : nullptr;
}
const CXXDestructorDecl* GetSiblingDestructorFor(
const CXXConstructExpr* ctor_expr) {
return GetSiblingDestructorFor(ctor_expr->getConstructor());
}
const FunctionType* GetCalleeFunctionType(CallExpr* expr) {
const Type* callee_type = expr->getCallee()->getType().getTypePtr();
if (const PointerType* ptr_type = callee_type->getAs<PointerType>()) {
callee_type = ptr_type->getPointeeType().getTypePtr();
} else if (const BlockPointerType* bptr_type =
callee_type->getAs<BlockPointerType>()) {
callee_type = bptr_type->getPointeeType().getTypePtr();
} else if (const MemberPointerType* mptr_type =
callee_type->getAs<MemberPointerType>()) {
callee_type = mptr_type->getPointeeType().getTypePtr();
}
return callee_type->getAs<FunctionType>();
}
TemplateArgumentListInfo GetExplicitTplArgs(const Expr* expr) {
TemplateArgumentListInfo explicit_tpl_args;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
if (const DeclRefExpr* decl_ref = DynCastFrom(expr))
decl_ref->copyTemplateArgumentsInto(explicit_tpl_args);
else if (const MemberExpr* member_expr = DynCastFrom(expr))
member_expr->copyTemplateArgumentsInto(explicit_tpl_args);
else if (const OverloadExpr* overload_expr = DynCastFrom(expr))
overload_expr->copyTemplateArgumentsInto(explicit_tpl_args);
else if (const DependentScopeDeclRefExpr* dependent_decl_ref = DynCastFrom(expr))
dependent_decl_ref->copyTemplateArgumentsInto(explicit_tpl_args);
return explicit_tpl_args;
A major revamp of the way we handle template arguments. The crux of the change is that the InstantiatedTemplateVisitor no longer takes a set of types-of-interest, but instead takes a map called the resugar_map. The resugar map is a tool to deal with the fact that clang canonicalizes all substituted template types (the "T"'s in a written template), so if you say typedef int MyTypedef; template<class T> void MyFunc() { T foo; } MyFunc<MyTypedef>(); clang will say the body is 'int foo;', not 'MyTypedef foo;'. This is difficult for include-what-you-use. There's one entry in the resugar map for every template parameter. Each entry has the form <canonical-type, type-as-written>, to make it easy to map from the canonical type back to the type-as-written. When the type-as-written has component types (e.g. both Foo* and vector<Foo> have a component type of Foo), there is also an entry for each component type. Both template classes and template functions have a complicating factor. For template classes, it's default template args, which are *not* written by the caller and usually want to attribute to the function-author. We store these in the resugar map with a value of NULL, to indicate they are default arguments that have no as-written form. For template functions, likewise some or all template arguments may be omitted by the caller, in which case the compiler derives them from the function arguments. We do something similar, looking for plausible mappings between types-as-written in template functions (or their components) and the clang-derived template arguments. This part could still use improvement. To really do it right, we'd need to refactor SemaTemplateDeduction to take an argument to not canonicalize deduced template arguments. In InstantiatedTemplateVisitor, we use the resugar-map to beef up CanIgnoreType() and ReportTypeUse(). We ignore types that are not in the resugar map (and thus do not correspond to template arguments as typed). When we do use a type, we resugar it before reporting its use. The net result is that we should see much lower incidence of clang reporting a weird dependency because of a type that the template-caller has never even heard of. We also use the fact we can tell an argument is a default template argument to decide if the template-author or the template-caller is responsible for the type. We say the template-caller is *unless* the author intends-to-provide the type, based on #includes. This handles the case when hash<T> uses the default implementation (in stl_hash, which stl_hashtable.h #includes) vs when it uses a user-provided implementation (which stl_hashtable.h obviously doesn't #include). Implementation-wise, we needed to beef up HandleFunctionCall to pass around the calling Expr, which holds the template arguments as written in some cases. We also needed to update the cache to handle the new data structures. Administrative note: wan reviewed this but had to bow out before finishing all the back-and-forth, and dsturtevant reviewed it but didn't feel qualified to judge entirely, so take the "R=" below with a grain of salt. This may require more work in the future. R=wan,dsturtevant DELTA=1173 (852 added, 138 deleted, 183 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=1077
2011-03-26 22:16:53 +00:00
}
string GetKindName(const Decl* decl) {
return string(decl->getDeclKindName()) + "Decl";
}
string GetKindName(const Stmt* stmt) {
return stmt->getStmtClassName();
}
string GetKindName(const Type* type) {
return string(type->getTypeClassName()) + "Type";
}
string GetKindName(const TypeLoc typeloc) {
return string(typeloc.getTypePtr()->getTypeClassName()) + "TypeLoc";
}
bool IsDeclaredInsideFunction(const Decl* decl) {
const DeclContext* decl_ctx = decl->getDeclContext();
return isa<FunctionDecl>(decl_ctx);
}
} // namespace include_what_you_use