blob: e64a26c60ba1b57c353b221129761600fe7bc63d [file] [log] [blame]
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +00001//===- Consumed.cpp --------------------------------------------*- C++ --*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// A intra-procedural analysis for checking consumed properties. This is based,
11// in part, on research on linear types.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/Attr.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/ExprCXX.h"
19#include "clang/AST/RecursiveASTVisitor.h"
20#include "clang/AST/StmtVisitor.h"
21#include "clang/AST/StmtCXX.h"
22#include "clang/AST/Type.h"
23#include "clang/Analysis/Analyses/PostOrderCFGView.h"
24#include "clang/Analysis/AnalysisContext.h"
25#include "clang/Analysis/CFG.h"
26#include "clang/Analysis/Analyses/Consumed.h"
27#include "clang/Basic/OperatorKinds.h"
28#include "clang/Basic/SourceLocation.h"
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +000029#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/SmallVector.h"
31#include "llvm/Support/raw_ostream.h"
32
33// TODO: Add support for methods with CallableWhenUnconsumed.
34// TODO: Mark variables as Unknown going into while- or for-loops only if they
35// are referenced inside that block. (Deferred)
36// TODO: Add a method(s) to identify which method calls perform what state
37// transitions. (Deferred)
38// TODO: Take notes on state transitions to provide better warning messages.
39// (Deferred)
40// TODO: Test nested conditionals: A) Checking the same value multiple times,
41// and 2) Checking different values. (Deferred)
42// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred)
43// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred)
44
45using namespace clang;
46using namespace consumed;
47
48// Key method definition
49ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
50
51static StringRef stateToString(ConsumedState State) {
52 switch (State) {
53 case consumed::CS_None:
54 return "none";
55
56 case consumed::CS_Unknown:
57 return "unknown";
58
59 case consumed::CS_Unconsumed:
60 return "unconsumed";
61
62 case consumed::CS_Consumed:
63 return "consumed";
64 }
Reid Klecknera72f7202013-08-13 00:11:59 +000065 llvm_unreachable("invalid enum");
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +000066}
67
68namespace {
69class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
70
71 union PropagationUnion {
72 ConsumedState State;
73 const VarDecl *Var;
74 };
75
76 class PropagationInfo {
77 PropagationUnion StateOrVar;
78
79 public:
80 bool IsVar;
81
82 PropagationInfo() : IsVar(false) {
83 StateOrVar.State = consumed::CS_None;
84 }
85
86 PropagationInfo(ConsumedState State) : IsVar(false) {
87 StateOrVar.State = State;
88 }
89
90 PropagationInfo(const VarDecl *Var) : IsVar(true) {
91 StateOrVar.Var = Var;
92 }
93
94 ConsumedState getState() { return StateOrVar.State; };
95
96 const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
97 };
98
99 typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
100 typedef std::pair<const Stmt *, PropagationInfo> PairType;
101 typedef MapType::iterator InfoEntry;
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000102
103 AnalysisDeclContext &AC;
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000104 ConsumedAnalyzer &Analyzer;
105 ConsumedStateMap *StateMap;
106 MapType PropagationMap;
107
108 void forwardInfo(const Stmt *From, const Stmt *To);
109 bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
110
111public:
112
113 void Visit(const Stmt *StmtNode);
114
115 void VisitBinaryOperator(const BinaryOperator *BinOp);
116 void VisitCallExpr(const CallExpr *Call);
117 void VisitCastExpr(const CastExpr *Cast);
118 void VisitCXXConstructExpr(const CXXConstructExpr *Call);
119 void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
120 void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
121 void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
122 void VisitDeclStmt(const DeclStmt *DelcS);
123 void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
124 void VisitMemberExpr(const MemberExpr *MExpr);
125 void VisitUnaryOperator(const UnaryOperator *UOp);
126 void VisitVarDecl(const VarDecl *Var);
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000127
128 ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
129 ConsumedStateMap *StateMap)
130 : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
131
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000132 void reset() {
133 PropagationMap.clear();
134 }
135};
136
137void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
138 InfoEntry Entry = PropagationMap.find(From);
139
140 if (Entry != PropagationMap.end()) {
141 PropagationMap.insert(PairType(To, PropagationInfo(Entry->second)));
142 }
143}
144
145bool ConsumedStmtVisitor::isLikeMoveAssignment(
146 const CXXMethodDecl *MethodDecl) {
147
148 return MethodDecl->isMoveAssignmentOperator() ||
149 (MethodDecl->getOverloadedOperator() == OO_Equal &&
150 MethodDecl->getNumParams() == 1 &&
151 MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
152}
153
154void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
155 switch (BinOp->getOpcode()) {
156 case BO_PtrMemD:
157 case BO_PtrMemI:
158 forwardInfo(BinOp->getLHS(), BinOp);
159 break;
160
161 default:
162 break;
163 }
164}
165
166void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
Hans Wennborg2d66dfa2013-08-12 22:02:09 +0000167 ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000168
169 for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
170 CE = StmtNode->child_end(); CI != CE; ++CI) {
171
172 PropagationMap.erase(*CI);
173 }
174}
175
176void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
177 if (const FunctionDecl *FunDecl =
178 dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
179
180 // Special case for the std::move function.
181 // TODO: Make this more specific. (Deferred)
182 if (FunDecl->getNameAsString() == "move") {
183 InfoEntry Entry = PropagationMap.find(Call->getArg(0));
184
185 if (Entry != PropagationMap.end()) {
186 PropagationMap.insert(PairType(Call, Entry->second));
187 }
188
189 return;
190 }
191
192 unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
193
194 for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
195 QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
196
197 InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
198
199 if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
200 continue;
201 }
202
203 PropagationInfo PState = Entry->second;
204
205 if (ParamType->isRValueReferenceType() ||
206 (ParamType->isLValueReferenceType() &&
207 !cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
208
209 StateMap->setState(PState.getVar(), consumed::CS_Consumed);
210
211 } else if (!(ParamType.isConstQualified() ||
212 ((ParamType->isReferenceType() ||
213 ParamType->isPointerType()) &&
214 ParamType->getPointeeType().isConstQualified()))) {
215
216 StateMap->setState(PState.getVar(), consumed::CS_Unknown);
217 }
218 }
219 }
220}
221
222void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
223 InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
224
225 if (Entry != PropagationMap.end())
226 PropagationMap.insert(PairType(Cast, Entry->second));
227}
228
229void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
230 CXXConstructorDecl *Constructor = Call->getConstructor();
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000231
232 ASTContext &CurrContext = AC.getASTContext();
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000233 QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
234
235 if (Analyzer.isConsumableType(ThisType)) {
236 if (Constructor->hasAttr<ConsumesAttr>() ||
237 Constructor->isDefaultConstructor()) {
238
239 PropagationMap.insert(PairType(Call,
240 PropagationInfo(consumed::CS_Consumed)));
241
242 } else if (Constructor->isMoveConstructor()) {
243
244 PropagationInfo PState =
245 PropagationMap.find(Call->getArg(0))->second;
246
247 if (PState.IsVar) {
248 const VarDecl* Var = PState.getVar();
249
250 PropagationMap.insert(PairType(Call,
251 PropagationInfo(StateMap->getState(Var))));
252
253 StateMap->setState(Var, consumed::CS_Consumed);
254
255 } else {
256 PropagationMap.insert(PairType(Call, PState));
257 }
258
259 } else if (Constructor->isCopyConstructor()) {
260 MapType::iterator Entry = PropagationMap.find(Call->getArg(0));
261
262 if (Entry != PropagationMap.end())
263 PropagationMap.insert(PairType(Call, Entry->second));
264
265 } else {
266 PropagationMap.insert(PairType(Call,
267 PropagationInfo(consumed::CS_Unconsumed)));
268 }
269 }
270}
271
272void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
273 const CXXMemberCallExpr *Call) {
274
275 VisitCallExpr(Call);
276
277 InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
278
279 if (Entry != PropagationMap.end()) {
280 PropagationInfo PState = Entry->second;
281 if (!PState.IsVar) return;
282
283 const CXXMethodDecl *Method = Call->getMethodDecl();
284
285 if (Method->hasAttr<ConsumesAttr>())
286 StateMap->setState(PState.getVar(), consumed::CS_Consumed);
287 else if (!Method->isConst())
288 StateMap->setState(PState.getVar(), consumed::CS_Unknown);
289 }
290}
291
292void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
293 const CXXOperatorCallExpr *Call) {
294
295 const FunctionDecl *FunDecl =
296 dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
297
298 if (!FunDecl) return;
299
300 if (isa<CXXMethodDecl>(FunDecl) &&
301 isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
302
303 InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
304 InfoEntry REntry = PropagationMap.find(Call->getArg(1));
305
306 PropagationInfo LPState, RPState;
307
308 if (LEntry != PropagationMap.end() &&
309 REntry != PropagationMap.end()) {
310
311 LPState = LEntry->second;
312 RPState = REntry->second;
313
314 if (LPState.IsVar && RPState.IsVar) {
315 StateMap->setState(LPState.getVar(),
316 StateMap->getState(RPState.getVar()));
317
318 StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
319
320 PropagationMap.insert(PairType(Call, LPState));
321
322 } else if (LPState.IsVar && !RPState.IsVar) {
323 StateMap->setState(LPState.getVar(), RPState.getState());
324
325 PropagationMap.insert(PairType(Call, LPState));
326
327 } else if (!LPState.IsVar && RPState.IsVar) {
328 PropagationMap.insert(PairType(Call,
329 PropagationInfo(StateMap->getState(RPState.getVar()))));
330
331 StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
332
333 } else {
334 PropagationMap.insert(PairType(Call, RPState));
335 }
336
337 } else if (LEntry != PropagationMap.end() &&
338 REntry == PropagationMap.end()) {
339
340 LPState = LEntry->second;
341
342 if (LPState.IsVar) {
343 StateMap->setState(LPState.getVar(), consumed::CS_Unknown);
344
345 PropagationMap.insert(PairType(Call, LPState));
346
347 } else {
348 PropagationMap.insert(PairType(Call,
349 PropagationInfo(consumed::CS_Unknown)));
350 }
351
352 } else if (LEntry == PropagationMap.end() &&
353 REntry != PropagationMap.end()) {
354
355 RPState = REntry->second;
356
357 if (RPState.IsVar) {
358 const VarDecl *Var = RPState.getVar();
359
360 PropagationMap.insert(PairType(Call,
361 PropagationInfo(StateMap->getState(Var))));
362
363 StateMap->setState(Var, consumed::CS_Consumed);
364
365 } else {
366 PropagationMap.insert(PairType(Call, RPState));
367 }
368 }
369
370 } else {
371
372 VisitCallExpr(Call);
373
374 InfoEntry Entry = PropagationMap.find(Call->getArg(0));
375
376 if (Entry != PropagationMap.end()) {
377
378 PropagationInfo PState = Entry->second;
379
380 // TODO: When we support CallableWhenConsumed this will have to check for
381 // the different attributes and change the behavior bellow.
382 // (Deferred)
383 if (FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) {
384 if (PState.IsVar) {
385 const VarDecl *Var = PState.getVar();
386
387 switch (StateMap->getState(Var)) {
388 case CS_Consumed:
389 Analyzer.WarningsHandler.warnUseWhileConsumed(
390 FunDecl->getNameAsString(), Var->getNameAsString(),
391 Call->getExprLoc());
392 break;
393
394 case CS_Unknown:
395 Analyzer.WarningsHandler.warnUseInUnknownState(
396 FunDecl->getNameAsString(), Var->getNameAsString(),
397 Call->getExprLoc());
398 break;
399
400 default:
401 break;
402 }
403
404 } else {
405 switch (PState.getState()) {
406 case CS_Consumed:
407 Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
408 FunDecl->getNameAsString(), Call->getExprLoc());
409 break;
410
411 case CS_Unknown:
412 Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
413 FunDecl->getNameAsString(), Call->getExprLoc());
414 break;
415
416 default:
417 break;
418 }
419 }
420 }
421
422 // Handle non-constant member operators.
423 if (const CXXMethodDecl *MethodDecl =
424 dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
425
426 if (!MethodDecl->isConst() && PState.IsVar)
427 StateMap->setState(PState.getVar(), consumed::CS_Unknown);
428 }
429 }
430 }
431}
432
433void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
434 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
435 if (StateMap->getState(Var) != consumed::CS_None)
436 PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
437}
438
439void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
440 for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
441 DE = DeclS->decl_end(); DI != DE; ++DI) {
442
443 if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
444 }
445
446 if (DeclS->isSingleDecl())
447 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
448 PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
449}
450
451void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
452 const MaterializeTemporaryExpr *Temp) {
453
454 InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr());
455
456 if (Entry != PropagationMap.end())
457 PropagationMap.insert(PairType(Temp, Entry->second));
458}
459
460void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
461 forwardInfo(MExpr->getBase(), MExpr);
462}
463
464void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
465 if (UOp->getOpcode() == UO_AddrOf) {
466 InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
467
468 if (Entry != PropagationMap.end())
469 PropagationMap.insert(PairType(UOp, Entry->second));
470 }
471}
472
473void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
474 if (Analyzer.isConsumableType(Var->getType())) {
475 PropagationInfo PState =
476 PropagationMap.find(Var->getInit())->second;
477
478 StateMap->setState(Var, PState.IsVar ?
479 StateMap->getState(PState.getVar()) : PState.getState());
480 }
481}
482} // end anonymous::ConsumedStmtVisitor
483
484namespace {
485
486// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
487// if (valid) ...; (Deferred)
488class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
489
490 bool Invert;
491 SourceLocation CurrTestLoc;
492
493 ConsumedStateMap *StateMap;
494
495public:
496 bool IsUsefulConditional;
497 VarTestResult Test;
498
499 TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
500 StateMap(StateMap), IsUsefulConditional(false) {}
501
502 bool VisitCallExpr(CallExpr *Call);
503 bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
504 bool VisitUnaryOperator(UnaryOperator *UnaryOp);
505};
506
507bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
508 if (const CXXMethodDecl *Method =
509 dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
510
511 if (isTestingFunction(Method)) {
512 CurrTestLoc = Call->getExprLoc();
513 IsUsefulConditional = true;
514 return true;
515 }
516
517 IsUsefulConditional = false;
518 }
519
520 return false;
521}
522
523bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
524 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
525 if (StateMap->getState(Var) != consumed::CS_None) {
526 Test = VarTestResult(Var, CurrTestLoc, !Invert);
527 }
528
529 } else {
530 IsUsefulConditional = false;
531 }
532
533 return IsUsefulConditional;
534}
535
536bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
537 if (UnaryOp->getOpcode() == UO_LNot) {
538 Invert = true;
539 TraverseStmt(UnaryOp->getSubExpr());
540
541 } else {
542 IsUsefulConditional = false;
543 }
544
545 return false;
546}
547} // end anonymouse::TestedVarsVisitor
548
549namespace clang {
550namespace consumed {
551
552void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
553 ConsumedStateMap *StateMap,
554 bool &AlreadyOwned) {
555
556 if (VisitedBlocks.alreadySet(Block)) return;
557
558 ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
559
560 if (Entry) {
561 Entry->intersect(StateMap);
562
563 } else if (AlreadyOwned) {
564 StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
565
566 } else {
567 StateMapsArray[Block->getBlockID()] = StateMap;
568 AlreadyOwned = true;
569 }
570}
571
572void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
573 ConsumedStateMap *StateMap) {
574
575 if (VisitedBlocks.alreadySet(Block)) {
576 delete StateMap;
577 return;
578 }
579
580 ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
581
582 if (Entry) {
583 Entry->intersect(StateMap);
584 delete StateMap;
585
586 } else {
587 StateMapsArray[Block->getBlockID()] = StateMap;
588 }
589}
590
591ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
592 return StateMapsArray[Block->getBlockID()];
593}
594
595void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
596 VisitedBlocks.insert(Block);
597}
598
599ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
600 MapType::const_iterator Entry = Map.find(Var);
601
602 if (Entry != Map.end()) {
603 return Entry->second;
604
605 } else {
606 return CS_None;
607 }
608}
609
610void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
611 ConsumedState LocalState;
612
613 for (MapType::const_iterator DMI = Other->Map.begin(),
614 DME = Other->Map.end(); DMI != DME; ++DMI) {
615
616 LocalState = this->getState(DMI->first);
617
618 if (LocalState != CS_None && LocalState != DMI->second)
619 setState(DMI->first, CS_Unknown);
620 }
621}
622
623void ConsumedStateMap::makeUnknown() {
624 PairType Pair;
625
626 for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
627 ++DMI) {
628
629 Pair = *DMI;
630
631 Map.erase(Pair.first);
632 Map.insert(PairType(Pair.first, CS_Unknown));
633 }
634}
635
636void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
637 Map[Var] = State;
638}
639
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000640
641bool ConsumedAnalyzer::isConsumableType(QualType Type) {
642 const CXXRecordDecl *RD =
643 dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
644
645 if (!RD) return false;
646
647 std::pair<CacheMapType::iterator, bool> Entry =
648 ConsumableTypeCache.insert(std::make_pair(RD, false));
649
650 if (Entry.second)
651 Entry.first->second = hasConsumableAttributes(RD);
652
653 return Entry.first->second;
654}
655
656// TODO: Walk the base classes to see if any of them are unique types.
657// (Deferred)
658bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
659 for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
660 ME = RD->method_end(); MI != ME; ++MI) {
661
662 for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
663 AI != AE; ++AI) {
664
665 switch ((*AI)->getKind()) {
666 case attr::CallableWhenUnconsumed:
667 case attr::TestsUnconsumed:
668 return true;
669
670 default:
671 break;
672 }
673 }
674 }
675
676 return false;
677}
678
679// TODO: Handle other forms of branching with precision, including while- and
680// for-loops. (Deferred)
681void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
682 const IfStmt *Terminator) {
683
684 TestedVarsVisitor Visitor(CurrStates);
685 Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
686
687 bool HasElse = Terminator->getElse() != NULL;
688
689 ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
690
691 if (Visitor.IsUsefulConditional) {
692 ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
693
694 if (VarState != CS_Unknown) {
695 // FIXME: Make this not warn if the test is from a macro expansion.
696 // (Deferred)
697 WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
698 stateToString(VarState), Visitor.Test.Loc);
699 }
700
701 if (Visitor.Test.UnconsumedInTrueBranch) {
702 CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
703 if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed);
704
705 } else {
706 CurrStates->setState(Visitor.Test.Var, CS_Consumed);
707 if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed);
708 }
709 }
710
711 CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
712
713 if (*SI) BlockInfo.addInfo(*SI, CurrStates);
714 if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
715}
716
717void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
718 const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
719
720 if (!D) return;
721
722 BlockInfo = ConsumedBlockInfo(AC.getCFG());
723
724 PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
725
726 CurrStates = new ConsumedStateMap();
727
728 // Visit all of the function's basic blocks.
729 for (PostOrderCFGView::iterator I = SortedGraph->begin(),
730 E = SortedGraph->end(); I != E; ++I) {
731
732 const CFGBlock *CurrBlock = *I;
733 BlockInfo.markVisited(CurrBlock);
734
735 if (CurrStates == NULL)
736 CurrStates = BlockInfo.getInfo(CurrBlock);
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000737
738 ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
739
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000740 // Visit all of the basic block's statements.
741 for (CFGBlock::const_iterator BI = CurrBlock->begin(),
742 BE = CurrBlock->end(); BI != BE; ++BI) {
743
744 if (BI->getKind() == CFGElement::Statement)
745 Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
746 }
747
748 // TODO: Remove any variables that have reached the end of their
749 // lifetimes from the state map. (Deferred)
750
751 if (const IfStmt *Terminator =
752 dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
753
754 splitState(CurrBlock, Terminator);
755 CurrStates = NULL;
756
757 } else if (CurrBlock->succ_size() > 1) {
758 CurrStates->makeUnknown();
759
760 bool OwnershipTaken = false;
761
762 for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
763 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
764
765 if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
766 }
767
768 if (!OwnershipTaken)
769 delete CurrStates;
770
771 CurrStates = NULL;
772
773 } else if (CurrBlock->succ_size() == 1 &&
774 (*CurrBlock->succ_begin())->pred_size() > 1) {
775
776 BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
777 CurrStates = NULL;
778 }
779
780 Visitor.reset();
781 } // End of block iterator.
782
783 // Delete the last existing state map.
784 delete CurrStates;
785
786 WarningsHandler.emitDiagnostics();
787}
788
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000789bool isTestingFunction(const CXXMethodDecl *Method) {
790 return Method->hasAttr<TestsUnconsumedAttr>();
791}
792
793}} // end namespace clang::consumed