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