clang-tools  9.0.0
ExprSequence.cpp
Go to the documentation of this file.
1 //===---------- ExprSequence.cpp - clang-tidy -----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ExprSequence.h"
10 
11 namespace clang {
12 namespace tidy {
13 namespace utils {
14 
15 // Returns the Stmt nodes that are parents of 'S', skipping any potential
16 // intermediate non-Stmt nodes.
17 //
18 // In almost all cases, this function returns a single parent or no parents at
19 // all.
20 //
21 // The case that a Stmt has multiple parents is rare but does actually occur in
22 // the parts of the AST that we're interested in. Specifically, InitListExpr
23 // nodes cause ASTContext::getParent() to return multiple parents for certain
24 // nodes in their subtree because RecursiveASTVisitor visits both the syntactic
25 // and semantic forms of InitListExpr, and the parent-child relationships are
26 // different between the two forms.
27 static SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,
28  ASTContext *Context) {
29  SmallVector<const Stmt *, 1> Result;
30 
31  ASTContext::DynTypedNodeList Parents = Context->getParents(*S);
32 
33  SmallVector<ast_type_traits::DynTypedNode, 1> NodesToProcess(Parents.begin(),
34  Parents.end());
35 
36  while (!NodesToProcess.empty()) {
37  ast_type_traits::DynTypedNode Node = NodesToProcess.back();
38  NodesToProcess.pop_back();
39 
40  if (const auto *S = Node.get<Stmt>()) {
41  Result.push_back(S);
42  } else {
43  Parents = Context->getParents(Node);
44  NodesToProcess.append(Parents.begin(), Parents.end());
45  }
46  }
47 
48  return Result;
49 }
50 
51 namespace {
52 bool isDescendantOrEqual(const Stmt *Descendant, const Stmt *Ancestor,
53  ASTContext *Context) {
54  if (Descendant == Ancestor)
55  return true;
56  for (const Stmt *Parent : getParentStmts(Descendant, Context)) {
57  if (isDescendantOrEqual(Parent, Ancestor, Context))
58  return true;
59  }
60 
61  return false;
62 }
63 }
64 
65 ExprSequence::ExprSequence(const CFG *TheCFG, const Stmt *Root,
66  ASTContext *TheContext)
67  : Context(TheContext), Root(Root) {
68  for (const auto &SyntheticStmt : TheCFG->synthetic_stmts()) {
69  SyntheticStmtSourceMap[SyntheticStmt.first] = SyntheticStmt.second;
70  }
71 }
72 
73 bool ExprSequence::inSequence(const Stmt *Before, const Stmt *After) const {
74  Before = resolveSyntheticStmt(Before);
75  After = resolveSyntheticStmt(After);
76 
77  // If 'After' is in the subtree of the siblings that follow 'Before' in the
78  // chain of successors, we know that 'After' is sequenced after 'Before'.
79  for (const Stmt *Successor = getSequenceSuccessor(Before); Successor;
80  Successor = getSequenceSuccessor(Successor)) {
81  if (isDescendantOrEqual(After, Successor, Context))
82  return true;
83  }
84 
85  // If 'After' is a parent of 'Before' or is sequenced after one of these
86  // parents, we know that it is sequenced after 'Before'.
87  for (const Stmt *Parent : getParentStmts(Before, Context)) {
88  if (Parent == After || inSequence(Parent, After))
89  return true;
90  }
91 
92  return false;
93 }
94 
95 bool ExprSequence::potentiallyAfter(const Stmt *After,
96  const Stmt *Before) const {
97  return !inSequence(After, Before);
98 }
99 
100 const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const {
101  for (const Stmt *Parent : getParentStmts(S, Context)) {
102  // If a statement has multiple parents, make sure we're using the parent
103  // that lies within the sub-tree under Root.
104  if (!isDescendantOrEqual(Parent, Root, Context))
105  continue;
106 
107  if (const auto *BO = dyn_cast<BinaryOperator>(Parent)) {
108  // Comma operator: Right-hand side is sequenced after the left-hand side.
109  if (BO->getLHS() == S && BO->getOpcode() == BO_Comma)
110  return BO->getRHS();
111  } else if (const auto *InitList = dyn_cast<InitListExpr>(Parent)) {
112  // Initializer list: Each initializer clause is sequenced after the
113  // clauses that precede it.
114  for (unsigned I = 1; I < InitList->getNumInits(); ++I) {
115  if (InitList->getInit(I - 1) == S)
116  return InitList->getInit(I);
117  }
118  } else if (const auto *Compound = dyn_cast<CompoundStmt>(Parent)) {
119  // Compound statement: Each sub-statement is sequenced after the
120  // statements that precede it.
121  const Stmt *Previous = nullptr;
122  for (const auto *Child : Compound->body()) {
123  if (Previous == S)
124  return Child;
125  Previous = Child;
126  }
127  } else if (const auto *TheDeclStmt = dyn_cast<DeclStmt>(Parent)) {
128  // Declaration: Every initializer expression is sequenced after the
129  // initializer expressions that precede it.
130  const Expr *PreviousInit = nullptr;
131  for (const Decl *TheDecl : TheDeclStmt->decls()) {
132  if (const auto *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
133  if (const Expr *Init = TheVarDecl->getInit()) {
134  if (PreviousInit == S)
135  return Init;
136  PreviousInit = Init;
137  }
138  }
139  }
140  } else if (const auto *ForRange = dyn_cast<CXXForRangeStmt>(Parent)) {
141  // Range-based for: Loop variable declaration is sequenced before the
142  // body. (We need this rule because these get placed in the same
143  // CFGBlock.)
144  if (S == ForRange->getLoopVarStmt())
145  return ForRange->getBody();
146  } else if (const auto *TheIfStmt = dyn_cast<IfStmt>(Parent)) {
147  // If statement:
148  // - Sequence init statement before variable declaration.
149  // - Sequence variable declaration (along with the expression used to
150  // initialize it) before the evaluation of the condition.
151  if (S == TheIfStmt->getInit())
152  return TheIfStmt->getConditionVariableDeclStmt();
153  if (S == TheIfStmt->getConditionVariableDeclStmt())
154  return TheIfStmt->getCond();
155  } else if (const auto *TheSwitchStmt = dyn_cast<SwitchStmt>(Parent)) {
156  // Ditto for switch statements.
157  if (S == TheSwitchStmt->getInit())
158  return TheSwitchStmt->getConditionVariableDeclStmt();
159  if (S == TheSwitchStmt->getConditionVariableDeclStmt())
160  return TheSwitchStmt->getCond();
161  } else if (const auto *TheWhileStmt = dyn_cast<WhileStmt>(Parent)) {
162  // While statement: Sequence variable declaration (along with the
163  // expression used to initialize it) before the evaluation of the
164  // condition.
165  if (S == TheWhileStmt->getConditionVariableDeclStmt())
166  return TheWhileStmt->getCond();
167  }
168  }
169 
170  return nullptr;
171 }
172 
173 const Stmt *ExprSequence::resolveSyntheticStmt(const Stmt *S) const {
174  if (SyntheticStmtSourceMap.count(S))
175  return SyntheticStmtSourceMap.lookup(S);
176  return S;
177 }
178 
179 StmtToBlockMap::StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext)
180  : Context(TheContext) {
181  for (const auto *B : *TheCFG) {
182  for (const auto &Elem : *B) {
183  if (Optional<CFGStmt> S = Elem.getAs<CFGStmt>())
184  Map[S->getStmt()] = B;
185  }
186  }
187 }
188 
189 const CFGBlock *StmtToBlockMap::blockContainingStmt(const Stmt *S) const {
190  while (!Map.count(S)) {
191  SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);
192  if (Parents.empty())
193  return nullptr;
194  S = Parents[0];
195  }
196 
197  return Map.lookup(S);
198 }
199 
200 } // namespace utils
201 } // namespace tidy
202 } // namespace clang
bool inSequence(const Stmt *Before, const Stmt *After) const
Returns whether Before is sequenced before After.
const CFGBlock * blockContainingStmt(const Stmt *S) const
Returns the block that S is contained in.
bool potentiallyAfter(const Stmt *After, const Stmt *Before) const
Returns whether After can potentially be evaluated after Before.
StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext)
Initializes the map for the given CFG.
ExprSequence(const CFG *TheCFG, const Stmt *Root, ASTContext *TheContext)
Initializes this ExprSequence with sequence information for the given CFG.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
static SmallVector< const Stmt *, 1 > getParentStmts(const Stmt *S, ASTContext *Context)