blob: 7882d4f54ea347a9583995c15c3fc6d62b7a62fc [file] [log] [blame]
/*
* Copyright 2014 The Kythe Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Defines AST visitors and consumers used by the indexer tool.
#ifndef KYTHE_CXX_INDEXER_CXX_INDEXER_AST_HOOKS_H_
#define KYTHE_CXX_INDEXER_CXX_INDEXER_AST_HOOKS_H_
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "absl/memory/memory.h"
#include "absl/types/optional.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Sema/SemaConsumer.h"
#include "clang/Sema/Template.h"
#include "glog/logging.h"
#include "GraphObserver.h"
#include "IndexerLibrarySupport.h"
#include "indexed_parent_map.h"
#include "indexer_worklist.h"
#include "kythe/cxx/indexer/cxx/node_set.h"
#include "kythe/cxx/indexer/cxx/semantic_hash.h"
#include "marked_source.h"
#include "type_map.h"
namespace kythe {
/// \brief Specifies whether uncommonly-used data should be dropped.
enum Verbosity : bool {
Classic = true, ///< Emit all data.
Lite = false ///< Emit only common data.
};
/// \brief Specifies what the indexer should do if it encounters a case it
/// doesn't understand.
enum BehaviorOnUnimplemented : bool {
Abort = false, ///< Stop indexing and exit with an error.
Continue = true ///< Continue indexing, possibly emitting less data.
};
/// \brief Specifies what the indexer should do with template instantiations.
enum BehaviorOnTemplates : bool {
SkipInstantiations = false, ///< Don't visit template instantiations.
VisitInstantiations = true ///< Visit template instantiations.
};
/// \brief Specifies if the indexer should emit documentation nodes for comments
/// associated with forward declarations.
enum BehaviorOnFwdDeclComments : bool { Emit = true, Ignore = false };
/// \brief A byte range that links to some node.
struct MiniAnchor {
size_t Begin;
size_t End;
GraphObserver::NodeId AnchoredTo;
};
/// Adds brackets to Text to define anchor locations (escaping existing ones)
/// and sorts Anchors such that the ith Anchor corresponds to the ith opening
/// bracket. Drops empty or negative-length spans.
void InsertAnchorMarks(std::string& Text, std::vector<MiniAnchor>& Anchors);
/// \brief Used internally to check whether parts of the AST can be ignored.
class PruneCheck;
/// \brief An AST visitor that extracts information for a translation unit and
/// writes it to a `GraphObserver`.
class IndexerASTVisitor : public clang::RecursiveASTVisitor<IndexerASTVisitor> {
public:
IndexerASTVisitor(clang::ASTContext& C, BehaviorOnUnimplemented B,
BehaviorOnTemplates T, Verbosity V,
BehaviorOnFwdDeclComments ObjC,
BehaviorOnFwdDeclComments Cpp, const LibrarySupports& S,
clang::Sema& Sema, std::function<bool()> ShouldStopIndexing,
GraphObserver* GO = nullptr, int UsrByteSize = 0)
: IgnoreUnimplemented(B),
TemplateMode(T),
Verbosity(V),
ObjCFwdDocs(ObjC),
CppFwdDocs(Cpp),
Observer(GO ? *GO : NullObserver),
Context(C),
Supports(S),
Sema(Sema),
MarkedSources(&Sema, &Observer),
ShouldStopIndexing(std::move(ShouldStopIndexing)),
UsrByteSize(UsrByteSize) {}
bool VisitDecl(const clang::Decl* Decl);
bool VisitFieldDecl(const clang::FieldDecl* Decl);
bool VisitVarDecl(const clang::VarDecl* Decl);
bool VisitNamespaceDecl(const clang::NamespaceDecl* Decl);
bool VisitBindingDecl(const clang::BindingDecl* Decl);
bool VisitDeclRefExpr(const clang::DeclRefExpr* DRE);
bool VisitDesignatedInitExpr(const clang::DesignatedInitExpr* DIE);
bool VisitCXXConstructExpr(const clang::CXXConstructExpr* E);
bool VisitCXXDeleteExpr(const clang::CXXDeleteExpr* E);
bool VisitCXXNewExpr(const clang::CXXNewExpr* E);
bool VisitCXXPseudoDestructorExpr(const clang::CXXPseudoDestructorExpr* E);
bool VisitCXXUnresolvedConstructExpr(
const clang::CXXUnresolvedConstructExpr* E);
bool VisitCallExpr(const clang::CallExpr* Expr);
bool VisitMemberExpr(const clang::MemberExpr* Expr);
bool VisitCXXDependentScopeMemberExpr(
const clang::CXXDependentScopeMemberExpr* Expr);
bool TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc NNS);
// Visitors for leaf TypeLoc types.
bool VisitBuiltinTypeLoc(clang::BuiltinTypeLoc TL);
bool VisitEnumTypeLoc(clang::EnumTypeLoc TL);
bool VisitRecordTypeLoc(clang::RecordTypeLoc TL);
bool VisitTemplateTypeParmTypeLoc(clang::TemplateTypeParmTypeLoc TL);
bool VisitSubstTemplateTypeParmTypeLoc(
clang::SubstTemplateTypeParmTypeLoc TL);
bool VisitTemplateSpecializationTypeLoc(
clang::TemplateSpecializationTypeLoc TL);
// Handles AutoTypeLoc and DeducedTemplateSpecializationTypeLoc
bool VisitDeducedTypeLoc(clang::DeducedTypeLoc TL);
bool VisitDecltypeTypeLoc(clang::DecltypeTypeLoc TL);
bool VisitElaboratedTypeLoc(clang::ElaboratedTypeLoc TL);
bool VisitTypedefTypeLoc(clang::TypedefTypeLoc TL);
bool VisitInjectedClassNameTypeLoc(clang::InjectedClassNameTypeLoc TL);
bool VisitDependentNameTypeLoc(clang::DependentNameTypeLoc TL);
bool VisitPackExpansionTypeLoc(clang::PackExpansionTypeLoc TL);
bool VisitObjCObjectTypeLoc(clang::ObjCObjectTypeLoc TL);
bool VisitObjCTypeParamTypeLoc(clang::ObjCTypeParamTypeLoc TL);
bool TraverseAttributedTypeLoc(clang::AttributedTypeLoc TL);
bool TraverseDependentAddressSpaceTypeLoc(
clang::DependentAddressSpaceTypeLoc TL);
bool TraverseMemberPointerTypeLoc(clang::MemberPointerTypeLoc TL);
// Emit edges for an anchor pointing to the indicated type.
NodeSet RecordTypeLocSpellingLocation(clang::TypeLoc TL);
bool TraverseDeclarationNameInfo(clang::DeclarationNameInfo NameInfo);
// Visit the subtypes of TypedefNameDecl individually because we want to do
// something different with ObjCTypeParamDecl.
bool VisitTypedefDecl(const clang::TypedefDecl* Decl);
bool VisitTypeAliasDecl(const clang::TypeAliasDecl* Decl);
bool VisitObjCTypeParamDecl(const clang::ObjCTypeParamDecl* Decl);
bool VisitUsingShadowDecl(const clang::UsingShadowDecl* Decl);
bool VisitRecordDecl(const clang::RecordDecl* Decl);
bool VisitEnumDecl(const clang::EnumDecl* Decl);
bool VisitEnumConstantDecl(const clang::EnumConstantDecl* Decl);
bool VisitFunctionDecl(clang::FunctionDecl* Decl);
bool TraverseDecl(clang::Decl* Decl);
bool TraverseConstructorInitializer(clang::CXXCtorInitializer* Init);
bool TraverseCXXNewExpr(clang::CXXNewExpr* E);
bool TraverseCXXFunctionalCastExpr(clang::CXXFunctionalCastExpr* E);
bool IndexConstructExpr(const clang::CXXConstructExpr* E,
const clang::TypeSourceInfo* TSI);
// Objective C specific nodes
bool VisitObjCPropertyImplDecl(const clang::ObjCPropertyImplDecl* Decl);
bool VisitObjCCompatibleAliasDecl(const clang::ObjCCompatibleAliasDecl* Decl);
bool VisitObjCCategoryDecl(const clang::ObjCCategoryDecl* Decl);
bool VisitObjCImplementationDecl(
const clang::ObjCImplementationDecl* ImplDecl);
bool VisitObjCCategoryImplDecl(const clang::ObjCCategoryImplDecl* ImplDecl);
bool VisitObjCInterfaceDecl(const clang::ObjCInterfaceDecl* Decl);
bool VisitObjCProtocolDecl(const clang::ObjCProtocolDecl* Decl);
bool VisitObjCMethodDecl(const clang::ObjCMethodDecl* Decl);
bool VisitObjCPropertyDecl(const clang::ObjCPropertyDecl* Decl);
bool VisitObjCIvarRefExpr(const clang::ObjCIvarRefExpr* IRE);
bool VisitObjCMessageExpr(const clang::ObjCMessageExpr* Expr);
bool VisitObjCPropertyRefExpr(const clang::ObjCPropertyRefExpr* Expr);
// TODO(salguarnieri) We could link something here (the square brackets?) to
// the setter and getter methods
// bool VisitObjCSubscriptRefExpr(const clang::ObjCSubscriptRefExpr *Expr);
// TODO(salguarnieri) delete this comment block when we have more objective-c
// support implemented.
//
// Visitors that are left to their default behavior because we do not need
// to take any action while visiting them.
// bool VisitObjCDictionaryLiteral(const clang::ObjCDictionaryLiteral *D);
// bool VisitObjCArrayLiteral(const clang::ObjCArrayLiteral *D);
// bool VisitObjCBoolLiteralExpr(const clang::ObjCBoolLiteralExpr *D);
// bool VisitObjCStringLiteral(const clang::ObjCStringLiteral *D);
// bool VisitObjCEncodeExpr(const clang::ObjCEncodeExpr *Expr);
// bool VisitObjCBoxedExpr(const clang::ObjCBoxedExpr *Expr);
// bool VisitObjCSelectorExpr(const clang::ObjCSelectorExpr *Expr);
// bool VisitObjCIndirectCopyRestoreExpr(
// const clang::ObjCIndirectCopyRestoreExpr *Expr);
// bool VisitObjCIsaExpr(const clang::ObjCIsaExpr *Expr);
//
// We visit the subclasses of ObjCContainerDecl so there is nothing to do.
// bool VisitObjCContainerDecl(const clang::ObjCContainerDecl *D);
//
// We visit the subclasses of ObjCImpleDecl so there is nothing to do.
// bool VisitObjCImplDecl(const clang::ObjCImplDecl *D);
//
// There is nothing specific we need to do for blocks. The recursive visitor
// will visit the components of them correctly.
// bool VisitBlockDecl(const clang::BlockDecl *Decl);
// bool VisitBlockExpr(const clang::BlockExpr *Expr);
/// \brief For functions that support it, controls the emission of range
/// information.
enum class EmitRanges {
No, ///< Don't emit range information.
Yes ///< Emit range information when it's available.
};
// Objective C methods don't have TypeSourceInfo so we must construct a type
// for the methods to be used in the graph.
absl::optional<GraphObserver::NodeId> CreateObjCMethodTypeNode(
const clang::ObjCMethodDecl* MD, EmitRanges ER);
/// \brief Builds a stable node ID for a compile-time expression.
/// \param Expr The expression to represent.
/// \param ER Whether to notify the `GraphObserver` about source text
/// ranges for expressions.
absl::optional<GraphObserver::NodeId> BuildNodeIdForExpr(
const clang::Expr* Expr, EmitRanges ER);
/// \brief Builds a stable node ID for a special template argument.
/// \param Id A string representing the special argument.
GraphObserver::NodeId BuildNodeIdForSpecialTemplateArgument(
llvm::StringRef Id);
/// \brief Builds a stable node ID for a template expansion template argument.
/// \param Name The template pattern being expanded.
absl::optional<GraphObserver::NodeId> BuildNodeIdForTemplateExpansion(
clang::TemplateName Name);
/// \brief Builds a stable NodeSet for the given TypeLoc.
/// \param TL The TypeLoc for which to build a NodeSet.
/// \returns NodeSet instance indicating claimability of the contained
/// NodeIds.
NodeSet BuildNodeSetForType(const clang::TypeLoc& TL);
NodeSet BuildNodeSetForType(const clang::QualType& QT);
NodeSet BuildNodeSetForBuiltin(clang::BuiltinTypeLoc TL) const;
NodeSet BuildNodeSetForEnum(clang::EnumTypeLoc TL);
NodeSet BuildNodeSetForRecord(clang::RecordTypeLoc TL);
NodeSet BuildNodeSetForInjectedClassName(clang::InjectedClassNameTypeLoc TL);
NodeSet BuildNodeSetForTemplateTypeParm(clang::TemplateTypeParmTypeLoc TL);
NodeSet BuildNodeSetForPointer(clang::PointerTypeLoc TL);
NodeSet BuildNodeSetForMemberPointer(clang::MemberPointerTypeLoc TL);
NodeSet BuildNodeSetForLValueReference(clang::LValueReferenceTypeLoc TL);
NodeSet BuildNodeSetForRValueReference(clang::RValueReferenceTypeLoc TL);
NodeSet BuildNodeSetForAuto(clang::AutoTypeLoc TL);
NodeSet BuildNodeSetForDeducedTemplateSpecialization(
clang::DeducedTemplateSpecializationTypeLoc TL);
NodeSet BuildNodeSetForQualified(clang::QualifiedTypeLoc TL);
NodeSet BuildNodeSetForConstantArray(clang::ConstantArrayTypeLoc TL);
NodeSet BuildNodeSetForIncompleteArray(clang::IncompleteArrayTypeLoc TL);
NodeSet BuildNodeSetForDependentSizedArray(
clang::DependentSizedArrayTypeLoc TL);
NodeSet BuildNodeSetForFunctionProto(clang::FunctionProtoTypeLoc TL);
NodeSet BuildNodeSetForFunctionNoProto(clang::FunctionNoProtoTypeLoc TL);
NodeSet BuildNodeSetForParen(clang::ParenTypeLoc TL);
NodeSet BuildNodeSetForDecltype(clang::DecltypeTypeLoc TL);
NodeSet BuildNodeSetForElaborated(clang::ElaboratedTypeLoc TL);
NodeSet BuildNodeSetForTypedef(clang::TypedefTypeLoc TL);
NodeSet BuildNodeSetForSubstTemplateTypeParm(
clang::SubstTemplateTypeParmTypeLoc TL);
NodeSet BuildNodeSetForDependentName(clang::DependentNameTypeLoc TL);
NodeSet BuildNodeSetForTemplateSpecialization(
clang::TemplateSpecializationTypeLoc TL);
NodeSet BuildNodeSetForPackExpansion(clang::PackExpansionTypeLoc TL);
NodeSet BuildNodeSetForBlockPointer(clang::BlockPointerTypeLoc TL);
NodeSet BuildNodeSetForObjCObjectPointer(clang::ObjCObjectPointerTypeLoc TL);
NodeSet BuildNodeSetForObjCObject(clang::ObjCObjectTypeLoc TL);
NodeSet BuildNodeSetForObjCTypeParam(clang::ObjCTypeParamTypeLoc TL);
NodeSet BuildNodeSetForObjCInterface(clang::ObjCInterfaceTypeLoc TL);
NodeSet BuildNodeSetForAttributed(clang::AttributedTypeLoc TL);
NodeSet BuildNodeSetForDependentAddressSpace(
clang::DependentAddressSpaceTypeLoc TL);
// Helper used for Auto and DeducedTemplateSpecialization.
NodeSet BuildNodeSetForDeduced(clang::DeducedTypeLoc TL);
// Helper function which constructs marked source and records
// a tnominal node for the given `Decl`.
GraphObserver::NodeId BuildNominalNodeIdForDecl(const clang::NamedDecl* Decl);
// Helper used by BuildNodeSetForRecord and BuildNodeSetForInjectedClassName.
NodeSet BuildNodeSetForNonSpecializedRecordDecl(
const clang::RecordDecl* Decl);
const clang::TemplateTypeParmDecl* FindTemplateTypeParmTypeLocDecl(
clang::TemplateTypeParmTypeLoc TL) const;
absl::optional<GraphObserver::NodeId> BuildNodeIdForObjCProtocols(
clang::ObjCObjectTypeLoc TL);
GraphObserver::NodeId BuildNodeIdForObjCProtocols(
const clang::ObjCObjectType* T);
GraphObserver::NodeId BuildNodeIdForObjCProtocols(
GraphObserver::NodeId BaseType, const clang::ObjCObjectType* T);
/// \brief Builds a stable node ID for `Type`.
/// \param TypeLoc The type that is being identified.
/// \return The Node ID for `Type`.
absl::optional<GraphObserver::NodeId> BuildNodeIdForType(
const clang::TypeLoc& TypeLoc);
/// \brief Builds a stable node ID for `QT`.
/// \param QT The type that is being identified.
/// \return The Node ID for `QT`.
///
/// This function will invent a `TypeLoc` with an invalid location.
absl::optional<GraphObserver::NodeId> BuildNodeIdForType(
const clang::QualType& QT);
/// \brief Builds a stable node ID for the given `TemplateName`.
absl::optional<GraphObserver::NodeId> BuildNodeIdForTemplateName(
const clang::TemplateName& Name);
/// \brief Builds a stable node ID for the given `TemplateArgument`.
absl::optional<GraphObserver::NodeId> BuildNodeIdForTemplateArgument(
const clang::TemplateArgumentLoc& Arg, EmitRanges EmitRanges);
/// \brief Builds a stable node ID for the given `TemplateArgument`.
absl::optional<GraphObserver::NodeId> BuildNodeIdForTemplateArgument(
const clang::TemplateArgument& Arg, clang::SourceLocation L);
/// \brief Builds a stable node ID for `Stmt`.
///
/// This mechanism for generating IDs should only be used in contexts where
/// identifying statements through source locations/wraith contexts is not
/// possible (e.g., in implicit code).
///
/// \param Decl The statement that is being identified
/// \return The node for `Stmt` if the statement was implicit; otherwise,
/// None.
absl::optional<GraphObserver::NodeId> BuildNodeIdForImplicitStmt(
const clang::Stmt* Stmt);
/// \brief Builds a stable node ID for `Decl`'s tapp if it's an implicit
/// template instantiation.
absl::optional<GraphObserver::NodeId>
BuildNodeIdForImplicitTemplateInstantiation(const clang::Decl* Decl);
/// \brief Builds a stable node ID for `Decl`'s tapp if it's an implicit
/// function template instantiation.
absl::optional<GraphObserver::NodeId>
BuildNodeIdForImplicitFunctionTemplateInstantiation(
const clang::FunctionDecl* FD);
/// \brief Builds a stable node ID for `Decl`'s tapp if it's an implicit
/// class template instantiation.
absl::optional<GraphObserver::NodeId>
BuildNodeIdForImplicitClassTemplateInstantiation(
const clang::ClassTemplateSpecializationDecl* CTSD);
/// \brief Builds a stable node ID for an external reference to `Decl`.
///
/// This is equivalent to BuildNodeIdForDecl for Decls that are not
/// implicit template instantiations; otherwise, it returns the `NodeId`
/// for the tapp node for the instantiation.
///
/// \param Decl The declaration that is being identified.
/// \return The node for `Decl`.
GraphObserver::NodeId BuildNodeIdForRefToDecl(const clang::Decl* Decl);
/// \brief Builds a stable node ID for `Decl`.
///
/// \param Decl The declaration that is being identified.
/// \return The node for `Decl`.
GraphObserver::NodeId BuildNodeIdForDecl(const clang::Decl* Decl);
/// \brief Builds a stable node ID for the definition of `Decl`, if
/// `Decl` is not already a definition and its definition can be found.
///
/// \param Decl The declaration that is being identified.
/// \return The node for the definition `Decl` if `Decl` isn't a definition
/// and its definition can be found; or None.
template <typename D>
absl::optional<GraphObserver::NodeId> BuildNodeIdForDefnOfDecl(
const D* Decl) {
if (const auto* Defn = Decl->getDefinition()) {
if (Defn != Decl) {
return BuildNodeIdForDecl(Defn);
}
}
return absl::nullopt;
}
/// \brief Builds a stable node ID for `TND`.
///
/// \param Decl The declaration that is being identified.
absl::optional<GraphObserver::NodeId> BuildNodeIdForTypedefNameDecl(
const clang::TypedefNameDecl* Decl);
/// \brief Builds a stable node ID for `Decl`.
///
/// There is not a one-to-one correspondence between `Decl`s and nodes.
/// Certain `Decl`s, like `TemplateTemplateParmDecl`, are split into a
/// node representing the parameter and a node representing the kind of
/// the abstraction. The primary node is returned by the normal
/// `BuildNodeIdForDecl` function.
///
/// \param Decl The declaration that is being identified.
/// \param Index The index of the sub-id to generate.
///
/// \return A stable node ID for `Decl`'s `Index`th subnode.
GraphObserver::NodeId BuildNodeIdForDecl(const clang::Decl* Decl,
unsigned Index);
/// \brief Categorizes the name of `Decl` according to the equivalence classes
/// defined by `GraphObserver::NameId::NameEqClass`.
GraphObserver::NameId::NameEqClass BuildNameEqClassForDecl(
const clang::Decl* Decl) const;
/// \brief Builds a stable name ID for the name of `Decl`.
///
/// \param Decl The declaration that is being named.
/// \return The name for `Decl`.
GraphObserver::NameId BuildNameIdForDecl(const clang::Decl* Decl);
/// \brief Records parameter and lookup edges for the given dependent name.
///
/// \param NNS The qualifier on the name.
/// \param Id The name itself.
/// \param IdLoc The name's location.
/// \param Root If provided, the primary NodeId is morally prepended to `NNS`
/// such that the dependent name is lookup(lookup*(Root, NNS), Id).
/// \return The NodeId for the dependent name.
absl::optional<GraphObserver::NodeId> RecordEdgesForDependentName(
const clang::NestedNameSpecifierLoc& NNS,
const clang::DeclarationName& Id, const clang::SourceLocation IdLoc,
const absl::optional<GraphObserver::NodeId>& Root = absl::nullopt);
/// \brief Records parameter edges for the given dependent name.
/// Also records Lookup edges for any nested Identifiers.
/// \param DId The NodeId of the dependent name to use.
/// \param NNSLoc The qualifier prefix of the dependent name, if any.
/// \param Root If provided, the primary NodeId to prepend to `NNS`.
/// \return The provided NodeId or nullopt if an unhandled element is
/// encountered.
absl::optional<GraphObserver::NodeId> RecordParamEdgesForDependentName(
const GraphObserver::NodeId& DId, clang::NestedNameSpecifierLoc NNSLoc,
const absl::optional<GraphObserver::NodeId>& Root = absl::nullopt);
/// \brief Records the lookup edge for a dependent name.
///
/// \param DId The NodeId of the name being looked up.
/// \param Name The kind of name being looked up.
/// \return The provided NodeId or absl::nullopt if Name is unsupported.
absl::optional<GraphObserver::NodeId> RecordLookupEdgeForDependentName(
const GraphObserver::NodeId& DId, const clang::DeclarationName& Name);
/// \brief Builds a NodeId for the DependentName at IdLoc.
///
/// \param NNSLoc The qualifier preceding the name.
/// \param IdLoc The starting location of the name itself.
GraphObserver::NodeId BuildNodeIdForDependentLoc(
const clang::NestedNameSpecifierLoc& NNSLoc,
const clang::SourceLocation& IdLoc);
/// \brief Builds a NodeId for the DependentName at IdRange.
///
/// \param NNSLoc The qualifier preceding the name.
/// \param IdRange The source range of the name itself.
GraphObserver::NodeId BuildNodeIdForDependentRange(
const clang::NestedNameSpecifierLoc& NNSLoc,
const clang::SourceRange& IdRange);
/// \brief Builds a NodeId for the provided NestedNameSpecifier, depending on
/// its type.
///
/// \param NNSLoc The NestedNameSpecifierLoc from which to construct a NodeId.
absl::optional<GraphObserver::NodeId> BuildNodeIdForNestedNameSpecifierLoc(
const clang::NestedNameSpecifierLoc& NNSLoc);
/// \brief Is `VarDecl` a definition?
///
/// A parameter declaration is considered a definition if it appears as part
/// of a function definition; otherwise it is always a declaration. This
/// differs from the C++ Standard, which treats these parameters as
/// definitions (basic.scope.proto).
static bool IsDefinition(const clang::VarDecl* VD);
/// \brief Is `FunctionDecl` a definition?
static bool IsDefinition(const clang::FunctionDecl* FunctionDecl);
/// \brief Gets a range for the name of a declaration, whether that name is a
/// single token or otherwise.
///
/// The returned range is a best-effort attempt to cover the "name" of
/// the entity as written in the source code.
clang::SourceRange RangeForNameOfDeclaration(
const clang::NamedDecl* Decl) const;
/// \brief Gets a suitable range for an AST entity from the `start_location`.
clang::SourceRange RangeForASTEntity(
clang::SourceLocation start_location) const;
clang::SourceRange RangeForSingleToken(
clang::SourceLocation start_location) const;
/// Consume a token of the `ExpectedKind` from the `StartLocation`,
/// returning the range for that token on success and an invalid
/// range otherwise.
///
/// The begin location for the returned range may be different than
/// StartLocation. For example, this can happen if StartLocation points to
/// whitespace before the start of the token.
clang::SourceRange ConsumeToken(clang::SourceLocation StartLocation,
clang::tok::TokenKind ExpectedKind) const;
bool TraverseClassTemplateDecl(clang::ClassTemplateDecl* TD);
bool TraverseClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl* TD);
bool TraverseClassTemplatePartialSpecializationDecl(
clang::ClassTemplatePartialSpecializationDecl* TD);
bool TraverseVarTemplateDecl(clang::VarTemplateDecl* TD);
bool TraverseVarTemplateSpecializationDecl(
clang::VarTemplateSpecializationDecl* TD);
bool ForceTraverseVarTemplateSpecializationDecl(
clang::VarTemplateSpecializationDecl* TD);
bool TraverseVarTemplatePartialSpecializationDecl(
clang::VarTemplatePartialSpecializationDecl* TD);
bool TraverseFunctionDecl(clang::FunctionDecl* FD);
bool TraverseFunctionTemplateDecl(clang::FunctionTemplateDecl* FTD);
bool TraverseTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl* TATD);
bool shouldVisitTemplateInstantiations() const {
return TemplateMode == BehaviorOnTemplates::VisitInstantiations;
}
bool shouldEmitObjCForwardClassDeclDocumentation() const {
return ObjCFwdDocs == BehaviorOnFwdDeclComments::Emit;
}
bool shouldEmitCppForwardDeclDocumentation() const {
return CppFwdDocs == BehaviorOnFwdDeclComments::Emit;
}
bool shouldVisitImplicitCode() const { return true; }
// Disables data recursion. We intercept Traverse* methods in the RAV, which
// are not triggered during data recursion.
bool shouldUseDataRecursionFor(clang::Stmt* S) const { return false; }
/// \brief Records the range of a definition. If the range cannot be placed
/// somewhere inside a source file, no record is made.
void MaybeRecordDefinitionRange(
const absl::optional<GraphObserver::Range>& R,
const GraphObserver::NodeId& Id,
const absl::optional<GraphObserver::NodeId>& DeclId);
/// \brief Returns the attached GraphObserver.
GraphObserver& getGraphObserver() { return Observer; }
/// \brief Returns the ASTContext.
const clang::ASTContext& getASTContext() { return Context; }
/// If `SR` is empty (getBegin() == getEnd()) and a valid file id, expands the
/// range. Otherwise, returns the input unmodified.
clang::SourceRange ExpandRangeIfEmptyFileID(const clang::SourceRange& SR);
// If `SR` is a valid macro id, attempt to map it to a file range,
// otherwise returns the input unmodified.
clang::SourceRange MapRangeToFileIfMacroID(const clang::SourceRange& SR);
/// Returns `SR` as a `Range` in this `RecursiveASTVisitor`'s current
/// RangeContext after expanding empty ranges and mapping macros to a file
/// location.
absl::optional<GraphObserver::Range> ExpandedFileRangeInCurrentContext(
const clang::SourceRange& SR);
/// Returns `SR` as a `Range` in this `RecursiveASTVisitor`'s current
/// RangeContext.
absl::optional<GraphObserver::Range> ExplicitRangeInCurrentContext(
const clang::SourceRange& SR);
/// Returns `SR` as a `Range` in this `RecursiveASTVisitor`'s current
/// RangeContext. If SR is in a macro, the returned Range will be mapped
/// to a file first. If the range would be zero-width, it will be expanded
/// via RangeForASTEntityFromSourceLocation.
absl::optional<GraphObserver::Range> ExpandedRangeInCurrentContext(
clang::SourceRange SR);
/// If `Implicit` is true, returns `Id` as an implicit Range; otherwise,
/// returns `SR` as a `Range` in this `RecursiveASTVisitor`'s current
/// RangeContext.
absl::optional<GraphObserver::Range> RangeInCurrentContext(
bool Implicit, const GraphObserver::NodeId& Id,
const clang::SourceRange& SR);
/// If `Id` is some NodeId, returns it as an implicit Range; otherwise,
/// returns `SR` as a `Range` in this `RecursiveASTVisitor`'s current
/// RangeContext.
absl::optional<GraphObserver::Range> RangeInCurrentContext(
const absl::optional<GraphObserver::NodeId>& Id,
const clang::SourceRange& SR);
void RunJob(std::unique_ptr<IndexJob> JobToRun) {
Job = std::move(JobToRun);
if (Job->IsDeclJob) {
TraverseDecl(Job->Decl);
} else {
// There is no declaration attached to a top-level file comment.
HandleFileLevelComments(Job->FileId, Job->FileNode);
}
}
const IndexJob& getCurrentJob() {
CHECK(Job != nullptr);
return *Job;
}
void Work(clang::Decl* InitialDecl,
std::unique_ptr<IndexerWorklist> NewWorklist) {
Worklist = std::move(NewWorklist);
Worklist->EnqueueJob(llvm::make_unique<IndexJob>(InitialDecl));
while (!ShouldStopIndexing() && Worklist->DoWork())
;
Observer.iterateOverClaimedFiles(
[this, InitialDecl](clang::FileID Id,
const GraphObserver::NodeId& FileNode) {
RunJob(llvm::make_unique<IndexJob>(InitialDecl, Id, FileNode));
return !ShouldStopIndexing();
});
Worklist.reset();
}
/// \brief Provides execute-only access to ShouldStopIndexing. Should be used
/// from the same thread that's walking the AST.
bool shouldStopIndexing() const { return ShouldStopIndexing(); }
/// Blames a call to `Callee` at `Range` on everything at the top of
/// `BlameStack` (or does nothing if there's nobody to blame).
void RecordCallEdges(const GraphObserver::Range& Range,
const GraphObserver::NodeId& Callee);
/// \return whether `range` should be considered to be implicit under the
/// current context.
GraphObserver::Implicit IsImplicit(const GraphObserver::Range& range);
private:
using Base = RecursiveASTVisitor;
friend class PruneCheck;
/// Whether we should stop on missing cases or continue on.
BehaviorOnUnimplemented IgnoreUnimplemented;
/// Should we visit template instantiations?
BehaviorOnTemplates TemplateMode;
/// Should we emit all data?
enum Verbosity Verbosity;
/// Should we emit documentation for forward class decls in ObjC?
BehaviorOnFwdDeclComments ObjCFwdDocs;
/// Should we emit documentation for forward decls in C++?
BehaviorOnFwdDeclComments CppFwdDocs;
NullGraphObserver NullObserver;
GraphObserver& Observer;
clang::ASTContext& Context;
/// \brief The result of calling into the lexer.
enum class LexerResult {
Failure, ///< The operation failed.
Success ///< The operation completed.
};
/// \brief Using the `Observer`'s preprocessor, relexes the token at the
/// specified location. Ignores whitespace.
/// \param StartLocation Where to begin lexing.
/// \param Token The token to overwrite.
/// \return `Failure` if there was a failure, `Success` on success.
LexerResult getRawToken(clang::SourceLocation StartLocation,
clang::Token& Token) const {
return Observer.getPreprocessor()->getRawToken(StartLocation, Token,
true /* ignoreWhiteSpace */)
? LexerResult::Failure
: LexerResult::Success;
}
/// \brief Handle the file-level comments for `Id` with node ID `FileId`.
void HandleFileLevelComments(clang::FileID Id,
const GraphObserver::NodeId& FileNode);
/// \brief Emit data for `Comment` that documents `DocumentedNode`, using
/// `DC` for lookups.
void VisitComment(const clang::RawComment* Comment,
const clang::DeclContext* DC,
const GraphObserver::NodeId& DocumentedNode);
/// \brief Emit data for attributes attached to `Decl`, whose `NodeId`
/// is `TargetNode`.
void VisitAttributes(const clang::Decl* Decl,
const GraphObserver::NodeId& TargetNode);
/// \brief Attempts to find the ID of the first parent of `Decl` for
/// attaching a `childof` relationship.
absl::optional<GraphObserver::NodeId> GetDeclChildOf(const clang::Decl* D);
/// \brief Attempts to add some representation of `ND` to `Ostream`.
/// \return true on success; false on failure.
bool AddNameToStream(llvm::raw_string_ostream& Ostream,
const clang::NamedDecl* ND);
/// \brief Assign `ND` (whose node ID is `TargetNode`) a USR if USRs are
/// enabled.
///
/// USRs are added only for NamedDecls that:
/// * are not under implicit template instantiations
/// * are not in a DeclContext inside a function body
/// * can actually be assigned USRs from Clang
///
/// Similar to the way we deal with JVM names, the corpus, path,
/// and root fields of a usr vname are cleared. Clients are permitted
/// to write their own USR tickets. The USR value itself is encoded
/// in capital hex (to match Clang's own internal USR stringification,
/// modulo the configurable size of the SHA1 prefix).
void AssignUSR(const GraphObserver::NodeId& TargetNode,
const clang::NamedDecl* ND);
/// Assigns a USR to an alias.
void AssignUSR(const GraphObserver::NameId& TargetName,
const GraphObserver::NodeId& AliasedType,
const clang::NamedDecl* ND) {
AssignUSR(Observer.nodeIdForTypeAliasNode(TargetName, AliasedType), ND);
}
GraphObserver::NodeId ApplyBuiltinTypeConstructor(
const char* BuiltinName, const GraphObserver::NodeId& Param);
/// \brief Ascribes a type to `AscribeTo`.
/// \param Type The `TypeLoc` referring to the type
/// \param DType A possibly deduced type (or simply Type->getType()).
/// \param AscribeTo The node to which the type should be ascribed.
///
/// `auto` does not update TypeSourceInfo records after deduction, so
/// a deduced `auto` in the source text will appear to be undeduced.
/// In this case, it's useful to query the object being ascribed for its
/// unlocated QualType, as this does get updated.
void AscribeSpelledType(const clang::TypeLoc& Type,
const clang::QualType& TrueType,
const GraphObserver::NodeId& AscribeTo);
/// \brief Returns the parent of the given node, along with the index
/// at which the node appears underneath each parent.
///
/// Note that this will lazily compute the parents of all nodes
/// and store them for later retrieval. Thus, the first call is O(n)
/// in the number of AST nodes.
///
/// Caveats and FIXMEs:
/// Calculating the parent map over all AST nodes will need to load the
/// full AST. This can be undesirable in the case where the full AST is
/// expensive to create (for example, when using precompiled header
/// preambles). Thus, there are good opportunities for optimization here.
/// One idea is to walk the given node downwards, looking for references
/// to declaration contexts - once a declaration context is found, compute
/// the parent map for the declaration context; if that can satisfy the
/// request, loading the whole AST can be avoided. Note that this is made
/// more complex by statements in templates having multiple parents - those
/// problems can be solved by building closure over the templated parts of
/// the AST, which also avoids touching large parts of the AST.
/// Additionally, we will want to add an interface to already give a hint
/// where to search for the parents, for example when looking at a statement
/// inside a certain function.
///
/// 'NodeT' can be one of Decl, Stmt, Type, TypeLoc,
/// NestedNameSpecifier or NestedNameSpecifierLoc.
template <typename NodeT>
const IndexedParent* getIndexedParent(const NodeT& Node) {
return getIndexedParent(clang::ast_type_traits::DynTypedNode::create(Node));
}
/// \return true if `Decl` and all of the nodes underneath it are prunable.
///
/// A subtree is prunable if it's "the same" in all possible indexer runs.
/// This excludes, for example, certain template instantiations.
bool declDominatesPrunableSubtree(const clang::Decl* Decl);
const IndexedParent* getIndexedParent(
const clang::ast_type_traits::DynTypedNode& Node);
/// Initializes AllParents, if necessary, and then returns a pointer to it.
const IndexedParentMap* getAllParents();
/// A map from memoizable DynTypedNodes to their parent nodes
/// and their child indices with respect to those parents.
/// Filled on the first call to `getIndexedParents`.
std::unique_ptr<IndexedParentMap> AllParents;
/// Records information about the template `Template` wrapping the node
/// `BodyId`, including the edge linking the template and its body. Returns
/// the `NodeId` for the dominating template.
template <typename TemplateDeclish>
GraphObserver::NodeId RecordTemplate(
const TemplateDeclish* Decl, const GraphObserver::NodeId& BodyDeclNode);
/// Records information about the generic class by wrapping the node
/// `BodyId`. Returns the `NodeId` for the dominating generic type.
GraphObserver::NodeId RecordGenericClass(
const clang::ObjCInterfaceDecl* IDecl,
const clang::ObjCTypeParamList* TPL, const GraphObserver::NodeId& BodyId);
/// \brief Returns a vector of NodeId for each template argument.
absl::optional<std::vector<GraphObserver::NodeId>> BuildTemplateArgumentList(
llvm::ArrayRef<clang::TemplateArgument> Args);
absl::optional<std::vector<GraphObserver::NodeId>> BuildTemplateArgumentList(
llvm::ArrayRef<clang::TemplateArgumentLoc> Args);
/// Dumps information about `TypeContext` to standard error when looking for
/// an entry at (`Depth`, `Index`).
void DumpTypeContext(unsigned Depth, unsigned Index);
/// \brief Attempts to add a childof edge between DeclNode and its parent.
/// \param Decl The (outer, in the case of a template) decl.
/// \param DeclNode The (outer) `NodeId` for `Decl`.
void AddChildOfEdgeToDeclContext(const clang::Decl* Decl,
const GraphObserver::NodeId& DeclNode);
/// Points at the inner node of the DeclContext, if it's a template.
/// Otherwise points at the DeclContext as a Decl.
absl::optional<GraphObserver::NodeId> BuildNodeIdForDeclContext(
const clang::DeclContext* DC);
/// Points at the tapp node for a DeclContext, if it's an implicit template
/// instantiation. Otherwise behaves as `BuildNodeIdForDeclContext`.
absl::optional<GraphObserver::NodeId> BuildNodeIdForRefToDeclContext(
const clang::DeclContext* DC);
/// Avoid regenerating type node IDs and keep track of where we are while
/// generating node IDs for recursive types. The key is opaque and
/// makes sense only within the implementation of this class.
TypeMap<NodeSet> TypeNodes;
/// \brief Visit an Expr that refers to some NamedDecl.
///
/// DeclRefExpr and ObjCIvarRefExpr are similar entities and can be processed
/// in the same way but do not have a useful common ancestry.
///
/// \param IsInit set to true if this is an initializing reference.
bool VisitDeclRefOrIvarRefExpr(const clang::Expr* Expr,
const clang::NamedDecl* const FoundDecl,
clang::SourceLocation SL,
bool IsImplicit = false, bool IsInit = false);
/// \brief Connect a NodeId to the super and implemented protocols for a
/// ObjCInterfaceDecl.
///
/// Helper method used to connect an interface to the super and protocols it
/// implements. It is also used to connect the interface implementation to
/// these nodes as well. In that case, the interface implementation node is
/// passed in as the first argument and the interface decl is passed in as the
/// second.
///
/// \param BodyDeclNode The node to connect to the super and protocols for
/// the interface. This may be a interface decl node or an interface impl
/// node.
/// \param IFace The interface decl to use to look up the super and
//// protocols.
void ConnectToSuperClassAndProtocols(const GraphObserver::NodeId BodyDeclNode,
const clang::ObjCInterfaceDecl* IFace);
void ConnectToProtocols(const GraphObserver::NodeId BodyDeclNode,
clang::ObjCProtocolList::loc_iterator locStart,
clang::ObjCProtocolList::loc_iterator locEnd,
clang::ObjCProtocolList::iterator itStart,
clang::ObjCProtocolList::iterator itEnd);
/// \brief Connect a parameter to a function decl.
///
/// For FunctionDecl and ObjCMethodDecl, this connects the parameters to the
/// function/method decl.
/// \param Decl This should be a FunctionDecl or ObjCMethodDecl.
void ConnectParam(const clang::Decl* Decl,
const GraphObserver::NodeId& FuncNode,
bool IsFunctionDefinition, const unsigned int ParamNumber,
const clang::ParmVarDecl* Param, bool DeclIsImplicit);
/// \brief Draw the completes edge from a Decl to each of its redecls.
void RecordCompletesForRedecls(const clang::Decl* Decl,
const clang::SourceRange& NameRange,
const GraphObserver::NodeId& DeclNode);
/// \brief Draw an extends/category edge from the category to the class the
/// category is extending.
///
/// For example, @interface A (Cat) ... We draw an extends edge from the
/// ObjCCategoryDecl for Cat to the ObjCInterfaceDecl for A.
///
/// \param DeclNode The node for the category (impl or decl).
/// \param IFace The class interface for class we are adding a category to.
void ConnectCategoryToBaseClass(const GraphObserver::NodeId& DeclNode,
const clang::ObjCInterfaceDecl* IFace);
void LogErrorWithASTDump(const std::string& msg,
const clang::Decl* Decl) const;
void LogErrorWithASTDump(const std::string& msg,
const clang::Expr* Expr) const;
/// \brief This is used to handle the visitation of a clang::TypedefDecl
/// or a clang::TypeAliasDecl.
bool VisitCTypedef(const clang::TypedefNameDecl* Decl);
/// \brief Find the implementation for `MD`. If `MD` is a definition, `MD` is
/// returned. Otherwise, the method tries to find the implementation by
/// looking through the interface and its implementation. If a method
/// implementation is found, it is returned otherwise `MD` is returned.
const clang::ObjCMethodDecl* FindMethodDefn(
const clang::ObjCMethodDecl* MD, const clang::ObjCInterfaceDecl* I);
void VisitObjCInterfaceDeclComment(
const clang::ObjCInterfaceDecl* Decl, const clang::RawComment* Comment,
const clang::DeclContext* DCxt,
absl::optional<GraphObserver::NodeId> DCID);
void VisitRecordDeclComment(const clang::RecordDecl* Decl,
const clang::RawComment* Comment,
const clang::DeclContext* DCxt,
absl::optional<GraphObserver::NodeId> DCID);
/// \brief Maps known Decls to their NodeIds.
llvm::DenseMap<const clang::Decl*, GraphObserver::NodeId> DeclToNodeId;
/// \brief Used for calculating semantic hashes.
SemanticHash Hash{
[this](const clang::Decl* Decl) {
return BuildNameIdForDecl(Decl).ToString();
},
// These enums are intentionally compatible.
static_cast<SemanticHash::OnUnimplemented>(IgnoreUnimplemented)};
/// \brief Enabled library-specific callbacks.
const LibrarySupports& Supports;
/// \brief The `Sema` instance to use.
clang::Sema& Sema;
/// \brief The cache to use to generate signatures.
MarkedSourceCache MarkedSources;
/// \return true if we should stop indexing.
std::function<bool()> ShouldStopIndexing;
/// \brief The active indexing job.
std::unique_ptr<IndexJob> Job;
/// \brief The controlling worklist.
std::unique_ptr<IndexerWorklist> Worklist;
/// \brief Comments we've already visited.
std::unordered_set<const clang::RawComment*> VisitedComments;
/// \brief The number of (raw) bytes to use to represent a USR. If 0,
/// no USRs will be recorded.
int UsrByteSize = 0;
};
/// \brief An `ASTConsumer` that passes events to a `GraphObserver`.
class IndexerASTConsumer : public clang::SemaConsumer {
public:
explicit IndexerASTConsumer(
GraphObserver* GO, BehaviorOnUnimplemented B, BehaviorOnTemplates T,
Verbosity V, BehaviorOnFwdDeclComments ObjC,
BehaviorOnFwdDeclComments Cpp, const LibrarySupports& S,
std::function<bool()> ShouldStopIndexing,
std::function<std::unique_ptr<IndexerWorklist>(IndexerASTVisitor*)>
CreateWorklist,
int UsrByteSize)
: Observer(GO),
IgnoreUnimplemented(B),
TemplateMode(T),
Verbosity(V),
ObjCFwdDocs(ObjC),
CppFwdDocs(Cpp),
Supports(S),
ShouldStopIndexing(std::move(ShouldStopIndexing)),
CreateWorklist(std::move(CreateWorklist)),
UsrByteSize(UsrByteSize) {}
void HandleTranslationUnit(clang::ASTContext& Context) override {
CHECK(Sema != nullptr);
IndexerASTVisitor Visitor(Context, IgnoreUnimplemented, TemplateMode,
Verbosity, ObjCFwdDocs, CppFwdDocs, Supports,
*Sema, ShouldStopIndexing, Observer, UsrByteSize);
{
ProfileBlock block(Observer->getProfilingCallback(), "traverse_tu");
Visitor.Work(Context.getTranslationUnitDecl(), CreateWorklist(&Visitor));
}
}
void InitializeSema(clang::Sema& S) override { Sema = &S; }
void ForgetSema() override { Sema = nullptr; }
private:
GraphObserver* const Observer;
/// Whether we should stop on missing cases or continue on.
BehaviorOnUnimplemented IgnoreUnimplemented;
/// Whether we should visit template instantiations.
BehaviorOnTemplates TemplateMode;
/// Whether we should emit all data.
enum Verbosity Verbosity;
/// Should we emit documentation for forward class decls in ObjC?
BehaviorOnFwdDeclComments ObjCFwdDocs;
/// Should we emit documentation for forward decls in C++?
BehaviorOnFwdDeclComments CppFwdDocs;
/// Which library supports are enabled.
const LibrarySupports& Supports;
/// The active Sema instance.
clang::Sema* Sema;
/// \return true if we should stop indexing.
std::function<bool()> ShouldStopIndexing;
/// \return a new worklist for the given visitor.
std::function<std::unique_ptr<IndexerWorklist>(IndexerASTVisitor*)>
CreateWorklist;
/// \brief The number of (raw) bytes to use to represent a USR. If 0,
/// no USRs will be recorded.
int UsrByteSize = 0;
};
} // namespace kythe
#endif // KYTHE_CXX_INDEXER_CXX_INDEXER_AST_HOOKS_H_