Commit 0a717d5b authored by Stephen Kelly's avatar Stephen Kelly
Browse files

Make it possible control matcher traversal kind with ASTContext

Summary:
This will eventually allow traversal of an AST while ignoring invisible
AST nodes.  Currently it depends on the available enum values for
TraversalKinds.  That can be extended to ignore all invisible nodes in
the future.

Reviewers: klimek, aaron.ballman

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D61837
parent b4f4e370
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -569,7 +569,17 @@ private:
  clang::PrintingPolicy PrintingPolicy;
  std::unique_ptr<interp::Context> InterpContext;

  ast_type_traits::TraversalKind Traversal = ast_type_traits::TK_AsIs;

public:
  ast_type_traits::TraversalKind getTraversalKind() const { return Traversal; }
  void setTraversalKind(ast_type_traits::TraversalKind TK) { Traversal = TK; }

  const Expr *traverseIgnored(const Expr *E) const;
  Expr *traverseIgnored(Expr *E) const;
  ast_type_traits::DynTypedNode
  traverseIgnored(const ast_type_traits::DynTypedNode &N) const;

  IdentifierTable &Idents;
  SelectorTable &Selectors;
  Builtin::Context &BuiltinInfo;
@@ -2996,7 +3006,7 @@ private:

  std::vector<Decl *> TraversalScope;
  class ParentMap;
  std::unique_ptr<ParentMap> Parents;
  std::map<ast_type_traits::TraversalKind, std::unique_ptr<ParentMap>> Parents;

  std::unique_ptr<VTableContextBase> VTContext;

@@ -3040,6 +3050,22 @@ inline Selector GetUnarySelector(StringRef name, ASTContext &Ctx) {
  return Ctx.Selectors.getSelector(1, &II);
}

class TraversalKindScope {
  ASTContext &Ctx;
  ast_type_traits::TraversalKind TK = ast_type_traits::TK_AsIs;

public:
  TraversalKindScope(ASTContext &Ctx,
                     llvm::Optional<ast_type_traits::TraversalKind> ScopeTK)
      : Ctx(Ctx) {
    TK = Ctx.getTraversalKind();
    if (ScopeTK)
      Ctx.setTraversalKind(*ScopeTK);
  }

  ~TraversalKindScope() { Ctx.setTraversalKind(TK); }
};

} // namespace clang

// operator new and delete aren't allowed inside namespaces.
+18 −1
Original line number Diff line number Diff line
@@ -65,6 +65,9 @@ class ASTNodeTraverser
  /// not already been loaded.
  bool Deserialize = false;

  ast_type_traits::TraversalKind Traversal =
      ast_type_traits::TraversalKind::TK_AsIs;

  NodeDelegateType &getNodeDelegate() {
    return getDerived().doGetNodeDelegate();
  }
@@ -74,6 +77,8 @@ public:
  void setDeserialize(bool D) { Deserialize = D; }
  bool getDeserialize() const { return Deserialize; }

  void SetTraversalKind(ast_type_traits::TraversalKind TK) { Traversal = TK; }

  void Visit(const Decl *D) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(D);
