//===--- 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 // for set #include // for string, operator+, etc #include // for pair #include "iwyu_globals.h" #include "iwyu_location_util.h" #include "iwyu_path_util.h" #include "iwyu_port.h" // for CHECK_ #include "iwyu_stl_util.h" #include "iwyu_verrs.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDumper.h" #include "clang/AST/CanonicalType.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/Builtins.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" namespace clang { class FileEntry; } // namespace clang using clang::ASTDumper; 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::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::Expr; using clang::ExprWithCleanups; using clang::FileEntry; using clang::FullSourceLoc; using clang::FunctionDecl; 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::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::SourceRange; using clang::Stmt; using clang::SubstTemplateTypeParmType; using clang::TagDecl; using clang::TagType; using clang::TemplateArgument; using clang::TemplateArgumentList; using clang::TemplateArgumentListInfo; using clang::TemplateArgumentLoc; using clang::TemplateDecl; using clang::TemplateName; using clang::TemplateParameterList; using clang::TemplateSpecializationKind; using clang::TemplateSpecializationType; using clang::TranslationUnitDecl; using clang::Type; using clang::TypeAliasTemplateDecl; 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::ArrayRef; using llvm::PointerUnion; using llvm::cast; using llvm::dyn_cast; using llvm::dyn_cast_or_null; using llvm::errs; using llvm::isa; using llvm::raw_string_ostream; namespace include_what_you_use { namespace { void DumpASTNode(llvm::raw_ostream& ostream, const ASTNode* node) { if (const Decl *decl = node->GetAs()) { ostream << "[" << GetKindName(decl) << "] " << PrintableDecl(decl); } else if (const Stmt *stmt = node->GetAs()) { ostream << "[" << GetKindName(stmt) << "] " << PrintableStmt(stmt); } else if (const TypeLoc* typeloc = node->GetAs()) { ostream << "[" << GetKindName(*typeloc) << "] " << PrintableTypeLoc(*typeloc); } else if (const Type* type = node->GetAs()) { // +typeloc ostream << "[" << GetKindName(type) << "] " << PrintableType(type); } else if (const NestedNameSpecifier* nns = node->GetAs()) { ostream << "[NestedNameSpecifier] " << PrintableNestedNameSpecifier(nns); } else if (const TemplateName* tpl_name = node->GetAs()) { ostream << "[TemplateName] " << PrintableTemplateName(*tpl_name); } else if (const TemplateArgumentLoc* tpl_argloc = node->GetAs()) { ostream << "[TemplateArgumentLoc] " << PrintableTemplateArgumentLoc(*tpl_argloc); } else if (const TemplateArgument* tpl_arg = node->GetAs()) { ostream << "[TemplateArgument] " << PrintableTemplateArgument(*tpl_arg); } else { CHECK_UNREACHABLE_("Unknown kind for ASTNode"); } } TemplateSpecializationKind GetTemplateSpecializationKind(const Decl* decl) { if (const auto* record = dyn_cast(decl)) { return record->getTemplateSpecializationKind(); } return clang::TSK_Undeclared; } } // anonymous namespace //------------------------------------------------------------ // 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 != nullptr; 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()) { clang::SourceManager& sm = *GlobalSourceManager(); FullSourceLoc full_loc(retval, sm); const FileEntry* spelling_file = sm.getFileEntryForID(sm.getFileID(full_loc.getSpellingLoc())); const FileEntry* instantiation_file = sm.getFileEntryForID(sm.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; } CHECK_UNREACHABLE_("Unexpected kind of ASTNode"); } // --- Utilities for ASTNode. bool IsElaboratedTypeSpecifier(const ASTNode* ast_node) { if (ast_node == nullptr) return false; const ElaboratedType* elaborated_type = ast_node->GetAs(); return elaborated_type && elaborated_type->getKeyword() != clang::ETK_None; } const ASTNode* MostElaboratedAncestor(const ASTNode* ast_node) { // Read past elaborations like 'class' keyword or namespaces. while (ast_node->ParentIsA()) { ast_node = ast_node->parent(); } return ast_node; } bool IsQualifiedNameNode(const ASTNode* ast_node) { if (ast_node == nullptr) return false; const ElaboratedType* elaborated_type = ast_node->GetAs(); if (elaborated_type == nullptr) return false; return elaborated_type->getQualifier() != nullptr; } 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()) return true; for (; ast_node != nullptr; 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()) { 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()) { if (ast_node->ContentIs(method_decl->getBody())) { return true; } } } return false; } UseFlags ComputeUseFlags(const ASTNode* ast_node) { UseFlags flags = UF_None; if (IsNodeInsideCXXMethodBody(ast_node)) flags |= UF_InCxxMethodBody; // Definitions of free functions are a little special, because they themselves // count as uses of all prior declarations (ideally we should probably just // require one but it's hard to say which, so we pick all previously seen). // Later IWYU analysis phases do some canonicalization that isn't // necessary/valid for this case, so mark it up for later. if (const auto* fd = ast_node->GetAs()) { if (fd->getKind() == Decl::Function && fd->isThisDeclarationADefinition()) flags |= UF_FunctionDfn; } return flags; } bool IsNestedTagAsWritten(const ASTNode* ast_node) { return (ast_node->IsA() && (ast_node->ParentIsA() || // For templated nested-classes, a ClassTemplateDecl is interposed. (ast_node->ParentIsA() && ast_node->AncestorIsA(2)))); } bool IsDefaultTemplateTemplateArg(const ASTNode* ast_node) { // Is ast_node the 'D' in the following: // template