clang-tools 23.0.0git
StaticInitializationCycleCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/DynamicRecursiveASTVisitor.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Analysis/CallGraph.h"
14#include "llvm/ADT/DenseMap.h"
15#include "llvm/ADT/SCCIterator.h"
16
17using namespace clang;
18using namespace clang::ast_matchers;
19
20// Check if a reference to a static variable (that was reached while traversal
21// of a function declaration) should be ignored by the check. This returns true
22// if the value of the variable has no effect on the return value of the
23// function, or the reference is ignored for other reason to eliminate FP
24// results.
25// Ignore happens if the variable appears at LHS of an assignment or it appears
26// inside a compile-time constant expression (like 'sizeof').
27// Additional condition is if the reference appears in a not immediately called
28// lambda function.
29static bool shouldIgnoreRef(const DeclRefExpr *DRE, const Decl *ParentD) {
30 ASTContext &ACtx = ParentD->getASTContext();
31 ParentMapContext &PMC = ACtx.getParentMapContext();
32 DynTypedNodeList Parents = PMC.getParents(*DRE);
33 // While going upwards on the parent graph, this stores the last encountered
34 // lambda expression that did not appear (until now) as a callee of a
35 // 'operator ()'.
36 const LambdaExpr *ParentLambda = nullptr;
37 while (!Parents.empty()) {
38 if (Parents.size() > 1)
39 return true;
40 if (const Expr *E = Parents[0].get<Expr>()) {
41 if (!E->getType().isNull() && !E->isValueDependent() &&
42 E->isIntegerConstantExpr(ACtx))
43 return true;
44 if (const auto *ParentBO = dyn_cast<BinaryOperator>(E)) {
45 if (ParentBO->isAssignmentOp() &&
46 ParentBO->getLHS()->IgnoreParenCasts() == DRE)
47 return true;
48 } else if (const auto *LambdaE = dyn_cast<LambdaExpr>(E)) {
49 // Found another lambda while the last found do not appear to be called
50 // by '()'.
51 if (ParentLambda)
52 return true;
53 ParentLambda = LambdaE;
54 } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(E)) {
55 // Check if the last found lambda is called with this 'operator ()'.
56 if (ParentLambda &&
57 OpCallE->getOperator() == OverloadedOperatorKind::OO_Call &&
58 OpCallE->getCalleeDecl() == ParentLambda->getCallOperator())
59 ParentLambda = nullptr;
60 }
61 } else if (const Decl *D = Parents[0].get<Decl>()) {
62 // Check if we reached the root of the context (variable or function
63 // declaration) to check.
64 if ([D, ParentD]() {
65 if (const auto *ParentF = dyn_cast<FunctionDecl>(ParentD)) {
66 if (const auto *FD = dyn_cast<FunctionDecl>(D))
67 return FD == ParentF->getDefinition();
68 return false;
69 }
70 return D->getCanonicalDecl() == ParentD->getCanonicalDecl();
71 }())
72 return ParentLambda != nullptr;
73 }
74 Parents = PMC.getParents(Parents[0]);
75 }
76 llvm_unreachable("declaration of ParentD should be reached");
77 return false;
78}
79
80namespace {
81
82class VarUseNode;
83
84// Store the reference to a variable or the call location of a function.
85// 'Ref' is a DeclRefExpr or a CallExpr.
86// 'Node' contains information about corresponding VarDecl or FunctionDecl.
87struct VarUseRecord {
88 const Expr *Ref;
89 VarUseNode *Node;
90
91 VarUseRecord() = default;
92 VarUseRecord(const Expr *Ref, VarUseNode *N) : Ref(Ref), Node(N) {}
93 operator VarUseNode *() const { return Node; }
94};
95
96// One node in the variable usage graph.
97// If 'D' is a VarDecl:
98// 'Uses' contains all static variables and global function calls in the
99// initializer expression.
100// If 'D' is a FunctionDecl:
101// 'Uses' contains all static variable references and global function calls in
102// the function body.
103class VarUseNode {
104 const NamedDecl *D;
105 llvm::SmallVector<VarUseRecord, 2> Uses;
106
107public:
108 VarUseNode(const NamedDecl *D) : D(D) {}
109
110 const NamedDecl *getDecl() const { return D; }
111 bool isVar() const { return isa<VarDecl>(D); }
112 bool isFunction() const { return isa<FunctionDecl>(D); }
113 const VarDecl *getVar() const { return cast<VarDecl>(D); }
114 const FunctionDecl *getFunction() const { return cast<FunctionDecl>(D); }
115
116 using const_iterator = llvm::SmallVectorImpl<VarUseRecord>::const_iterator;
117
118 const_iterator begin() const { return Uses.begin(); }
119 const_iterator end() const { return Uses.end(); }
120
121 llvm::iterator_range<const_iterator> uses() const {
122 return llvm::make_range(begin(), end());
123 }
124
125 bool empty() const { return Uses.empty(); }
126 unsigned size() const { return Uses.size(); }
127
128 friend class VarUseCollector;
129 friend class VarUseGraphBuilder;
130 friend class VarUseGraph;
131};
132
133// "Variable usage graph":
134// Stores dependencies of variables from other variables or function calls,
135// and dependencies of function results from variables or functions.
136// Only static variables (static member, static local variable, or global
137// variable) and global or static functions are stored.
138// Stored are the canonical declarations of variables and definitions of
139// functions.
140class VarUseGraph {
141 using UseMapTy = llvm::DenseMap<const Decl *, std::unique_ptr<VarUseNode>>;
142
143 UseMapTy UseMap;
144
145public:
146 VarUseGraph() {
147 // A special "root" is added at nullptr location.
148 // It contains edges to all other nodes, without a "Ref" expression.
149 // This is used by the SCC algorithm.
150 UseMap[nullptr] = std::make_unique<VarUseNode>(nullptr);
151 }
152
153 VarUseNode *addNode(const NamedDecl *D) {
154 std::unique_ptr<VarUseNode> &N = UseMap[D];
155 if (N)
156 return N.get();
157 N = std::make_unique<VarUseNode>(D);
158 UseMap[nullptr]->Uses.emplace_back(nullptr, N.get());
159 return N.get();
160 }
161
162 using const_iterator = UseMapTy::const_iterator;
163
164 const_iterator begin() const { return UseMap.begin(); }
165 const_iterator end() const { return UseMap.end(); }
166
167 unsigned size() const { return UseMap.size(); }
168
169 VarUseNode *getRoot() { return UseMap[nullptr].get(); }
170
171 friend class VarUseGraphBuilder;
172};
173
174// Collect static variable references and static function calls.
175// This is used with initializer expressions and function body statements.
176// At initializer expressions only statements (and expressions) should be
177// traversed. But for functions declarations are needed too (to reach
178// initializations of variables) (only inside the given function).
179class VarUseCollector : public DynamicRecursiveASTVisitor {
180 VarUseNode *Node;
181 VarUseGraph &G;
182 const DeclContext *DC;
183
184public:
185 VarUseCollector(VarUseNode *N, VarUseGraph &G)
186 : Node(N), G(G), DC(N->isFunction() ? N->getFunction() : nullptr) {}
187
188 bool TraverseType(QualType T, bool TraverseQualifier) override {
189 return true;
190 }
191 bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier) override {
192 return true;
193 }
194 bool TraverseAttr(Attr *At) override { return true; }
195 bool TraverseDecl(Decl *D) override {
196 if (D && DC && DC->containsDecl(D))
197 return DynamicRecursiveASTVisitor::TraverseDecl(D);
198 return true;
199 }
200
201 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
202 if (const auto *VarD = dyn_cast<VarDecl>(DRE->getDecl())) {
203 if (!shouldIgnoreRef(DRE, Node->getDecl()) &&
204 (VarD->hasGlobalStorage() || VarD->isStaticLocal()))
205 Node->Uses.emplace_back(DRE, G.addNode(VarD->getCanonicalDecl()));
206 }
207 return true;
208 }
209
210 bool VisitCallExpr(CallExpr *CE) override {
211 if (const FunctionDecl *F = CE->getDirectCallee()) {
212 if (F->isGlobal() || F->isStatic()) {
213 const FunctionDecl *Def = F->getDefinition();
214 if (Def)
215 Node->Uses.emplace_back(CE, G.addNode(Def));
216 }
217 }
218 return true;
219 }
220};
221
222// Build the complete graph by visiting all static variables and functions and
223// add all "usages" (children in the graph) to it.
224// Every variable and function is visited once (at canonical declaration or the
225// definition). When visiting an object, a node for it may already exist
226// (without added children) if a reference to it was found already.
227class VarUseGraphBuilder : public DynamicRecursiveASTVisitor {
228 VarUseGraph &G;
229
230public:
231 VarUseGraphBuilder(VarUseGraph &G) : G(G) {}
232
233 bool VisitVarDecl(VarDecl *VD) override {
234 if ((VD->hasGlobalStorage() || VD->isStaticLocal()) &&
235 VD->isCanonicalDecl()) {
236 if (VarDecl *InitD = VD->getInitializingDeclaration()) {
237 VarUseNode *N = G.addNode(VD);
238 VarUseCollector Collector(N, G);
239 Collector.TraverseStmt(InitD->getInit());
240 }
241 }
242 return true;
243 }
244
245 bool VisitFunctionDecl(FunctionDecl *FD) override {
246 if (FD->isGlobal() || FD->isStatic()) {
247 if (Stmt *Body = FD->getBody()) {
248 VarUseNode *N = G.addNode(FD);
249 VarUseCollector Collector(N, G);
250 Collector.TraverseStmt(Body);
251 }
252 }
253 return true;
254 }
255};
256
257} // namespace
258
259namespace llvm {
260
261// These structures are required by scc_iterator.
262
263template <> struct GraphTraits<const VarUseNode *> {
264 using NodeType = const VarUseNode;
265 using NodeRef = const VarUseNode *;
266 using ChildIteratorType = NodeType::const_iterator;
267
268 static NodeType *getEntryNode(const VarUseNode *N) { return N; }
269 static ChildIteratorType
270 child_begin(NodeType *N) { // NOLINT(readability-identifier-naming)
271 return N->begin();
272 }
273 static ChildIteratorType
274 child_end(NodeType *N) { // NOLINT(readability-identifier-naming)
275 return N->end();
276 }
277};
278
279template <>
280struct GraphTraits<const VarUseGraph *>
281 : public GraphTraits<const VarUseNode *> {
282 static NodeType *getEntryNode(const VarUseGraph *G) {
283 return const_cast<VarUseGraph *>(G)->getRoot();
284 }
285
286 static VarUseNode *getValue(VarUseGraph::const_iterator::value_type &P) {
287 return P.second.get();
288 }
289
291 mapped_iterator<VarUseGraph::const_iterator, decltype(&getValue)>;
292
293 static nodes_iterator
294 nodes_begin(const VarUseGraph *G) { // NOLINT(readability-identifier-naming)
295 return {G->begin(), &getValue};
296 }
297
298 static nodes_iterator
299 nodes_end(const VarUseGraph *G) { // NOLINT(readability-identifier-naming)
300 return {G->end(), &getValue};
301 }
302
303 static unsigned size(const VarUseGraph *G) { return G->size(); }
304};
305
306} // namespace llvm
307
308static void
309reportCycles(ArrayRef<const VarUseNode *> SCC,
311 // Check if the SCC contains any variable, otherwise it is a function
312 // recursion.
313 auto NodeIsVar = [](const VarUseNode *N) { return N->isVar(); };
314 const auto *VarNode = llvm::find_if(SCC, NodeIsVar);
315 if (VarNode == SCC.end())
316 return;
317
318 Chk.diag((*VarNode)->getDecl()->getLocation(),
319 "static variable initialization cycle detected involving %0")
320 << (*VarNode)->getDecl();
321
322 // SCC may contain multiple cycles.
323 // Find one path with the front node as start.
324
325 // Lookup if a node is part of current SCC.
326 const llvm::SmallPtrSet<const VarUseNode *, 4> SCCElts(SCC.begin(),
327 SCC.end());
328
329 // Visit all paths in the SCC until we reach the front again.
330 llvm::DenseMap<const VarUseNode *, VarUseNode::const_iterator> NextNode;
331 llvm::SmallVector<const VarUseNode *> FoundPath;
332 FoundPath.push_back(SCC.front());
333 while (!FoundPath.empty()) {
334 if (!NextNode.contains(FoundPath.back())) {
335 NextNode[FoundPath.back()] = FoundPath.back()->begin();
336 } else {
337 NextNode[FoundPath.back()]++;
338 if (NextNode[FoundPath.back()] == FoundPath.back()->end()) {
339 FoundPath.pop_back();
340 continue;
341 }
342 }
343 const VarUseNode *N = (*NextNode[FoundPath.back()]).Node;
344 if (N == SCC.front())
345 break;
346 if (!SCCElts.contains(N) || NextNode.contains(N))
347 continue;
348 FoundPath.push_back(N);
349 }
350
351 std::string OutStr;
352 llvm::raw_string_ostream CycleOs(OutStr);
353
354 for (const VarUseNode *N : FoundPath) {
355 const VarUseRecord &U = *NextNode[N];
356 // 'U' is the source of the value, 'N->getDecl()' is the destination
357 Chk.diag(U.Ref->getBeginLoc(),
358 "%select{result|value}2 of %0 may be used to %select{compute "
359 "result of|initialize variable}3 %1 here",
360 DiagnosticIDs::Note)
361 << U.Node->getDecl() << N->getDecl() << U.Node->isVar() << N->isVar();
362
363 CycleOs << *N->getDecl() << " -> ";
364 }
365 CycleOs << *(FoundPath.front()->getDecl());
366
367 Chk.diag((*VarNode)->getDecl()->getLocation(),
368 "possible cyclical initialization: %0", DiagnosticIDs::Note)
369 << CycleOs.str();
370}
371
372namespace clang::tidy::misc {
373
375 Finder->addMatcher(translationUnitDecl().bind("TUDecl"), this);
376}
377
379 const MatchFinder::MatchResult &Result) {
380 const auto *TU = Result.Nodes.getNodeAs<TranslationUnitDecl>("TUDecl");
381
382 VarUseGraph Uses;
383 VarUseGraphBuilder Builder(Uses);
384 Builder.TraverseDecl(const_cast<TranslationUnitDecl *>(TU));
385
386 for (llvm::scc_iterator<const VarUseGraph *> SCCI =
387 llvm::scc_begin(const_cast<const VarUseGraph *>(&Uses));
388 !SCCI.isAtEnd(); ++SCCI) {
389 if (!SCCI.hasCycle())
390 continue;
391 reportCycles(*SCCI, *this);
392 }
393}
394
395} // namespace clang::tidy::misc
static void reportCycles(ArrayRef< const VarUseNode * > SCC, clang::tidy::misc::StaticInitializationCycleCheck &Chk)
static bool shouldIgnoreRef(const DeclRefExpr *DRE, const Decl *ParentD)
Finds cyclical initialization of static variables.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition Generators.h:150
static VarUseNode * getValue(VarUseGraph::const_iterator::value_type &P)
static nodes_iterator nodes_end(const VarUseGraph *G)
static NodeType * getEntryNode(const VarUseGraph *G)
mapped_iterator< VarUseGraph::const_iterator, decltype(&getValue)> nodes_iterator
static nodes_iterator nodes_begin(const VarUseGraph *G)
static NodeType * getEntryNode(const VarUseNode *N)
static ChildIteratorType child_begin(NodeType *N)