@@ -97,8 +102,20 @@ public:
    });
  }

  void Visit(const Stmt *S, StringRef Label = {}) {
  void Visit(const Stmt *Node, StringRef Label = {}) {
    getNodeDelegate().AddChild(Label, [=] {
      const Stmt *S = Node;

      if (auto *E = dyn_cast_or_null<Expr>(S)) {
        switch (Traversal) {
        case ast_type_traits::TK_AsIs:
          break;
        case ast_type_traits::TK_IgnoreImplicitCastsAndParentheses:
          S = E->IgnoreParenImpCasts();
          break;
        }
      }

      getNodeDelegate().Visit(S);

      if (!S) {
+25 −0
Original line number Diff line number Diff line
@@ -689,6 +689,31 @@ AST_POLYMORPHIC_MATCHER_P(
                             Builder);
}

/// Causes all nested matchers to be matched with the specified traversal kind.
///
/// Given
/// \code
///   void foo()
///   {
///       int i = 3.0;
///   }
/// \endcode
/// The matcher
/// \code
///   traverse(ast_type_traits::TK_IgnoreImplicitCastsAndParentheses,
///     varDecl(hasInitializer(floatLiteral().bind("init")))
///   )
/// \endcode
/// matches the variable declaration with "init" bound to the "3.0".
template <typename T>
internal::Matcher<T> traverse(ast_type_traits::TraversalKind TK,
                              const internal::Matcher<T> &InnerMatcher) {
  return internal::DynTypedMatcher::constructRestrictedWrapper(
             new internal::TraversalMatcher<T>(TK, InnerMatcher),
             InnerMatcher.getID().first)
      .template unconditionalConvertTo<T>();
}

/// Matches expressions that match InnerMatcher after any implicit AST
/// nodes are stripped off.
///
+36 −4
Original line number Diff line number Diff line
@@ -283,6 +283,10 @@ public:
  virtual bool dynMatches(const ast_type_traits::DynTypedNode &DynNode,
                          ASTMatchFinder *Finder,
                          BoundNodesTreeBuilder *Builder) const = 0;

  virtual llvm::Optional<ast_type_traits::TraversalKind> TraversalKind() const {
    return llvm::None;
  }
};

/// Generic interface for matchers on an AST node of type T.
@@ -371,6 +375,10 @@ public:
                    ast_type_traits::ASTNodeKind SupportedKind,
                    std::vector<DynTypedMatcher> InnerMatchers);

  static DynTypedMatcher
  constructRestrictedWrapper(const DynTypedMatcher &InnerMatcher,
                             ast_type_traits::ASTNodeKind RestrictKind);

  /// Get a "true" matcher for \p NodeKind.
  ///
  /// It only checks that the node is of the right kind.
@@ -1002,7 +1010,7 @@ public:
                  std::is_base_of<QualType, T>::value,
                  "unsupported type for recursive matching");
    return matchesChildOf(ast_type_traits::DynTypedNode::create(Node),
                          Matcher, Builder, Traverse, Bind);
                          getASTContext(), Matcher, Builder, Traverse, Bind);
  }

  template <typename T>
@@ -1018,7 +1026,7 @@ public:
                  std::is_base_of<QualType, T>::value,
                  "unsupported type for recursive matching");
    return matchesDescendantOf(ast_type_traits::DynTypedNode::create(Node),
                               Matcher, Builder, Bind);
                               getASTContext(), Matcher, Builder, Bind);
  }

  // FIXME: Implement support for BindKind.
@@ -1033,24 +1041,26 @@ public:
                      std::is_base_of<TypeLoc, T>::value,
                  "type not allowed for recursive matching");
    return matchesAncestorOf(ast_type_traits::DynTypedNode::create(Node),
                             Matcher, Builder, MatchMode);
                             getASTContext(), Matcher, Builder, MatchMode);
  }

  virtual ASTContext &getASTContext() const = 0;

protected:
  virtual bool matchesChildOf(const ast_type_traits::DynTypedNode &Node,
                              const DynTypedMatcher &Matcher,
                              ASTContext &Ctx, const DynTypedMatcher &Matcher,
                              BoundNodesTreeBuilder *Builder,
                              ast_type_traits::TraversalKind Traverse,
                              BindKind Bind) = 0;

  virtual bool matchesDescendantOf(const ast_type_traits::DynTypedNode &Node,
                                   ASTContext &Ctx,
                                   const DynTypedMatcher &Matcher,
                                   BoundNodesTreeBuilder *Builder,
                                   BindKind Bind) = 0;

  virtual bool matchesAncestorOf(const ast_type_traits::DynTypedNode &Node,
                                 ASTContext &Ctx,
                                 const DynTypedMatcher &Matcher,
                                 BoundNodesTreeBuilder *Builder,
                                 AncestorMatchMode MatchMode) = 0;
@@ -1162,6 +1172,28 @@ struct ArgumentAdaptingMatcherFunc {
  }
};

template <typename T>
class TraversalMatcher : public WrapperMatcherInterface<T> {
  ast_type_traits::TraversalKind Traversal;

public:
  explicit TraversalMatcher(ast_type_traits::TraversalKind TK,
                            const Matcher<T> &ChildMatcher)
      : TraversalMatcher::WrapperMatcherInterface(ChildMatcher), Traversal(TK) {
  }

