aboutsummaryrefslogtreecommitdiff
path: root/clang/include/clang/AST/ParentMapContext.h
blob: a0412380a864e7a7e3b88ac9481b8a67d4032bc7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//===- ParentMapContext.h - Map of parents using DynTypedNode -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Similar to ParentMap.h, but generalizes to non-Stmt nodes, which can have
// multiple parents.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_PARENTMAPCONTEXT_H
#define LLVM_CLANG_AST_PARENTMAPCONTEXT_H

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"

namespace clang {
class DynTypedNodeList;

class ParentMapContext {
public:
  ParentMapContext(ASTContext &Ctx);

  ~ParentMapContext();

  /// Returns the parents of the given node (within the traversal scope).
  ///
  /// 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> DynTypedNodeList getParents(const NodeT &Node);

  DynTypedNodeList getParents(const DynTypedNode &Node);

  /// Clear parent maps.
  void clear();

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

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

private:
  ASTContext &ASTCtx;
  class ParentMap;
  TraversalKind Traversal = TK_AsIs;
  std::unique_ptr<ParentMap> Parents;
};

class TraversalKindScope {
  ParentMapContext &Ctx;
  TraversalKind TK = TK_AsIs;

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

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

/// Container for either a single DynTypedNode or for an ArrayRef to
/// DynTypedNode. For use with ParentMap.
class DynTypedNodeList {
  llvm::AlignedCharArrayUnion<DynTypedNode, ArrayRef<DynTypedNode>> Storage;
  bool IsSingleNode;

public:
  DynTypedNodeList(const DynTypedNode &N) : IsSingleNode(true) {
    new (&Storage) DynTypedNode(N);
  }

  DynTypedNodeList(ArrayRef<DynTypedNode> A) : IsSingleNode(false) {
    new (&Storage) ArrayRef<DynTypedNode>(A);
  }

  const DynTypedNode *begin() const {
    if (!IsSingleNode)
      return reinterpret_cast<const ArrayRef<DynTypedNode> *>(&Storage)
          ->begin();
    return reinterpret_cast<const DynTypedNode *>(&Storage);
  }

  const DynTypedNode *end() const {
    if (!IsSingleNode)
      return reinterpret_cast<const ArrayRef<DynTypedNode> *>(&Storage)->end();
    return reinterpret_cast<const DynTypedNode *>(&Storage) + 1;
  }

  size_t size() const { return end() - begin(); }
  bool empty() const { return begin() == end(); }

  const DynTypedNode &operator[](size_t N) const {
    assert(N < size() && "Out of bounds!");
    return *(begin() + N);
  }
};

template <typename NodeT>
inline DynTypedNodeList ParentMapContext::getParents(const NodeT &Node) {
  return getParents(DynTypedNode::create(Node));
}

template <typename NodeT>
inline DynTypedNodeList ASTContext::getParents(const NodeT &Node) {
  return getParentMapContext().getParents(Node);
}

template <>
inline DynTypedNodeList ASTContext::getParents(const DynTypedNode &Node) {
  return getParentMapContext().getParents(Node);
}

} // namespace clang

#endif