forked from include-what-you-use/include-what-you-use
-
Notifications
You must be signed in to change notification settings - Fork 0
/
iwyu_cache.h
175 lines (153 loc) · 7.15 KB
/
iwyu_cache.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//===--- 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
#include "clang/AST/Type.h"
#include "iwyu_port.h" // for CHECK_
#include "iwyu_stl_util.h"
// TODO: Clean out pragmas as IWYU improves.
// IWYU pragma: no_include <iterator>
namespace clang {
class LangOptions;
class NamedDecl;
}
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,
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?
cache_.insert(pair<Key,Value>(Key(decl_or_type, resugar_map),
Value(reported_types, reported_decls)));
}
// 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(
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(
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.
static map<const clang::Type*, const clang::Type*> GetPrecomputedResugarMap(
const clang::TemplateSpecializationType* tpl_type,
const clang::LangOptions&);
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,
const map<const clang::Type*, const clang::Type*>& resugar)
: cache_storers_(cache_storers), cache_(cache),
key_(key), resugar_map_(resugar) {
// Register ourselves so ReportDeclUse() and ReportTypeUse()
// will call back to us.
cache_storers_->insert(this);
}
~CacheStoringScope() {
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_;
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_