include-what-you-use/iwyu_cache.h

172 lines
7.0 KiB
C
Raw Permalink Normal View History

//===--- iwyu_cache.h - 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.
//
//===----------------------------------------------------------------------===//
// This file holds caches for information that clang might have to use
// many times, but is expensive to compute. For now, the only cache
// is the 'instantiation cache': when instantiating a template, what
// methods are called, and what template arguments are fully used?
#ifndef INCLUDE_WHAT_YOU_USE_IWYU_CACHE_H_
#define INCLUDE_WHAT_YOU_USE_IWYU_CACHE_H_
#include <map> // for map
#include <set> // for set
#include <utility> // for pair
2019-12-26 12:18:47 +00:00
#include "iwyu_port.h" // for CHECK_
#include "iwyu_stl_util.h"
namespace clang {
class NamedDecl;
class TemplateSpecializationType;
class Type;
}
namespace include_what_you_use {
using std::map;
using std::pair;
using std::set;
// This cache is used to store 'full use information' for a given
// templated function call or type instantiation:
// 1) If you call MyClass<Foo, Bar>::baz(), what template arguments
// do you need the full type information for? (Might be Foo, Bar,
// both, or neither, depending on what baz() does.)
// 2) If you do 'sizeof(MyClass<Foo, Bar>)', what template arguments
// do you need the full type information for? (Might be Foo,
// Bar, both, or neither, depending on what fields MyClass has.)
// 3) If you call MyClass<Foo, Bar>::baz(), what methods are called
// indirectly? (ie baz() might call another method MyClass::bar(),
// or OtherClass::foo().) This is used to detect cases where we
// call a method that is written in a different file from the
// class it belongs to -- we will need to #include the file with
// that method in it. The method called may well depend on the
// template arguments, hence the need to instantiate.
// For each of these, the answer is always the same for the given
// Decl (function call or class instantiation) with the same template
// args. So we store this info in a cache, since it's very expensive
// to compute.
class FullUseCache {
public:
// The first part of the key is the decl or type that we're
// caching reporting-info for. Since what we report depends on
// what the types-of-interest were, we store that in the key too.
typedef pair<const void*,
map<const clang::Type*, const clang::Type*>> Key;
// The value are the types and decls we reported.
typedef pair<const set<const clang::Type*>,
const set<const clang::NamedDecl*>> Value;
void Insert(const void* decl_or_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
const map<const clang::Type*, const clang::Type*>& resugar_map,
const set<const clang::Type*>& reported_types,
const set<const clang::NamedDecl*>& reported_decls) {
// TODO(csilvers): should in_forward_declare_context() be in Key too?
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
cache_.insert(pair<Key,Value>(Key(decl_or_type, resugar_map),
Value(reported_types, reported_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
// resguar_map is the 'uncanonicalize' map for the template
// arguments used to instantiate this template.
bool Contains(
const void* key,
const map<const clang::Type*, const clang::Type*>& resugar_map) const {
return ContainsKey(cache_, Key(key, resugar_map));
}
// You must call Contains() before calling these, to make sure the
// key is in the cache.
const set<const clang::Type*>& GetFullUseTypes(
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 void* key,
const map<const clang::Type*, const clang::Type*>& resugar_map) const {
const Value* value = FindInMap(&cache_, Key(key, resugar_map));
CHECK_(value && "Must call Contains() before calling GetFullUseTypes()");
return value->first;
}
const set<const clang::NamedDecl*>& GetFullUseDecls(
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 void* key,
const map<const clang::Type*, const clang::Type*>& resugar_map) const {
const Value* value = FindInMap(&cache_, Key(key, resugar_map));
CHECK_(value && "Must call Contains() before calling GetFullUseDecls()");
return value->second;
}
// In addition to the normal cache, which is filled via Insert()
// calls, we also have a special, hard-coded cache holding full-use
// type information for common STL types. Note that since we only
// have full-use type information, and not full-use decl
// information, this cache is only appropriate when instantiating a
// type ('sizeof(vector<MyClass>)'), not when making a function call
// ('myclass_vector->clear()').
// NOTE: because this cache is hard-coded, the types may not be
// sugared properly: the output might be 'MyUnderlyingType' when the
// input is 'vector<MyTypedef>'. You will have to resugar yourself.
// That is why this is implemented in a different function, and not
// available via GetFullUseType(), which does not have this problem
// with sugaring.
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 clang::Type*, const clang::Type*> GetPrecomputedResugarMap(
const clang::TemplateSpecializationType* tpl_type);
private:
map<Key, Value> cache_;
};
// This class allows us to update multiple cache entries at once.
// For instance, suppose A<Foo, Bar>() calls B<Foo, Bar>(), which
// requires the full type info for Foo. Then we want to add a cache
// entry (B, Foo) ("B requires the full type info for Foo"), but we
// also want to add a cache entry (A, Foo) ("A requires the full
// type info for Foo", due to its calling B). The way we do this is
// whenever we enter a function -- or instantiate a type -- we add
// it to our set of 'currently active functions', cache_storers_.
// Whenever we decide we need full type info for some type Foo, we
// add a new cache entry for every function/type in cache_storers_.
class CacheStoringScope {
public:
CacheStoringScope(set<CacheStoringScope*>* cache_storers,
FullUseCache* cache,
const void* key,
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 map<const clang::Type*, const clang::Type*>& resugar)
: cache_storers_(cache_storers), cache_(cache),
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
key_(key), resugar_map_(resugar) {
// Register ourselves so ReportDeclUse() and ReportTypeUse()
// will call back to us.
cache_storers_->insert(this);
}
~CacheStoringScope() {
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
cache_->Insert(key_, resugar_map_, reported_types_, reported_decls_);
cache_storers_->erase(this);
}
// These are what ReportDeclUse() and ReportTypeUse() call to
// populate this cache entry.
void NoteReportedType(const clang::Type* type) {
reported_types_.insert(type);
}
void NoteReportedDecl(const clang::NamedDecl* decl) {
reported_decls_.insert(decl);
}
private:
set<CacheStoringScope*>* const cache_storers_;
FullUseCache* const cache_;
const void* const key_;
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 map<const clang::Type*, const clang::Type*>& resugar_map_;
set<const clang::Type*> reported_types_;
set<const clang::NamedDecl*> reported_decls_;
};
} // namespace include_what_you_use
#endif // INCLUDE_WHAT_YOU_USE_IWYU_CACHE_H_