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"
30 ASTContext &ACtx = ParentD->getASTContext();
31 ParentMapContext &PMC = ACtx.getParentMapContext();
32 DynTypedNodeList Parents = PMC.getParents(*DRE);
36 const LambdaExpr *ParentLambda =
nullptr;
37 while (!Parents.empty()) {
38 if (Parents.size() > 1)
40 if (
const Expr *E = Parents[0].get<Expr>()) {
41 if (!E->getType().isNull() && !E->isValueDependent() &&
42 E->isIntegerConstantExpr(ACtx))
44 if (
const auto *ParentBO = dyn_cast<BinaryOperator>(E)) {
45 if (ParentBO->isAssignmentOp() &&
46 ParentBO->getLHS()->IgnoreParenCasts() == DRE)
48 }
else if (
const auto *LambdaE = dyn_cast<LambdaExpr>(E)) {
53 ParentLambda = LambdaE;
54 }
else if (
const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(E)) {
57 OpCallE->getOperator() == OverloadedOperatorKind::OO_Call &&
58 OpCallE->getCalleeDecl() == ParentLambda->getCallOperator())
59 ParentLambda =
nullptr;
61 }
else if (
const Decl *D = Parents[0].get<Decl>()) {
65 if (
const auto *ParentF = dyn_cast<FunctionDecl>(ParentD)) {
66 if (
const auto *FD = dyn_cast<FunctionDecl>(D))
67 return FD == ParentF->getDefinition();
70 return D->getCanonicalDecl() == ParentD->getCanonicalDecl();
72 return ParentLambda !=
nullptr;
74 Parents = PMC.getParents(Parents[0]);
76 llvm_unreachable(
"declaration of ParentD should be reached");
91 VarUseRecord() =
default;
92 VarUseRecord(
const Expr *Ref, VarUseNode *N) : Ref(Ref), Node(N) {}
93 operator VarUseNode *()
const {
return Node; }
105 llvm::SmallVector<VarUseRecord, 2> Uses;
108 VarUseNode(
const NamedDecl *D) : D(D) {}
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); }
116 using const_iterator = llvm::SmallVectorImpl<VarUseRecord>::const_iterator;
118 const_iterator begin()
const {
return Uses.begin(); }
119 const_iterator end()
const {
return Uses.end(); }
121 llvm::iterator_range<const_iterator> uses()
const {
122 return llvm::make_range(begin(), end());
125 bool empty()
const {
return Uses.empty(); }
126 unsigned size()
const {
return Uses.size(); }
128 friend class VarUseCollector;
129 friend class VarUseGraphBuilder;
130 friend class VarUseGraph;
141 using UseMapTy = llvm::DenseMap<const Decl *, std::unique_ptr<VarUseNode>>;
150 UseMap[
nullptr] = std::make_unique<VarUseNode>(
nullptr);
153 VarUseNode *addNode(
const NamedDecl *D) {
154 std::unique_ptr<VarUseNode> &N = UseMap[
D];
157 N = std::make_unique<VarUseNode>(D);
158 UseMap[
nullptr]->Uses.emplace_back(
nullptr, N.get());
162 using const_iterator = UseMapTy::const_iterator;
164 const_iterator begin()
const {
return UseMap.begin(); }
165 const_iterator end()
const {
return UseMap.end(); }
167 unsigned size()
const {
return UseMap.size(); }
169 VarUseNode *getRoot() {
return UseMap[
nullptr].get(); }
171 friend class VarUseGraphBuilder;
179class VarUseCollector :
public DynamicRecursiveASTVisitor {
182 const DeclContext *DC;
185 VarUseCollector(VarUseNode *N, VarUseGraph &G)
186 : Node(N), G(G), DC(N->isFunction() ? N->getFunction() : nullptr) {}
188 bool TraverseType(QualType T,
bool TraverseQualifier)
override {
191 bool TraverseTypeLoc(TypeLoc TL,
bool TraverseQualifier)
override {
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);
201 bool VisitDeclRefExpr(DeclRefExpr *DRE)
override {
202 if (
const auto *VarD = dyn_cast<VarDecl>(DRE->getDecl())) {
204 (VarD->hasGlobalStorage() || VarD->isStaticLocal()))
205 Node->Uses.emplace_back(DRE, G.addNode(VarD->getCanonicalDecl()));
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();
215 Node->Uses.emplace_back(CE, G.addNode(Def));
227class VarUseGraphBuilder :
public DynamicRecursiveASTVisitor {
231 VarUseGraphBuilder(VarUseGraph &G) : G(G) {}
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());
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);
263template <>
struct GraphTraits<const VarUseNode *> {
269 static ChildIteratorType
273 static ChildIteratorType
280struct GraphTraits<const VarUseGraph *>
281 :
public GraphTraits<const VarUseNode *> {
283 return const_cast<VarUseGraph *
>(G)->getRoot();
286 static VarUseNode *
getValue(VarUseGraph::const_iterator::value_type &P) {
287 return P.second.get();
291 mapped_iterator<VarUseGraph::const_iterator,
decltype(&
getValue)>;
298 static nodes_iterator
303 static unsigned size(
const VarUseGraph *G) {
return G->size(); }
313 auto NodeIsVar = [](
const VarUseNode *N) {
return N->isVar(); };
314 const auto *VarNode = llvm::find_if(SCC, NodeIsVar);
315 if (VarNode == SCC.end())
318 Chk.diag((*VarNode)->getDecl()->getLocation(),
319 "static variable initialization cycle detected involving %0")
320 << (*VarNode)->getDecl();
326 const llvm::SmallPtrSet<const VarUseNode *, 4> SCCElts(SCC.begin(),
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();
337 NextNode[FoundPath.back()]++;
338 if (NextNode[FoundPath.back()] == FoundPath.back()->end()) {
339 FoundPath.pop_back();
343 const VarUseNode *N = (*NextNode[FoundPath.back()]).Node;
344 if (N == SCC.front())
346 if (!SCCElts.contains(N) || NextNode.contains(N))
348 FoundPath.push_back(N);
352 llvm::raw_string_ostream CycleOs(OutStr);
354 for (
const VarUseNode *N : FoundPath) {
355 const VarUseRecord &U = *NextNode[N];
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",
361 << U.Node->getDecl() << N->getDecl() << U.Node->isVar() << N->isVar();
363 CycleOs << *N->getDecl() <<
" -> ";
365 CycleOs << *(FoundPath.front()->getDecl());
367 Chk.diag((*VarNode)->getDecl()->getLocation(),
368 "possible cyclical initialization: %0", DiagnosticIDs::Note)
375 Finder->addMatcher(translationUnitDecl().bind(
"TUDecl"),
this);
379 const MatchFinder::MatchResult &Result) {
380 const auto *TU = Result.Nodes.getNodeAs<TranslationUnitDecl>(
"TUDecl");
383 VarUseGraphBuilder Builder(Uses);
384 Builder.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TU));
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())
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.
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 unsigned size(const VarUseGraph *G)
static nodes_iterator nodes_begin(const VarUseGraph *G)
const VarUseNode NodeType
static NodeType * getEntryNode(const VarUseNode *N)
static ChildIteratorType child_end(NodeType *N)
NodeType::const_iterator ChildIteratorType
const VarUseNode * NodeRef
static ChildIteratorType child_begin(NodeType *N)