| //===--- iwyu_ast_util.cc - clang-AST utilities 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. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // Utilities that make it easier to work with Clang's AST. |
| |
| #include "iwyu_ast_util.h" |
| |
| #include <set> // for set |
| #include <string> // for string, operator+, etc |
| #include <utility> // for pair |
| |
| #include "iwyu_globals.h" |
| #include "iwyu_location_util.h" |
| #include "iwyu_path_util.h" |
| #include "iwyu_stl_util.h" |
| #include "iwyu_string_util.h" |
| #include "iwyu_verrs.h" |
| #include "port.h" // for CHECK_ |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclBase.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/NestedNameSpecifier.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/AST/Stmt.h" |
| #include "clang/AST/TemplateBase.h" |
| #include "clang/AST/TemplateName.h" |
| #include "clang/AST/Type.h" |
| #include "clang/AST/TypeLoc.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "clang/Basic/SourceManager.h" |
| |
| namespace clang { |
| class FileEntry; |
| } // namespace clang |
| |
| using clang::ASTTemplateArgumentListInfo; |
| using clang::BlockPointerType; |
| using clang::CXXConstructExpr; |
| using clang::CXXConstructorDecl; |
| using clang::CXXDeleteExpr; |
| using clang::CXXDependentScopeMemberExpr; |
| using clang::CXXDestructorDecl; |
| using clang::CXXMethodDecl; |
| using clang::CXXNewExpr; |
| using clang::CXXRecordDecl; |
| using clang::CallExpr; |
| using clang::CastExpr; |
| using clang::ClassTemplateDecl; |
| using clang::ClassTemplatePartialSpecializationDecl; |
| using clang::ClassTemplateSpecializationDecl; |
| using clang::Decl; |
| using clang::DeclContext; |
| using clang::DeclRefExpr; |
| using clang::DeclaratorDecl; |
| using clang::DependentNameType; |
| using clang::DependentScopeDeclRefExpr; |
| using clang::DependentTemplateName; |
| using clang::DependentTemplateSpecializationType; |
| using clang::ElaboratedType; |
| using clang::EnumDecl; |
| using clang::ExplicitCastExpr; |
| using clang::Expr; |
| using clang::ExprWithCleanups; |
| using clang::FileEntry; |
| using clang::FriendDecl; |
| using clang::FriendTemplateDecl; |
| using clang::FullSourceLoc; |
| using clang::FunctionDecl; |
| using clang::FunctionProtoType; |
| using clang::FunctionType; |
| using clang::ImplicitCastExpr; |
| using clang::InjectedClassNameType; |
| using clang::LValueReferenceType; |
| using clang::MemberExpr; |
| using clang::MemberPointerType; |
| using clang::NamedDecl; |
| using clang::NestedNameSpecifier; |
| using clang::NestedNameSpecifierLoc; |
| using clang::ObjCObjectType; |
| using clang::OverloadExpr; |
| using clang::PointerType; |
| using clang::QualType; |
| using clang::QualifiedTemplateName; |
| using clang::RecordDecl; |
| using clang::RecordType; |
| using clang::RecursiveASTVisitor; |
| using clang::SourceLocation; |
| using clang::SourceManager; |
| using clang::SourceRange; |
| 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::TemplateParameterList; |
| using clang::TemplateSpecializationType; |
| using clang::Type; |
| using clang::TypeDecl; |
| using clang::TypeLoc; |
| using clang::TypedefNameDecl; |
| using clang::TypedefType; |
| using clang::UnaryOperator; |
| using clang::UsingDirectiveDecl; |
| using clang::ValueDecl; |
| using clang::VarDecl; |
| using llvm::PointerUnion; |
| using llvm::cast; |
| using llvm::dyn_cast_or_null; |
| using llvm::errs; |
| using llvm::isa; |
| using llvm::raw_string_ostream; |
| |
| namespace include_what_you_use { |
| |
| //------------------------------------------------------------ |
| // ASTNode and associated utilities. |
| |
| SourceLocation ASTNode::GetLocation() const { |
| SourceLocation retval; |
| if (FillLocationIfKnown(&retval)) |
| return retval; |
| |
| // OK, let's ask a parent node. |
| for (const ASTNode* node = parent_; node != NULL; node = node->parent_) { |
| if (node->FillLocationIfKnown(&retval)) |
| break; |
| } |
| // If the parent node shows the spelling and instantiation |
| // locations are in a different file, then we're uncertain of our |
| // own location. Return an invalid location. |
| if (retval.isValid()) { |
| FullSourceLoc full_loc(retval, source_manager_); |
| const FileEntry* spelling_file = |
| source_manager_.getFileEntryForID( |
| source_manager_.getFileID(full_loc.getSpellingLoc())); |
| const FileEntry* instantiation_file = |
| source_manager_.getFileEntryForID( |
| source_manager_.getFileID(full_loc.getExpansionLoc())); |
| if (spelling_file != instantiation_file) |
| return SourceLocation(); |
| } |
| |
| return retval; |
| } |
| |
| bool ASTNode::FillLocationIfKnown(SourceLocation* loc) const { |
| using include_what_you_use::GetLocation; |
| switch (kind_) { |
| case kDeclKind: |
| *loc = GetLocation(as_decl_); // in iwyu_location_util.h |
| return true; |
| case kStmtKind: |
| *loc = GetLocation(as_stmt_); |
| return true; |
| case kTypelocKind: |
| *loc = GetLocation(as_typeloc_); |
| return true; |
| case kNNSLocKind: |
| *loc = GetLocation(as_nnsloc_); |
| return true; |
| case kTemplateArgumentLocKind: |
| *loc = GetLocation(as_template_argloc_); |
| return true; |
| case kTypeKind: |
| case kNNSKind: |
| case kTemplateNameKind: |
| case kTemplateArgumentKind: |
| return false; |
| default: |
| CHECK_(false && "Unexpected kind of ASTNode"); |
| return false; |
| } |
| } |
| |
| // --- Utilities for ASTNode. |
| |
| bool IsElaborationNode(const ASTNode* ast_node) { |
| if (ast_node == NULL) |
| return false; |
| const ElaboratedType* elaborated_type = ast_node->GetAs<ElaboratedType>(); |
| return elaborated_type && elaborated_type->getKeyword() != clang::ETK_None; |
| } |
| |
| bool IsNamespaceQualifiedNode(const ASTNode* ast_node) { |
| if (ast_node == NULL) |
| return false; |
| const ElaboratedType* elaborated_type = ast_node->GetAs<ElaboratedType>(); |
| return (elaborated_type && elaborated_type->getQualifier() |
| && elaborated_type->getQualifier()->getKind() == |
| NestedNameSpecifier::Namespace); |
| } |
| |
| bool IsNodeInsideCXXMethodBody(const ASTNode* ast_node) { |
| // If we're a destructor, we're definitely part of a method body; |
| // destructors don't have any other parts to them. This case is |
| // triggered when we see implicit destruction of member vars. |
| if (ast_node && ast_node->IsA<CXXDestructorDecl>()) |
| return true; |
| for (; ast_node != NULL; ast_node = ast_node->parent()) { |
| // If we're a constructor, check if we're part of the |
| // initializers, which also count as 'the body' of the method. |
| if (const CXXConstructorDecl* ctor = |
| ast_node->GetParentAs<CXXConstructorDecl>()) { |
| for (CXXConstructorDecl::init_const_iterator |
| it = ctor->init_begin(); it != ctor->init_end(); ++it) { |
| if (ast_node->ContentIs((*it)->getInit())) |
| return true; |
| } |
| // Now fall through to see if we're the body of the constructor. |
| } |
| if (const CXXMethodDecl* method_decl = |
| ast_node->GetParentAs<CXXMethodDecl>()) { |
| if (ast_node->ContentIs(method_decl->getBody())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool IsNestedClassAsWritten(const ASTNode* ast_node) { |
| return (ast_node->IsA<RecordDecl>() && |
| (ast_node->ParentIsA<CXXRecordDecl>() || |
| // For templated nested-classes, a ClassTemplateDecl is interposed. |
| (ast_node->ParentIsA<ClassTemplateDecl>() && |
| ast_node->AncestorIsA<CXXRecordDecl>(2)))); |
| } |
| |
| bool IsDefaultTemplateTemplateArg(const ASTNode* ast_node) { |
| // Is ast_node the 'D' in the following: |
| // template<template <typename A> class T = D> class C { ... } |
| // ('D' might be something like 'vector'). |
| // D is a TemplateName, since it's a template, and its parent |
| // is a TemplateArgument, since D is inside a template argument. |
| // The only way a template name can be in a template argument |
| // is if it's a default parameter. |
| return (ast_node->IsA<TemplateName>() && |
| ast_node->ParentIsA<TemplateArgument>()); |
| } |
| |
| bool IsCXXConstructExprInInitializer(const ASTNode* ast_node) { |
| if (!ast_node->IsA<CXXConstructExpr>()) |
| return false; |
| |
| CHECK_(ast_node->parent() && "Constructor should not be a top-level node!"); |
| |
| // Typically, you can tell an initializer because its parent is a |
| // constructor decl. But sometimes -- I'm not exactly sure when -- |
| // there can be an ExprWithCleanups in the middle. |
| return ((ast_node->ParentIsA<CXXConstructorDecl>()) || |
| (ast_node->ParentIsA<ExprWithCleanups>() && |
| ast_node->AncestorIsA<CXXConstructorDecl>(2))); |
| } |
| |
| bool IsCXXConstructExprInNewExpr(const ASTNode* ast_node) { |
| if (!ast_node->IsA<CXXConstructExpr>()) |
| return false; |
| |
| CHECK_(ast_node->parent() && "Constructor should not be a top-level node!"); |
| return ast_node->ParentIsA<CXXNewExpr>(); |
| } |
| |
| template<typename T> |
| NestedNameSpecifier* TryGetQualifier(const ASTNode* ast_node) { |
| if (ast_node->IsA<T>()) |
| return ast_node->GetAs<T>()->getQualifier(); |
| return NULL; |
| } |
| |
| const NestedNameSpecifier* GetQualifier(const ASTNode* ast_node) { |
| const NestedNameSpecifier* nns = NULL; |
| if (ast_node->IsA<TemplateName>()) { |
| const TemplateName* tn = ast_node->GetAs<TemplateName>(); |
| if (const DependentTemplateName* dtn |
| = tn->getAsDependentTemplateName()) |
| nns = dtn->getQualifier(); |
| else if (const QualifiedTemplateName* qtn |
| = tn->getAsQualifiedTemplateName()) |
| nns = qtn->getQualifier(); |
| } |
| if (!nns) nns = TryGetQualifier<ElaboratedType>(ast_node); |
| if (!nns) nns = TryGetQualifier<DependentNameType>(ast_node); |
| if (!nns) |
| nns = TryGetQualifier<DependentTemplateSpecializationType>(ast_node); |
| if (!nns) nns = TryGetQualifier<UsingDirectiveDecl>(ast_node); |
| if (!nns) nns = TryGetQualifier<EnumDecl>(ast_node); |
| if (!nns) nns = TryGetQualifier<RecordDecl>(ast_node); |
| if (!nns) nns = TryGetQualifier<DeclaratorDecl>(ast_node); |
| if (!nns) nns = TryGetQualifier<FunctionDecl>(ast_node); |
| if (!nns) nns = TryGetQualifier<CXXDependentScopeMemberExpr>(ast_node); |
| if (!nns) nns = TryGetQualifier<DeclRefExpr>(ast_node); |
| if (!nns) nns = TryGetQualifier<DependentScopeDeclRefExpr>(ast_node); |
| if (!nns) nns = TryGetQualifier<MemberExpr>(ast_node); |
| return nns; |
| } |
| |
| bool IsMemberOfATypedef(const ASTNode* ast_node) { |
| // TODO(csilvers): is this ever triggered in practice? |
| if (ast_node->ParentIsA<TypedefType>()) { // my_typedef.a |
| return true; |
| } |
| |
| // If we're one of those objects that exposes its qualifier |
| // (stuff before the ::), use that. |
| const NestedNameSpecifier* nns = GetQualifier(ast_node); |
| |
| // If that doesn't work, see if our parent in the tree is an nns |
| // node. We have to be a bit careful here: 1) If we're a typedef |
| // ourselves, the nns-parent is just us. We have to go a level up |
| // to see our 'real' qualifier. 2) Often the parent will be an |
| // elaborated type, and we get to the qualifier that way. |
| if (!nns) { |
| nns = ast_node->GetParentAs<NestedNameSpecifier>(); |
| if (nns && ast_node->IsA<TypedefType>()) { |
| nns = nns->getPrefix(); |
| } else if (!nns) { |
| // nns will be non-NULL when processing 'a' in MyTypedef::a::b |
| // But typically, such as processing 'a' in MyTypedef::a or 'b' in |
| // MyTypedef::a::b, the parent will be an ElaboratedType. |
| if (const ElaboratedType* elab_type = |
| ast_node->GetParentAs<ElaboratedType>()) |
| nns = elab_type->getQualifier(); |
| } |
| } |
| |
| for (; nns; nns = nns->getPrefix()) { |
| if (nns->getAsType() && isa<TypedefType>(nns->getAsType())) |
| return true; |
| } |
| return false; |
| } |
| |
| const DeclContext* GetDeclContext(const ASTNode* ast_node) { |
| for (; ast_node != NULL; ast_node = ast_node->parent()) { |
| if (ast_node->IsA<Decl>()) |
| return ast_node->GetAs<Decl>()->getDeclContext(); |
| } |
| return NULL; |
| } |
| |
| |
| //------------------------------------------------------------ |
| // Helper functions for working with raw Clang AST nodes. |
| |
| // --- Printers. |
| |
| string PrintableLoc(SourceLocation loc) { |
| if (loc.isInvalid()) { |
| return "Invalid location"; |
| } else { |
| std::string buffer; // llvm wants regular string, not our versa-string |
| raw_string_ostream ostream(buffer); |
| loc.print(ostream, *GlobalSourceManager()); |
| return NormalizeFilePath(ostream.str()); |
| } |
| } |
| |
| string PrintableSourceRange(SourceRange range) { |
| return PrintableLoc(range.getBegin()) + " - " + PrintableLoc(range.getEnd()); |
| } |
| |
| string PrintableDecl(const Decl* decl) { |
| std::string buffer; // llvm wants regular string, not our versa-string |
| raw_string_ostream ostream(buffer); |
| decl->print(ostream); // Note: can also set indentation and printingpolicy |
| return ostream.str(); |
| } |
| |
| void PrintStmt(const Stmt* stmt) { |
| stmt->dump(*GlobalSourceManager()); // This prints to errs(). |
| } |
| |
| string PrintableType(const Type* type) { |
| return QualType(type, 0).getAsString(); |
| } |
| |
| string PrintableTypeLoc(const TypeLoc& typeloc) { |
| return PrintableType(typeloc.getTypePtr()); |
| } |
| |
| string PrintableNestedNameSpecifier( |
| const NestedNameSpecifier* nns) { |
| std::string buffer; // llvm wants regular string, not our versa-string |
| raw_string_ostream ostream(buffer); |
| nns->print(ostream, DefaultPrintPolicy()); |
| return ostream.str(); |
| } |
| |
| string PrintableTemplateName(const TemplateName& tpl_name) { |
| std::string buffer; // llvm wants regular string, not our versa-string |
| raw_string_ostream ostream(buffer); |
| tpl_name.print(ostream, DefaultPrintPolicy()); |
| return ostream.str(); |
| } |
| |
| string PrintableTemplateArgument(const TemplateArgument& arg) { |
| return TemplateSpecializationType::PrintTemplateArgumentList( |
| &arg, 1, DefaultPrintPolicy()); |
| } |
| |
| string PrintableTemplateArgumentLoc( |
| const TemplateArgumentLoc& arg) { |
| return TemplateSpecializationType::PrintTemplateArgumentList( |
| &arg, 1, DefaultPrintPolicy()); |
| } |
| |
| // This prints to errs(). It's useful for debugging (e.g. inside gdb). |
| void PrintASTNode(const ASTNode* node) { |
| if (const Decl* decl = node->GetAs<Decl>()) { |
| errs() << "[" << decl->getDeclKindName() << "Decl] " |
| << PrintableDecl(decl) << "\n"; |
| } else if (const Stmt* stmt = node->GetAs<Stmt>()) { |
| errs() << "[" << stmt->getStmtClassName() << "] "; |
| PrintStmt(stmt); |
| errs() << "\n"; |
| } else if (const Type* type = node->GetAs<Type>()) { // +typeloc |
| errs() << "[" << type->getTypeClassName() |
| << (node->IsA<TypeLoc>() ? "TypeLoc" : "Type") << "] " |
| << PrintableType(type) << "\n"; |
| } else if (const NestedNameSpecifier* nns |
| = node->GetAs<NestedNameSpecifier>()) { |
| errs() << "[NestedNameSpecifier] " |
| << PrintableNestedNameSpecifier(nns) << "\n"; |
| } else if (const TemplateName* tpl_name |
| = node->GetAs<TemplateName>()) { |
| errs() << "[TemplateName] " |
| << PrintableTemplateName(*tpl_name) << "\n"; |
| } else if (const TemplateArgumentLoc* tpl_argloc |
| = node->GetAs<TemplateArgumentLoc>()) { |
| errs() << "[TemplateArgumentLoc] " |
| << PrintableTemplateArgumentLoc(*tpl_argloc) << "\n"; |
| } else if (const TemplateArgument* tpl_arg |
| = node->GetAs<TemplateArgument>()) { |
| errs() << "[TemplateArgument] " |
| << PrintableTemplateArgument(*tpl_arg) << "\n"; |
| } else { |
| CHECK_(false && "Unknown kind for ASTNode"); |
| } |
| } |
| |
| // --- Utilities for Template Arguments. |
| |
| // If the TemplateArgument is a type (and not an expression such as |
| // 'true', or a template such as 'vector', etc), return it. Otherwise |
| // return NULL. |
| static const Type* GetTemplateArgAsType(const TemplateArgument& tpl_arg) { |
| if (tpl_arg.getKind() == TemplateArgument::Type) |
| return tpl_arg.getAsType().getTypePtr(); |
| return NULL; |
| } |
| |
| // These utilities figure out the template arguments that are |
| // specified in various contexts: TemplateSpecializationType (for |
| // template classes) and FunctionDecl (for template functions). |
| // |
| // For classes, we care only about explicitly specified template args, |
| // not implicit, default args. For functions, we care about all |
| // template args, since if not specified they're derived from the |
| // function arguments. In either case, we only care about template |
| // arguments that are types (including template types), not other |
| // kinds of arguments such as built-in types. |
| |
| // This helper class visits a given AST node and finds all the types |
| // beneath it, which it returns as a set. For example, if you have a |
| // VarDecl 'vector<int(*)(const MyClass&)> x', it would return |
| // (vector<int(*)(const MyClass&)>, int(*)(const MyClass&), |
| // int(const MyClass&), int, const MyClass&, MyClass). Note that |
| // this function only returns types-as-typed, so it does *not* return |
| // alloc<int(*)(const MyClass&)>, even though it's part of vector. |
| class TypeEnumerator : public RecursiveASTVisitor<TypeEnumerator> { |
| public: |
| typedef RecursiveASTVisitor<TypeEnumerator> Base; |
| |
| // --- Public interface |
| // We can add more entry points as needed. |
| set<const Type*> Enumerate(const Type* type) { |
| seen_types_.clear(); |
| if (!type) |
| return seen_types_; |
| TraverseType(QualType(type, 0)); |
| return seen_types_; |
| } |
| |
| set<const Type*> Enumerate(const TemplateArgument& tpl_arg) { |
| seen_types_.clear(); |
| TraverseTemplateArgument(tpl_arg); |
| return seen_types_; |
| } |
| |
| // --- Methods on RecursiveASTVisitor |
| bool VisitType(Type* type) { |
| seen_types_.insert(type); |
| return true; |
| } |
| |
| private: |
| set<const Type*> seen_types_; |
| }; |
| |
| // A 'component' of a type is a type beneath it in the AST tree. |
| // So 'Foo*' has component 'Foo', as does 'vector<Foo>', while |
| // vector<pair<Foo, Bar> > has components pair<Foo,Bar>, Foo, and Bar. |
| set<const Type*> GetComponentsOfType(const Type* type) { |
| TypeEnumerator type_enumerator; |
| return type_enumerator.Enumerate(type); |
| } |
| |
| // --- Utilities for Decl. |
| |
| bool IsTemplatizedFunctionDecl(const FunctionDecl* decl) { |
| return decl && decl->getTemplateSpecializationArgs() != NULL; |
| } |
| |
| bool HasImplicitConversionCtor(const CXXRecordDecl* cxx_class) { |
| for (CXXRecordDecl::ctor_iterator ctor = cxx_class->ctor_begin(); |
| ctor != cxx_class->ctor_end(); ++ctor) { |
| if (ctor->isExplicit() || ctor->getNumParams() != 1 || |
| ctor->isCopyConstructor()) |
| continue; |
| return true; |
| } |
| return false; |
| } |
| |
| const RecordDecl* GetDefinitionForClass(const Decl* decl) { |
| const RecordDecl* as_record = DynCastFrom(decl); |
| const ClassTemplateDecl* as_tpl = DynCastFrom(decl); |
| if (as_tpl) { // Convert the template to its underlying class defn. |
| as_record = dyn_cast_or_null<RecordDecl>(as_tpl->getTemplatedDecl()); |
| } |
| if (as_record) { |
| if (const RecordDecl* record_dfn = as_record->getDefinition()) { |
| return record_dfn; |
| } |
| // If we're a templated class that was never instantiated (because |
| // we were never "used"), then getDefinition() will return NULL. |
| if (const ClassTemplateSpecializationDecl* spec_decl = DynCastFrom(decl)) { |
| PointerUnion<ClassTemplateDecl*, |
| ClassTemplatePartialSpecializationDecl*> |
| specialized_decl = spec_decl->getSpecializedTemplateOrPartial(); |
| if (const ClassTemplatePartialSpecializationDecl* |
| partial_spec_decl = |
| specialized_decl.dyn_cast< |
| ClassTemplatePartialSpecializationDecl*>()) { |
| // decl would be instantiated from a template partial |
| // specialization. |
| CHECK_(partial_spec_decl->hasDefinition()); |
| return partial_spec_decl->getDefinition(); |
| } else if (const ClassTemplateDecl* tpl_decl = |
| specialized_decl.dyn_cast<ClassTemplateDecl*>()) { |
| // decl would be instantiated from a non-specialized |
| // template. |
| if (tpl_decl->getTemplatedDecl()->hasDefinition()) |
| return tpl_decl->getTemplatedDecl()->getDefinition(); |
| } |
| } |
| } |
| return NULL; |
| } |
| |
| SourceRange GetSourceRangeOfClassDecl(const Decl* decl) { |
| // If we're a templatized class, go 'up' a level to get the |
| // template<...> prefix as well. |
| if (const CXXRecordDecl* cxx_decl = DynCastFrom(decl)) { |
| if (cxx_decl->getDescribedClassTemplate()) |
| return cxx_decl->getDescribedClassTemplate()->getSourceRange(); |
| } |
| // We can get source ranges of classes and template classes. |
| if (const TagDecl* tag_decl = DynCastFrom(decl)) |
| return tag_decl->getSourceRange(); |
| if (const TemplateDecl* tpl_decl = DynCastFrom(decl)) |
| return tpl_decl->getSourceRange(); |
| CHECK_(false && "Cannot get source range for this decl type"); |
| return SourceRange(); |
| } |
| |
| // Helper for the Get*ResugarMap*() functions. Given a map from |
| // desugared->resugared types, looks at each component of the |
| // resugared type (eg, both hash_set<Foo>* and vector<hash_set<Foo> > |
| // have two components: hash_set<Foo> and Foo), and returns a map that |
| // contains the original map elements plus mapping for the components. |
| // This is because when a type is 'owned' by the template |
| // instantiator, all parts of the type are owned. We only consider |
| // type-components as typed. |
| static map<const Type*, const Type*> ResugarTypeComponents( |
| const map<const Type*, const Type*>& resugar_map) { |
| map<const Type*, const Type*> retval = resugar_map; |
| for (Each<const Type*, const Type*> it(&resugar_map); !it.AtEnd(); ++it) { |
| const set<const Type*>& components = GetComponentsOfType(it->second); |
| for (Each<const Type*> component_type(&components); |
| !component_type.AtEnd(); ++component_type) { |
| const Type* desugared_type = GetCanonicalType(*component_type); |
| if (!ContainsKey(retval, desugared_type)) { |
| retval[desugared_type] = *component_type; |
| VERRS(6) << "Adding a type-components of interest: " |
| << PrintableType(*component_type) << "\n"; |
| } |
| } |
| } |
| return retval; |
| } |
| |
| // Helpers for GetTplTypeResugarMapForFunction(). |
| static map<const Type*, const Type*> GetTplTypeResugarMapForFunctionNoCallExpr( |
| const FunctionDecl* decl, unsigned start_arg) { |
| map<const Type*, const Type*> retval; |
| if (!decl) // can be NULL if the function call is via a function pointer |
| return retval; |
| if (const TemplateArgumentList* tpl_list |
| = decl->getTemplateSpecializationArgs()) { |
| for (unsigned i = start_arg; i < tpl_list->size(); ++i) { |
| if (const Type* arg_type = GetTemplateArgAsType(tpl_list->get(i))) { |
| retval[GetCanonicalType(arg_type)] = arg_type; |
| VERRS(6) << "Adding an implicit tpl-function type of interest: " |
| << PrintableType(arg_type) << "\n"; |
| } |
| } |
| } |
| return retval; |
| } |
| |
| static map<const Type*, const Type*> |
| GetTplTypeResugarMapForFunctionExplicitTplArgs( |
| const FunctionDecl* decl, |
| const ASTTemplateArgumentListInfo* explicit_tpl_list) { |
| map<const Type*, const Type*> retval; |
| if (explicit_tpl_list) { |
| for (unsigned i = 0; i < explicit_tpl_list->NumTemplateArgs; ++i) { |
| const TemplateArgument& arg |
| = explicit_tpl_list->getTemplateArgs()[i].getArgument(); |
| if (const Type* arg_type = GetTemplateArgAsType(arg)) { |
| retval[GetCanonicalType(arg_type)] = arg_type; |
| VERRS(6) << "Adding an explicit template-function type of interest: " |
| << PrintableType(arg_type) << "\n"; |
| } |
| } |
| } |
| return retval; |
| } |
| |
| map<const Type*, const Type*> GetTplTypeResugarMapForFunction( |
| const FunctionDecl* decl, const Expr* calling_expr) { |
| map<const Type*, const Type*> retval; |
| |
| // If calling_expr is NULL, then we can't find any explicit template |
| // arguments, if they were specified (e.g. 'Fn<int>()'), and we |
| // won't be able to get the function arguments as written. So we |
| // can't resugar at all. We just have to hope that the types happen |
| // to be already sugared, because the actual-type is already canonical. |
| if (calling_expr == NULL) { |
| retval = GetTplTypeResugarMapForFunctionNoCallExpr(decl, 0); |
| retval = ResugarTypeComponents(retval); // add in retval's decomposition |
| return retval; |
| } |
| |
| // If calling_expr is a CXXConstructExpr of CXXNewExpr, then it's |
| // impossible to explicitly specify template arguments; all we have |
| // to go on is function arguments. If it's a CallExpr, and some |
| // arguments might be explicit, and others implicit. Otherwise, |
| // it's a type that doesn't take function template args at all (like |
| // CXXDeleteExpr) or only takes explicit args (like DeclRefExpr). |
| Expr** fn_args = NULL; |
| unsigned num_args = 0; |
| unsigned start_of_implicit_args = 0; |
| if (const CXXConstructExpr* ctor_expr = DynCastFrom(calling_expr)) { |
| fn_args = ctor_expr->getArgs(); |
| num_args = ctor_expr->getNumArgs(); |
| } else if (const CallExpr* call_expr = DynCastFrom(calling_expr)) { |
| fn_args = const_cast<CallExpr*>(call_expr)->getArgs(); |
| num_args = call_expr->getNumArgs(); |
| const Expr* callee_expr = call_expr->getCallee()->IgnoreParenCasts(); |
| if (const ASTTemplateArgumentListInfo* explicit_tpl_args |
| = GetExplicitTplArgs(callee_expr)) { |
| retval = GetTplTypeResugarMapForFunctionExplicitTplArgs( |
| decl, explicit_tpl_args); |
| start_of_implicit_args = explicit_tpl_args->NumTemplateArgs; |
| } |
| } else { |
| // If calling_expr has explicit template args, then consider them. |
| if (const ASTTemplateArgumentListInfo* explicit_tpl_args |
| = GetExplicitTplArgs(calling_expr)) { |
| retval = GetTplTypeResugarMapForFunctionExplicitTplArgs( |
| decl, explicit_tpl_args); |
| retval = ResugarTypeComponents(retval); |
| } |
| return retval; |
| } |
| |
| // Now we have to figure out, as best we can, the sugar-mappings for |
| // compiler-deduced template args. We do this by looking at every |
| // type specified in any part of the function arguments as written. |
| // If any of these types matches a template type, then we take that |
| // to be the resugar mapping. If none of the types match, then we |
| // assume that the template is matching some desugared part of the |
| // type, and we ignore it. For instance: |
| // operator<<(basic_ostream<char, T>& o, int i); |
| // If I pass in an ostream as the first argument, then no part |
| // of the (sugared) argument types match T, so we ignore it. |
| const map<const Type*, const Type*>& desugared_types |
| = GetTplTypeResugarMapForFunctionNoCallExpr(decl, start_of_implicit_args); |
| |
| // TODO(csilvers): SubstTemplateTypeParms are always desugared, |
| // making this less useful than it should be. |
| // TODO(csilvers): if the GetArg(i) expr has an implicit cast |
| // under it, take the pre-cast type instead? |
| set<const Type*> fn_arg_types; |
| for (unsigned i = 0; i < num_args; ++i) { |
| const Type* argtype = GetTypeOf(fn_args[i]); |
| // TODO(csilvers): handle RecordTypes that are a TemplateSpecializationDecl |
| InsertAllInto(GetComponentsOfType(argtype), &fn_arg_types); |
| } |
| |
| for (Each<const Type*> it(&fn_arg_types); !it.AtEnd(); ++it) { |
| // See if any of the template args in retval are the desugared form of us. |
| const Type* desugared_type = GetCanonicalType(*it); |
| if (ContainsKey(desugared_types, desugared_type)) { |
| retval[desugared_type] = *it; |
| if (desugared_type != *it) { |
| VERRS(6) << "Remapping template arg of interest: " |
| << PrintableType(desugared_type) << " -> " |
| << PrintableType(*it) << "\n"; |
| } |
| } |
| } |
| |
| // Log the types we never mapped. |
| for (Each<const Type*, const Type*> it(&desugared_types); !it.AtEnd(); ++it) { |
| if (!ContainsKey(retval, it->first)) { |
| VERRS(6) << "Ignoring unseen-in-fn-args template arg of interest: " |
| << PrintableType(it->first) << "\n"; |
| } |
| } |
| |
| retval = ResugarTypeComponents(retval); // add in the decomposition of retval |
| return retval; |
| } |
| |
| const NamedDecl* GetInstantiatedFromDecl(const CXXRecordDecl* class_decl) { |
| if (const ClassTemplateSpecializationDecl* tpl_sp_decl = |
| DynCastFrom(class_decl)) { // an instantiated class template |
| PointerUnion<ClassTemplateDecl*, ClassTemplatePartialSpecializationDecl*> |
| instantiated_from = tpl_sp_decl->getInstantiatedFrom(); |
| if (const ClassTemplateDecl* tpl_decl = |
| instantiated_from.dyn_cast<ClassTemplateDecl*>()) { |
| // class_decl is instantiated from a non-specialized template. |
| return tpl_decl; |
| } else if (const ClassTemplatePartialSpecializationDecl* |
| partial_spec_decl = |
| instantiated_from.dyn_cast< |
| ClassTemplatePartialSpecializationDecl*>()) { |
| // class_decl is instantiated from a template partial specialization. |
| return partial_spec_decl; |
| } |
| } |
| // class_decl is not instantiated from a template. |
| return class_decl; |
| } |
| |
| const NamedDecl* GetDefinitionAsWritten(const NamedDecl* decl) { |
| // First, get to decl-as-written. |
| if (const CXXRecordDecl* class_decl = DynCastFrom(decl)) { |
| decl = GetInstantiatedFromDecl(class_decl); |
| if (const ClassTemplateDecl* tpl_decl = DynCastFrom(decl)) |
| decl = tpl_decl->getTemplatedDecl(); // convert back to CXXRecordDecl |
| } else if (const FunctionDecl* func_decl = DynCastFrom(decl)) { |
| if (const FunctionDecl* tpl_pattern = |
| func_decl->getTemplateInstantiationPattern()) |
| decl = tpl_pattern; |
| } |
| // Then, get to definition. |
| if (const NamedDecl* class_dfn = GetDefinitionForClass(decl)) { |
| return class_dfn; |
| } else if (const FunctionDecl* fn_decl = DynCastFrom(decl)) { |
| for (FunctionDecl::redecl_iterator it = fn_decl->redecls_begin(); |
| it != fn_decl->redecls_end(); ++it) { |
| if ((*it)->isThisDeclarationADefinition()) |
| return *it; |
| } |
| } |
| // Couldn't find a definition, just return the original declaration. |
| return decl; |
| } |
| |
| bool IsDefaultNewOrDelete(const FunctionDecl* decl, |
| const string& decl_loc_as_quoted_include) { |
| // Clang will report <new> as the location of the default new and |
| // delete operators if <new> is included. Otherwise, it reports the |
| // (fake) file "<built_in>". |
| if (decl_loc_as_quoted_include != "<new>" && |
| !IsBuiltinFile(GetFileEntry(decl))) |
| return false; |
| |
| const string decl_name = decl->getNameAsString(); |
| if (!StartsWith(decl_name, "operator new") && |
| !StartsWith(decl_name, "operator delete")) |
| return false; |
| |
| // Placement-new/delete has 2 args, second is void*. The only other |
| // 2-arg overloads of new/delete in <new> take a const nothrow_t&. |
| if (decl->getNumParams() == 2 && |
| !decl->getParamDecl(1)->getType().isConstQualified()) |
| return false; |
| |
| return true; |
| } |
| |
| bool IsFriendDecl(const Decl* decl) { |
| // For 'template<...> friend class T', the decl will just be 'class T'. |
| // We need to go 'up' a level to check friendship in the right place. |
| if (const CXXRecordDecl* cxx_decl = DynCastFrom(decl)) |
| if (cxx_decl->getDescribedClassTemplate()) |
| decl = cxx_decl->getDescribedClassTemplate(); |
| return decl->getFriendObjectKind() != Decl::FOK_None; |
| } |
| |
| bool IsForwardDecl(const clang::TagDecl* decl) { |
| return (isa<RecordDecl>(decl) && // not an enum |
| !decl->isCompleteDefinition() && !IsFriendDecl(decl) && |
| !decl->isEmbeddedInDeclarator()); |
| } |
| |
| // Two possibilities: it's written as a nested class (that is, with a |
| // qualifier) or it's actually living inside another class. |
| bool IsNestedClass(const TagDecl* decl) { |
| if (decl->getQualifier() && |
| decl->getQualifier()->getKind() == NestedNameSpecifier::TypeSpec) { |
| return true; |
| } |
| return isa<RecordDecl>(decl->getDeclContext()); |
| } |
| |
| bool HasDefaultTemplateParameters(const TemplateDecl* decl) { |
| TemplateParameterList* tpl_params = decl->getTemplateParameters(); |
| return tpl_params->getMinRequiredArguments() < tpl_params->size(); |
| } |
| |
| template <class T> inline set<const clang::NamedDecl*> GetRedeclsOfRedeclarable( |
| const clang::Redeclarable<T>* decl) { |
| return set<const clang::NamedDecl*>(decl->redecls_begin(), |
| decl->redecls_end()); |
| } |
| |
| // The only way to find out whether a decl can be dyn_cast to a |
| // Redeclarable<T> and what T is is to enumerate the possibilities. |
| // Hence we hard-code the list. |
| set<const clang::NamedDecl*> GetNonclassRedecls(const clang::NamedDecl* decl) { |
| CHECK_(!isa<RecordDecl>(decl) && "For classes, call GetClassRedecls()"); |
| CHECK_(!isa<ClassTemplateDecl>(decl) && "For tpls, call GetClassRedecls()"); |
| if (const TagDecl* specific_decl = DynCastFrom(decl)) |
| return GetRedeclsOfRedeclarable(specific_decl); |
| // TODO(wan): go through iwyu to replace TypedefDecl with |
| // TypedefNameDecl as needed. |
| if (const TypedefNameDecl* specific_decl = DynCastFrom(decl)) |
| return GetRedeclsOfRedeclarable(specific_decl); |
| if (const FunctionDecl* specific_decl = DynCastFrom(decl)) |
| return GetRedeclsOfRedeclarable(specific_decl); |
| if (const VarDecl* specific_decl = DynCastFrom(decl)) |
| return GetRedeclsOfRedeclarable(specific_decl); |
| // Not redeclarable, so the output is just the input. |
| set<const clang::NamedDecl*> retval; |
| retval.insert(decl); |
| return retval; |
| } |
| |
| set<const NamedDecl*> GetClassRedecls(const NamedDecl* decl) { |
| const RecordDecl* record_decl = DynCastFrom(decl); |
| const ClassTemplateDecl* tpl_decl = DynCastFrom(decl); |
| if (tpl_decl) |
| record_decl = tpl_decl->getTemplatedDecl(); |
| if (!record_decl) |
| return set<const NamedDecl*>(); |
| |
| set<const NamedDecl*> redecls; |
| for (TagDecl::redecl_iterator it = record_decl->redecls_begin(); |
| it != record_decl->redecls_end(); ++it) { |
| const RecordDecl* redecl = cast<RecordDecl>(*it); |
| // If this decl is a friend decl, don't count it: friend decls |
| // don't serve as forward-declarations. (This should never |
| // happen, I think, but it sometimes does due to a clang bug: |
| // http://llvm.org/bugs/show_bug.cgi?id=8669). The only exception |
| // is made because every decl is a redecl of itself. |
| if (IsFriendDecl(redecl) && redecl != decl) |
| continue; |
| |
| if (tpl_decl) { // need to convert back to a ClassTemplateDecl |
| CHECK_(isa<CXXRecordDecl>(redecl) && |
| cast<CXXRecordDecl>(redecl)->getDescribedClassTemplate()); |
| const CXXRecordDecl* cxx_redecl = cast<CXXRecordDecl>(redecl); |
| redecls.insert(cxx_redecl->getDescribedClassTemplate()); |
| } else { |
| redecls.insert(redecl); |
| } |
| } |
| return redecls; |
| } |
| |
| const NamedDecl* GetFirstRedecl(const NamedDecl* decl) { |
| const NamedDecl* first_decl = decl; |
| FullSourceLoc first_decl_loc(GetLocation(first_decl), *GlobalSourceManager()); |
| set<const NamedDecl*> all_redecls = GetClassRedecls(decl); |
| if (all_redecls.empty()) // input is not a class or class template |
| return NULL; |
| |
| for (Each<const NamedDecl*> it(&all_redecls); !it.AtEnd(); ++it) { |
| const FullSourceLoc redecl_loc(GetLocation(*it), *GlobalSourceManager()); |
| if (redecl_loc.isBeforeInTranslationUnitThan(first_decl_loc)) { |
| first_decl = *it; |
| first_decl_loc = redecl_loc; |
| } |
| } |
| return first_decl; |
| } |
| |
| const NamedDecl* GetNonfriendClassRedecl(const NamedDecl* decl) { |
| const RecordDecl* record_decl = DynCastFrom(decl); |
| const ClassTemplateDecl* tpl_decl = DynCastFrom(decl); |
| if (tpl_decl) |
| record_decl = tpl_decl->getTemplatedDecl(); |
| // This check is so we return the input decl whenever possible. |
| if (!record_decl || !IsFriendDecl(record_decl)) |
| return decl; |
| |
| set<const NamedDecl*> all_redecls = GetClassRedecls(decl); |
| CHECK_(!all_redecls.empty() && "Uncaught non-class decl"); |
| return *all_redecls.begin(); // arbitrary choice |
| } |
| |
| bool DeclsAreInSameClass(const Decl* decl1, const Decl* decl2) { |
| if (!decl1 || !decl2) |
| return false; |
| if (decl1->getDeclContext() != decl2->getDeclContext()) |
| return false; |
| return decl1->getDeclContext()->isRecord(); |
| } |
| |
| |
| // --- Utilities for Type. |
| |
| const Type* GetTypeOf(const Expr* expr) { |
| return expr->getType().getTypePtr(); |
| } |
| |
| const Type* GetTypeOf(const ValueDecl* decl) { |
| return decl->getType().getTypePtr(); |
| } |
| |
| const Type* GetTypeOf(const TypeDecl* decl) { |
| return decl->getTypeForDecl(); |
| } |
| |
| const Type* GetCanonicalType(const Type* type) { |
| QualType canonical_type = type->getCanonicalTypeUnqualified(); |
| return canonical_type.getTypePtr(); |
| } |
| |
| const Type* RemoveElaboration(const Type* type) { |
| if (const ElaboratedType* elaborated_type = DynCastFrom(type)) |
| return elaborated_type->getNamedType().getTypePtr(); |
| else |
| return type; |
| } |
| |
| bool IsTemplatizedType(const Type* type) { |
| return (type && isa<TemplateSpecializationType>(RemoveElaboration(type))); |
| } |
| |
| bool IsClassType(const clang::Type* type) { |
| return (type && (isa<TemplateSpecializationType>(RemoveElaboration(type)) || |
| isa<RecordType>(RemoveElaboration(type)))); |
| } |
| |
| const Type* RemoveSubstTemplateTypeParm(const Type* type) { |
| if (const SubstTemplateTypeParmType* subst_type = DynCastFrom(type)) |
| return subst_type->getReplacementType().getTypePtr(); |
| else |
| return type; |
| } |
| |
| bool IsPointerOrReferenceAsWritten(const Type* type) { |
| type = RemoveElaboration(type); |
| return isa<PointerType>(type) || isa<LValueReferenceType>(type); |
| } |
| |
| const Type* RemovePointersAndReferencesAsWritten(const Type* type) { |
| type = RemoveElaboration(type); |
| while (isa<PointerType>(type) || |
| isa<LValueReferenceType>(type)) { |
| type = type->getPointeeType().getTypePtr(); |
| } |
| return type; |
| } |
| |
| const Type* RemovePointerFromType(const Type* type) { |
| if (!IsPointerOrReferenceAsWritten(type)) { // ah well, have to desugar |
| type = type->getUnqualifiedDesugaredType(); |
| } |
| if (!IsPointerOrReferenceAsWritten(type)) { |
| return NULL; |
| } |
| type = RemoveElaboration(type); |
| type = type->getPointeeType().getTypePtr(); |
| return type; |
| } |
| |
| // This follows typedefs/etc to remove pointers, if necessary. |
| const Type* RemovePointersAndReferences(const Type* type) { |
| while (1) { |
| const Type* deref_type = RemovePointerFromType(type); |
| if (deref_type == NULL) // type wasn't a pointer (or reference) type |
| break; // removed all pointers |
| type = deref_type; |
| } |
| return type; |
| } |
| |
| const NamedDecl* TypeToDeclAsWritten(const Type* type) { |
| // Get past all the 'class' and 'struct' prefixes, and namespaces. |
| type = RemoveElaboration(type); |
| |
| // Read past SubstTemplateTypeParmType (this can happen if a |
| // template function returns the tpl-arg type: e.g. for |
| // 'T MyFn<T>() {...}; MyFn<X>.a', the type of MyFn<X> will be a Subst. |
| type = RemoveSubstTemplateTypeParm(type); |
| |
| CHECK_(!isa<ObjCObjectType>(type) && "IWYU doesn't support Objective-C"); |
| |
| // We have to be a bit careful about the order, because we want |
| // to keep typedefs as typedefs, so we do the record check last. |
| // We use getAs<> when we can -- unfortunately, it only exists |
| // for a few types so far. |
| if (const TypedefType* typedef_type = DynCastFrom(type)) { |
| return typedef_type->getDecl(); |
| } else if (const InjectedClassNameType* icn_type |
| = type->getAs<InjectedClassNameType>()) { |
| return icn_type->getDecl(); |
| } else if (const RecordType* record_type |
| = type->getAs<RecordType>()) { |
| return record_type->getDecl(); |
| } else if (const TagType* tag_type = DynCastFrom(type)) { |
| return tag_type->getDecl(); // probably just enums |
| } else if (const TemplateSpecializationType* template_spec_type |
| = DynCastFrom(type)) { |
| // A non-concrete template class, such as 'Myclass<T>' |
| return template_spec_type->getTemplateName().getAsTemplateDecl(); |
| } else if (const FunctionType* function_type = DynCastFrom(type)) { |
| // TODO(csilvers): is it possible to map from fn type to fn decl? |
| (void)function_type; |
| return NULL; |
| } else { |
| return NULL; |
| } |
| } |
| |
| const Type* RemoveReferenceAsWritten(const Type* type) { |
| if (const LValueReferenceType* ref_type = DynCastFrom(type)) |
| return ref_type->getPointeeType().getTypePtr(); |
| else |
| return type; |
| } |
| |
| bool HasImplicitConversionConstructor(const Type* type) { |
| type = RemoveElaboration(type); // get rid of the class keyword |
| if (isa<PointerType>(type)) |
| return false; // can't implicitly convert to a pointer |
| if (isa<LValueReferenceType>(type) && |
| !type->getPointeeType().isConstQualified()) |
| return false; // can't implicitly convert to a non-const reference |
| |
| type = RemoveReferenceAsWritten(type); |
| const NamedDecl* decl = TypeToDeclAsWritten(type); |
| if (!decl) // not the kind of type that has a decl (e.g. built-in) |
| return false; |
| |
| const CXXRecordDecl* cxx_class = DynCastFrom(decl); |
| if (!cxx_class) |
| return false; // can't implicitly convert to a non-class type |
| |
| return HasImplicitConversionCtor(cxx_class); |
| } |
| |
| map<const clang::Type*, const clang::Type*> |
| GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type) { |
| map<const Type*, const Type*> retval; |
| type = RemoveElaboration(type); // get rid of the class keyword |
| const TemplateSpecializationType* tpl_spec_type = DynCastFrom(type); |
| if (!tpl_spec_type) |
| return retval; |
| |
| // Get the list of template args that apply to the decls. |
| const NamedDecl* decl = TypeToDeclAsWritten(tpl_spec_type); |
| const ClassTemplateSpecializationDecl* tpl_decl = DynCastFrom(decl); |
| if (!tpl_decl) // probably because tpl_spec_type is a dependent type |
| return retval; |
| const TemplateArgumentList& tpl_args |
| = tpl_decl->getTemplateInstantiationArgs(); |
| |
| // TemplateSpecializationType only includes explicitly specified |
| // types in its args list, so we start with that. Note that an |
| // explicitly specified type may fulfill multiple template args: |
| // template <typename R, typename A1> struct Foo<R(A1)> { ... } |
| set<unsigned> explicit_args; // indices into tpl_args we've filled |
| TypeEnumerator type_enumerator; |
| for (unsigned i = 0; i < tpl_spec_type->getNumArgs(); ++i) { |
| set<const Type*> arg_components |
| = type_enumerator.Enumerate(tpl_spec_type->getArg(i)); |
| // Go through all template types mentioned in the arg-as-written, |
| // and compare it against each of the types in the template decl |
| // (the latter are all desugared). If there's a match, update |
| // the mapping. |
| for (Each<const Type*> it(&arg_components); !it.AtEnd(); ++it) { |
| for (unsigned i = 0; i < tpl_args.size(); ++i) { |
| if (const Type* arg_type = GetTemplateArgAsType(tpl_args[i])) { |
| if (GetCanonicalType(*it) == arg_type) { |
| retval[arg_type] = *it; |
| VERRS(6) << "Adding a template-class type of interest: " |
| << PrintableType(*it) << "\n"; |
| explicit_args.insert(i); |
| } |
| } |
| } |
| } |
| } |
| |
| // Now take a look at the args that were not filled explicitly. |
| for (unsigned i = 0; i < tpl_args.size(); ++i) { |
| if (ContainsKey(explicit_args, i)) |
| continue; |
| if (const Type* arg_type = GetTemplateArgAsType(tpl_args[i])) { |
| retval[arg_type] = NULL; |
| VERRS(6) << "Adding a template-class default type of interest: " |
| << PrintableType(arg_type) << "\n"; |
| } |
| } |
| |
| return retval; |
| } |
| |
| map<const clang::Type*, const clang::Type*> GetTplTypeResugarMapForClass( |
| const clang::Type* type) { |
| return ResugarTypeComponents( // add in the decomposition of retval |
| GetTplTypeResugarMapForClassNoComponentTypes(type)); |
| } |
| |
| // --- Utilities for Stmt. |
| |
| bool IsAddressOf(const Expr* expr) { |
| if (const UnaryOperator* unary = DynCastFrom(expr->IgnoreParens())) |
| return unary->getOpcode() == clang::UO_AddrOf; |
| return false; |
| } |
| |
| const Type* TypeOfParentIfMethod(const CallExpr* expr) { |
| // callee_expr is a MemberExpr if we're a normal class method, or |
| // DeclRefExpr if we're a static class method or an overloaded operator. |
| const Expr* callee_expr = expr->getCallee()->IgnoreParenCasts(); |
| if (const MemberExpr* member_expr = DynCastFrom(callee_expr)) { |
| const Type* class_type = GetTypeOf(member_expr->getBase()); |
| // For class->member(), class_type is a pointer. |
| return RemovePointersAndReferencesAsWritten(class_type); |
| } else if (const DeclRefExpr* ref_expr = DynCastFrom(callee_expr)) { |
| if (ref_expr->getQualifier()) { // static methods like C<T>::a() |
| return ref_expr->getQualifier()->getAsType(); |
| } |
| } |
| return NULL; |
| } |
| |
| const Expr* GetFirstClassArgument(CallExpr* expr) { |
| for (CallExpr::arg_iterator it = expr->arg_begin(); |
| it != expr->arg_end(); ++it) { |
| const Type* argtype = GetTypeOf(*it); |
| // Make sure we do the right thing given a function like |
| // template <typename T> void operator>>(const T& x, ostream& os); |
| // In this case ('myclass >> os'), we want to be returning the |
| // type of os, not of myclass, and we do, because myclass will be |
| // a SubstTemplateTypeParmType, not a RecordType. |
| if (isa<SubstTemplateTypeParmType>(argtype)) |
| continue; |
| argtype = argtype->getUnqualifiedDesugaredType(); // see through typedefs |
| if (isa<RecordType>(argtype) || |
| isa<TemplateSpecializationType>(argtype)) { |
| return *it; |
| } |
| } |
| return NULL; |
| } |
| |
| const CXXDestructorDecl* GetDestructorForDeleteExpr(const CXXDeleteExpr* expr) { |
| const Type* type = expr->getDestroyedType().getTypePtrOrNull(); |
| // type is NULL when deleting a dependent type: 'T foo; delete foo' |
| if (type == NULL) |
| return NULL; |
| const NamedDecl* decl = TypeToDeclAsWritten(type); |
| if (const CXXRecordDecl* cxx_record = DynCastFrom(decl)) |
| return cxx_record->getDestructor(); |
| return NULL; |
| } |
| |
| const CXXDestructorDecl* GetSiblingDestructorFor( |
| const CXXConstructorDecl* ctor) { |
| return ctor ? ctor->getParent()->getDestructor() : NULL; |
| } |
| |
| const CXXDestructorDecl* GetSiblingDestructorFor( |
| const CXXConstructExpr* ctor_expr) { |
| return GetSiblingDestructorFor(ctor_expr->getConstructor()); |
| } |
| |
| const FunctionType* GetCalleeFunctionType(CallExpr* expr) { |
| const Type* callee_type = expr->getCallee()->getType().getTypePtr(); |
| if (const PointerType* ptr_type |
| = callee_type->getAs<PointerType>()) { |
| callee_type = ptr_type->getPointeeType().getTypePtr(); |
| } else if (const BlockPointerType* bptr_type |
| = callee_type->getAs<BlockPointerType>()) { |
| callee_type = bptr_type->getPointeeType().getTypePtr(); |
| } else if (const MemberPointerType* mptr_type |
| = callee_type->getAs<MemberPointerType>()) { |
| callee_type = mptr_type->getPointeeType().getTypePtr(); |
| } |
| return callee_type->getAs<FunctionType>(); |
| } |
| |
| bool IsCastToReferenceType(const CastExpr* expr) { |
| if (const ExplicitCastExpr* explicit_cast = DynCastFrom(expr)) { |
| return explicit_cast->getTypeAsWritten()->isReferenceType(); |
| } else if (const ImplicitCastExpr* implicit_cast = DynCastFrom(expr)) { |
| return implicit_cast->getValueKind() == clang::VK_LValue; |
| } else { |
| CHECK_(false && "Unexpected type of cast expression"); |
| return false; |
| } |
| } |
| |
| const ASTTemplateArgumentListInfo* GetExplicitTplArgs(const Expr* expr) { |
| if (const DeclRefExpr* decl_ref = DynCastFrom(expr)) |
| return decl_ref->getOptionalExplicitTemplateArgs(); |
| if (const MemberExpr* member_expr = DynCastFrom(expr)) |
| return member_expr->getOptionalExplicitTemplateArgs(); |
| // Ugh, annoying casts needed because no const methods exist. |
| if (const OverloadExpr* overload_expr = DynCastFrom(expr)) |
| return const_cast<OverloadExpr*>(overload_expr) |
| ->getOptionalExplicitTemplateArgs(); |
| if (const DependentScopeDeclRefExpr* dependent_decl_ref = DynCastFrom(expr)) |
| return const_cast<DependentScopeDeclRefExpr*>(dependent_decl_ref) |
| ->getOptionalExplicitTemplateArgs(); |
| return NULL; |
| } |
| |
| } // namespace include_what_you_use |