include-what-you-use/iwyu.cc

4346 lines
178 KiB
C++
Raw Normal View History

//===--- iwyu.cc - main logic and driver 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.
//
//===----------------------------------------------------------------------===//
// A Clang-based tool that catches Include-What-You-Use violations:
//
// The analysis enforces the following rule:
//
// - For every symbol (variable, function, constant, type, and macro)
// X in C++ file CU.cc, X must be declared in CU.cc or in a header
// file directly included by itself, CU.h, or CU-inl.h. Likewise
// for CU.h and CU-inl.h.
//
// The rule has a few slight wrinkles:
// 1) CU_test.cc can also 'inherit' #includes from CU.h and CU-inl.h.
// Likewise for CU_unittest.cc, CU_regtest.cc, etc.
// 2) CU.cc can inherit #includes from ../public/CU.h in addition to
// ./CU.h (likewise for -inl.h).
// 3) For system #includes, and a few google #includes, we hard-code
// in knowledge of which #include files are public and which are not.
// (For instance, <vector> is public, <bits/stl_vector.h> is not.)
// We force CU.cc, CU.h, and CU-inl.h to #include the public version.
//
// iwyu.cc checks if a symbol can be forward-declared instead of fully
// declared. If so, it will enforce the rule that the symbol is
// forward-declared in the file that references it. We only forward-
// declare classes and structs (possibly templatized). We will not
// try to forward-declare variables or functions.
//
// Checking iwyu violations for variables, functions, constants, and
// macros is easy. Types can be trickier. Obviously, if you declare
// a variable of type Foo in cu.cc, it's straightforward to check
// whether it's an iwyu violation. But what if you call a function
// that returns a type, e.g. 'if (FnReturningSomeSTLType()->empty())'?
// Is it an iwyu violation if you don't #include the header for that
// STL type? We say no: whatever file provided the function
// FnReturningSomeSTLType is also responsible for providing whatever
// the STL type is, so we don't have to. Otherwise, we get into an
// un-fun propagation problem if we change the signature of
// FnReturningSomeSTLType to return a different type of STL type. So
// in general, types are only iwyu-checked if they appear explicitly
// in the source code.
//
// It can likewise be difficult to say whether a template arg is
// forward-declable: set<Foo*> x does not require the full type info
// for Foo, but remove_pointer<Foo*>::type does. And f<Foo>() doesn't
// require full type info for Foo if f doesn't actually use Foo in it.
// For now we do the simple heuristic that if the template arg is a
// pointer, it's ok if it's forward-declared, and if not, it's not.
//
// We use the following terminology:
//
// - A *symbol* is the name of a function, variable, constant, type,
// macro, etc.
//
// - A *quoted include path* is an include path with the surrounding <>
// or "", e.g. <stdio.h> or "ads/util.h".
//
// - Any #include falls into exactly one of three (non-overlapping)
// categories:
//
// * it's said to be *necessary* if it's a compiler or IWYU error to
// omit the #include;
//
// * it's said to be *optional* if the #include is unnecessary but
// having it is not an IWYU error either (e.g. if bar.h is a
// necessary #include of foo.h, and foo.cc uses symbols from
// bar.h, it's optional for foo.cc to #include bar.h.);
//
// * it's said to be *undesired* if it's an IWYU error to have the
// #include.
//
// Therefore, when we say a #include is *desired*, we mean that it's
// either necessary or optional.
//
// - We also say that a #include is *recommended* if the IWYU tool
// recommends to have it in the C++ source file. Obviously, any
// necessary #include must be recommended, and no undesired
// #include can be recommended. An optional #include can be
// either recommended or not -- the IWYU tool can decide which
// case it is. For example, if foo.cc desires bar.h, but can
// already get it via foo.h, IWYU won't recommend foo.cc to
// #include bar.h, unless it already does so.
#include <algorithm> // for swap, find, make_pair
#include <cstddef> // for size_t
#include <cstdlib> // for atoi, exit
#include <cstring>
#include <deque> // for swap
#include <iterator> // for find
#include <list> // for swap
#include <map> // for map, swap, etc
#include <memory> // for unique_ptr
#include <set> // for set, set<>::iterator, swap
#include <string> // for string, operator+, etc
#include <utility> // for pair, make_pair
#include <vector> // for vector, swap
#include "iwyu_ast_util.h"
#include "iwyu_cache.h"
#include "iwyu_globals.h"
#include "iwyu_lexer_utils.h"
#include "iwyu_location_util.h"
#include "iwyu_output.h"
#include "iwyu_path_util.h"
2019-12-26 12:18:47 +00:00
#include "iwyu_port.h" // for CHECK_
#include "iwyu_preprocessor.h"
#include "iwyu_stl_util.h"
#include "iwyu_string_util.h"
2019-12-26 12:18:47 +00:00
#include "iwyu_use_flags.h"
#include "iwyu_verrs.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclTemplate.h"
2022-07-22 15:03:33 +01:00
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/Sema.h"
namespace clang {
class FileEntry;
class PPCallbacks;
} // namespace clang
namespace include_what_you_use {
// I occasionally clean up this list by running:
// $ grep "using clang":: iwyu.cc | cut -b14- | tr -d ";" | while read t; do grep -q "[^:]$t" iwyu.cc || echo $t; done
using clang::ASTConsumer;
using clang::ASTContext;
using clang::ASTFrontendAction;
using clang::Attr;
using clang::CXXConstructExpr;
using clang::CXXConstructorDecl;
using clang::CXXDeleteExpr;
using clang::CXXDestructorDecl;
using clang::CXXMethodDecl;
using clang::CXXNewExpr;
using clang::CXXOperatorCallExpr;
using clang::CXXRecordDecl;
using clang::CallExpr;
using clang::ClassTemplateDecl;
using clang::ClassTemplateSpecializationDecl;
using clang::CompilerInstance;
2022-07-22 15:03:33 +01:00
using clang::ConceptSpecializationExpr;
using clang::ConstructorUsingShadowDecl;
using clang::Decl;
using clang::DeclContext;
using clang::DeclRefExpr;
using clang::DeducedTemplateSpecializationType;
using clang::EnumConstantDecl;
using clang::EnumDecl;
using clang::EnumType;
using clang::Expr;
using clang::FileEntry;
using clang::FriendDecl;
using clang::FriendTemplateDecl;
using clang::FunctionDecl;
using clang::FunctionProtoType;
using clang::FunctionTemplateDecl;
using clang::FunctionType;
using clang::LValueReferenceType;
using clang::LinkageSpecDecl;
using clang::MemberExpr;
using clang::NamedDecl;
using clang::NestedNameSpecifier;
using clang::NestedNameSpecifierLoc;
using clang::OverloadExpr;
using clang::ParmVarDecl;
using clang::PPCallbacks;
using clang::PointerType;
using clang::QualType;
using clang::QualifiedTypeLoc;
using clang::RecordDecl;
using clang::RecursiveASTVisitor;
using clang::ReferenceType;
using clang::Sema;
using clang::SourceLocation;
using clang::Stmt;
using clang::SubstTemplateTypeParmType;
using clang::TagDecl;
using clang::TagType;
using clang::TemplateArgument;
using clang::TemplateArgumentList;
using clang::TemplateArgumentLoc;
using clang::TemplateDecl;
using clang::TemplateName;
using clang::TemplateSpecializationType;
using clang::TemplateSpecializationTypeLoc;
using clang::TranslationUnitDecl;
using clang::Type;
using clang::TypeDecl;
using clang::TypeLoc;
using clang::TypedefDecl;
using clang::TypedefNameDecl;
using clang::TypedefType;
using clang::UnaryExprOrTypeTraitExpr;
using clang::UsingDecl;
using clang::UsingDirectiveDecl;
using clang::UsingShadowDecl;
using clang::ValueDecl;
using clang::VarDecl;
using llvm::cast;
using llvm::dyn_cast;
using llvm::dyn_cast_or_null;
using llvm::errs;
using llvm::isa;
using std::make_pair;
using std::map;
using std::set;
using std::string;
using std::swap;
using std::vector;
namespace {
bool CanIgnoreLocation(SourceLocation loc) {
// If we're in a macro expansion, we always want to treat this as
// being in the expansion location, never the as-written location,
// since that's what the compiler does. CanIgnoreCurrentASTNode()
// is an optimization, so we want to be conservative about what we
// ignore.
const FileEntry* file_entry = GetFileEntry(loc);
const FileEntry* file_entry_after_macro_expansion =
GetFileEntry(GetInstantiationLoc(loc));
// ignore symbols used outside foo.{h,cc} + check_also
return (!ShouldReportIWYUViolationsFor(file_entry) &&
!ShouldReportIWYUViolationsFor(file_entry_after_macro_expansion));
}
} // anonymous namespace
// ----------------------------------------------------------------------
// --- BaseAstVisitor
// ----------------------------------------------------------------------
//
// We have a hierarchy of AST visitor classes, to keep the logic
// as clear as possible. The top level, BaseAstVisitor, has some
// basic, not particularly iwyu-related, functionality:
//
// 1) Maintain current_ast_node_, the current chain in the AST tree.
// 2) Provide functions related to the current location.
// 3) Print each node we're visiting, depending on --verbose settings.
// 4) Add appropriate implicit text. iwyu acts as if all constructor
// initializers were explicitly written, all default constructors
// were explicitly written, etc, even if they're not. We traverse
// the implicit stuff as if it were explicit.
// 5) Add two callbacks that subclasses can override (just like any
// other AST callback): TraverseImplicitDestructorCall and
// HandleFunctionCall. TraverseImplicitDestructorCall is a
// callback for a "pseudo-AST" node that covers destruction not
// specified in source, such as a destructor destroying one of the
// fields in its class. HandleFunctionCall is a convenience
// callback that bundles callbacks from many different kinds of
// function-calling callbacks (CallExpr, CXXConstructExpr, etc)
// into one place.
//
// To maintain current_ast_node_ properly, this class also implements
// VisitNestedNameSpecifier, VisitTemplateName, VisitTemplateArg, and
// VisitTemplateArgLoc, which are parallel to the Visit*Decl()/etc
// visitors. Subclasses should override these Visit routines, and not
// the Traverse routine directly.
template <class Derived>
class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
public:
typedef RecursiveASTVisitor<Derived> Base;
// We need to create implicit ctor/dtor nodes, which requires
// non-const methods on CompilerInstance, so the var can't be const.
explicit BaseAstVisitor(CompilerInstance* compiler)
: compiler_(compiler),
current_ast_node_(nullptr) {}
virtual ~BaseAstVisitor() = default;
//------------------------------------------------------------
// Pure virtual methods that a subclass must implement.
// Returns true if we are not interested in the current ast node for
// any reason (for instance, it lives in a file we're not
// analyzing).
virtual bool CanIgnoreCurrentASTNode() const = 0;
// Returns true if we should print the information for the
// current AST node, given what file it's in. For instance,
// except at very high verbosity levels, we don't print AST
// nodes from system header files.
virtual bool ShouldPrintSymbolFromCurrentFile() const = 0;
// A string to add to the information we print for each symbol.
// Each subclass can thus annotate if it's handling a node.
// The return value, if not empty, should start with a space!
virtual string GetSymbolAnnotation() const = 0;
//------------------------------------------------------------
// (1) Maintain current_ast_node_
// How subclasses can access current_ast_node_;
const ASTNode* current_ast_node() const {
return current_ast_node_;
}
ASTNode* current_ast_node() {
return current_ast_node_;
}
void set_current_ast_node(ASTNode* an) { current_ast_node_ = an; }
bool TraverseDecl(Decl* decl) {
if (current_ast_node_ && current_ast_node_->StackContainsContent(decl))
return true; // avoid recursion
ASTNode node(decl);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseDecl(decl);
}
bool TraverseStmt(Stmt* stmt) {
if (current_ast_node_ && current_ast_node_->StackContainsContent(stmt))
return true; // avoid recursion
ASTNode node(stmt);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseStmt(stmt);
}
bool TraverseType(QualType qualtype) {
if (qualtype.isNull())
return Base::TraverseType(qualtype);
const Type* type = qualtype.getTypePtr();
if (current_ast_node_ && current_ast_node_->StackContainsContent(type))
return true; // avoid recursion
ASTNode node(type);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseType(qualtype);
}
// RecursiveASTVisitor has a hybrid type-visiting system: it will
// call TraverseTypeLoc when it can, but will call TraverseType
// otherwise. For instance, if we see a FunctionDecl, and it
// exposes the return type via a TypeLoc, it will recurse via
// TraverseTypeLoc. If it exposes the return type only via a
// QualType, though, it will recurse via TraverseType. The upshot
// is we need two versions of all the Traverse*Type routines. (We
// don't need two versions the Visit*Type routines, since the
// default behavior of Visit*TypeLoc is to just call Visit*Type.)
bool TraverseTypeLoc(TypeLoc typeloc) {
// QualifiedTypeLoc is a bit of a special case in the typeloc
// system, off to the side. We don't care about qualifier
// positions, so avoid the need for special-casing by just
// traversing the unqualified version instead.
if (typeloc.getAs<QualifiedTypeLoc>()) {
typeloc = typeloc.getUnqualifiedLoc();
}
if (current_ast_node_ && current_ast_node_->StackContainsContent(&typeloc))
return true; // avoid recursion
ASTNode node(&typeloc);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseTypeLoc(typeloc);
}
bool TraverseNestedNameSpecifier(NestedNameSpecifier* nns) {
if (nns == nullptr)
return true;
ASTNode node(nns);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitNestedNameSpecifier(nns))
return false;
return Base::TraverseNestedNameSpecifier(nns);
}
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc nns_loc) {
if (!nns_loc) // using NNSLoc::operator bool()
return true;
ASTNode node(&nns_loc);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
// TODO(csilvers): have VisitNestedNameSpecifierLoc instead.
if (!this->getDerived().VisitNestedNameSpecifier(
nns_loc.getNestedNameSpecifier()))
return false;
return Base::TraverseNestedNameSpecifierLoc(nns_loc);
}
bool TraverseTemplateName(TemplateName template_name) {
ASTNode node(&template_name);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitTemplateName(template_name))
return false;
return Base::TraverseTemplateName(template_name);
}
bool TraverseTemplateArgument(const TemplateArgument& arg) {
ASTNode node(&arg);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitTemplateArgument(arg))
return false;
return Base::TraverseTemplateArgument(arg);
}
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc& argloc) {
ASTNode node(&argloc);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitTemplateArgumentLoc(argloc))
return false;
return Base::TraverseTemplateArgumentLoc(argloc);
}
//------------------------------------------------------------
// (2) Provide functions related to the current location.
SourceLocation CurrentLoc() const {
CHECK_(current_ast_node_ && "Call CurrentLoc within Visit* or Traverse*");
return current_ast_node_->GetLocation();
}
string CurrentFilePath() const {
return GetFilePath(CurrentLoc());
}
const FileEntry* CurrentFileEntry() const {
return GetFileEntry(CurrentLoc());
}
string PrintableCurrentLoc() const {
return PrintableLoc(CurrentLoc());
}
//------------------------------------------------------------
// (3) Print each node we're visiting.
// The current file location, the class or decl or type name in
// brackets, along with annotations such as the indentation depth,
// etc.
string AnnotatedName(const string& name) const {
return (PrintableCurrentLoc() + ": (" +
2020-08-01 13:08:13 +01:00
std::to_string(current_ast_node_->depth()) + GetSymbolAnnotation() +
(current_ast_node_->in_forward_declare_context() ?
", fwd decl" : "") +
") [ " + name + " ] ");
}
// At verbose level 7 and above, returns a printable version of
// the pointer, suitable for being emitted after AnnotatedName.
// At lower verbose levels, returns the empty string.
string PrintablePtr(const void* ptr) const {
if (ShouldPrint(7)) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%p ", ptr);
return buffer;
}
return "";
}
// The top-level Decl class. All Decls call this visitor (in
// addition to any more-specific visitors that apply for a
// particular decl).
bool VisitDecl(clang::Decl* decl) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName(GetKindName(decl)) << PrintablePtr(decl)
<< PrintableDecl(decl) << "\n";
}
return true;
}
bool VisitStmt(clang::Stmt* stmt) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName(GetKindName(stmt)) << PrintablePtr(stmt)
<< PrintableStmt(stmt) << "\n";
}
return true;
}
bool VisitType(clang::Type* type) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName(GetKindName(type)) << PrintablePtr(type)
<< PrintableType(type) << "\n";
}
return true;
}
// Make sure our logging message shows we're in the TypeLoc hierarchy.
bool VisitTypeLoc(clang::TypeLoc typeloc) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName(GetKindName(typeloc)) << PrintableTypeLoc(typeloc)
<< "\n";
}
return true;
}
bool VisitNestedNameSpecifier(NestedNameSpecifier* nns) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName("NestedNameSpecifier")
<< PrintablePtr(nns) << PrintableNestedNameSpecifier(nns) << "\n";
}
return true;
}
bool VisitTemplateName(TemplateName template_name) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName("TemplateName")
<< PrintableTemplateName(template_name) << "\n";
}
return true;
}
bool VisitTemplateArgument(const TemplateArgument& arg) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName("TemplateArgument")
<< PrintablePtr(&arg) << PrintableTemplateArgument(arg) << "\n";
}
return true;
}
bool VisitTemplateArgumentLoc(const TemplateArgumentLoc& argloc) {
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName("TemplateArgumentLoc")
<< PrintablePtr(&argloc) << PrintableTemplateArgumentLoc(argloc)
<< "\n";
}
return true;
}
//------------------------------------------------------------
// (4) Add implicit text.
//
// This simulates a call to the destructor of every non-POD field and base
// class in all classes with destructors, to mark them as used by virtue of
// being class members.
bool TraverseCXXDestructorDecl(clang::CXXDestructorDecl* decl) {
if (!Base::TraverseCXXDestructorDecl(decl))
return false;
if (CanIgnoreCurrentASTNode())
return true;
// We only care about calls that are actually defined.
if (!decl || !decl->isThisDeclarationADefinition())
return true;
// Collect all the fields (and bases) we destroy, and call the dtor.
set<const Type*> member_types;
const CXXRecordDecl* record = decl->getParent();
for (RecordDecl::field_iterator it = record->field_begin();
it != record->field_end(); ++it) {
member_types.insert(it->getType().getTypePtr());
}
for (CXXRecordDecl::base_class_const_iterator it = record->bases_begin();
it != record->bases_end(); ++it) {
member_types.insert(it->getType().getTypePtr());
}
for (const Type* type : member_types) {
const NamedDecl* member_decl = TypeToDeclAsWritten(type);
// We only want those fields that are c++ classes.
if (const CXXRecordDecl* cxx_field_decl = DynCastFrom(member_decl)) {
if (const CXXDestructorDecl* field_dtor =
cxx_field_decl->getDestructor()) {
if (!this->getDerived().TraverseImplicitDestructorCall(
const_cast<CXXDestructorDecl*>(field_dtor), type))
return false;
}
}
}
return true;
}
// Class template specialization are similar to regular C++ classes,
// particularly they need the same custom handling of implicit destructors.
bool TraverseClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl* decl) {
if (!Base::TraverseClassTemplateSpecializationDecl(decl))
return false;
return Base::TraverseCXXRecordDecl(decl);
}
//------------------------------------------------------------
// (5) Add TraverseImplicitDestructorCall and HandleFunctionCall.
// TraverseImplicitDestructorCall: This is a callback this class
// introduces that is a first-class callback just like any AST-node
// callback. It is used to cover two places where the compiler
// destroys objects, but there's no written indication of that in
// the text: (1) when a local variable or a temporary goes out of
// scope (NOTE: in this case, we attribute the destruction to the
// same line as the corresponding construction, not to where the
// scope ends). (2) When a destructor destroys one of the fields of
// a class. For instance: 'class Foo { MyClass b; }': In addition
// to executing its body, Foo::~Foo calls MyClass::~Myclass on b.
// Note we only call this if an actual destructor is being executed:
// we don't call it when an int goes out of scope!
//
// HandleFunctionCall: A convenience callback that 'bundles'
// the following Expr's, each of which causes one or more
// function calls when evaluated (though most of them are
// not a child of CallExpr):
// * CallExpr (obviously)
// * CXXMemberCallExpr
// * CXXOperatorCallExpr -- a call to operatorXX()
// * CXXConstructExpr -- calls a constructor to create an object,
// and maybe a destructor when the object goes out of scope.
// * CXXTemporaryObjectExpr -- subclass of CXXConstructExpr
// * CXXNewExpr -- calls operator new and a constructor
// * CXXDeleteExpr -- calls operator delete and a destructor
// * DeclRefExpr -- if the declref is a function pointer, we
// treat it as a function call, since it can act like one
// in the future
// * ImplicitDestructorCall -- calls a destructor
// Each of these calls HandleFunctionCall for the function calls
// it does. A subclass interested only in function calls, and
// not exactly what expression caused them, can override
// HandleFunctionCall. Note: subclasses should expect that
// the first argument to HandleFunctionCall may be nullptr (e.g. when
// constructing a built-in type), in which case the handler should
// immediately return.
// If the function being called is a member of a class, parent_type
// is the type of the method's owner (parent), as it is written in
// the source. (We need the type-as-written so we can distinguish
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
// explicitly-written template args from default template args.) We
// also pass in the CallExpr (or CXXConstructExpr, etc). This may
// be nullptr if the function call is implicit.
bool HandleFunctionCall(clang::FunctionDecl* callee,
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 clang::Type* parent_type,
const clang::Expr* calling_expr) {
if (!callee)
return true;
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName("FunctionCall")
<< PrintablePtr(callee) << PrintableDecl(callee) << "\n";
}
return true;
}
bool TraverseImplicitDestructorCall(clang::CXXDestructorDecl* decl,
const Type* type_being_destroyed) {
if (CanIgnoreCurrentASTNode())
return true;
if (!decl)
return true;
if (ShouldPrintSymbolFromCurrentFile()) {
errs() << AnnotatedName("Destruction")
<< PrintableType(type_being_destroyed) << "\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 this->getDerived().HandleFunctionCall(decl, type_being_destroyed,
static_cast<Expr*>(nullptr));
}
bool TraverseCallExpr(clang::CallExpr* expr) {
if (!Base::TraverseCallExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
return this->getDerived().HandleFunctionCall(expr->getDirectCallee(),
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
TypeOfParentIfMethod(expr),
expr);
}
bool TraverseCXXMemberCallExpr(clang::CXXMemberCallExpr* expr) {
if (!Base::TraverseCXXMemberCallExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
return this->getDerived().HandleFunctionCall(expr->getDirectCallee(),
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
TypeOfParentIfMethod(expr),
expr);
}
bool TraverseCXXOperatorCallExpr(clang::CXXOperatorCallExpr* expr) {
if (!Base::TraverseCXXOperatorCallExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
const Type* parent_type = TypeOfParentIfMethod(expr);
// If we're a free function -- bool operator==(MyClass a, MyClass b) --
// we still want to have a parent_type, as if we were defined as
// MyClass::operator==. So we go through the arguments and take the
// first one that's a class, and associate the function with that.
if (!parent_type) {
if (const Expr* first_argument = GetFirstClassArgument(expr))
parent_type = GetTypeOf(first_argument);
}
return this->getDerived().HandleFunctionCall(expr->getDirectCallee(),
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
parent_type, expr);
}
bool TraverseCXXConstructExpr(clang::CXXConstructExpr* expr) {
if (!Base::TraverseCXXConstructExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
if (!this->getDerived().HandleFunctionCall(expr->getConstructor(),
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
GetTypeOf(expr),
expr))
return false;
// When creating a local variable or a temporary, but not a pointer, the
// constructor is also responsible for destruction (which happens
// implicitly when the variable goes out of scope). Only when initializing
// a field of a class does the constructor not have to worry
// about destruction. It turns out it's easier to check for that.
2016-03-27 20:06:38 +01:00
bool will_call_implicit_destructor_on_leaving_scope =
!IsCXXConstructExprInInitializer(current_ast_node()) &&
!IsCXXConstructExprInNewExpr(current_ast_node());
2016-03-27 20:06:38 +01:00
if (will_call_implicit_destructor_on_leaving_scope) {
if (const CXXDestructorDecl* dtor_decl = GetSiblingDestructorFor(expr)) {
if (!this->getDerived().TraverseImplicitDestructorCall(
const_cast<CXXDestructorDecl*>(dtor_decl), GetTypeOf(expr)))
return false;
}
}
return true;
}
bool TraverseCXXTemporaryObjectExpr(clang::CXXTemporaryObjectExpr* expr) {
if (!Base::TraverseCXXTemporaryObjectExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
// In this case, we *know* we're responsible for destruction as well.
CXXConstructorDecl* ctor_decl = expr->getConstructor();
CXXDestructorDecl* dtor_decl =
const_cast<CXXDestructorDecl*>(GetSiblingDestructorFor(expr));
const Type* type = GetTypeOf(expr);
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 (this->getDerived().HandleFunctionCall(ctor_decl, type, expr) &&
this->getDerived().HandleFunctionCall(dtor_decl, type, expr));
}
bool TraverseCXXNewExpr(clang::CXXNewExpr* expr) {
if (!Base::TraverseCXXNewExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
const Type* parent_type = expr->getAllocatedType().getTypePtrOrNull();
// 'new' calls operator new in addition to the ctor of the new-ed type.
if (FunctionDecl* operator_new = expr->getOperatorNew()) {
// If operator new is a method, it must (by the semantics of
// per-class operator new) be a method on the class we're newing.
const Type* op_parent = nullptr;
if (isa<CXXMethodDecl>(operator_new))
op_parent = parent_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
if (!this->getDerived().HandleFunctionCall(operator_new, op_parent, expr))
return false;
}
return true;
}
bool TraverseCXXDeleteExpr(clang::CXXDeleteExpr* expr) {
if (!Base::TraverseCXXDeleteExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
const Type* parent_type = expr->getDestroyedType().getTypePtrOrNull();
// We call operator delete in addition to the dtor of the deleted type.
if (FunctionDecl* operator_delete = expr->getOperatorDelete()) {
// If operator delete is a method, it must (by the semantics of per-
// class operator delete) be a method on the class we're deleting.
const Type* op_parent = nullptr;
if (isa<CXXMethodDecl>(operator_delete))
op_parent = parent_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
if (!this->getDerived().HandleFunctionCall(operator_delete, op_parent,
expr))
return false;
}
const CXXDestructorDecl* dtor = GetDestructorForDeleteExpr(expr);
return this->getDerived().HandleFunctionCall(
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_cast<CXXDestructorDecl*>(dtor), parent_type, expr);
}
// This is to catch function pointers to templates.
// For instance, 'MyFunctionPtr p = &TplFn<MyClass*>;': we need to
// expand TplFn to see if it needs full type info for MyClass.
bool TraverseDeclRefExpr(clang::DeclRefExpr* expr) {
if (!Base::TraverseDeclRefExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
if (FunctionDecl* fn_decl = DynCastFrom(expr->getDecl())) {
// If fn_decl has a class-name before it -- 'MyClass::method' --
// it's a method pointer.
const Type* parent_type = nullptr;
if (expr->getQualifier() && expr->getQualifier()->getAsType())
parent_type = expr->getQualifier()->getAsType();
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 (!this->getDerived().HandleFunctionCall(fn_decl, parent_type, expr))
return false;
}
return true;
}
/// Return whether this visitor should recurse into implicit
/// code, e.g., implicit constructors and destructors.
bool shouldVisitImplicitCode() const {
return true;
}
protected:
CompilerInstance* compiler() {
return compiler_;
}
private:
template <typename T> friend class BaseAstVisitor;
CompilerInstance* const compiler_;
// The currently active decl/stmt/type/etc -- that is, the node
// being currently visited in a Visit*() or Traverse*() method. The
// advantage of ASTNode over the object passed in to Visit*() and
// Traverse*() is ASTNode knows its parent.
ASTNode* current_ast_node_;
};
// ----------------------------------------------------------------------
// --- AstTreeFlattenerVisitor
// ----------------------------------------------------------------------
//
// This simple visitor just creates a set of all AST nodes (stored as
// void*'s) seen while traversing via BaseAstVisitor.
class AstFlattenerVisitor : public BaseAstVisitor<AstFlattenerVisitor> {
public:
typedef BaseAstVisitor<AstFlattenerVisitor> Base;
// We divide our set of nodes into category by type. For most AST
// nodes, we can store just a pointer to the node. However, for
// some AST nodes we don't get a pointer into the AST, we get a
// temporary (stack-allocated) object, and have to store the full
// object ourselves and use its operator== to test for equality.
// These types each get their own set (or, usually, vector, since
// the objects tend not to support operator< or hash<>()).
class NodeSet {
public:
// We could add more versions, but these are the only useful ones so far.
bool Contains(const Type* type) const {
return ContainsKey(others, type);
}
bool Contains(const Decl* decl) const {
return ContainsKey(others, decl);
}
bool Contains(const ASTNode& node) const {
if (const TypeLoc* tl = node.GetAs<TypeLoc>()) {
return ContainsValue(typelocs, *tl);
} else if (const NestedNameSpecifierLoc* nl =
node.GetAs<NestedNameSpecifierLoc>()) {
return ContainsValue(nnslocs, *nl);
} else if (const TemplateName* tn = node.GetAs<TemplateName>()) {
// The best we can do is to compare the associated decl
if (tn->getAsTemplateDecl() == nullptr)
return false; // be conservative if we can't compare decls
for (const TemplateName& tpl_name : tpl_names) {
if (tpl_name.getAsTemplateDecl() == tn->getAsTemplateDecl())
return true;
}
return false;
} else if (const TemplateArgument* ta = node.GetAs<TemplateArgument>()) {
// TODO(csilvers): figure out how to compare template arguments
(void)ta;
return false;
} else if (const TemplateArgumentLoc* tal =
node.GetAs<TemplateArgumentLoc>()) {
// TODO(csilvers): figure out how to compare template argument-locs
(void)tal;
return false;
} else {
return ContainsKey(others, node.GetAs<void>());
}
}
void AddAll(const NodeSet& that) {
Extend(&typelocs, that.typelocs);
Extend(&nnslocs, that.nnslocs);
Extend(&tpl_names, that.tpl_names);
Extend(&tpl_args, that.tpl_args);
Extend(&tpl_arglocs, that.tpl_arglocs);
InsertAllInto(that.others, &others);
}
// Needed since we're treated like an stl-like object.
bool empty() const {
return (typelocs.empty() && nnslocs.empty() &&
tpl_names.empty() && tpl_args.empty() &&
tpl_arglocs.empty() && others.empty());
}
void clear() {
typelocs.clear();
nnslocs.clear();
tpl_names.clear();
tpl_args.clear();
tpl_arglocs.clear();
others.clear();
}
private:
friend class AstFlattenerVisitor;
// It's ok not to check for duplicates; we're just traversing the tree.
void Add(TypeLoc tl) { typelocs.push_back(tl); }
void Add(NestedNameSpecifierLoc nl) { nnslocs.push_back(nl); }
void Add(TemplateName tn) { tpl_names.push_back(tn); }
void Add(TemplateArgument ta) { tpl_args.push_back(ta); }
void Add(TemplateArgumentLoc tal) { tpl_arglocs.push_back(tal); }
void Add(const void* o) { others.insert(o); }
vector<TypeLoc> typelocs;
vector<NestedNameSpecifierLoc> nnslocs;
vector<TemplateName> tpl_names;
vector<TemplateArgument> tpl_args;
vector<TemplateArgumentLoc> tpl_arglocs;
set<const void*> others;
};
//------------------------------------------------------------
// Public interface:
explicit AstFlattenerVisitor(CompilerInstance* compiler) : Base(compiler) { }
const NodeSet& GetNodesBelow(Decl* decl) {
CHECK_(seen_nodes_.empty() && "Nodes should be clear before GetNodesBelow");
NodeSet* node_set = &nodeset_decl_cache_[decl];
if (node_set->empty()) {
TraverseDecl(decl);
swap(*node_set, seen_nodes_); // move the seen_nodes_ into the cache
}
return *node_set; // returns the cache entry
}
//------------------------------------------------------------
// Pure virtual methods that the base class requires.
bool CanIgnoreCurrentASTNode() const override {
return false;
}
bool ShouldPrintSymbolFromCurrentFile() const override {
return false;
}
string GetSymbolAnnotation() const override {
return "[Uninstantiated template AST-node] ";
}
//------------------------------------------------------------
// Top-level handlers that construct the tree.
bool VisitDecl(Decl*) {
AddCurrentAstNodeAsPointer();
return true;
}
bool VisitStmt(Stmt*) {
AddCurrentAstNodeAsPointer();
return true;
}
bool VisitType(Type*) {
AddCurrentAstNodeAsPointer();
return true;
}
bool VisitTypeLoc(TypeLoc typeloc) {
VERRS(7) << GetSymbolAnnotation() << PrintableTypeLoc(typeloc) << "\n";
seen_nodes_.Add(typeloc);
return true;
}
bool VisitNestedNameSpecifier(NestedNameSpecifier*) {
AddCurrentAstNodeAsPointer();
return true;
}
bool VisitTemplateName(TemplateName tpl_name) {
VERRS(7) << GetSymbolAnnotation()
<< PrintableTemplateName(tpl_name) << "\n";
seen_nodes_.Add(tpl_name);
return true;
}
bool VisitTemplateArgument(const TemplateArgument& tpl_arg) {
VERRS(7) << GetSymbolAnnotation()
<< PrintableTemplateArgument(tpl_arg) << "\n";
seen_nodes_.Add(tpl_arg);
return true;
}
bool VisitTemplateArgumentLoc(const TemplateArgumentLoc& tpl_argloc) {
VERRS(7) << GetSymbolAnnotation()
<< PrintableTemplateArgumentLoc(tpl_argloc) << "\n";
seen_nodes_.Add(tpl_argloc);
return true;
}
bool TraverseImplicitDestructorCall(clang::CXXDestructorDecl* decl,
const Type* type) {
VERRS(7) << GetSymbolAnnotation() << "[implicit dtor] "
<< static_cast<void*>(decl) << " "
<< PrintableDecl(decl) << "\n";
AddAstNodeAsPointer(decl);
return Base::TraverseImplicitDestructorCall(decl, type);
}
bool HandleFunctionCall(clang::FunctionDecl* callee,
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 clang::Type* parent_type,
const clang::Expr* calling_expr) {
VERRS(7) << GetSymbolAnnotation() << "[function call] "
<< static_cast<void*>(callee) << " "
<< PrintableDecl(callee) << "\n";
AddAstNodeAsPointer(callee);
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 Base::HandleFunctionCall(callee, parent_type, calling_expr);
}
//------------------------------------------------------------
// Class logic.
void AddAstNodeAsPointer(const void* node) {
seen_nodes_.Add(node);
}
void AddCurrentAstNodeAsPointer() {
if (ShouldPrint(7)) {
errs() << GetSymbolAnnotation() << current_ast_node()->GetAs<void>()
<< " " << PrintableASTNode(current_ast_node()) << "\n";
}
AddAstNodeAsPointer(current_ast_node()->GetAs<void>());
}
private:
NodeSet seen_nodes_;
// Because we make a new AstFlattenerVisitor each time we flatten, we
// need to make this map static.
// TODO(csilvers): just have one flattener, so this needn't be static.
static map<const Decl*, NodeSet> nodeset_decl_cache_;
};
map<const Decl*, AstFlattenerVisitor::NodeSet>
AstFlattenerVisitor::nodeset_decl_cache_;
// ----------------------------------------------------------------------
// --- VisitorState
// ----------------------------------------------------------------------
//
// This is a simple struct holding data that IwyuBaseASTVisitor will
// need to access and manipulate. It's held separately from
// IwyuBaseASTVisitor because we want this information to be shared
// between the IwyuASTConsumer and the InstantiatedTemplateVisitor,
// each of which gets its own copy of IwyuBaseASTVisitor data. So to
// share data, we need to hold it somewhere else.
struct VisitorState {
VisitorState(CompilerInstance* c, const IwyuPreprocessorInfo& ipi)
: compiler(c), preprocessor_info(ipi) {}
CompilerInstance* const compiler;
// Information gathered at preprocessor time, including #include info.
const IwyuPreprocessorInfo& preprocessor_info;
// When we see an overloaded function that depends on a template
// parameter, we can't resolve the overload until the template
// is instantiated (e.g., MyFunc<int> in the following example):
// template<typename T> MyFunc() { OverloadedFunction(T()); }
// However, sometimes we can do iwyu even before resolving the
// overload, if *all* potential overloads live in the same file. We
// mark the location of such 'early-processed' functions here, so
// when we see the function again at template-instantiation time, we
// know not to do iwyu-checking on it again. (Since the actual
// function-call exprs are different between the uninstantiated and
// instantiated calls, we can't store the exprs themselves, but have
// to store their location.)
set<SourceLocation> processed_overload_locs;
};
// ----------------------------------------------------------------------
// --- IwyuBaseAstVisitor
// ----------------------------------------------------------------------
//
// We use two AST visitor classes to implement IWYU: IwyuAstConsumer
// is the main visitor that traverses the AST corresponding to what's
// actually written in the source code, and
// InstantiatedTemplateVisitor is for traversing template
// instantiations. This class template holds iwyu work that is be
// shared by both.
template <class Derived>
class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
public:
typedef BaseAstVisitor<Derived> Base;
explicit IwyuBaseAstVisitor(VisitorState* visitor_state)
: Base(visitor_state->compiler),
visitor_state_(visitor_state) {}
~IwyuBaseAstVisitor() override = default;
// To avoid having this-> pointers everywhere, we re-export Base's
// functions that we use in this class. This is a language nit(?)
// when a templated class subclasses from another templated class.
using Base::CanIgnoreCurrentASTNode;
using Base::CurrentLoc;
using Base::CurrentFileEntry;
using Base::PrintableCurrentLoc;
using Base::current_ast_node;
using Base::compiler;
enum class IgnoreKind {
ForUse,
ForExpansion,
};
//------------------------------------------------------------
// Pure virtual methods that a subclass must implement.
// Returns true if we are not interested in iwyu information for the
// given type, where the type is *not* the current AST node.
// TODO(csilvers): check we're calling this consistent with its API.
virtual bool CanIgnoreType(const Type* type,
IgnoreKind = IgnoreKind::ForUse) const = 0;
// Returns true if we are not interested in doing an iwyu check on
// the given decl, where the decl is *not* the current AST node.
// TODO(csilvers): check we're calling this consistent with its API.
virtual bool CanIgnoreDecl(const Decl* decl) const = 0;
//------------------------------------------------------------
// IWYU logic.
// Helper for MapPrivateDeclToPublicDecl. Returns true if the decl
// is a template specialization whose (written qualified) name matches
// the given name, has the given number of template arguments, and
// whose specified tpl argument is a type.
bool DeclIsTemplateWithNameAndNumArgsAndTypeArg(
const Decl* decl, const string& name,
size_t num_args, size_t type_arg_idx) const {
const ClassTemplateSpecializationDecl* tpl_decl = DynCastFrom(decl);
if (!tpl_decl)
return false;
const string actual_name = GetWrittenQualifiedNameAsString(tpl_decl);
if (name != actual_name)
return false;
const TemplateArgumentList& tpl_args = tpl_decl->getTemplateArgs();
if (tpl_args.size() != num_args)
return false;
if (tpl_args.get(type_arg_idx).getKind() != TemplateArgument::Type)
return false;
return true;
}
// This requires the above function to have been called on decl, first.
const Type* GetTplTypeArg(const Decl* decl, size_t type_arg_idx) const {
const ClassTemplateSpecializationDecl* tpl_decl = DynCastFrom(decl);
CHECK_(tpl_decl && "Must call DeclIsTemplateWithNameAndNumArgsAndTypeArg");
const TemplateArgumentList& tpl_args = tpl_decl->getTemplateArgs();
CHECK_(tpl_args.size() > type_arg_idx && "Invalid type_arg_idx");
CHECK_(tpl_args.get(type_arg_idx).getKind() == TemplateArgument::Type);
return tpl_args.get(type_arg_idx).getAsType().getTypePtr();
}
// Some types, such as __gnu_cxx::__normal_iterator or std::__wrap_iter, are
// private types that should not be exposed to the user. Instead, they're
// exposed to the user via typedefs, like vector::iterator.
// Sometimes, the typedef gets lost (such as for find(myvec.begin(),
// myvec.end(), foo)), so we need to manually map back. We map
// __normal_iterator<foo, vector> to vector<> and __wrap_iter<foo> to foo,
// assuming that the vector<> class includes the typedef. Likewise, we map
// any free function taking a private iterator (such as operator==) the
// same way, assuming that that (templatized) function is instantiated
// as part of the vector class.
// We do something similar for _List_iterator and _List_const_iterator
// from GNU libstdc++, and for __list_iterator and __list_const_iterator
// from libc++. These private names are defined in stl_list.h and list
// respectively, so we don't need to re-map them, but we do want to re-map
// reverse_iterator<_List_iterator> to something in list header.
// If the input decl does not correspond to one of these private
// decls, we return nullptr. This method is actually a helper for
// MapPrivateDeclToPublicDecl() and MapPrivateTypeToPublicType().
const Type* MapPrivateDeclToPublicType(const NamedDecl* decl) const {
const NamedDecl* class_decl = decl;
// If we're a member method, then the __normal_iterator or __wrap_iter will
// be the parent: __normal_iterator::operator=. If we're a free
// overloaded operator, then the __normal_iterator will be the
// first argument: operator==(__normal_iterator<...>& lhs, ...);
if (const CXXMethodDecl* method_decl = DynCastFrom(class_decl)) {
class_decl = method_decl->getParent();
} else if (const FunctionDecl* fn = DynCastFrom(decl)) {
if (fn->isOverloadedOperator() && fn->getNumParams() >= 1) {
const Type* firstarg_type = fn->getParamDecl(0)->getType().getTypePtr();
firstarg_type = RemovePointersAndReferencesAsWritten(firstarg_type);
class_decl = TypeToDeclAsWritten(firstarg_type);
}
}
// In addition to __normal_iterator<x> and __wrap_iter<x>, we want
// to handle reverse_iterator<__normal_iterator<x>>, and in the same way.
if (DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "std::reverse_iterator", 1, 0)) {
const Type* reversed_iterator_type = GetTplTypeArg(class_decl, 0);
// Gets class_decl to be reversed iterator.
class_decl = TypeToDeclAsWritten(reversed_iterator_type);
// If it's reverse_iterator<_List_iterator>, map to
// _List_iterator, which is defined in stl_list like we want. Also map
// reverse_iterator<__list_iterator> to __list_iterator which is defined
// in list.
if (DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "std::_List_iterator", 1, 0) ||
DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "std::_List_const_iterator", 1, 0) ||
DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "std::__list_iterator", 2, 0) ||
DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "std::__list_const_iterator", 2, 0)) {
return reversed_iterator_type;
}
}
if (DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "__gnu_cxx::__normal_iterator", 2, 1)) {
return GetTplTypeArg(class_decl, 1);
}
if (DeclIsTemplateWithNameAndNumArgsAndTypeArg(
class_decl, "std::__wrap_iter", 1, 0)) {
return GetTplTypeArg(class_decl, 0);
}
return nullptr;
}
const NamedDecl* MapPrivateDeclToPublicDecl(const NamedDecl* decl) const {
const Type* public_type = MapPrivateDeclToPublicType(decl);
if (public_type)
return TypeToDeclAsWritten(public_type);
return decl;
}
const Type* MapPrivateTypeToPublicType(const Type* type) const {
const NamedDecl* private_decl = TypeToDeclAsWritten(type);
const Type* public_type = MapPrivateDeclToPublicType(private_decl);
if (public_type)
return public_type;
return type;
}
// Get the canonical use location for a (location, decl) pair.
// Decide whether the file expanding the macro or the file defining the macro
// should be held responsible for a use.
SourceLocation GetCanonicalUseLocation(SourceLocation use_loc,
const NamedDecl* decl) {
CHECK_(decl != nullptr) << ": Need a decl to compute use location";
// If we're not in a macro, just echo the use location.
if (!use_loc.isMacroID())
return use_loc;
VERRS(5) << "Trying to determine use location for '" << PrintableDecl(decl)
<< "'\n";
clang::SourceManager* sm = GlobalSourceManager();
SourceLocation spelling_loc = sm->getSpellingLoc(use_loc);
SourceLocation expansion_loc = sm->getExpansionLoc(use_loc);
// If the file defining the macro contains a forward decl, keep it around
// and treat it as a hint that the expansion loc is responsible for the
// symbol.
const FileEntry* macro_def_file = GetLocFileEntry(spelling_loc);
VERRS(5) << "Macro is defined in '" << GetFilePath(macro_def_file) << "'\n";
const NamedDecl* fwd_decl = nullptr;
for (const NamedDecl* redecl : GetTagRedecls(decl)) {
if (GetFileEntry(redecl) == macro_def_file && IsForwardDecl(redecl)) {
VERRS(5) << "Found fwd-decl hint at "
<< PrintableLoc(GetLocation(redecl)) << "\n";
fwd_decl = redecl;
break;
}
}
if (!fwd_decl) {
if (const auto* func_decl = dyn_cast<FunctionDecl>(decl)) {
if (const FunctionTemplateDecl* ft_decl =
func_decl->getPrimaryTemplate()) {
VERRS(5) << "No fwd-decl found, looking for function template decl\n";
for (const NamedDecl* redecl : ft_decl->redecls()) {
if (GetFileEntry(redecl) == macro_def_file) {
VERRS(5) << "Found function template at "
<< PrintableLoc(GetLocation(redecl)) << "\n";
fwd_decl = redecl;
break;
}
}
}
}
}
if (fwd_decl) {
// Make sure we keep that forward-declaration, even if it's probably
// unused in this file.
IwyuFileInfo* file_info = preprocessor_info().FileInfoFor(macro_def_file);
file_info->ReportForwardDeclareUse(
spelling_loc, fwd_decl, ComputeUseFlags(current_ast_node()), nullptr);
}
// Resolve the best use location based on our current knowledge.
//
// 1) If the use_loc is in <scratch space>, we assume it's formed by macro
// argument concatenation, and attribute responsibility to the expansion
// location.
// 2) If the macro definition file forward-declares the used decl, that's a
// hint that it wants the expansion location to take responsibility.
//
// Otherwise, the spelling loc is responsible.
const char* side;
if (IsInScratchSpace(spelling_loc)) {
VERRS(5) << "Spelling location is in <scratch space>, presumably as a "
<< "result of macro arg concatenation\n";
use_loc = expansion_loc;
side = "expansion";
} else if (fwd_decl != nullptr) {
VERRS(5) << "Found a hint decl in macro definition file\n";
use_loc = expansion_loc;
side = "expansion";
} else {
use_loc = spelling_loc;
side = "spelling";
}
VERRS(4) << "Attributing use of '" << PrintableDecl(decl) << "' to " << side
<< " location at " << PrintableLoc(use_loc) << "\n";
return use_loc;
}
// There are a few situations where iwyu is more restrictive than
// C++: where C++ allows a forward-declare but iwyu wants the full
// type. One is in a typedef: if you write 'typedef Foo MyTypedef',
// iwyu says that you are responsible for #including "foo.h", but
// the language allows a forward-declare. Another is for
// 'autocast': if your function has a parameter with a conversion
// (one-arg, not-explicit) constructor, iwyu requires that the
// function-author provides the full type of that parameter, but
// the language doesn't. (It's ok with all callers providing the
// full type instead.)
//
// In each of these situations, we allow the user to say that iwyu
// should not require #includes for these underlying types, but
// allow forward-declares instead. The author can do this by
// explicitly forward-declaring in the same file: for instance, they
// would do
// class Foo; typedef Foo MyTypedef; // can be on different lines :-)
// class AutocastType; void MyFunc(AutocastType); // but in same file
// If a definition- or declaration-site does this forward-declaring
// *and* does not directly #include the necessary file for Foo or
// AutocastType, we take that as a signal from the code-author that
// iwyu should relax its policies. These functions calculate the
// types (which may have many component-types if it's a templated
// type) for which the code-author has made this decision.
bool CodeAuthorWantsJustAForwardDeclare(const Type* type,
SourceLocation use_loc) {
const NamedDecl* decl = TypeToDeclAsWritten(type);
if (decl == nullptr) // only class-types are candidates for returning true
return false;
// Sometimes a type points back to an implicit decl (e.g. a bultin type),
// and we can't do author-intent analysis without location information.
// Assume that it's not forward-declarable.
if (decl->isImplicit()) {
VERRS(5) << "Skipping forward-declare analysis for implicit decl: '"
<< PrintableDecl(decl) << "'\n";
return false;
}
// If we're a template specialization, we also accept
// forward-declarations of the underlying template (vector<T>, not
// vector<int>).
set<const NamedDecl*> redecls = GetTagRedecls(decl);
if (const ClassTemplateSpecializationDecl* spec_decl = DynCastFrom(decl)) {
InsertAllInto(GetTagRedecls(spec_decl->getSpecializedTemplate()),
&redecls);
}
// Check if the author forward-declared the class in the same file.
bool found_earlier_forward_declare_in_same_file = false;
for (const NamedDecl* redecl : redecls) {
if (IsBeforeInSameFile(redecl, use_loc)) {
found_earlier_forward_declare_in_same_file = true;
break;
}
}
if (!found_earlier_forward_declare_in_same_file)
return false;
// Check if the the author is not #including the file with the
// definition. PublicHeaderIntendsToProvide has exactly the
// semantics we want. Note if there's no definition anywhere, we
// say the author does not want the full type (which is a good
// thing, since there isn't one!)
if (const NamedDecl* dfn = GetTagDefinition(decl)) {
if (IsBeforeInSameFile(dfn, use_loc))
return false;
if (preprocessor_info().PublicHeaderIntendsToProvide(
GetFileEntry(use_loc), GetFileEntry(dfn))) {
return false;
}
}
// OK, looks like the author has stated they don't want the fulll type.
return true;
}
// Returns the first type that is not a typedef in a template. For example,
// for template
//
// template<typename T> class Foo {
// typedef T value_type;
// typedef value_type& reference;
// };
//
// for type 'reference' it will return type T with which Foo was instantiated.
const Type* DesugarDependentTypedef(const TypedefType* typedef_type) {
const DeclContext* parent =
typedef_type->getDecl()->getLexicalDeclContext();
if (const ClassTemplateSpecializationDecl* template_parent =
DynCastFrom(parent)) {
return DesugarDependentTypedef(typedef_type, template_parent);
}
return typedef_type;
}
const Type* DesugarDependentTypedef(
const TypedefType* typedef_type, const RecordDecl* parent) {
const Type* underlying_type =
typedef_type->getDecl()->getUnderlyingType().getTypePtr();
if (const TypedefType* underlying_typedef = DynCastFrom(underlying_type)) {
if (underlying_typedef->getDecl()->getLexicalDeclContext() == parent) {
return DesugarDependentTypedef(underlying_typedef, parent);
}
}
return underlying_type;
}
set<const Type*> GetCallerResponsibleTypesForTypedef(
const TypedefNameDecl* decl) {
set<const Type*> retval;
const Type* underlying_type = decl->getUnderlyingType().getTypePtr();
// If the underlying type is itself a typedef, we recurse.
if (const auto* underlying_typedef =
underlying_type->getAs<TypedefType>()) {
if (const auto* underlying_typedef_decl = dyn_cast<TypedefNameDecl>(TypeToDeclAsWritten(underlying_typedef))) {
// TODO(csilvers): if one of the intermediate typedefs
// #includes the necessary definition of the 'final'
// underlying type, do we want to return the empty set here?
return GetCallerResponsibleTypesForTypedef(underlying_typedef_decl);
}
}
const Type* deref_type =
RemovePointersAndReferencesAsWritten(underlying_type);
if (isa<SubstTemplateTypeParmType>(underlying_type) ||
CodeAuthorWantsJustAForwardDeclare(deref_type, GetLocation(decl))) {
retval.insert(deref_type);
// TODO(csilvers): include template type-args if appropriate.
// This requires doing an iwyu visit of the instantiated
// underlying type and seeing which type-args we require full
// use for. Also have to handle the case where the type-args
// are themselves templates. It will require pretty substantial
// iwyu surgery.
}
return retval;
}
// ast_node is the node for the autocast CastExpr. We use it to get
// the parent CallExpr to figure out what function is being called.
set<const Type*> GetCallerResponsibleTypesForAutocast(
const ASTNode* ast_node) {
while (ast_node && !ast_node->IsA<CallExpr>())
ast_node = ast_node->parent();
CHECK_(ast_node && "Should only check Autocast if under a CallExpr");
const CallExpr* call_expr = ast_node->GetAs<CallExpr>();
const FunctionDecl* fn_decl = call_expr->getDirectCallee();
if (!fn_decl) // TODO(csilvers): what to do for fn ptrs and the like?
return set<const Type*>();
// Collect the non-explicit, one-arg constructor ('autocast') types.
set<const Type*> autocast_types;
for (FunctionDecl::param_const_iterator param = fn_decl->param_begin();
param != fn_decl->param_end(); ++param) {
const Type* param_type = GetTypeOf(*param);
if (HasImplicitConversionConstructor(param_type)) {
const Type* deref_param_type =
RemovePointersAndReferencesAsWritten(param_type);
autocast_types.insert(Desugar(deref_param_type));
}
}
// Now look at all the function decls that are visible from the
// call-location. We keep only the autocast params that *all*
// the function decl authors want the caller to be responsible
// for. We do this by elimination: start with all types, and
// remove them as we see authors providing the full type.
set<const Type*> retval = autocast_types;
for (FunctionDecl::redecl_iterator fn_redecl = fn_decl->redecls_begin();
fn_redecl != fn_decl->redecls_end(); ++fn_redecl) {
// Ignore function-decls that we can't see from the use-location.
if (!preprocessor_info().FileTransitivelyIncludes(
GetFileEntry(call_expr), GetFileEntry(*fn_redecl))) {
continue;
}
if (fn_redecl->isThisDeclarationADefinition() && !IsInHeader(*fn_redecl))
continue;
for (set<const Type*>::iterator it = retval.begin();
it != retval.end(); ) {
if (!CodeAuthorWantsJustAForwardDeclare(*it, GetLocation(*fn_redecl))) {
// set<> has nice property that erasing doesn't invalidate iterators.
retval.erase(it++);
} else {
++it;
}
}
}
// TODO(csilvers): include template type-args of each entry of retval.
return retval;
}
set<const Type*> GetCallerResponsibleTypesForFnReturn(
const FunctionDecl* decl) {
set<const Type*> retval;
const Type* return_type = Desugar(decl->getReturnType().getTypePtr());
if (CodeAuthorWantsJustAForwardDeclare(return_type, GetLocation(decl))) {
retval.insert(return_type);
// TODO(csilvers): include template type-args if appropriate.
}
return retval;
}
//------------------------------------------------------------
// Checkers, that tell iwyu_output about uses of symbols.
// We let, but don't require, subclasses to override these.
// The comment, if not nullptr, is extra text that is included along with
// the warning message that iwyu emits. The extra use flags is optional
// info that can be assigned to the use (see the UF_* constants)
virtual void ReportDeclUse(SourceLocation used_loc,
const NamedDecl* used_decl,
const char* comment = nullptr,
UseFlags extra_use_flags = 0) {
const NamedDecl* target_decl = used_decl;
// Sometimes a shadow decl comes between us and the 'real' decl.
const UsingDecl* using_decl = nullptr;
if (const auto* shadow_decl = dyn_cast<UsingShadowDecl>(used_decl)) {
target_decl = shadow_decl->getTargetDecl();
using_decl = dyn_cast<UsingDecl>(shadow_decl->getIntroducer());
}
// Map private decls like __normal_iterator to their public counterpart.
target_decl = MapPrivateDeclToPublicDecl(target_decl);
if (CanIgnoreDecl(target_decl))
return;
UseFlags use_flags = ComputeUseFlags(current_ast_node()) | extra_use_flags;
// Canonicalize the use location and report the use.
used_loc = GetCanonicalUseLocation(used_loc, target_decl);
const FileEntry* used_in = GetFileEntry(used_loc);
preprocessor_info().FileInfoFor(used_in)->ReportFullSymbolUse(
used_loc, target_decl, use_flags, comment);
// Sometimes using a decl drags in a few other uses as well:
// If we're a use that depends on a using declaration, make sure
// we #include the file with the using declaration. Need to check
// the original reported decl so we don't lose the shadow information.
// TODO(csilvers): check that our getQualifier() does not match
// the namespace of the decl. If we have 'using std::vector;' +
// 'std::vector<int> foo;' we don't actually care about the
// using-decl.
// TODO(csilvers): maybe just insert our own using declaration
// instead? We can call it "Use what you use". :-)
// TODO(csilvers): check for using statements and namespace aliases too.
if (using_decl) {
preprocessor_info().FileInfoFor(used_in)->ReportUsingDeclUse(
used_loc, using_decl, use_flags, "(for using decl)");
}
}
// The comment, if not nullptr, is extra text that is included along
// with the warning message that iwyu emits.
virtual void ReportDeclForwardDeclareUse(SourceLocation used_loc,
const NamedDecl* used_decl,
const char* comment = nullptr) {
const NamedDecl* target_decl = used_decl;
// Sometimes a shadow decl comes between us and the 'real' decl.
const UsingDecl* using_decl = nullptr;
if (const auto* shadow_decl = dyn_cast<UsingShadowDecl>(used_decl)) {
target_decl = shadow_decl->getTargetDecl();
using_decl = dyn_cast<UsingDecl>(shadow_decl->getIntroducer());
}
target_decl = MapPrivateDeclToPublicDecl(target_decl);
if (CanIgnoreDecl(target_decl))
return;
UseFlags use_flags = ComputeUseFlags(current_ast_node());
// Canonicalize the use location and report the use.
used_loc = GetCanonicalUseLocation(used_loc, target_decl);
const FileEntry* used_in = GetFileEntry(used_loc);
preprocessor_info().FileInfoFor(used_in)->ReportForwardDeclareUse(
used_loc, target_decl, use_flags, comment);
// If we're a use that depends on a using declaration, make sure
// we #include the file with the using declaration.
if (using_decl) {
preprocessor_info().FileInfoFor(used_in)->ReportUsingDeclUse(
used_loc, using_decl, use_flags, "(for using decl)");
}
}
void ReportDeclsUse(SourceLocation used_loc,
const set<const NamedDecl*>& decls) {
for (const NamedDecl* decl : decls)
ReportDeclUse(used_loc, decl);
}
// Called when the given type is fully used at used_loc, regardless
// of the type being explicitly written in the source code or not.
// The comment, if not nullptr, is extra text that is included along
// with the warning message that iwyu emits.
virtual void ReportTypeUse(SourceLocation used_loc, const Type* type,
const char* comment = nullptr) {
// TODO(csilvers): figure out if/when calling CanIgnoreType() is correct.
if (!type)
return;
// Enum type uses can be ignored. Their size is known (either implicitly
// 'int' or from a mandatory transitive inclusion of a non-fixed enum full
// declaration, or explicitly using a C++ 11 enum base). Only if an enum
// type or its enumerators are explicitly mentioned will they be reported
// by IWYU from VisitTagType or VisitDeclRefExpr correspondingly.
if (type->getAs<EnumType>())
return;
// Types in fwd-decl-context should be ignored here and reported from more
// specialized places, i.e. when they are explicitly written. But in fact,
// this check is redundant because TypeToDeclAsWritten returns nullptr for
// pointers and references.
if (IsPointerOrReferenceAsWritten(type))
return;
// For typedefs, the user of the type is sometimes the one
// responsible for the underlying type. We check if that is the
// case here, since we might be using a typedef type from
// anywhere. ('autocast' is similar, but is handled in
// VisitCastExpr; 'fn-return-type' is also similar and is
// handled in HandleFunctionCall.)
if (const auto* typedef_type = type->getAs<TypedefType>()) {
// One exception: if this TypedefType is being used in another
// typedef (that is, 'typedef MyTypedef OtherTypdef'), then the
// user -- the other typedef -- is never responsible for the
// underlying type. Instead, users of that typedef are.
const ASTNode* ast_node = MostElaboratedAncestor(current_ast_node());
if (!ast_node->ParentIsA<TypedefNameDecl>()) {
const TypedefNameDecl* typedef_decl = typedef_type->getDecl();
const set<const Type*>& underlying_types =
GetCallerResponsibleTypesForTypedef(typedef_decl);
if (!underlying_types.empty()) {
VERRS(6) << "User, not author, of typedef "
<< typedef_decl->getQualifiedNameAsString()
<< " owns the underlying type:\n";
// If any of the used types are themselves typedefs, this will
// result in a recursive expansion. Note we are careful to
// recurse inside this class, and not go back to subclasses.
for (const Type* type : underlying_types)
IwyuBaseAstVisitor<Derived>::ReportTypeUse(used_loc, type);
}
}
return;
}
// Map private types like __normal_iterator to their public counterpart.
type = MapPrivateTypeToPublicType(type);
// For the below, we want to be careful to call *our*
// ReportDeclUse(), not any of the ones in subclasses.
if (const auto* template_spec_type =
dyn_cast<TemplateSpecializationType>(Desugar(type))) {
this->getDerived().ReportTplSpecComponentTypes(template_spec_type);
}
if (const NamedDecl* decl = TypeToDeclAsWritten(type)) {
decl = GetDefinitionAsWritten(decl);
VERRS(6) << "(For type " << PrintableType(type) << "):\n";
IwyuBaseAstVisitor<Derived>::ReportDeclUse(used_loc, decl, comment);
}
}
void ReportTypesUse(SourceLocation used_loc, const set<const Type*>& types) {
for (const Type* type : types)
ReportTypeUse(used_loc, type);
}
//------------------------------------------------------------
// Visitors of types derived from clang::Decl.
// Friend declarations only need their types forward-declared.
bool VisitFriendDecl(clang::FriendDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
current_ast_node()->set_in_forward_declare_context(true);
return true;
}
bool VisitFriendTemplateDecl(clang::FriendTemplateDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
current_ast_node()->set_in_forward_declare_context(true);
return true;
}
bool VisitEnumDecl(EnumDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
clang::QualType integer_type = decl->getIntegerType();
if (const clang::Type* type = integer_type.getTypePtrOrNull()) {
ReportTypeUse(CurrentLoc(), type);
}
return true;
}
// If you say 'typedef Foo Bar', C++ says you just need to
// forward-declare Foo. But iwyu would rather you fully define Foo,
// so all users of Bar don't have to. We make two exceptions:
// 1) The author of the typedef doesn't *want* to provide Foo, and
// is happy making all the callers do so. The author indicates
// this by explicitly forward-declaring Foo and not #including
// foo.h.
// 2) The typedef is a member of a templated class, and the
// underlying type is a template parameter:
// template<class T> struct C { typedef T value_type; };
// This is not a re-export because you need the type to
// access the typedef (via 'C<Someclass>::value_type'), so
// there's no need for the typedef-file to provide the type
// too. TODO(csilvers): this is patently wrong; figure out
// something better. We need something that doesn't require
// the full type info for creating a scoped_ptr<MyClass>.
// As an extension of (2), if the typedef is a template type that
// contains T as a template parameter, the typedef still re-exports
// the template type (it's not (2)), but the template parameter
// itself can be forward-declared, just as in (2). That is:
// template<class T> struct C { typedef pair<T,T> value_type; };
// iwyu will demand the full type of pair, but not of its template
// arguments. This is handled not here, but below, in
// VisitSubstTemplateTypeParmType.
bool VisitTypedefNameDecl(clang::TypedefNameDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
const Type* underlying_type = decl->getUnderlyingType().getTypePtr();
const Type* deref_type =
RemovePointersAndReferencesAsWritten(underlying_type);
if (CodeAuthorWantsJustAForwardDeclare(deref_type, GetLocation(decl)) ||
isa<SubstTemplateTypeParmType>(underlying_type)) {
current_ast_node()->set_in_forward_declare_context(true);
} else {
current_ast_node()->set_in_forward_declare_context(false);
}
return true;
}
// If we're a declared (not defined) function, all our types --
// return type and argument types -- are forward-declarable. The
// one exception required by the language is the throw types, which
// we clean up in VisitType().
// There are two more exceptions that iwyu imposes:
// (1) iwyu asks the function author to provide the full type
// information for the return type. That way the user doesn't
// have to.
// (2) If any of our function parameters have a type with a
// non-explicit, one-arg constructor, or is a const reference to
// such a type, mark that type as not forward declarable. The
// worry is that people might need the full type for the
// implicit conversion (the 'autocast'), for instance, passing
// in a char* to Fn(const StringPiece& foo) { ... }
// Both of these iwyu requirements can be overridden by the function
// author; for details, see CodeAuthorWantsJustAForwardDeclare.
bool VisitFunctionDecl(clang::FunctionDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
if (decl->isThisDeclarationADefinition()) {
// For free functions, report use of all previously seen decls.
if (decl->getKind() == Decl::Function) {
FunctionDecl* redecl = decl;
while ((redecl = redecl->getPreviousDecl()))
ReportDeclUse(CurrentLoc(), redecl);
}
if (!IsInHeader(decl)) {
// No point in author-intent analysis of function definitions
// in source files or for builtins.
return true;
}
} else {
// Make all our types forward-declarable...
current_ast_node()->set_in_forward_declare_context(true);
}
// (The exceptions below don't apply to friend declarations; we
// never need full types for them.)
if (IsFriendDecl(decl))
return true;
// ...except the return value.
const Type* return_type = Desugar(decl->getReturnType().getTypePtr());
const bool is_responsible_for_return_type =
(!CanIgnoreType(return_type) &&
!IsPointerOrReferenceAsWritten(return_type) &&
!CodeAuthorWantsJustAForwardDeclare(return_type, GetLocation(decl)));
// Don't bother to report here, when the language agrees with us
// we need the full type; that will be reported elsewhere, so
// reporting here would be double-counting.
const bool type_use_reported_in_visit_function_type =
(!current_ast_node()->in_forward_declare_context() ||
!IsClassType(return_type));
if (is_responsible_for_return_type &&
!type_use_reported_in_visit_function_type) {
ReportTypeUse(GetLocation(decl), return_type, "(for fn return type)");
}
// ...and non-explicit, one-arg ('autocast') constructor types.
for (FunctionDecl::param_iterator param = decl->param_begin();
param != decl->param_end(); ++param) {
const Type* param_type = GetTypeOf(*param);
if (!HasImplicitConversionConstructor(param_type))
continue;
const Type* deref_param_type =
RemovePointersAndReferencesAsWritten(param_type);
if (CanIgnoreType(param_type) && CanIgnoreType(deref_param_type))
continue;
// TODO(csilvers): remove this 'if' check when we've resolved the
// clang bug where getTypeSourceInfo() can return nullptr.
if ((*param)->getTypeSourceInfo()) {
const TypeLoc param_tl = (*param)->getTypeSourceInfo()->getTypeLoc();
// While iwyu requires the full type of autocast parameters,
// c++ does not. Function-writers can force iwyu to follow
// the language by explicitly forward-declaring the type.
// Check for that now, and don't require the full type.
if (CodeAuthorWantsJustAForwardDeclare(deref_param_type,
GetLocation(&param_tl)))
continue;
// This is a 'full type required' check, to 'turn off' fwd decl.
// But don't bother to report in situations where we need the
// full type for other reasons; that's just double-reporting.
if (current_ast_node()->in_forward_declare_context() ||
IsPointerOrReferenceAsWritten(param_type)) {
ReportTypeUse(GetLocation(&param_tl), deref_param_type,
"(for autocast)");
}
} else {
VERRS(6) << "WARNING: nullptr TypeSourceInfo for "
<< PrintableDecl(*param)
<< " (type " << PrintableType(param_type) << ")\n";
}
}
return true;
}
// Special handling for C++ methods to detect covariant return types.
// These are defined as a derived class overriding a method with a different
// return type from the base.
bool VisitCXXMethodDecl(CXXMethodDecl* method_decl) {
if (CanIgnoreCurrentASTNode())
return true;
if (HasCovariantReturnType(method_decl)) {
const Type* return_type = RemovePointersAndReferencesAsWritten(
method_decl->getReturnType().getTypePtr());
VERRS(6) << "Found covariant return type in "
<< method_decl->getQualifiedNameAsString()
<< ", needs complete type of " << PrintableType(return_type)
<< "\n";
ReportTypeUse(CurrentLoc(), return_type);
}
return true;
}
//------------------------------------------------------------
// Visitors of types derived from clang::Stmt.
// Catch statements always require the full type to be visible,
// no matter if we're catching by value, reference or pointer.
bool VisitCXXCatchStmt(clang::CXXCatchStmt* stmt) {
if (CanIgnoreCurrentASTNode())
return true;
if (const VarDecl* exception_decl = stmt->getExceptionDecl()) {
// Get the caught type from the decl via associated type source info to
// get more precise location info for the type use.
TypeLoc typeloc = exception_decl->getTypeSourceInfo()->getTypeLoc();
const Type* caught_type = typeloc.getType().getTypePtr();
// Strip off pointers/references to get to the pointee type.
caught_type = RemovePointersAndReferencesAsWritten(caught_type);
ReportTypeUse(GetLocation(&typeloc), caught_type);
} else {
// catch(...): no type to act on here.
}
return true;
}
// The type of the for-range-init expression is fully required, because the
// compiler generates method calls to it, e.g. 'for (auto t : ts)' translates
// roughly into 'for (auto i = std::begin(ts); i != std::end(ts); ++i)'.
// Both the iterator type and the begin/end calls depend on the complete type
// of ts, so make sure we include it.
bool VisitCXXForRangeStmt(clang::CXXForRangeStmt* stmt) {
if (CanIgnoreCurrentASTNode())
return true;
if (const Type* type = stmt->getRangeInit()->getType().getTypePtrOrNull()) {
ReportTypeUse(CurrentLoc(), RemovePointersAndReferencesAsWritten(type));
// TODO: We should probably find a way to require inclusion of any
// argument-dependent begin/end declarations.
}
return true;
}
// When casting non-pointers, iwyu will do the right thing
// automatically, but sometimes when casting from one pointer to
// another, you still need the full type information of both types:
// for instance, when static-casting from a sub-class to a
// super-class. Testing shows this is true for static, dynamic
// casts, and implicit casts, but not for reinterpret casts, const
// casts, or C-style casts. (Functional casts like int(3.5) are
// treated the same as C-style casts.) clang helpfully provides a
// 'cast kind', which we use to determine when full types are
// needed. When we notice that the cast is a cast up or down a
// class hierarchy, we require full type info for both types even
// for C-style casts (though the language doesn't), to give the
// compiler a fighting chance of generating correct code.
bool VisitCastExpr(clang::CastExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
const Type* from_type = GetTypeOf(expr->getSubExprAsWritten());
const Type* to_type = GetTypeOf(expr);
// If this cast requires a user-defined conversion of the from-type, look up
// its return type so we can see through up/down-casts via such conversions.
const Type* converted_from_type = nullptr;
if (const NamedDecl* conv_decl = expr->getConversionFunction()) {
converted_from_type =
cast<FunctionDecl>(conv_decl)->getReturnType().getTypePtr();
}
std::vector<const Type*> required_full_types;
// The list of kinds: http://clang.llvm.org/doxygen/namespaceclang.html
switch (expr->getCastKind()) {
// This cast still isn't handled directly.
case clang::CK_Dependent:
break;
// These casts don't require any iwyu action.
case clang::CK_LValueToRValue:
case clang::CK_AtomicToNonAtomic:
case clang::CK_NonAtomicToAtomic:
case clang::CK_ReinterpretMemberPointer:
case clang::CK_BuiltinFnToFnPtr:
case clang::CK_ZeroToOCLOpaqueType: // OpenCL opaque types are built-in.
case clang::CK_IntToOCLSampler: // OpenCL sampler_t is a built-in type.
case clang::CK_AddressSpaceConversion: // Address spaces are associated
// with pointers, so no need for
// the full type.
break;
// Ignore non-ptr-to-ptr casts.
case clang::CK_ArrayToPointerDecay:
case clang::CK_BooleanToSignedIntegral:
case clang::CK_FixedPointCast:
case clang::CK_FixedPointToBoolean:
case clang::CK_FixedPointToFloating:
case clang::CK_FixedPointToIntegral:
case clang::CK_FloatingCast:
case clang::CK_FloatingComplexCast:
case clang::CK_FloatingComplexToBoolean:
case clang::CK_FloatingComplexToIntegralComplex:
case clang::CK_FloatingComplexToReal:
case clang::CK_FloatingRealToComplex:
case clang::CK_FloatingToBoolean:
case clang::CK_FloatingToFixedPoint:
case clang::CK_FloatingToIntegral:
case clang::CK_FunctionToPointerDecay:
case clang::CK_IntegralCast:
case clang::CK_IntegralComplexCast:
case clang::CK_IntegralComplexToBoolean:
case clang::CK_IntegralComplexToFloatingComplex:
case clang::CK_IntegralComplexToReal:
case clang::CK_IntegralRealToComplex:
case clang::CK_IntegralToBoolean:
case clang::CK_IntegralToFixedPoint:
case clang::CK_IntegralToFloating:
case clang::CK_IntegralToPointer:
case clang::CK_MatrixCast:
case clang::CK_MemberPointerToBoolean:
case clang::CK_NullToMemberPointer:
case clang::CK_NullToPointer:
case clang::CK_PointerToBoolean:
case clang::CK_PointerToIntegral:
case clang::CK_ToUnion:
case clang::CK_ToVoid:
break;
case clang::CK_AnyPointerToBlockPointerCast:
case clang::CK_ARCConsumeObject:
case clang::CK_ARCExtendBlockObject:
case clang::CK_ARCProduceObject:
case clang::CK_ARCReclaimReturnedObject:
case clang::CK_BlockPointerToObjCPointerCast:
case clang::CK_CopyAndAutoreleaseBlockObject:
case clang::CK_CPointerToObjCPointerCast:
case clang::CK_ObjCObjectLValueCast:
case clang::CK_VectorSplat:
CHECK_UNREACHABLE_(
"TODO(csilvers): for objc and clang lang extensions");
break;
// Kinds for reinterpret_cast and const_cast, which need no full types.
case clang::CK_BitCast: // used for reinterpret_cast
case clang::CK_LValueBitCast: // used for reinterpret_cast
case clang::CK_LValueToRValueBitCast: // used for reinterpret_cast
case clang::CK_NoOp: // used for const_cast, etc
break;
// Need the full to-type so we can call its constructor.
case clang::CK_ConstructorConversion:
// 'Autocast' in function calls is handled in VisitCXXConstructExpr.
if (!current_ast_node()->template HasAncestorOfType<CallExpr>())
required_full_types.push_back(to_type);
break;
// Need the full from-type so we can call its 'operator <totype>()'.
case clang::CK_UserDefinedConversion:
required_full_types.push_back(from_type);
break;
// Kinds that cast up or down an inheritance hierarchy.
case clang::CK_BaseToDerived:
case clang::CK_BaseToDerivedMemberPointer:
// Just 'to' type is enough: full type for derived gets base type too.
required_full_types.push_back(to_type);
break;
case clang::CK_DerivedToBase:
case clang::CK_UncheckedDerivedToBase:
case clang::CK_DerivedToBaseMemberPointer:
// Just 'from' type is enough: full type for derived gets base type too.
required_full_types.push_back(from_type);
// If this derived-to-base cast had an associated conversion function,
// it's a user-defined conversion operator. The from-type in this cast
// is not necessarily related to the base type, but the converted
// from-type must be, so make sure we require both.
if (converted_from_type) {
required_full_types.push_back(converted_from_type);
}
break;
case clang::CK_Dynamic:
// Usually dynamic casting is a base-to-derived cast, but it is
// possible to dynamic-cast between siblings, in which case we
// need both types.
required_full_types.push_back(from_type);
required_full_types.push_back(to_type);
break;
}
// TODO(csilvers): test if we correctly say we use FooPtr for
// typedef Foo* FooPtr; ... static_cast<FooPtr>(...) ...
for (const Type* type : required_full_types) {
const Type* deref_type = RemovePointersAndReferences(type);
if (CanIgnoreType(deref_type))
continue;
ReportTypeUse(CurrentLoc(), deref_type);
}
return true;
}
// Mark that we need the full type info for our base type -- the
// thing we're a member of -- and it's not just forward-declarable.
// For instance, for code 'Mytype* myvar; myvar->a;', we'll get a
// MemberExpr callback whose base has the type of myvar.
bool VisitMemberExpr(clang::MemberExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
const Expr* base_expr = expr->getBase()->IgnoreParenImpCasts();
const Type* base_type = GetTypeOf(base_expr);
CHECK_(base_type && "Member's base does not have a type?");
const Type* deref_base_type // For myvar->a, base-type will have a *
= expr->isArrow() ? RemovePointerFromType(base_type) : base_type;
if (CanIgnoreType(base_type) && CanIgnoreType(deref_base_type))
return true;
if (const TypedefType* typedef_type = DynCastFrom(deref_base_type)) {
deref_base_type = DesugarDependentTypedef(typedef_type);
}
// Technically, we should say the type is being used at the
// location of base_expr. That may be a different file than us in
// cases like MACRO.b(). However, while one can imagine
// situations where the base-type is the responsibility of the
// macro-author ('SOME_GLOBAL_OBJECT.a()'), the more common case
// is it's our responsibility ('CHECK_NOTNULL(x).a()'). Until we
// can better distinguish whether a macro body is an expression
// that's responsible for its type or not, we just assume it is.
// TODO(csilvers): fix when we can determine what the macro-text
// is responsible for and what we're responsible for.
// TODO(csilvers): we should be reporting a fwd-decl use for
// GetTypeOf(expr), not on deref_base_type.
ReportTypeUse(CurrentLoc(), deref_base_type);
return true;
}
// For a[4], report that we need the full type of *a (to get its
// size; otherwise the compiler can't tell the address of a[4]).
bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
const Type* element_type = GetTypeOf(expr);
if (CanIgnoreType(element_type))
return true;
ReportTypeUse(CurrentLoc(), element_type);
return true;
}
// If a binary operator expression results in pointer arithmetic, we need the
// full types of all pointers involved.
bool VisitBinaryOperator(clang::BinaryOperator* expr) {
if (CanIgnoreCurrentASTNode())
return true;
// If it's not +, +=, - or -=, this can't be pointer arithmetic
clang::BinaryOperator::Opcode op = expr->getOpcode();
if (op != clang::BO_Add && op != clang::BO_Sub &&
op != clang::BO_AddAssign && op != clang::BO_SubAssign)
return true;
for (const Stmt* child : expr->children()) {
if (const PointerType* pointer_type =
dyn_cast<PointerType>(GetTypeOf(cast<Expr>(child)))) {
// It's a pointer-typed expression. Get the pointed-to type (which may
// itself be a pointer) and report it.
const Type* deref_type = pointer_type->getPointeeType().getTypePtr();
if (!CanIgnoreType(deref_type))
ReportTypeUse(CurrentLoc(), deref_type);
}
}
return true;
}
2019-11-25 21:49:14 +00:00
bool VisitTypeTraitExpr(clang::TypeTraitExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
// We assume that all type traits with >= 2 arguments require
// full type information even for pointer types. For example,
// this is the case for `__is_convertible_to` trait.
if (expr == nullptr || expr->getNumArgs() < 2)
return true;
for (const clang::TypeSourceInfo* arg : expr->getArgs()) {
clang::QualType qual_type = arg->getType();
const Type* type = qual_type.getTypePtr();
const Type* deref_type = RemovePointersAndReferencesAsWritten(type);
if (!CanIgnoreType(deref_type)) {
ReportTypeUse(CurrentLoc(), deref_type);
}
}
return true;
}
// Mark that we need the full type info for the thing we're taking
// sizeof of. Sometimes this is double-counting: for
// sizeof(some_type), RecursiveASTVisitor will visit some_type and
// say it needs the full type information there, and for
// sizeof(some_var), we'll report we need full type information when
// some_var is defined. But if the arg is a reference, nobody else
// will say we need full type info but us.
bool VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
current_ast_node()->set_in_forward_declare_context(false);
// Calling sizeof on a reference-to-X is the same as calling it on X.
// If sizeof() takes a type, this is easy to check. If sizeof()
// takes an expr, it's hard to tell -- GetTypeOf(expr) 'sees through'
// references. Luckily, we want to see through references, so we
// just use the GetTypeOf().
if (expr->isArgumentType()) {
const TypeLoc& arg_tl = expr->getArgumentTypeInfo()->getTypeLoc();
if (const auto* reftype = arg_tl.getTypePtr()->getAs<ReferenceType>()) {
const Type* dereftype = reftype->getPointeeTypeAsWritten().getTypePtr();
if (!CanIgnoreType(reftype) || !CanIgnoreType(dereftype))
ReportTypeUse(GetLocation(&arg_tl), dereftype);
} else {
// No need to report on non-ref types, RecursiveASTVisitor will get 'em.
}
} else {
const Expr* arg_expr = expr->getArgumentExpr();
const Type* dereftype = arg_expr->getType().getTypePtr();
if (!CanIgnoreType(dereftype))
// This reports even if the expr ends up not being a reference, but
// that's ok (if potentially redundant).
ReportTypeUse(GetLocation(arg_expr->IgnoreParenImpCasts()), dereftype);
}
return true;
}
// We want to mark use of the base type For 'free function' operator
// overloads ('ostream& operator<<(ostream& o, int x)') just like we
// do for member functions ('ostream& ostream::operator<<(int x)')
// -- for iwyu purposes, 'x << 4' is just semantic sugar around
// x.operator<<(4).
bool VisitCXXOperatorCallExpr(clang::CXXOperatorCallExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
if (const Expr* owner_expr = GetFirstClassArgument(expr)) {
const Type* owner_type = GetTypeOf(owner_expr);
// Note we report the type use is the location of owner_expr
// (the 'a' in 'a << b' or the 'MACRO' in 'MACRO << b'), rather
// than our location (which is the '<<'). That way, we properly
// situate the owner when it's a macro.
if (!CanIgnoreType(owner_type))
ReportTypeUse(GetLocation(owner_expr), owner_type);
}
return true;
}
// We have to check the type being deleted is fully defined (the
// language doesn't require it, but bad things happen if it's not:
// the destructor isn't run).
bool VisitCXXDeleteExpr(clang::CXXDeleteExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
const Expr* delete_arg = expr->getArgument()->IgnoreParenImpCasts();
// We always delete a pointer, so do one dereference to get the
// actual type being deleted.
const Type* delete_ptr_type = GetTypeOf(delete_arg);
const Type* delete_type = RemovePointerFromType(delete_ptr_type);
if (CanIgnoreType(delete_ptr_type) && CanIgnoreType(delete_type))
return true;
if (delete_type && !IsPointerOrReferenceAsWritten(delete_type))
ReportTypeUse(CurrentLoc(), delete_type);
return true;
}
// Handle the case of passing references to variadic functions
// (those with '...'). We need the full type information for the
// reference in that case, since compilers seem to just deref the
// var before passing it in. Note we subclass all the
// function-calling methods rather than HandleFunctionCall, because
// a) we need type-specific caller information anyway, and b)
// HandleFunctionCall isn't called for calls via function-pointers,
// which we want.
void ReportIfReferenceVararg(const Expr* const* args, unsigned num_args,
const FunctionProtoType* callee_type) {
if (callee_type && callee_type->isVariadic()) {
const unsigned first_vararg_index = callee_type->getNumParams();
for (unsigned i = first_vararg_index; i < num_args; i++) {
// We only care about reporting for references, but it's ok if
// we catch a few non-ref types too (it's just redundant).
// All expressions that are references will have their
// valuekind be an LValue, so we use that as the test.
if (args[i]->getValueKind() == clang::VK_LValue) {
// The types of expressions 'see through' the reference to
// the underlying type, which is exactly what we want here.
ReportTypeUse(CurrentLoc(), GetTypeOf(args[i]));
}
}
}
}
void ReportIfReferenceVararg(const Expr* const* args, unsigned num_args,
const FunctionDecl* callee) {
if (callee) {
const FunctionProtoType* callee_type =
DynCastFrom(callee->getType().getTypePtr());
CHECK_(callee_type &&
"The type of a FunctionDecl must be a FunctionProtoType.");
ReportIfReferenceVararg(args, num_args, callee_type);
}
}
// We only need visitors for CallExpr, ConstructExpr, and NewExpr
// (which also captures their subclasses). We can ignore DeleteExpr
// since destructors never have arguments. NewExpr we treat below,
// since it requires other checks as well.
bool VisitCallExpr(clang::CallExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
// Nothing to do if the called function is an old K&R-style function.
const FunctionType* fn_type = GetCalleeFunctionType(expr);
if (const FunctionProtoType* fn_proto = DynCastFrom(fn_type))
ReportIfReferenceVararg(expr->getArgs(), expr->getNumArgs(), fn_proto);
return true;
}
bool VisitCXXConstructExpr(clang::CXXConstructExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
ReportIfReferenceVararg(expr->getArgs(), expr->getNumArgs(),
expr->getConstructor());
// 'Autocast' -- calling a one-arg, non-explicit constructor
// -- is a special case when it's done for a function call.
// iwyu requires the function-writer to provide the #include
// for the casted-to type, just so we don't have to require it
// here. *However*, the function-author can override this
// iwyu requirement, in which case we're responsible for the
// casted-to type. See IwyuBaseASTVisitor::VisitFunctionDecl.
// Explicitly written CXXTemporaryObjectExpr are ignored here.
if (expr->getStmtClass() == Stmt::StmtClass::CXXConstructExprClass) {
const Type* type = Desugar(expr->getType().getTypePtr());
if (current_ast_node()->template HasAncestorOfType<CallExpr>() &&
ContainsKey(GetCallerResponsibleTypesForAutocast(current_ast_node()),
RemoveReferenceAsWritten(type))) {
if (!CanIgnoreType(type))
ReportTypeUse(CurrentLoc(), type);
}
}
return true;
}
// An OverloadExpr is an overloaded function (or method) in an
// uninstantiated template, that can't be resolved until the
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
// template is instantiated. The simplest case is something like:
// void Foo(int) { ... }
// void Foo(float) { ... }
// template<typename T> Fn(T t) { Foo(t); }
// But by far the most common case is when the function-to-be-called
// is also a templated function:
// template<typename T> Fn1(T t) { ... }
// template<typename T> Fn2(T t) { Fn1(t); }
// In either case, we look at all the potential overloads. If they
// all exist in the same file -- which is pretty much always the
// case, especially with a template calling a template -- we can do
// an iwyu warning now, even without knowing the exact overload.
// In that case, we store the fact we warned, so we won't warn again
// when the template is instantiated.
// TODO(csilvers): to be really correct, we should report *every*
// overload that callers couldn't match via ADL.
bool VisitOverloadExpr(clang::OverloadExpr* expr) {
// No CanIgnoreCurrentASTNode() check here! It's later in the function.
// Make sure all overloads are in the same file.
if (expr->decls_begin() == expr->decls_end()) // not sure this is possible
return true;
const NamedDecl* first_decl = *expr->decls_begin();
const FileEntry* first_decl_file_entry = GetFileEntry(first_decl);
for (OverloadExpr::decls_iterator it = expr->decls_begin();
it != expr->decls_end(); ++it) {
if (GetFileEntry(*it) != first_decl_file_entry)
return true;
}
// For now, we're only worried about function calls.
// TODO(csilvers): are there other kinds of overloads we need to check?
const FunctionDecl* arbitrary_fn_decl = nullptr;
for (OverloadExpr::decls_iterator it = expr->decls_begin();
it != expr->decls_end(); ++it) {
const NamedDecl* decl = *it;
// Sometimes a UsingShadowDecl comes between us and the 'real' decl.
if (const UsingShadowDecl* using_shadow_decl = DynCastFrom(decl))
decl = using_shadow_decl->getTargetDecl();
if (const FunctionDecl* fn_decl = DynCastFrom(decl)) {
arbitrary_fn_decl = fn_decl;
break;
} else if (const FunctionTemplateDecl* tpl_decl = DynCastFrom(decl)) {
arbitrary_fn_decl = tpl_decl->getTemplatedDecl();
break;
}
}
// If we're an overloaded operator, we can never do the iwyu check
// before instantiation-time, because we don't know if we might
// end up being the built-in form of the operator. (Even if the
// only operator==() we see is in foo.h, we don't need to #include
// foo.h if the only call to operator== we see is on two integers.)
if (arbitrary_fn_decl && !arbitrary_fn_decl->isOverloadedOperator()) {
AddProcessedOverloadLoc(CurrentLoc());
VERRS(7) << "Adding to processed_overload_locs: "
<< PrintableCurrentLoc() << "\n";
// Because processed_overload_locs might be set in one visitor
// but used in another, each with a different definition of
// CanIgnoreCurrentASTNode(), we have to be conservative and set
// the has-considered flag always. But of course we only
// actually report the function use if CanIgnoreCurrentASTNode()
// is *currently* false.
if (!CanIgnoreCurrentASTNode())
ReportDeclUse(CurrentLoc(), arbitrary_fn_decl);
}
return true;
}
// TODO(csilvers): handle some special cases when we're a
// CXXDependentScopeMemberExpr (e.g. vector<T>::resize().). If the
// base class is a TemplateSpecializationType, get its TemplateDecl
// and if all explicit specializations and patterns are defined in
// the same file, treat it as an expr with only one decl. May have
// trouble with methods defined in a different file than they're
// declared.
// If getOperatorNew() returns nullptr, it means the operator-new is
// overloaded, and technically we can't know which operator-new is
// being called until the template is instantiated. But if it looks
// like a placement-new, we handle it at template-writing time
// anyway.
bool VisitCXXNewExpr(clang::CXXNewExpr* expr) {
// Like in VisitOverloadExpr(), we update processed_overload_locs
// regardless of the value of CanIgnoreCurrentASTNode().
// We say it's placement-new if the (lone) placment-arg is a
// pointer. Unfortunately, often clang will just say it's a
// dependent type. In that case, we can still say it's a pointer
// in the (common) case the placement arg looks like '&something'.
// (This is possibly wrong for classes that override operator&, but
// those classes deserve what they get.)
if (!expr->getOperatorNew() &&
expr->getNumPlacementArgs() == 1 &&
(GetTypeOf(expr->getPlacementArg(0))->isPointerType() ||
GetTypeOf(expr->getPlacementArg(0))->isArrayType() ||
IsAddressOf(expr->getPlacementArg(0)))) {
// Treat this like an OverloadExpr.
AddProcessedOverloadLoc(CurrentLoc());
VERRS(7) << "Adding to processed_overload_locs (placement-new): "
<< PrintableCurrentLoc() << "\n";
if (!CanIgnoreCurrentASTNode()) {
// We have to 'make up' a full file path for 'new'. We'll
// parse it to '<new>' before using, so any path that does
// that, and is clearly a c++ path, is fine; its exact
// contents don't matter that much.
using clang::OptionalFileEntryRef;
const FileEntry* use_file = CurrentFileEntry();
OptionalFileEntryRef file = compiler()->getPreprocessor().LookupFile(
CurrentLoc(), "new", true, nullptr, use_file, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, false);
if (file) {
preprocessor_info().FileInfoFor(use_file)->ReportFullSymbolUse(
CurrentLoc(), *file, "operator new");
}
}
}
// We also need to do a varargs check, like for other function calls.
if (CanIgnoreCurrentASTNode())
return true;
// ... only if this NewExpr involves a constructor call.
2016-03-27 20:06:13 +01:00
const Expr* initializer = expr->getInitializer();
if (const CXXConstructExpr* cce = DynCastFrom(initializer)) {
2016-03-27 20:06:13 +01:00
ReportIfReferenceVararg(cce->getArgs(),
cce->getNumArgs(),
cce->getConstructor());
}
return true;
}
bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
// Report EnumName instead of EnumName::Item.
// It supports for removing EnumName forward- (opaque-) declarations
// from output.
if (const auto* enum_constant_decl =
dyn_cast<EnumConstantDecl>(expr->getDecl())) {
const auto* enum_decl =
cast<EnumDecl>(enum_constant_decl->getDeclContext());
// For unnamed enums, enumerator name is still preferred.
if (enum_decl->getIdentifier())
ReportDeclUse(CurrentLoc(), enum_decl);
else
ReportDeclUse(CurrentLoc(), enum_constant_decl);
}
return true;
}
// When we call (or potentially call) a function, do an IWYU check
// via ReportDeclUse() to make sure the definition of the function
// is properly #included.
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
bool HandleFunctionCall(FunctionDecl* callee, const Type* parent_type,
const clang::Expr* calling_expr) {
if (!Base::HandleFunctionCall(callee, parent_type, calling_expr))
return false;
if (!callee || CanIgnoreCurrentASTNode() || CanIgnoreDecl(callee))
return true;
// We may have already been checked in a previous
// VisitOverloadExpr() call. Don't check again in that case.
if (IsProcessedOverloadLoc(CurrentLoc()))
return true;
ReportDeclUse(CurrentLoc(), callee);
// Usually the function-author is responsible for providing the
// full type information for the return type of the function, but
// in cases where it's not, we have to take responsibility.
// TODO(csilvers): check the fn argument types as well.
const Type* return_type = Desugar(callee->getReturnType().getTypePtr());
if (ContainsKey(GetCallerResponsibleTypesForFnReturn(callee),
return_type)) {
ReportTypeUse(CurrentLoc(), return_type);
}
return true;
}
//------------------------------------------------------------
// Visitors of types derived from clang::Type.
bool VisitType(clang::Type* type) {
// In VisitFunctionDecl(), we say all children of function
// declarations are forward-declarable. This is true, *except*
// for the exception (throw) types. We clean that up here.
// TODO(csilvers): figure out how to do these two steps in one place.
const FunctionProtoType* fn_type = nullptr;
if (!fn_type) {
fn_type = current_ast_node()->template GetParentAs<FunctionProtoType>();
}
if (!fn_type) {
if (const FunctionDecl* fn_decl =
current_ast_node()->template GetParentAs<FunctionDecl>())
fn_type = dyn_cast<FunctionProtoType>(GetTypeOf(fn_decl));
}
if (fn_type) {
for (FunctionProtoType::exception_iterator it =
fn_type->exception_begin();
it != fn_type->exception_end(); ++it)
if (it->getTypePtr() == type) { // *we're* an exception decl
current_ast_node()->set_in_forward_declare_context(false);
break;
}
}
return true;
}
bool VisitTemplateSpecializationType(TemplateSpecializationType* type) {
if (CanIgnoreCurrentASTNode() || CanIgnoreType(type))
return true;
const NamedDecl* decl = TypeToDeclAsWritten(type);
// If we are forward-declarable, so are our template arguments.
if (CanForwardDeclareType(current_ast_node())) {
ReportDeclForwardDeclareUse(CurrentLoc(), decl);
current_ast_node()->set_in_forward_declare_context(true);
} else {
ReportDeclUse(CurrentLoc(), decl);
}
return true;
}
//------------------------------------------------------------
// Visitors defined by BaseAstVisitor.
bool VisitNestedNameSpecifier(NestedNameSpecifier* nns) {
if (!Base::VisitNestedNameSpecifier(nns))
return false;
// If we're in an nns (e.g. the Foo in Foo::bar), we're never
// forward-declarable, even if we're part of a pointer type, or in
// a template argument, or whatever.
current_ast_node()->set_in_forward_declare_context(false);
return true;
}
// Template arguments are forward-declarable by default. However,
// default template template args shouldn't be: we're responsible for
// the full type info for default args. So no forward-declaring
// MyClass in 'template<template<typename A> class T = MyClass> C ...'
// We detect because MyClass's parent is TemplateTemplateParmDecl.
// TODO(csilvers): And not when they're a type that's in
// known_fully_used_tpl_type_args_. See if that solves the problem with
// I1_TemplateClass<std::vector<I1_Class>> i1_nested_templateclass(...)
void DetermineForwardDeclareStatusForTemplateArg(ASTNode* ast_node) {
CHECK_(ast_node->IsA<TemplateArgument>() &&
"Should only pass in a template arg to DFDSFTA");
if (!IsDefaultTemplateTemplateArg(ast_node)) {
ast_node->set_in_forward_declare_context(true);
}
}
bool VisitTemplateArgument(const TemplateArgument& arg) {
if (!Base::VisitTemplateArgument(arg))
return false;
// Template arguments are forward-declarable...usually.
DetermineForwardDeclareStatusForTemplateArg(current_ast_node());
return true;
}
bool VisitTemplateArgumentLoc(const TemplateArgumentLoc& argloc) {
if (!Base::VisitTemplateArgumentLoc(argloc))
return false;
// Template arguments are forward-declarable...usually.
DetermineForwardDeclareStatusForTemplateArg(current_ast_node());
return true;
}
//------------------------------------------------------------
// Helper routines for visiting and traversing. These helpers
// encode the logic of whether a particular type of object
// can be forward-declared or not.
// TODO(csilvers): get rid of in_forward_declare_context() and make
// this the canonical place to figure out if we can forward-declare.
bool CanForwardDeclareType(const ASTNode* ast_node) const {
CHECK_(ast_node->IsA<Type>());
if (const auto* type = ast_node->GetAs<Type>()) {
if (const auto* enum_type = type->getAs<EnumType>()) {
return CanBeOpaqueDeclared(enum_type);
}
}
// If we're in a forward-declare context, well then, there you have it.
if (ast_node->in_forward_declare_context())
return true;
// Another place we disregard what the language allows: if we're
// a dependent type, in theory we can be forward-declared. But
// we require the full definition anyway, so all the template
// callers don't have to provide it instead. Thus we don't
// run the following commented-out code (left here for reference):
//if (ast_node->GetAs<TemplateSpecializationType>()->isDependentType())
// return false;
// Read past elaborations like 'class' keyword or namespaces.
ast_node = MostElaboratedAncestor(ast_node);
// Now there are two options: either we are part of a type or we are part of
// a declaration involving a type.
const Type* parent_type = ast_node->GetParentAs<Type>();
if (parent_type == nullptr) {
// It's not a type; analyze different kinds of declarations.
if (const auto *decl = ast_node->GetParentAs<ValueDecl>()) {
// We can shortcircuit static data member declarations immediately,
// they can always be forward-declared.
if (const auto *var_decl = dyn_cast<VarDecl>(decl)) {
if (!var_decl->isThisDeclarationADefinition() &&
var_decl->isStaticDataMember()) {
return true;
}
}
parent_type = GetTypeOf(decl);
} else if (const auto *decl = ast_node->GetParentAs<TypeDecl>()) {
// Elaborated types in type decls are always forward-declarable
// (and usually count as forward declarations themselves).
if (IsElaboratedTypeSpecifier(ast_node)) {
return true;
}
// If we ourselves are a forward-decl -- that is, we're the type
// component of a forward-declaration (which would be our parent
// AST node) -- then we're forward-declarable by definition.
if (const auto* parent_decl = ast_node->GetParentAs<TagDecl>()) {
if (IsForwardDecl(parent_decl))
return true;
}
// If we're part of a typedef declaration, we don't want to forward-
// declare even if we're a pointer ('typedef Foo* Bar; Bar x; x->a'
// needs full type of Foo.)
if (ast_node->ParentIsA<TypedefNameDecl>()) {
return false;
}
}
}
// TODO(csilvers): should probably just be IsPointerOrReference
return parent_type && IsPointerOrReferenceAsWritten(parent_type);
}
protected:
const IwyuPreprocessorInfo& preprocessor_info() const {
return visitor_state_->preprocessor_info;
}
private:
template <typename T> friend class IwyuBaseAstVisitor;
bool IsProcessedOverloadLoc(SourceLocation loc) const {
return ContainsKey(visitor_state_->processed_overload_locs, loc);
}
void AddProcessedOverloadLoc(SourceLocation loc) {
visitor_state_->processed_overload_locs.insert(loc);
}
// Report types needed for template instantiation in cases when template
// specialization type isn't explicitly written in a source code
// in a non-fwd-declarable context (otherwise, they should be reported from
// VisitTemplateSpecializationType), e.g.:
// template <class T> struct S { T t; };
// void fn(const S<Class>& s) { // Forward declarations are sufficient here.
// (void)s.t; // Full 'Class' type is needed due to template instantiation.
// }
void ReportTplSpecComponentTypes(const TemplateSpecializationType*) = delete;
2015-12-31 08:52:48 +00:00
// Do not add any variables here! If you do, they will not be shared
// between the normal iwyu ast visitor and the
// template-instantiation visitor, which is almost always a mistake.
// Instead, add them to the VisitorState struct, above.
VisitorState* const visitor_state_;
};
// ----------------------------------------------------------------------
// --- InstantiatedTemplateVisitor
// ----------------------------------------------------------------------
//
// This class is used to find all template-specified types used in an
// instantiated template class, function, or method -- or rather, all
// such types that are used in a way that can't be forward-declared.
// That is, for
// template<class T, class U> int Myfunc() { T* t; U u; Thirdclass z; }
// if we saw an instantiation such as myfunc<Foo, Bar>, we would pass
// that instantiation to this traversal class, and it would report
// that Bar is used in a non-forward-declarable way. (It would not
// report Foo, which is used only in a forward-declarable way, and
// would not report Thirdclass, which is not a type specified in a
// template.)
//
// This class has two main entry points: one for instantiated
// template functions and methods (including static methods,
// constructor calls, and operator overloads), and one for
// instantiated template classes.
//
// In each case, it is given the appropriate node from the AST that
// captures the instantiation (a TemplateSpecializationType or
// CallExpr), and returns a set of Type* nodes of types that are used
// in a non-forward-declarable way. Note it's safe to call this even
// on non-templatized functions and classes; we'll just always return
// the empty set in that case.
//
// The traversal of the AST is done via RecursiveASTVisitor, which uses
// CRTP (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
// TODO(csilvers): move this to its own file?
class InstantiatedTemplateVisitor
: public IwyuBaseAstVisitor<InstantiatedTemplateVisitor> {
public:
typedef IwyuBaseAstVisitor<InstantiatedTemplateVisitor> Base;
InstantiatedTemplateVisitor(VisitorState* visitor_state)
: Base(visitor_state) {
Clear();
}
//------------------------------------------------------------
// Public entry points
// ScanInstantiatedFunction() looks through the template definition of
// the given function as well as the definitions of all functions
// called from it (directly or indirectly) and records all template
// type arguments fully used by them and all methods used by them.
// The "fully used type arguments" are a subset of
// tpl_type_args_of_interest, which are the types we care about, and
// usually explicitly written at the call site.
//
// ScanInstantiatedType() is similar, except that it looks through
// the definition of a class template instead of a statement.
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
// resugar_map is a map from an unsugared (canonicalized) template
// type to the template type as written (or as close as we can find
// to it). If a type is not in resugar-map, it might be due to a
// recursive template call and encode a template type we don't care
// about ourselves. If it's in the resugar_map but with a 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
// value, it's a default template parameter, that the
// template-caller may or may not be responsible for.
void ScanInstantiatedFunction(
const FunctionDecl* fn_decl, const Type* parent_type,
const ASTNode* caller_ast_node,
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 Type*, const Type*>& resugar_map) {
Clear();
caller_ast_node_ = caller_ast_node;
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
resugar_map_ = resugar_map;
// Make sure that the caller didn't already put the decl on the ast-stack.
CHECK_(caller_ast_node->GetAs<Decl>() != fn_decl && "AST node already set");
// caller_ast_node requires a non-const ASTNode, but our node is
// const. This cast is safe because we don't do anything with this
// node (instead, we immediately push a new node on top of it).
set_current_ast_node(const_cast<ASTNode*>(caller_ast_node));
TraverseExpandedTemplateFunctionHelper(fn_decl, parent_type);
}
// This isn't a Stmt, but sometimes we need to fully instantiate
// a template class to get at a field of it, for instance:
// MyClass<T>::size_type s;
void ScanInstantiatedType(ASTNode* caller_ast_node,
const map<const Type*, const Type*>& resugar_map) {
Clear();
caller_ast_node_ = caller_ast_node;
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
resugar_map_ = resugar_map;
// The caller node *is* the current node, unlike ScanInstantiatedFunction
// which instead starts in the context of the parent expression and relies
// on a TraverseDecl call to push the decl to the top of the AST stack.
set_current_ast_node(caller_ast_node);
const TemplateSpecializationType* type =
caller_ast_node->GetAs<TemplateSpecializationType>();
CHECK_(type != nullptr && "Not a template specialization");
// As in TraverseExpandedTemplateFunctionHelper, we ignore all AST nodes
// that will be reported when we traverse the uninstantiated type.
if (const NamedDecl* type_decl_as_written =
GetDefinitionAsWritten(TypeToDeclAsWritten(type))) {
AstFlattenerVisitor nodeset_getter(compiler());
nodes_to_ignore_ = nodeset_getter.GetNodesBelow(
const_cast<NamedDecl*>(type_decl_as_written));
}
TraverseTemplateSpecializationType(
const_cast<TemplateSpecializationType*>(type));
}
//------------------------------------------------------------
// Implements virtual methods from Base.
// When checking a template instantiation, we don't care where the
// template definition is, so we never have any reason to ignore a
// node.
bool CanIgnoreCurrentASTNode() const override {
// TODO(csilvers): call CanIgnoreType() if we're a type.
return nodes_to_ignore_.Contains(*current_ast_node());
}
// For template instantiations, we want to print the symbol even if
// it's not from the main compilation unit.
bool ShouldPrintSymbolFromCurrentFile() const override {
return GlobalFlags().verbose >= 5;
}
string GetSymbolAnnotation() const override {
return " in tpl";
}
bool CanIgnoreType(const Type* type, IgnoreKind ignore_kind =
IgnoreKind::ForUse) const override {
if (!IsTypeInteresting(type))
return true;
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
switch (ignore_kind) {
case IgnoreKind::ForUse:
if (!IsKnownTemplateParam(type))
return true;
break;
case IgnoreKind::ForExpansion:
if (!InvolvesKnownTemplateParam(type))
return true;
break;
}
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 we're a default template argument, we should ignore the type
// if the template author intend-to-provide it, but otherwise we
// should not ignore it -- the caller is responsible for the type.
// This captures cases like hash_set<Foo>, where the caller is
// responsible for defining hash<Foo>.
// IsProvidedByTemplate handles the case we
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 a templated class that #includes "foo.h" and has a
// scoped_ptr<Foo>: we say the templated class provides Foo, even
// though it's scoped_ptr.h that's actually trying to call
// Foo::Foo and ::~Foo.
// TODO(csilvers): this isn't ideal: ideally we'd want
// 'TheInstantiatedTemplateForWhichTypeWasADefaultTemplateArgumentIntendsToProvide',
// but clang doesn't store that information.
return IsDefaultTemplateParameter(type) && IsProvidedByTemplate(type);
}
bool IsTypeInteresting(const Type* type) const {
// We only care about types that would have been dependent in the
// uninstantiated template: that is, SubstTemplateTypeParmType types
// or types derived from them. We use nodes_to_ignore_ to select down
// to those.
return !nodes_to_ignore_.Contains(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
bool IsKnownTemplateParam(const Type* type) const {
// Among all subst-type params, we only want those in the resugar-map. If
// we're not in the resugar-map at all, we're not a type corresponding to
// the template being instantiated, so we can be ignored.
type = Desugar(type);
return ContainsKey(resugar_map_, type);
}
bool InvolvesKnownTemplateParam(const Type* type) const {
return InvolvesTypeForWhich(
type, [&](const Type* type) { return IsKnownTemplateParam(type); });
}
// We ignore function calls in nodes_to_ignore_, which were already
// handled by the template-as-written, and function names that we
// are not responsible for because the template code is (for
// instance, we're not responsible for a vector's call to
// allocator::allocator(), because <vector> provides it for us).
bool CanIgnoreDecl(const Decl* decl) const override {
if (!decl)
return true;
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
return nodes_to_ignore_.Contains(decl);
}
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
// We always attribute type uses to the template instantiator. For
// decls, we do unless it looks like the template "intends to
// provide" the decl, by #including the file that defines the decl
// (if templates call other templates, we have to find the right
// template).
void ReportDeclUse(SourceLocation used_loc, const NamedDecl* decl,
const char* comment = nullptr,
UseFlags extra_use_flags = 0) override {
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
const SourceLocation actual_used_loc = GetLocOfTemplateThatProvides(decl);
if (actual_used_loc.isValid()) {
// If a template is responsible for this decl, then we don't add
// it to the cache; the cache is only for decls that the
// original caller is responsible for.
Base::ReportDeclUse(actual_used_loc, decl, comment, extra_use_flags);
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
} else {
// Let all the currently active types and decls know about this
// report, so they can update their cache entries.
for (CacheStoringScope* storer : cache_storers_)
storer->NoteReportedDecl(decl);
Base::ReportDeclUse(caller_loc(), decl, comment, extra_use_flags);
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
}
}
void ReportTypeUse(SourceLocation used_loc, const Type* type,
const char* comment = nullptr) override {
// clang desugars template types, so Foo<MyTypedef>() gets turned
// into Foo<UnderlyingType>(). Try to convert back.
type = ResugarType(type);
for (CacheStoringScope* storer : cache_storers_)
storer->NoteReportedType(type);
Base::ReportTypeUse(caller_loc(), type, comment);
}
//------------------------------------------------------------
// Overridden traverse-style methods from Base.
// The 'convenience' HandleFunctionCall is perfect for us!
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
bool HandleFunctionCall(FunctionDecl* callee, const Type* parent_type,
const clang::Expr* calling_expr) {
if (const Type* resugared_type = ResugarType(parent_type))
parent_type = resugared_type;
if (!Base::HandleFunctionCall(callee, parent_type, calling_expr))
return false;
if (!callee || CanIgnoreCurrentASTNode() || CanIgnoreDecl(callee))
return true;
return TraverseExpandedTemplateFunctionHelper(callee, parent_type);
}
bool TraverseUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr* expr) {
if (!Base::TraverseUnaryExprOrTypeTraitExpr(expr))
return false;
if (CanIgnoreCurrentASTNode())
return true;
const Type* arg_type = expr->getTypeOfArgument().getTypePtr();
// Calling sizeof on a reference-to-X is the same as calling it on X.
if (const auto* reftype = arg_type->getAs<ReferenceType>()) {
arg_type = reftype->getPointeeTypeAsWritten().getTypePtr();
}
if (const auto* type = arg_type->getAs<TemplateSpecializationType>()) {
// Even though sizeof(MyClass<T>) only requires knowing how much
// storage MyClass<T> takes, the language seems to require that
// MyClass<T> be fully instantiated, even typedefs. (Try
// compiling 'template<class T> struct C { typedef typename T::a t; };
// class S; int main() { return sizeof(C<S>); }'.)
return TraverseDataAndTypeMembersOfClassHelper(type);
}
return true;
}
bool TraverseTemplateSpecializationTypeHelper(
const TemplateSpecializationType* type) {
if (CanIgnoreCurrentASTNode())
return true;
// Skip the template traversal if this occurrence of the template name is
// just a class qualifier for an out of line method, as opposed to an object
// instantiation, where the templated code would need to be inspected.
//
// Class<T,U>::method() {
// |-Type---^
// |-NNS------^
// |-CXXMethodDecl--^
ASTNode* ast_node = current_ast_node();
if (const auto* nns = ast_node->GetParentAs<NestedNameSpecifier>()) {
if (nns->getAsType() == type) {
if (const auto* method = ast_node->GetAncestorAs<CXXMethodDecl>(2)) {
CHECK_(nns == method->getQualifier());
return true;
}
}
}
if (CanForwardDeclareType(ast_node))
ast_node->set_in_forward_declare_context(true);
return TraverseDataAndTypeMembersOfClassHelper(type);
}
bool TraverseTemplateSpecializationType(TemplateSpecializationType* type) {
if (!Base::TraverseTemplateSpecializationType(type))
return false;
return TraverseTemplateSpecializationTypeHelper(type);
}
bool TraverseTemplateSpecializationTypeLoc(
TemplateSpecializationTypeLoc typeloc) {
if (!Base::TraverseTemplateSpecializationTypeLoc(typeloc))
return false;
return TraverseTemplateSpecializationTypeHelper(typeloc.getTypePtr());
}
bool TraverseSubstTemplateTypeParmTypeHelper(
const clang::SubstTemplateTypeParmType* type) {
if (CanIgnoreCurrentASTNode() ||
CanIgnoreType(type, IgnoreKind::ForExpansion))
return true;
const Type* actual_type = ResugarType(type);
CHECK_(actual_type && "If !CanIgnoreType(), we should be resugar-able");
return TraverseType(QualType(actual_type, 0));
}
// When we see a template argument used inside an instantiated
// template, we want to explore the type recursively. For instance
// if we see Inner<Outer<Foo>>(), we want to recurse onto Foo.
bool TraverseSubstTemplateTypeParmType(
clang::SubstTemplateTypeParmType* type) {
if (!Base::TraverseSubstTemplateTypeParmType(type))
return false;
return TraverseSubstTemplateTypeParmTypeHelper(type);
}
bool TraverseSubstTemplateTypeParmTypeLoc(
clang::SubstTemplateTypeParmTypeLoc typeloc) {
if (!Base::TraverseSubstTemplateTypeParmTypeLoc(typeloc))
return false;
return TraverseSubstTemplateTypeParmTypeHelper(typeloc.getTypePtr());
}
// Check whether a use of a template parameter is a full use.
bool IsTemplateTypeParmUseFullUse(const Type* type) {
const ASTNode* node = MostElaboratedAncestor(current_ast_node());
// If we're a nested-name-specifier class (the Foo in Foo::bar),
// we need our full type info no matter what the context (even if
// we're a pointer, or a template arg, or whatever).
// TODO(csilvers): consider encoding this logic via
// in_forward_declare_context. I think this will require changing
// in_forward_declare_context to yes/no/maybe.
if (node->ParentIsA<NestedNameSpecifier>()) {
return true;
}
// If we're inside a typedef, we don't need our full type info --
// in this case we follow what the C++ language allows and let
// the underlying type of a typedef be forward-declared. This has
// the effect that code like:
// class MyClass;
// template<class T> struct Foo { typedef T value_type; ... }
// Foo<MyClass> f;
// does not make us require the full type of MyClass. The idea
// is that using Foo<MyClass>::value_type already requires the
// type for MyClass, so it doesn't make sense for the typedef
// to require it as well. TODO(csilvers): this doesn't really
// make any sense. Who figures out we need the full type if
// you do 'Foo<MyClass>::value_type m;'?
for (const ASTNode* ast_node = node; ast_node != caller_ast_node_;
ast_node = ast_node->parent()) {
if (ast_node->IsA<TypedefNameDecl>()) {
return false;
}
if (ast_node->IsA<TemplateSpecializationType>()) {
// If we hit a template specialization node before the typedef then we
// probably still need a full-use, so stop looking.
break;
}
}
// sizeof(a reference type) is the same as sizeof(underlying type).
// We have to handle that specially here, or else we'll say the
// reference is forward-declarable, below.
if (node->ParentIsA<UnaryExprOrTypeTraitExpr>() &&
isa<ReferenceType>(type)) {
return true;
}
// If we're used in a forward-declare context (MyFunc<T>() { T* t; }),
// or are ourselves a pointer type (MyFunc<Myclass*>()),
// we don't need to do anything: we're fine being forward-declared.
if (node->in_forward_declare_context())
return false;
if (node->ParentIsA<PointerType>() ||
node->ParentIsA<LValueReferenceType>() ||
IsPointerOrReferenceAsWritten(type))
return false;
return true;
}
// This helper is called on every use of a template argument type in an
// instantiated template. Its goal is to determine whether that use should
// constitute a full-use by the template caller, and perform other necessary
// bookkeeping.
void AnalyzeTemplateTypeParmUse(const Type* type) {
const ASTNode* node = MostElaboratedAncestor(current_ast_node());
// If the immediate parent node is a typedef, then register the new type as
// a new name for the template argument.
if (const TypedefNameDecl* typedef_decl =
node->GetParentAs<TypedefNameDecl>()) {
const Type* typedef_type = typedef_decl->getTypeForDecl();
VERRS(6) << "Registering " << PrintableType(typedef_type)
<< " as an alias for " << PrintableType(type)
<< " in the context of this instantiation\n";
template_argument_aliases_.emplace(typedef_type, type);
}
// Figure out how this type was actually written. clang always
// canonicalizes SubstTemplateTypeParmType, losing typedef info, etc.
const Type* actual_type = ResugarType(type);
CHECK_(actual_type &&
"Type passed to AnalyzeTemplateTypeParmUse should be resugar-able");
VERRS(6) << "AnalyzeTemplateTypeParmUse: type = " << PrintableType(type)
<< ", actual_type = " << PrintableType(actual_type) << '\n';
if (!IsTemplateTypeParmUseFullUse(actual_type)) {
// Non-full uses will already have been reported when they were used as
// template arguments, so nothing to do here.
return;
}
if (isa<ReferenceType>(actual_type)) {
// If the argument type is a reference type, then we actually care about
// the referred-to type.
const ReferenceType* actual_reftype = cast<ReferenceType>(actual_type);
type = actual_reftype->getPointeeTypeAsWritten().getTypePtr();
}
// At this point we know we are looking at a full-use of type. However,
// there are still two cases. If this is a template param that was written
// by the user, then we report a use of it. However, it might also be a
// parameter of some implementation detail template which just happens top
// involve the user's template parameter somehow. In this case, we need to
// recursively explore the instantiation of *that* template to see if the
// user's type is full-used therein.
if (IsKnownTemplateParam(type)) {
// We attribute all uses in an instantiated template to the
// template's caller.
ReportTypeUse(caller_loc(), type);
// Also report all previous explicit instantiations (declarations and
// definitions) as uses of the caller's location.
ReportExplicitInstantiations(type);
} else {
const Decl* decl = TypeToDeclAsWritten(type);
if (const auto* cts_decl =
dyn_cast<ClassTemplateSpecializationDecl>(decl)) {
if (ContainsKey(traversed_decls_, decl))
return; // avoid recursion & repetition
traversed_decls_.insert(decl);
VERRS(6)
<< "Recursively traversing " << PrintableDecl(cts_decl)
<< " which was full-used and involves a known template param\n";
TraverseDecl(const_cast<ClassTemplateSpecializationDecl*>(cts_decl));
}
}
}
// Our task is made easier since (at least in theory), every time we
// instantiate a template type, the instantiation has type
// SubstTemplateTypeParmTypeLoc in the AST tree.
bool VisitSubstTemplateTypeParmType(clang::SubstTemplateTypeParmType* type) {
if (CanIgnoreCurrentASTNode() ||
CanIgnoreType(type, IgnoreKind::ForExpansion))
return true;
AnalyzeTemplateTypeParmUse(type);
return Base::VisitSubstTemplateTypeParmType(type);
}
bool VisitTypedefType(clang::TypedefType* type) {
if (CanIgnoreCurrentASTNode())
return true;
// Typedefs of template arguments require special handling to ensure that
// we record full-uses of those arguments where appropriate. Those
// typedefs are stored in the template_argument_aliases_ map.
if (const Type* template_arg_type =
GetOrDefault(template_argument_aliases_, type, nullptr)) {
AnalyzeTemplateTypeParmUse(template_arg_type);
}
return Base::VisitTypedefType(type);
}
bool VisitTemplateSpecializationType(TemplateSpecializationType* type) {
if (CanIgnoreCurrentASTNode())
return true;
CHECK_(current_ast_node()->GetAs<TemplateSpecializationType>() == type)
<< "The current node must be equal to the template spec. type";
// Report previous explicit instantiations here, only if the type is needed
// fully.
if (!CanForwardDeclareType(current_ast_node()))
ReportExplicitInstantiations(type);
return Base::VisitTemplateSpecializationType(type);
}
void ReportExplicitInstantiations(const Type* type) {
const auto* decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
TypeToDeclAsWritten(type));
if (decl == nullptr)
return;
// Go through all previous redecls and filter out those that are not
// explicit template instantiations or already provided by the template.
for (const NamedDecl* redecl : decl->redecls()) {
if (!IsExplicitInstantiation(redecl) ||
!GlobalSourceManager()->isBeforeInTranslationUnit(
redecl->getLocation(), caller_loc()) ||
IsProvidedByTemplate(decl))
continue;
// Report the specific decl that points to the explicit instantiation
Base::ReportDeclUse(caller_loc(), redecl, "(for explicit instantiation)",
UF_ExplicitInstantiation);
}
}
// If constructing an object, check the type we're constructing.
// Normally we'd see that type later, when traversing the return
// type of the constructor-decl, but if we wait for that, we'll lose
// any SubstTemplateTypeParmType's we have (we lose all
// SubstTemplateTypeParmType's going from Expr to Decl).
// TODO(csilvers): This should maybe move to HandleFunctionCall.
bool VisitCXXConstructExpr(clang::CXXConstructExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
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* class_type = GetTypeOf(expr);
if (CanIgnoreType(class_type))
return true;
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 ctor type is a SubstTemplateTypeParmType, get the type-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
const Type* actual_type = ResugarType(class_type);
CHECK_(actual_type && "If !CanIgnoreType(), we should be resugar-able");
ReportTypeUse(caller_loc(), actual_type);
return Base::VisitCXXConstructExpr(expr);
}
// --- Handler declared in IwyuBaseASTVisitor.
void ReportTplSpecComponentTypes(const TemplateSpecializationType* type) {
TraverseDataAndTypeMembersOfClassHelper(type);
}
private:
// Clears the state of the visitor.
void Clear() {
caller_ast_node_ = 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
resugar_map_.clear();
template_argument_aliases_.clear();
traversed_decls_.clear();
nodes_to_ignore_.clear();
cache_storers_.clear();
}
// If we see the instantiated template using a type or decl (such as
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
// std::allocator), we want to know if the author of the template is
// providing the type or decl, so the code using the instantiated
// template doesn't have to. For instance:
// vector<int, /*allocator<int>*/> v; // in foo.cc
// Does <vector> provide the definition of allocator<int>? If not,
// foo.cc will have to #include <allocator>.
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
// We say the template-as-written does provide the decl if it, or
// any other header seen since we started instantiating the
// template, sees it. The latter requirement is to deal with a
// situation like this: we have a templated class that #includes
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
// "foo.h" and has a scoped_ptr<Foo>; we say the templated class
// provides Foo, even though it's scoped_ptr.h that's actually
// trying to call Foo::Foo and Foo::~Foo.
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
SourceLocation GetLocOfTemplateThatProvides(const NamedDecl* decl) const {
if (!decl)
return SourceLocation(); // an invalid source-loc
for (const ASTNode* ast_node = current_ast_node();
ast_node != caller_ast_node_; ast_node = ast_node->parent()) {
if (preprocessor_info().PublicHeaderIntendsToProvide(
GetFileEntry(ast_node->GetLocation()),
GetFileEntry(decl->getLocation())))
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
return ast_node->GetLocation();
}
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
return SourceLocation(); // an invalid source-loc
}
bool IsProvidedByTemplate(const NamedDecl* decl) const {
The intends-to-provide code wasn't working quite right. If I #include <vector>, then I intend-to-provide <vector>: you don't have to #include <vector> just because I use it in my (presumably templated) code, because I'm #including <vector> for you. However, this logic runs before the private->public mappings are done, so you're never actually wondering about <vector>, you're wondering about <bits/stl_vector.h>. In other words, if I'm #including <vector>, it's not to get the symbols just provided by <vector>, but to get the symbols provided by all the private headers that map to <vector> as well. I've changed the code that populates the intends-to-provide map to handle this case. Now we have many fewer instances of iwyu randomly deciding that you need to #include a file that is only used in templated code. I think there are more improvements to make in this area, but that will have to wait for another day. Another problem is that when we decided a template was responsible for a decl, because it intends-to-provide it, we were ignoring the decl, rather than keeping it and just assigning its use to the template. Usually this is harmless, but it could result in us deciding that the decl isn't used anywhere at all, and removing an #include (from the template file) that should be kept. In addition to fixing the relevant code, I've updated a test to make sure that doesn't happen. Testing showed up another issue, which is that the code for determining decl locations was correctly handling templated classes but not templated functions. This is now fixed. Fixing it, unfortunately, made visible a known weakness in IntendsToProvide, involving its ability to guess whether a file actually intends to provide a definition for its symbols or not, which is now a new TODO in badinc.cc. R=dsturtevant DELTA=285 (196 added, 66 deleted, 23 changed) Revision created by MOE tool push_codebase. MOE_MIGRATION=736
2011-03-04 00:28:14 +00:00
return GetLocOfTemplateThatProvides(decl).isValid();
}
bool IsProvidedByTemplate(const Type* type) const {
type = Desugar(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
type = RemovePointersAndReferences(type); // get down to the decl
if (const NamedDecl* decl = TypeToDeclAsWritten(type)) {
decl = GetDefinitionAsWritten(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 GetLocOfTemplateThatProvides(decl).isValid();
}
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 true; // we always provide non-decl types like int, etc.
}
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 a SubstTemplateTypeParmType, says whether it corresponds to a
// default template parameter (one not explicitly specified when the
// class was instantiated) or not. We store this in resugar_map by
// having the value be 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
bool IsDefaultTemplateParameter(const Type* type) const {
type = Desugar(type);
return ContainsKeyValue(resugar_map_, type, static_cast<Type*>(nullptr));
}
// clang desugars template types, so Foo<MyTypedef>() gets turned
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
// into Foo<UnderlyingType>(). We can 'resugar' using resugar_map_.
// If we're not in the resugar-map, then we weren't canonicalized,
// so we can just use the input type unchanged.
const Type* ResugarType(const Type* type) const {
type = Desugar(type);
// If we're the resugar-map but with a value of nullptr, it means
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
// we're a default template arg, which means we don't have anything
// to resugar to. So just return the input type.
if (ContainsKeyValue(resugar_map_, type, static_cast<const 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
return type;
return GetOrDefault(resugar_map_, type, type);
}
bool TraverseExpandedTemplateFunctionHelper(const FunctionDecl* fn_decl,
const Type* parent_type) {
if (!fn_decl || ContainsKey(traversed_decls_, fn_decl))
return true; // avoid recursion and repetition
traversed_decls_.insert(fn_decl);
// If we have cached the reporting done for this decl before,
// report again (but with the new caller_loc this time).
// Otherwise, for all reporting done in the rest of this scope,
// store in the cache for this function.
if (ReplayUsesFromCache(*FunctionCallsFullUseCache(),
fn_decl, caller_loc()))
return true;
// Make sure all the types we report in the recursive TraverseDecl
// calls, below, end up in the cache for fn_decl.
CacheStoringScope css(&cache_storers_, FunctionCallsFullUseCache(),
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
fn_decl, resugar_map_);
// We want to ignore all nodes that are the same in this
// instantiated function as they are in the uninstantiated version
// of the function. The latter will be reported when we traverse
// the uninstantiated function, so we don't need to re-traverse
// them here.
AstFlattenerVisitor nodeset_getter(compiler());
// This gets to the decl for the (uninstantiated) template-as-written:
const FunctionDecl* decl_as_written =
fn_decl->getTemplateInstantiationPattern();
if (!decl_as_written) {
if (fn_decl->isImplicit()) { // TIP not set up for implicit methods
// TODO(csilvers): handle implicit template methods
} else { // not a templated decl
decl_as_written = fn_decl;
}
}
if (decl_as_written) {
FunctionDecl* const daw = const_cast<FunctionDecl*>(decl_as_written);
nodes_to_ignore_.AddAll(nodeset_getter.GetNodesBelow(daw));
}
// We need to iterate over the function.
if (!TraverseDecl(const_cast<FunctionDecl*>(fn_decl)))
return false;
// If we're a constructor, we also need to construct the entire class,
// even typedefs that aren't used at construct time. Try compiling
// template<class T> struct C { typedef typename T::a t; };
// class S; int main() { C<S> c; }
if (isa<CXXConstructorDecl>(fn_decl)) {
CHECK_(parent_type && "How can a constructor have no parent?");
parent_type = Desugar(parent_type);
if (!TraverseDataAndTypeMembersOfClassHelper(
dyn_cast<TemplateSpecializationType>(parent_type)))
return false;
}
return true;
}
// Does the actual recursing over data members and type members of
// the instantiated class. Unlike
// TraverseClassTemplateSpecializationDecl() in the base class, it
// does *not* traverse the methods.
bool TraverseDataAndTypeMembersOfClassHelper(
const TemplateSpecializationType* type) {
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
if (!type)
return true;
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
// No point in doing traversal if we're forward-declared
if (current_ast_node()->in_forward_declare_context())
return true;
while (type->isTypeAlias()) {
type = type->getAliasedType()->getAs<TemplateSpecializationType>();
if (!type)
return true;
}
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
// If we're a dependent type, we only try to be analyzed if we're
// in the precomputed list -- in general, the only thing clang
// tells us about dependent types is their name (which is all we
// need for the precomputed list!). This means iwyu will properly
// analyze the use of SomeClass in code like 'map<T, SomeClass>',
// but not in 'MyMap<T, SomeClass>', since we have precomputed
// information about the STL map<>, but not the user type MyMap.
// TODO(csilvers): do better here.
if (type->isDependentType()) {
// TODO(csilvers): This is currently always a noop; need to fix
// GetTplTypeResugarMapForClassNoComponentTypes to do something
// useful for dependent types.
ReplayClassMemberUsesFromPrecomputedList(type); // best-effort
return true;
}
const NamedDecl* named_decl = TypeToDeclAsWritten(type);
const ClassTemplateSpecializationDecl* class_decl = DynCastFrom(named_decl);
// Bail out if we are not a proper class
if (class_decl == nullptr) {
// If the template specialization decl is not sugar for a class, we
// expect it to be another kind of template decl, like a built-in.
// Also in some rare cases named_decl can be a record decl (e.g. when
// using the built-in __type_pack_element).
CHECK_(llvm::isa<TemplateDecl>(named_decl) ||
llvm::isa<RecordDecl>(named_decl))
<< "TemplateSpecializationType has no decl of type TemplateDecl or "
"RecordDecl?";
return true;
}
if (ContainsKey(traversed_decls_, class_decl))
return true; // avoid recursion & repetition
traversed_decls_.insert(class_decl);
// If we have cached the reporting done for this decl before,
// report again (but with the new caller_loc this time).
// Otherwise, for all reporting done in the rest of this scope,
// store in the cache for this function.
if (ReplayUsesFromCache(*ClassMembersFullUseCache(),
class_decl, caller_loc()))
return true;
if (ReplayClassMemberUsesFromPrecomputedList(type))
return true;
// Make sure all the types we report in the recursive TraverseDecl
// calls, below, end up in the cache for class_decl.
CacheStoringScope css(&cache_storers_, ClassMembersFullUseCache(),
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
class_decl, resugar_map_);
for (DeclContext::decl_iterator it = class_decl->decls_begin();
it != class_decl->decls_end(); ++it) {
if (isa<CXXMethodDecl>(*it) || isa<FunctionTemplateDecl>(*it))
continue;
if (!TraverseDecl(*it))
return false;
}
// Most methods on template classes are instantiated when they're
// called, and we don't need to deal with them here. But virtual
// methods are instantiated when the class's key method is
// instantiated, and since template classes rarely have a key
// method, it means they're instantiated whenever the class is
// instantiated. So we need to instantiate virtual methods here.
for (DeclContext::decl_iterator it = class_decl->decls_begin();
it != class_decl->decls_end(); ++it) {
if (const CXXMethodDecl* method_decl = DynCastFrom(*it)) {
if (method_decl->isVirtual()) {
if (!TraverseExpandedTemplateFunctionHelper(method_decl, type))
return false;
}
}
}
return true;
}
//------------------------------------------------------------
// Cache methods. Caches hold the list of full uses found when we
// last instantiated a given decl, saving a lot of tree-walking if
// we have to do it again.
// Returns true if we replayed uses, false if key isn't in the cache.
bool ReplayUsesFromCache(const FullUseCache& cache, const NamedDecl* key,
SourceLocation use_loc) {
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 (!cache.Contains(key, resugar_map_))
return false;
VERRS(6) << "(Replaying full-use information from the cache for "
<< key->getQualifiedNameAsString() << ")\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
ReportTypesUse(use_loc, cache.GetFullUseTypes(key, resugar_map_));
ReportDeclsUse(use_loc, cache.GetFullUseDecls(key, resugar_map_));
return true;
}
// We precompute (hard-code) results of calling
// TraverseDataAndTypeMembersOfClassHelper for some types (mostly
// STL types). This way we don't even need to traverse them once.
// Returns true iff we did appropriate reporting for this type.
bool ReplayClassMemberUsesFromPrecomputedList(
const TemplateSpecializationType* tpl_type) {
if (current_ast_node() && current_ast_node()->in_forward_declare_context())
return true; // never depend on any types if a fwd-decl
const NamedDecl* tpl_decl = TypeToDeclAsWritten(tpl_type);
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
// This says how the template-args are used by this hard-coded type
// (a set<>, or map<>, or ...), to avoid having to recurse into them.
const map<const Type*, const Type*>& resugar_map_for_precomputed_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
FullUseCache::GetPrecomputedResugarMap(tpl_type);
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
// But we need to reconcile that with the types-of-interest, as
// stored in resugar_map_. To do this, we take only those entries
// from resugar_map_for_precomputed_type that are also present in
// resugar_map_. We consider type components, so if
// resugar_map_for_precomputed_type has less_than<Foo> or hash<Foo>,
// we'll add those in even if resugar_map_ only includes 'Foo'.
map<const Type*, const Type*> resugar_map;
for (const auto& item : resugar_map_for_precomputed_type) {
// TODO(csilvers): for default template args, item.first is sometimes
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 RecordType even when it's a template specialization. Figure out
// how to get the proper type components in that situation.
const set<const Type*> type_components = GetComponentsOfType(item.first);
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
if (ContainsAnyKey(resugar_map_, type_components)) {
resugar_map.insert(item);
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
}
}
if (resugar_map.empty())
return false;
VERRS(6) << "(Using pre-computed list of full-use information for "
<< tpl_decl->getQualifiedNameAsString() << ")\n";
// For entries with a non-nullptr value, we report the value, which
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
// is the unsugared type, as being fully used. Entries with a
// nullptr value are default template args, and we only report them
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
// if the template class doesn't intend-to-provide them.
for (const auto& item : resugar_map) {
const Type* resugared_type = nullptr;
if (item.second) {
resugared_type = item.second;
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
} else {
const NamedDecl* resugared_decl = TypeToDeclAsWritten(item.first);
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
if (!preprocessor_info().PublicHeaderIntendsToProvide(
GetFileEntry(tpl_decl), GetFileEntry(resugared_decl)))
resugared_type = item.first;
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
}
if (resugared_type && !resugared_type->isPointerType()) {
ReportTypeUse(caller_loc(), resugared_type);
// For a templated type, check the template args as well.
if (const TemplateSpecializationType* spec_type =
DynCastFrom(resugared_type)) {
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
TraverseDataAndTypeMembersOfClassHelper(spec_type);
}
}
}
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
return true;
}
//------------------------------------------------------------
// Member accessors.
SourceLocation caller_loc() const {
return caller_ast_node_->GetLocation();
}
//------------------------------------------------------------
// Member variables.
// The AST-chain when this template was instantiated.
const ASTNode* caller_ast_node_;
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
// resugar_map is a map from an unsugared (canonicalized) template
// type to the template type as written (or as close as we can find
// to it). If a type is not in resugar-map, it might be due to a
// recursive template call and encode a template type we don't care
// about ourselves. If it's in the resugar_map but with a 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
// value, it's a default template parameter, that the
// template-caller may or may not be responsible for.
map<const Type*, const Type*> resugar_map_;
// When we see a full-use of a template argument we need to assign that full
// use to the template-caller. Sometimes those uses are hidden behind
// type aliases (typedefs). This maps those aliases to the underlying
// template arguments.
map<const Type*, const Type*> template_argument_aliases_;
// Used to avoid recursion in the *Helper() methods.
set<const Decl*> traversed_decls_;
AstFlattenerVisitor::NodeSet nodes_to_ignore_;
// The current set of nodes we're updating cache entries for.
set<CacheStoringScope*> cache_storers_;
}; // class InstantiatedTemplateVisitor
// ----------------------------------------------------------------------
// --- IwyuAstConsumer
// ----------------------------------------------------------------------
//
// This class listens to Clang's events as the AST is generated.
//
// The traversal of the AST is done via RecursiveASTVisitor, which uses
// CRTP (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
class IwyuAstConsumer
: public ASTConsumer, public IwyuBaseAstVisitor<IwyuAstConsumer> {
public:
typedef IwyuBaseAstVisitor<IwyuAstConsumer> Base;
IwyuAstConsumer(VisitorState* visitor_state)
: Base(visitor_state),
instantiated_template_visitor_(visitor_state) {}
//------------------------------------------------------------
// Implements pure virtual methods from Base.
// Returns true if we are not interested in symbols used in used_in
// for whatever reason. For instance, we can ignore nodes that are
// neither in the file we're compiling nor in its associated .h file.
bool CanIgnoreCurrentASTNode() const override {
// If we're outside of foo.{h,cc} and the set of check_also files,
// just ignore.
if (CanIgnoreLocation(current_ast_node()->GetLocation()))
return true;
// TODO(csilvers): if we're a type, call CanIgnoreType().
return false;
}
// We print symbols from files in the main compilation unit (foo.cc,
// foo.h, foo-inl.h) if the debug level is 5 or 6, for non-system
// files if the debug level is 7, and all files if the debug level
// is 8 or more.
bool ShouldPrintSymbolFromCurrentFile() const override {
return ShouldPrintSymbolFromFile(CurrentFileEntry());
}
string GetSymbolAnnotation() const override {
return "";
}
// We are interested in all types for iwyu checking.
bool CanIgnoreType(const Type* type, IgnoreKind) const override {
return type == nullptr;
}
bool CanIgnoreDecl(const Decl* decl) const override {
return decl == nullptr;
}
//------------------------------------------------------------
// Parser event handlers. Clang will call them to notify this
// ASTConsumer as it parses the source code. See class ASTConsumer in
// clang/AST/ASTConsumer.h
// for all the handlers we can override.
// Called once at the beginning of the compilation.
void Initialize(ASTContext& context) override {} // NOLINT
// Called once at the end of the compilation.
void HandleTranslationUnit(ASTContext& context) override { // NOLINT
// TODO(csilvers): automatically detect preprocessing is done, somehow.
const_cast<IwyuPreprocessorInfo*>(&preprocessor_info())->
HandlePreprocessingDone();
TranslationUnitDecl* tu_decl = context.getTranslationUnitDecl();
// Sema::TUScope is reset after parsing, but Sema::getCurScope still points
// to the translation unit decl scope. TUScope is required for lookup in
// some complex scenarios, so re-wire it before running IWYU AST passes.
// Assert their expected state so we notice if these assumptions change.
Sema& sema = compiler()->getSema();
CHECK_(sema.TUScope == nullptr);
CHECK_(sema.getCurScope() != nullptr);
sema.TUScope = sema.getCurScope();
// We run a separate pass to force parsing of late-parsed function
// templates.
ParseFunctionTemplates(sema, tu_decl);
// Clang lazily constructs the implicit methods of a C++ class (the
// default constructor and destructor, etc) -- it only bothers to
// create a CXXMethodDecl if someone actually calls these classes.
// But we need to be non-lazy: IWYU depends on analyzing what future
// code *may* call in a class, not what current code *does*. So we
// force all the lazy evaluation to happen here.
InstantiateImplicitMethods(sema, tu_decl);
// Run IWYU analysis.
TraverseDecl(tu_decl);
// Check if any unrecoverable errors have occurred.
// There is no point in continuing when the AST is in a bad state.
if (compiler()->getDiagnostics().hasUnrecoverableErrorOccurred())
exit(EXIT_FAILURE);
const set<const FileEntry*>* const files_to_report_iwyu_violations_for =
preprocessor_info().files_to_report_iwyu_violations_for();
// Some analysis, such as UsingDecl resolution, is deferred until the
// entire AST is visited because it's only at that point that we know if
// the symbol was actually used or not.
// We perform that analysis here before CalculateAndReportIwyuViolations.
for (const FileEntry* file : *files_to_report_iwyu_violations_for) {
CHECK_(preprocessor_info().FileInfoFor(file));
preprocessor_info().FileInfoFor(file)->ResolvePendingAnalysis();
}
// We have to calculate the .h files before the .cc file, since
// the .cc file inherits #includes from the .h files, and we
// need to figure out what those #includes are going to be.
size_t num_edits = 0;
const FileEntry* const main_file = preprocessor_info().main_file();
for (const FileEntry* file : *files_to_report_iwyu_violations_for) {
if (file == main_file)
continue;
CHECK_(preprocessor_info().FileInfoFor(file));
num_edits += preprocessor_info().FileInfoFor(file)
->CalculateAndReportIwyuViolations();
}
CHECK_(preprocessor_info().FileInfoFor(main_file));
num_edits += preprocessor_info().FileInfoFor(main_file)
->CalculateAndReportIwyuViolations();
int exit_code = EXIT_SUCCESS;
if (GlobalFlags().exit_code_always) {
// If we should always fail, use --error_always value.
exit_code = GlobalFlags().exit_code_always;
} else if (num_edits > 0) {
// If there were IWYU violations, use --error value.
exit_code = GlobalFlags().exit_code_error;
}
exit(exit_code);
}
void ParseFunctionTemplates(Sema& sema, TranslationUnitDecl* tu_decl) {
set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(tu_decl);
// If we have any late-parsed functions, make sure the
// -fdelayed-template-parsing flag is on. Otherwise we don't know where
// they came from.
CHECK_((compiler()->getLangOpts().DelayedTemplateParsing ||
late_parsed_decls.empty()) &&
"Should not have late-parsed decls without "
"-fdelayed-template-parsing.");
for (const FunctionDecl* fd : late_parsed_decls) {
CHECK_(fd->isLateTemplateParsed());
if (CanIgnoreLocation(GetLocation(fd)))
continue;
// Force parsing and AST building of the yet-uninstantiated function
// template body.
clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd].get();
sema.LateTemplateParser(sema.OpaqueParser, *lpt);
}
}
void InstantiateImplicitMethods(Sema& sema, TranslationUnitDecl* tu_decl) {
// Collect all implicit ctors/dtors that need to be instantiated.
struct Visitor : public RecursiveASTVisitor<Visitor> {
Visitor(Sema& sema) : sema(sema) {
}
bool VisitCXXRecordDecl(CXXRecordDecl* decl) {
if (CanIgnoreLocation(GetLocation(decl)))
return true;
if (!decl->getDefinition() || decl->isDependentContext() ||
decl->isBeingDefined()) {
return true;
}
if (CXXConstructorDecl* ctor = sema.LookupDefaultConstructor(decl)) {
may_need_definition.insert(ctor);
} else if (CXXDestructorDecl* dtor = sema.LookupDestructor(decl)) {
may_need_definition.insert(dtor);
}
return true;
}
Sema& sema;
std::set<CXXMethodDecl*> may_need_definition;
};
// Run visitor to collect implicit methods.
Visitor v(sema);
v.TraverseDecl(tu_decl);
// For each method collected, let Sema define them.
for (CXXMethodDecl* method : v.may_need_definition) {
if (!method->isDefaulted() || method->isDeleted() || method->hasBody())
continue;
SourceLocation loc = GetLocation(method->getParent());
if (auto* ctor = dyn_cast<CXXConstructorDecl>(method)) {
sema.DefineImplicitDefaultConstructor(loc, ctor);
} else if (auto* dtor = dyn_cast<CXXDestructorDecl>(method)) {
sema.DefineImplicitDestructor(loc, dtor);
}
}
// Clang queues up method instantiations. Process them now.
sema.PerformPendingInstantiations();
}
//------------------------------------------------------------
// AST visitors. We start by adding a visitor callback for
// most of the subclasses of Decl/Stmt/Type listed in:
// clang/AST/DeclNodes.def
// clang/AST/StmtNodes.td
// clang/AST/TypeNodes.def
// We exclude only:
// 1) abstract declarations and types with no logic (e.g. NamedDecl)
// 2) ObjC declarations, statements, and types (e.g. ObjcIvarDecl)
// RecursiveASTVisitor defines specialized visitors for each specific
// math operation (MulAssign, OffsetOf, etc). We don't override
// those callbacks, but use their default behavior, which is to call
// back to VisitUnaryOperator, VisitBinaryOperator, etc.
//
// Over time, as we understand when a callback is called and
// which can be ignored by iwyu, we will pare down the list.
// Each of these returns a bool: false if we want to abort the
// traversal (we never do). For Visit*(), we can abort early if
// we're not in the main compilation-unit, since we only ever give
// iwyu warnings on symbols in those files.
// --- Visitors of types derived from clang::Decl.
bool VisitNamespaceAliasDecl(clang::NamespaceAliasDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
ReportDeclUse(CurrentLoc(), decl->getNamespace());
return Base::VisitNamespaceAliasDecl(decl);
}
bool VisitUsingDecl(clang::UsingDecl* decl) {
// The shadow decls hold the declarations for the var/fn/etc we're
// using. (There may be more than one if, say, we're using an
// overloaded function.) We don't want to add all of them at once
// though, because that will drag in every overload even if we're
// only using one. Instead, we keep track of the using decl and
// mark it as touched when something actually uses it.
IwyuFileInfo* file_info =
preprocessor_info().FileInfoFor(CurrentFileEntry());
if (file_info) {
file_info->AddUsingDecl(decl);
} else {
// For using declarations in a PCH, the preprocessor won't have any
// location information. As far as we know, that's the only time the
// file-info will be null, so assert that we have a PCH on the
// command-line.
const string& pch_include =
compiler()->getInvocation().getPreprocessorOpts().ImplicitPCHInclude;
CHECK_(!pch_include.empty());
}
if (CanIgnoreCurrentASTNode())
return true;
return Base::VisitUsingDecl(decl);
}
bool VisitTagDecl(clang::TagDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
// Skip the injected class name.
if (decl->isImplicit())
return Base::VisitTagDecl(decl);
if (IsForwardDecl(decl)) {
// If we're a templated class, make sure we add the whole template.
const NamedDecl* decl_to_fwd_declare = decl;
if (const CXXRecordDecl* cxx_decl = DynCastFrom(decl))
if (cxx_decl->getDescribedClassTemplate())
decl_to_fwd_declare = cxx_decl->getDescribedClassTemplate();
// We've found a forward-declaration. We'll report we've found
// it, but we also want to report if we know already that we
// should keep this forward-declaration around (and not consider
// it for deletion if it's never used). There are a few
// situations we can do this, described below.
bool definitely_keep_fwd_decl = false;
// (1) If the forward-decl has a linkage spec ('extern "C"')
// then it can't be removed, since that information probably
// isn't encoded anywhere else.
// (Surprisingly classes can have linkage specs! -- they are
// applied to all static methods of the class. See
// http://msdn.microsoft.com/en-us/library/ms882260.aspx.)
if (current_ast_node()->ParentIsA<LinkageSpecDecl>()) {
definitely_keep_fwd_decl = true;
// (2) GCC-style __attributes__ work the same way: we can't assume
// that attributes are consistent between declarations, so we can't
// remove a decl with attributes unless they're inherited, i.e. propagated
// from another redeclaration as opposed to explicitly written.
} else if (decl->hasAttrs()) {
for (const Attr* attr : decl->getAttrs()) {
if (!attr->isInherited()) {
definitely_keep_fwd_decl = true;
break;
}
}
// (3) If we're a nested class ("class A { class SubA; };"),
// then we can't necessary be removed either, since we're part
// of the public API of the enclosing class -- it's illegal to
// have a nested class and not at least declare it in the
// enclosing class. If the nested class is actually defined in
// the enclosing class, then we're fine; if not, we need to keep
// the first forward-declaration.
} else if (IsNestedTagAsWritten(current_ast_node())) {
if (!decl->getDefinition() || decl->getDefinition()->isOutOfLine()) {
// TODO(kimgr): Member class redeclarations are illegal, per C++
// standard DR85, so this check for first redecl can be removed.
// Nested classes should always be definitely kept. More details here:
// http://comments.gmane.org/gmane.comp.compilers.clang.scm/74782
// GCC and MSVC both still allow redeclarations of nested classes,
// though, so it seems hygienic to remove all but one.
if (const NamedDecl* first_decl = GetFirstRedecl(decl)) {
// Check if we're the decl with the smallest line number.
if (decl == first_decl)
definitely_keep_fwd_decl = true;
}
}
} else if (preprocessor_info().ForwardDeclareIsMarkedKeep(decl)) {
definitely_keep_fwd_decl = true;
} else if (preprocessor_info().ForwardDeclareIsExported(decl)) {
definitely_keep_fwd_decl = true;
}
preprocessor_info().FileInfoFor(CurrentFileEntry())->AddForwardDeclare(
decl_to_fwd_declare, definitely_keep_fwd_decl);
}
return Base::VisitTagDecl(decl);
}
// If you specialize a template, you need a declaration of the
// template you're specializing. That is, for code like this:
// template <class T> struct Foo;
// template<> struct Foo<int> { ... };
// we don't want iwyu to recommend removing the 'forward declare' of Foo.
//
// Additionally, this type of decl is also used to represent explicit template
// instantiations, in which case we want the full type, not only a forward
// declaration.
bool VisitClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
ClassTemplateDecl* specialized_decl = decl->getSpecializedTemplate();
if (IsExplicitInstantiation(decl))
ReportDeclUse(CurrentLoc(), specialized_decl);
else
ReportDeclForwardDeclareUse(CurrentLoc(), specialized_decl);
return Base::VisitClassTemplateSpecializationDecl(decl);
}
// Track use of namespace in using directive decl
// a.h:
// namespace a { ... };
//
// b.cpp:
// include "a.h"
// using namespace a;
// ...
bool VisitUsingDirectiveDecl(clang::UsingDirectiveDecl *decl) {
if (CanIgnoreCurrentASTNode())
return true;
ReportDeclUse(CurrentLoc(), decl->getNominatedNamespaceAsWritten());
return Base::VisitUsingDirectiveDecl(decl);
}
// If you say 'typedef Foo Bar', then clients can use Bar however
// they want without having to worry about #including anything
// except you. That puts you on the hook for all the #includes that
// Bar might need, for *anything* one might want to do to a Bar.
// TODO(csilvers): we can probably relax this rule in .cc files.
// TODO(csilvers): this should really move into IwyuBaseASTVisitor
// (that way we'll correctly identify need for hash<> in hash_set).
// This is called from Traverse*() because Visit*()
// can't call HandleFunctionCall().
bool HandleAliasedClassMethods(TypedefNameDecl* decl) {
if (CanIgnoreCurrentASTNode())
return true;
if (current_ast_node()->in_forward_declare_context())
return true;
const Type* underlying_type = decl->getUnderlyingType().getTypePtr();
const Decl* underlying_decl = TypeToDeclAsWritten(underlying_type);
// We simulate a user calling all the methods in a class.
if (const CXXRecordDecl* record_decl = DynCastFrom(underlying_decl)) {
for (DeclContext::decl_iterator it = record_decl->decls_begin();
it != record_decl->decls_end(); ++it) {
FunctionDecl* fn_decl = nullptr;
if (CXXMethodDecl* method_decl = DynCastFrom(*it)) {
fn_decl = method_decl;
} else if (FunctionTemplateDecl* tpl_decl = DynCastFrom(*it)) {
fn_decl = tpl_decl->getTemplatedDecl(); // templated method decl
} else {
continue; // not a method or static method
}
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 (!this->getDerived().HandleFunctionCall(
fn_decl, underlying_type, static_cast<Expr*>(nullptr)))
return false;
}
}
// We don't have to simulate a user instantiating the type, because
// RecursiveASTVisitor.h will recurse on the typedef'ed type for us.
return true;
}
bool TraverseTypedefDecl(clang::TypedefDecl* decl) {
if (!Base::TraverseTypedefDecl(decl))
return false;
return HandleAliasedClassMethods(decl);
}
bool TraverseTypeAliasDecl(clang::TypeAliasDecl* decl) {
if (!Base::TraverseTypeAliasDecl(decl))
return false;
return HandleAliasedClassMethods(decl);
}
// --- Visitors of types derived from clang::Stmt.
// Called whenever a variable, function, enum, etc is used.
bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
// Special case for UsingShadowDecl to track UsingDecls correctly. The
// actual decl will be reported by obtaining it from the UsingShadowDecl
// once we've tracked the UsingDecl use.
if (const UsingShadowDecl* found_decl = DynCastFrom(expr->getFoundDecl())) {
ReportDeclUse(CurrentLoc(), found_decl);
} else if (!isa<EnumConstantDecl>(expr->getDecl())) {
ReportDeclUse(CurrentLoc(), expr->getDecl());
}
return Base::VisitDeclRefExpr(expr);
}
2022-07-22 15:03:33 +01:00
bool VisitConceptSpecializationExpr(ConceptSpecializationExpr* expr) {
if (CanIgnoreCurrentASTNode())
return true;
ReportDeclUse(CurrentLoc(), expr->getNamedConcept());
// TODO(bolshakov): analyze type parameter usage.
return Base::VisitConceptSpecializationExpr(expr);
}
// --- Visitors of types derived from clang::Type.
bool VisitTypedefType(clang::TypedefType* type) {
if (CanIgnoreCurrentASTNode())
return true;
// TypedefType::getDecl() returns the place where the typedef is defined.
ReportDeclUse(CurrentLoc(), type->getDecl());
if (!CanForwardDeclareType(current_ast_node()))
ReportTypeUse(CurrentLoc(), type);
return Base::VisitTypedefType(type);
}
bool VisitUsingType(clang::UsingType* type) {
if (CanIgnoreCurrentASTNode())
return true;
if (CanForwardDeclareType(current_ast_node())) {
ReportDeclForwardDeclareUse(CurrentLoc(), type->getFoundDecl());
} else {
ReportDeclUse(CurrentLoc(), type->getFoundDecl());
// If UsingType refers to a typedef, report the underlying type of that
// typedef if needed (which is determined in ReportTypeUse).
const Type* underlying_type = type->getUnderlyingType().getTypePtr();
if (isa<TypedefType>(underlying_type))
ReportTypeUse(CurrentLoc(), underlying_type);
}
return Base::VisitUsingType(type);
}
// This is a superclass of RecordType and CXXRecordType.
bool VisitTagType(clang::TagType* type) {
if (CanIgnoreCurrentASTNode())
return true;
// If we're forward-declarable, then no complicated checking is
// needed: just forward-declare.
if (CanForwardDeclareType(current_ast_node())) {
current_ast_node()->set_in_forward_declare_context(true);
if (compiler()->getLangOpts().CPlusPlus) {
// In C++, if we're already elaborated ('class Foo x') but not
// a qualified name ('class ns::Foo x', 'class Class::Nested x') there's
// no need even to forward-declare.
// Note that enums are never forward-declarable, so elaborated enums are
// already short-circuited in CanForwardDeclareType.
const ASTNode* parent = current_ast_node()->parent();
if (!IsElaboratedTypeSpecifier(parent) || IsQualifiedNameNode(parent))
ReportDeclForwardDeclareUse(CurrentLoc(), type->getDecl());
} else {
// In C, all struct references are elaborated, so we really never need
// to forward-declare. But there's one case where an elaborated struct
// decl in a parameter list causes Clang to warn about constrained
// visibility, so we recommend forward declaration to avoid the warning.
// E.g.
// void init(struct mystruct* s);
// warning: declaration of 'struct mystruct' will not be visible
// outside of this function [-Wvisibility]
if (current_ast_node()->HasAncestorOfType<ParmVarDecl>())
ReportDeclForwardDeclareUse(CurrentLoc(), type->getDecl());
}
return Base::VisitTagType(type);
}
// OK, seems to be a use that requires the full type.
ReportDeclUse(CurrentLoc(), type->getDecl());
return Base::VisitTagType(type);
}
// Like for CXXConstructExpr, etc., we sometimes need to instantiate
// a class when looking at TemplateSpecializationType -- for instance,
// when we need to access a class typedef: MyClass<A>::value_type.
bool VisitTemplateSpecializationType(TemplateSpecializationType* type) {
if (CanIgnoreCurrentASTNode())
return true;
// If we're not in a forward-declare context, use of a template
// specialization requires having the full type information.
if (!CanForwardDeclareType(current_ast_node())) {
const map<const Type*, const Type*> resugar_map =
GetTplTypeResugarMapForClass(type);
instantiated_template_visitor_.ScanInstantiatedType(current_ast_node(),
resugar_map);
}
return Base::VisitTemplateSpecializationType(type);
}
// --- Visitors defined by BaseASTVisitor (not RecursiveASTVisitor).
bool VisitTemplateName(TemplateName template_name) {
if (CanIgnoreCurrentASTNode())
return true;
if (!Base::VisitTemplateName(template_name))
return false;
// We can see TemplateName outside the context of a
// TemplateSpecializationType when it's either the default argument of a
// template template arg:
// template<template<class T> class A = TplNameWithoutTST> class Foo ...
// or a deduced template specialization:
// std::pair x(10, 20); // type of x is really std::pair<int, int>
// So that's the only cases we need to handle here.
// TODO(csilvers): check if this is really forward-declarable or
// not. You *could* do something like: 'template<template<class
// T> class A = Foo> class C { A<int>* x; };' and never
// dereference x, but that's pretty unlikely. So for now, we just
// assume these default template template args always need full
// type info.
const ASTNode* ast_node = current_ast_node();
if (ast_node->ParentIsA<DeducedTemplateSpecializationType>() ||
IsDefaultTemplateTemplateArg(ast_node)) {
current_ast_node()->set_in_forward_declare_context(false);
// Not all template name kinds have an associated template decl; notably
// dependent names, overloaded template names and ADL-resolved names. Only
// report the use if we can find a decl.
if (TemplateDecl* template_decl = template_name.getAsTemplateDecl()) {
ReportDeclUse(CurrentLoc(), template_decl);
} else {
// TODO: There should probably be handling of these delayed-resolution
// template decls as well, but probably not here.
}
}
return true;
}
// For expressions that require us to instantiate a template
// (CallExpr of a template function, or CXXConstructExpr of a
// template class, etc), we need to instantiate the template and
// check IWYU status of the template parameters *in the template
// code* (so for 'MyFunc<T>() { T t; ... }', the contents of
// MyFunc<MyClass> add an iwyu requirement on 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
bool HandleFunctionCall(FunctionDecl* callee, const Type* parent_type,
const clang::Expr* calling_expr) {
if (!Base::HandleFunctionCall(callee, parent_type, calling_expr))
return false;
if (!callee || CanIgnoreCurrentASTNode() || CanIgnoreDecl(callee))
return true;
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 (!IsTemplatizedFunctionDecl(callee) && !IsTemplatizedType(parent_type))
return true;
map<const Type*, const Type*> resugar_map =
GetTplTypeResugarMapForFunction(callee, calling_expr);
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 (parent_type) { // means we're a method of a class
InsertAllInto(GetTplTypeResugarMapForClass(parent_type), &resugar_map);
}
instantiated_template_visitor_.ScanInstantiatedFunction(
callee, parent_type,
current_ast_node(), resugar_map);
return true;
}
// --- Handler declared in IwyuBaseASTVisitor.
void ReportTplSpecComponentTypes(const TemplateSpecializationType* type) {
const map<const Type*, const Type*> resugar_map =
GetTplTypeResugarMapForClass(type);
ASTNode node(type);
node.SetParent(current_ast_node());
instantiated_template_visitor_.ScanInstantiatedType(&node, resugar_map);
}
private:
// Class we call to handle instantiated template functions and classes.
InstantiatedTemplateVisitor instantiated_template_visitor_;
}; // class IwyuAstConsumer
// We use an ASTFrontendAction to hook up IWYU with Clang.
class IwyuAction : public ASTFrontendAction {
protected:
std::unique_ptr<ASTConsumer> CreateASTConsumer(
CompilerInstance& compiler, // NOLINT
llvm::StringRef /* dummy */) override {
// Do this first thing after getting our hands on a CompilerInstance.
InitGlobals(&compiler.getSourceManager(),
&compiler.getPreprocessor().getHeaderSearchInfo());
auto* const preprocessor_consumer = new IwyuPreprocessorInfo();
compiler.getPreprocessor().addPPCallbacks(
std::unique_ptr<PPCallbacks>(preprocessor_consumer));
compiler.getPreprocessor().addCommentHandler(preprocessor_consumer);
auto* const visitor_state =
new VisitorState(&compiler, *preprocessor_consumer);
return std::unique_ptr<IwyuAstConsumer>(new IwyuAstConsumer(visitor_state));
}
};
} // namespace include_what_you_use
#include "iwyu_driver.h"
#include "clang/Frontend/FrontendAction.h"
#include "llvm/Support/TargetSelect.h"
2014-02-23 00:31:52 +00:00
using include_what_you_use::OptionsParser;
using include_what_you_use::IwyuAction;
using include_what_you_use::CreateCompilerInstance;
int main(int argc, char **argv) {
llvm::llvm_shutdown_obj scoped_shutdown;
// X86 target is required to parse Microsoft inline assembly, so we hope it's
// part of all targets. Clang parser will complain otherwise.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
// The command line should look like
2022-02-25 21:27:04 +00:00
// path/to/iwyu -Xiwyu --verbose=4 [-Xiwyu --other_iwyu_flag]... \
// CLANG_FLAGS... foo.cc
2014-02-23 00:31:52 +00:00
OptionsParser options_parser(argc, argv);
std::unique_ptr<clang::CompilerInstance> compiler(CreateCompilerInstance(
2014-02-23 00:31:52 +00:00
options_parser.clang_argc(), options_parser.clang_argv()));
if (!compiler) {
return EXIT_FAILURE;
}
// Create and execute the frontend to generate an LLVM bitcode module.
std::unique_ptr<clang::ASTFrontendAction> action(new IwyuAction);
compiler->ExecuteAction(*action);
return EXIT_SUCCESS;
}