blob: 3671cd6d19fb00b6f3b8cc5d5244ac77101bc22e [file] [log] [blame]
//===--- 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 DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_IWYU_CACHE_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_IWYU_CACHE_H_
#include <map> // for map
#include <set> // for set
#include <utility> // for pair
#include "iwyu_stl_util.h"
#include "port.h" // for CHECK_
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,
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);
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 // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_IWYU_CACHE_H_