include-what-you-use/iwyu_cache.cc

92 lines
3.4 KiB
C++
Raw Permalink Normal View History

//===--- iwyu_cache.cc - cache of AST-derived information, for iwyu -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "iwyu_cache.h"
#include <set>
#include <string>
#include "iwyu_ast_util.h"
#include "iwyu_stl_util.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
using clang::ClassTemplateSpecializationDecl;
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
using clang::NamedDecl;
using clang::Type;
using clang::TemplateArgument;
using clang::TemplateArgumentList;
using clang::TemplateSpecializationType;
using std::set;
using std::string;
namespace include_what_you_use {
// These are types that all have the same property: instantiating
// the type requires full use of all explicitly-listed template
// arguments, and full use of all default templated arguments that
// the class does not intend-to-provide. (So vector<MyClass>
// required full use of MyClass, but not of allocator<MyClass>).
// TODO(csilvers): put this somewhere easier to modify, and add to it.
static const char* const kFullUseTypes[] = {
"__gnu_cxx::hash_map",
"__gnu_cxx::hash_multimap",
"__gnu_cxx::hash_multiset",
"__gnu_cxx::hash_set",
"std::deque",
"std::list",
"std::map",
"std::multimap",
"std::multiset",
"std::set",
"std::slist",
"std::vector",
};
// If the passed-in tpl_decl is one of the classes we have hard-coded
// the full-use type information for, populate the cache with the
// appropriate full-use type information for the given instantiation.
// Note that we only populate the cache for class member uses, not
// function calls -- that is, we can use the hard-coded data to say
// full use-info for 'sizeof(vector<MyClass>)', but not for
// 'myclass_vector.clear();'. This is because the former never tries
// to instantiate methods, making the hard-coding much easier.
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*> FullUseCache::GetPrecomputedResugarMap(
const TemplateSpecializationType* tpl_type) {
static const int fulluse_size = (sizeof(kFullUseTypes) /
sizeof(*kFullUseTypes));
static const set<string> fulluse_types(kFullUseTypes,
kFullUseTypes + fulluse_size);
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
const NamedDecl* tpl_decl = TypeToDeclAsWritten(tpl_type);
if (!ContainsKey(fulluse_types, GetWrittenQualifiedNameAsString(tpl_decl)))
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 map<const Type*, const Type*>();
// The code below doesn't handle template-template args/etc. None
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
// of the types in kFullUseTypes should have those. Just verify,
// when we can.
if (const ClassTemplateSpecializationDecl* tpl_spec_decl =
DynCastFrom(tpl_decl)) {
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
const TemplateArgumentList& all_tpl_args = tpl_spec_decl->getTemplateArgs();
for (unsigned i = 0; i < all_tpl_args.size(); ++i) {
CHECK_((all_tpl_args.get(i).getKind() == TemplateArgument::Type)
&& "kFullUseType types must contain only 'type' template 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
// The default resugar-map works correctly for all these types (by
// design): we fully use all template types. (Note: we'll have to
// do something more clever here if any types in kFullUseTypes start
// accepting template-template types.)
return GetTplTypeResugarMapForClassNoComponentTypes(tpl_type);
}
} // namespace include_what_you_use