  bool matches(const T &Node, ASTMatchFinder *Finder,
               BoundNodesTreeBuilder *Builder) const override {
    return this->InnerMatcher.matches(
        ast_type_traits::DynTypedNode::create(Node), Finder, Builder);
  }

  llvm::Optional<ast_type_traits::TraversalKind>
  TraversalKind() const override {
    return Traversal;
  }
};

/// A PolymorphicMatcherWithParamN<MatcherT, P1, ..., PN> object can be
/// created from N parameters p1, ..., pN (of type P1, ..., PN) and
/// used as a Matcher<T> where a MatcherT<T, P1, ..., PN>(p1, ..., pN)
+39 −9
Original line number Diff line number Diff line
@@ -99,6 +99,30 @@ using namespace clang;
enum FloatingRank {
  Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank
};
const Expr *ASTContext::traverseIgnored(const Expr *E) const {
  return traverseIgnored(const_cast<Expr *>(E));
}

Expr *ASTContext::traverseIgnored(Expr *E) const {
  if (!E)
    return nullptr;

  switch (Traversal) {
  case ast_type_traits::TK_AsIs:
    return E;
  case ast_type_traits::TK_IgnoreImplicitCastsAndParentheses:
    return E->IgnoreParenImpCasts();
  }
  llvm_unreachable("Invalid Traversal type!");
}

ast_type_traits::DynTypedNode
ASTContext::traverseIgnored(const ast_type_traits::DynTypedNode &N) const {
  if (const auto *E = N.get<Expr>()) {
    return ast_type_traits::DynTypedNode::create(*traverseIgnored(E));
  }
  return N;
}

/// \returns location that is relevant when searching for Doc comments related
/// to \p D.
@@ -959,7 +983,7 @@ public:

void ASTContext::setTraversalScope(const std::vector<Decl *> &TopLevelDecls) {
  TraversalScope = TopLevelDecls;
  Parents.reset();
  Parents.clear();
}

void ASTContext::AddDeallocation(void (*Callback)(void *), void *Data) const {
@@ -10397,7 +10421,8 @@ createDynTypedNode(const NestedNameSpecifierLoc &Node) {
class ASTContext::ParentMap::ASTVisitor
    : public RecursiveASTVisitor<ASTVisitor> {
public:
  ASTVisitor(ParentMap &Map) : Map(Map) {}
  ASTVisitor(ParentMap &Map, ASTContext &Context)
      : Map(Map), Context(Context) {}

private:
  friend class RecursiveASTVisitor<ASTVisitor>;
@@ -10467,8 +10492,11 @@ private:
  }

  bool TraverseStmt(Stmt *StmtNode) {
    return TraverseNode(
        StmtNode, StmtNode, [&] { return VisitorBase::TraverseStmt(StmtNode); },
    Stmt *FilteredNode = StmtNode;
    if (auto *ExprNode = dyn_cast_or_null<Expr>(FilteredNode))
      FilteredNode = Context.traverseIgnored(ExprNode);
    return TraverseNode(FilteredNode, FilteredNode,
                        [&] { return VisitorBase::TraverseStmt(FilteredNode); },
                        &Map.PointerParents);
  }

@@ -10487,20 +10515,22 @@ private:
  }

  ParentMap &Map;
  ASTContext &Context;
  llvm::SmallVector<ast_type_traits::DynTypedNode, 16> ParentStack;
};

ASTContext::ParentMap::ParentMap(ASTContext &Ctx) {
  ASTVisitor(*this).TraverseAST(Ctx);
  ASTVisitor(*this, Ctx).TraverseAST(Ctx);
}

ASTContext::DynTypedNodeList
ASTContext::getParents(const ast_type_traits::DynTypedNode &Node) {
  if (!Parents)
  std::unique_ptr<ParentMap> &P = Parents[Traversal];
  if (!P)
    // We build the parent map for the traversal scope (usually whole TU), as
    // hasAncestor can escape any subtree.
    Parents = std::make_unique<ParentMap>(*this);
  return Parents->getParents(Node);
    P = std::make_unique<ParentMap>(*this);
  return P->getParents(Node);
}

bool
Loading