Consumed.cpp revision e444ea0f5c8c8cf677edd05d9fb1254422765bd5
1//===- 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" 29#include "llvm/ADT/DenseMap.h" 30#include "llvm/ADT/SmallVector.h" 31#include "llvm/Support/Compiler.h" 32#include "llvm/Support/raw_ostream.h" 33 34// TODO: Correctly identify unreachable blocks when chaining boolean operators. 35// TODO: Warn about unreachable code. 36// TODO: Switch to using a bitmap to track unreachable blocks. 37// TODO: Mark variables as Unknown going into while- or for-loops only if they 38// are referenced inside that block. (Deferred) 39// TODO: Handle variable definitions, e.g. bool valid = x.isValid(); 40// if (valid) ...; (Deferred) 41// TODO: Add a method(s) to identify which method calls perform what state 42// transitions. (Deferred) 43// TODO: Take notes on state transitions to provide better warning messages. 44// (Deferred) 45// TODO: Test nested conditionals: A) Checking the same value multiple times, 46// and 2) Checking different values. (Deferred) 47// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred) 48// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred) 49 50using namespace clang; 51using namespace consumed; 52 53// Key method definition 54ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {} 55 56static ConsumedState invertConsumedUnconsumed(ConsumedState State) { 57 switch (State) { 58 case CS_Unconsumed: 59 return CS_Consumed; 60 case CS_Consumed: 61 return CS_Unconsumed; 62 case CS_None: 63 return CS_None; 64 case CS_Unknown: 65 return CS_Unknown; 66 } 67 llvm_unreachable("invalid enum"); 68} 69 70static bool isKnownState(ConsumedState State) { 71 switch (State) { 72 case CS_Unconsumed: 73 case CS_Consumed: 74 return true; 75 case CS_None: 76 case CS_Unknown: 77 return false; 78 } 79} 80 81static bool isTestingFunction(const FunctionDecl *FunDecl) { 82 return FunDecl->hasAttr<TestsUnconsumedAttr>(); 83} 84 85static StringRef stateToString(ConsumedState State) { 86 switch (State) { 87 case consumed::CS_None: 88 return "none"; 89 90 case consumed::CS_Unknown: 91 return "unknown"; 92 93 case consumed::CS_Unconsumed: 94 return "unconsumed"; 95 96 case consumed::CS_Consumed: 97 return "consumed"; 98 } 99 llvm_unreachable("invalid enum"); 100} 101 102namespace { 103struct VarTestResult { 104 const VarDecl *Var; 105 ConsumedState TestsFor; 106}; 107} // end anonymous::VarTestResult 108 109namespace clang { 110namespace consumed { 111 112enum EffectiveOp { 113 EO_And, 114 EO_Or 115}; 116 117class PropagationInfo { 118 enum { 119 IT_None, 120 IT_State, 121 IT_Test, 122 IT_BinTest, 123 IT_Var 124 } InfoType; 125 126 struct BinTestTy { 127 const BinaryOperator *Source; 128 EffectiveOp EOp; 129 VarTestResult LTest; 130 VarTestResult RTest; 131 }; 132 133 union { 134 ConsumedState State; 135 VarTestResult Test; 136 const VarDecl *Var; 137 BinTestTy BinTest; 138 }; 139 140public: 141 PropagationInfo() : InfoType(IT_None) {} 142 143 PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test), Test(Test) {} 144 PropagationInfo(const VarDecl *Var, ConsumedState TestsFor) 145 : InfoType(IT_Test) { 146 147 Test.Var = Var; 148 Test.TestsFor = TestsFor; 149 } 150 151 PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp, 152 const VarTestResult <est, const VarTestResult &RTest) 153 : InfoType(IT_BinTest) { 154 155 BinTest.Source = Source; 156 BinTest.EOp = EOp; 157 BinTest.LTest = LTest; 158 BinTest.RTest = RTest; 159 } 160 161 PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp, 162 const VarDecl *LVar, ConsumedState LTestsFor, 163 const VarDecl *RVar, ConsumedState RTestsFor) 164 : InfoType(IT_BinTest) { 165 166 BinTest.Source = Source; 167 BinTest.EOp = EOp; 168 BinTest.LTest.Var = LVar; 169 BinTest.LTest.TestsFor = LTestsFor; 170 BinTest.RTest.Var = RVar; 171 BinTest.RTest.TestsFor = RTestsFor; 172 } 173 174 PropagationInfo(ConsumedState State) : InfoType(IT_State), State(State) {} 175 PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {} 176 177 const ConsumedState & getState() const { 178 assert(InfoType == IT_State); 179 return State; 180 } 181 182 const VarTestResult & getTest() const { 183 assert(InfoType == IT_Test); 184 return Test; 185 } 186 187 const VarTestResult & getLTest() const { 188 assert(InfoType == IT_BinTest); 189 return BinTest.LTest; 190 } 191 192 const VarTestResult & getRTest() const { 193 assert(InfoType == IT_BinTest); 194 return BinTest.RTest; 195 } 196 197 const VarDecl * getVar() const { 198 assert(InfoType == IT_Var); 199 return Var; 200 } 201 202 EffectiveOp testEffectiveOp() const { 203 assert(InfoType == IT_BinTest); 204 return BinTest.EOp; 205 } 206 207 const BinaryOperator * testSourceNode() const { 208 assert(InfoType == IT_BinTest); 209 return BinTest.Source; 210 } 211 212 bool isValid() const { return InfoType != IT_None; } 213 bool isState() const { return InfoType == IT_State; } 214 bool isTest() const { return InfoType == IT_Test; } 215 bool isBinTest() const { return InfoType == IT_BinTest; } 216 bool isVar() const { return InfoType == IT_Var; } 217 218 PropagationInfo invertTest() const { 219 assert(InfoType == IT_Test || InfoType == IT_BinTest); 220 221 if (InfoType == IT_Test) { 222 return PropagationInfo(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); 223 224 } else if (InfoType == IT_BinTest) { 225 return PropagationInfo(BinTest.Source, 226 BinTest.EOp == EO_And ? EO_Or : EO_And, 227 BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor), 228 BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor)); 229 } else { 230 return PropagationInfo(); 231 } 232 } 233}; 234 235class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { 236 237 typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType; 238 typedef std::pair<const Stmt *, PropagationInfo> PairType; 239 typedef MapType::iterator InfoEntry; 240 typedef MapType::const_iterator ConstInfoEntry; 241 242 AnalysisDeclContext &AC; 243 ConsumedAnalyzer &Analyzer; 244 ConsumedStateMap *StateMap; 245 MapType PropagationMap; 246 247 void checkCallability(const PropagationInfo &PInfo, 248 const FunctionDecl *FunDecl, 249 const CallExpr *Call); 250 void forwardInfo(const Stmt *From, const Stmt *To); 251 void handleTestingFunctionCall(const CallExpr *Call, const VarDecl *Var); 252 bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl); 253 254public: 255 256 void Visit(const Stmt *StmtNode); 257 258 void VisitBinaryOperator(const BinaryOperator *BinOp); 259 void VisitCallExpr(const CallExpr *Call); 260 void VisitCastExpr(const CastExpr *Cast); 261 void VisitCXXConstructExpr(const CXXConstructExpr *Call); 262 void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call); 263 void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call); 264 void VisitDeclRefExpr(const DeclRefExpr *DeclRef); 265 void VisitDeclStmt(const DeclStmt *DelcS); 266 void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp); 267 void VisitMemberExpr(const MemberExpr *MExpr); 268 void VisitUnaryOperator(const UnaryOperator *UOp); 269 void VisitVarDecl(const VarDecl *Var); 270 271 ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer) 272 : AC(AC), Analyzer(Analyzer), StateMap(NULL) {} 273 274 PropagationInfo getInfo(const Stmt *StmtNode) const { 275 ConstInfoEntry Entry = PropagationMap.find(StmtNode); 276 277 if (Entry != PropagationMap.end()) 278 return Entry->second; 279 else 280 return PropagationInfo(); 281 } 282 283 void reset(ConsumedStateMap *NewStateMap) { 284 StateMap = NewStateMap; 285 } 286}; 287 288// TODO: When we support CallableWhenConsumed this will have to check for 289// the different attributes and change the behavior bellow. (Deferred) 290void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, 291 const FunctionDecl *FunDecl, 292 const CallExpr *Call) { 293 294 if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return; 295 296 if (PInfo.isVar()) { 297 const VarDecl *Var = PInfo.getVar(); 298 299 switch (StateMap->getState(Var)) { 300 case CS_Consumed: 301 Analyzer.WarningsHandler.warnUseWhileConsumed( 302 FunDecl->getNameAsString(), Var->getNameAsString(), 303 Call->getExprLoc()); 304 break; 305 306 case CS_Unknown: 307 Analyzer.WarningsHandler.warnUseInUnknownState( 308 FunDecl->getNameAsString(), Var->getNameAsString(), 309 Call->getExprLoc()); 310 break; 311 312 case CS_None: 313 case CS_Unconsumed: 314 break; 315 } 316 317 } else { 318 switch (PInfo.getState()) { 319 case CS_Consumed: 320 Analyzer.WarningsHandler.warnUseOfTempWhileConsumed( 321 FunDecl->getNameAsString(), Call->getExprLoc()); 322 break; 323 324 case CS_Unknown: 325 Analyzer.WarningsHandler.warnUseOfTempInUnknownState( 326 FunDecl->getNameAsString(), Call->getExprLoc()); 327 break; 328 329 case CS_None: 330 case CS_Unconsumed: 331 break; 332 } 333 } 334} 335 336void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) { 337 InfoEntry Entry = PropagationMap.find(From); 338 339 if (Entry != PropagationMap.end()) 340 PropagationMap.insert(PairType(To, Entry->second)); 341} 342 343void ConsumedStmtVisitor::handleTestingFunctionCall(const CallExpr *Call, 344 const VarDecl *Var) { 345 346 ConsumedState VarState = StateMap->getState(Var); 347 348 if (VarState != CS_Unknown) { 349 SourceLocation CallLoc = Call->getExprLoc(); 350 351 if (!CallLoc.isMacroID()) 352 Analyzer.WarningsHandler.warnUnnecessaryTest(Var->getNameAsString(), 353 stateToString(VarState), CallLoc); 354 } 355 356 PropagationMap.insert(PairType(Call, PropagationInfo(Var, CS_Unconsumed))); 357} 358 359bool ConsumedStmtVisitor::isLikeMoveAssignment( 360 const CXXMethodDecl *MethodDecl) { 361 362 return MethodDecl->isMoveAssignmentOperator() || 363 (MethodDecl->getOverloadedOperator() == OO_Equal && 364 MethodDecl->getNumParams() == 1 && 365 MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType()); 366} 367 368void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) { 369 370 ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode); 371 372 for (Stmt::const_child_iterator CI = StmtNode->child_begin(), 373 CE = StmtNode->child_end(); CI != CE; ++CI) { 374 375 PropagationMap.erase(*CI); 376 } 377} 378 379void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { 380 switch (BinOp->getOpcode()) { 381 case BO_LAnd: 382 case BO_LOr : { 383 InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()), 384 REntry = PropagationMap.find(BinOp->getRHS()); 385 386 VarTestResult LTest, RTest; 387 388 if (LEntry != PropagationMap.end() && LEntry->second.isTest()) { 389 LTest = LEntry->second.getTest(); 390 391 } else { 392 LTest.Var = NULL; 393 LTest.TestsFor = CS_None; 394 } 395 396 if (REntry != PropagationMap.end() && REntry->second.isTest()) { 397 RTest = REntry->second.getTest(); 398 399 } else { 400 RTest.Var = NULL; 401 RTest.TestsFor = CS_None; 402 } 403 404 if (!(LTest.Var == NULL && RTest.Var == NULL)) 405 PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp, 406 static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest))); 407 408 break; 409 } 410 411 case BO_PtrMemD: 412 case BO_PtrMemI: 413 forwardInfo(BinOp->getLHS(), BinOp); 414 break; 415 416 default: 417 break; 418 } 419} 420 421void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) { 422 if (const FunctionDecl *FunDecl = 423 dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) { 424 425 // Special case for the std::move function. 426 // TODO: Make this more specific. (Deferred) 427 if (FunDecl->getNameAsString() == "move") { 428 InfoEntry Entry = PropagationMap.find(Call->getArg(0)); 429 430 if (Entry != PropagationMap.end()) { 431 PropagationMap.insert(PairType(Call, Entry->second)); 432 } 433 434 return; 435 } 436 437 unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams(); 438 439 for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) { 440 QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType(); 441 442 InfoEntry Entry = PropagationMap.find(Call->getArg(Index)); 443 444 if (Entry == PropagationMap.end() || !Entry->second.isVar()) { 445 continue; 446 } 447 448 PropagationInfo PInfo = Entry->second; 449 450 if (ParamType->isRValueReferenceType() || 451 (ParamType->isLValueReferenceType() && 452 !cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) { 453 454 StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); 455 456 } else if (!(ParamType.isConstQualified() || 457 ((ParamType->isReferenceType() || 458 ParamType->isPointerType()) && 459 ParamType->getPointeeType().isConstQualified()))) { 460 461 StateMap->setState(PInfo.getVar(), consumed::CS_Unknown); 462 } 463 } 464 } 465} 466 467void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { 468 forwardInfo(Cast->getSubExpr(), Cast); 469} 470 471void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { 472 CXXConstructorDecl *Constructor = Call->getConstructor(); 473 474 ASTContext &CurrContext = AC.getASTContext(); 475 QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType(); 476 477 if (Analyzer.isConsumableType(ThisType)) { 478 if (Constructor->hasAttr<ConsumesAttr>() || 479 Constructor->isDefaultConstructor()) { 480 481 PropagationMap.insert(PairType(Call, 482 PropagationInfo(consumed::CS_Consumed))); 483 484 } else if (Constructor->isMoveConstructor()) { 485 486 PropagationInfo PInfo = 487 PropagationMap.find(Call->getArg(0))->second; 488 489 if (PInfo.isVar()) { 490 const VarDecl* Var = PInfo.getVar(); 491 492 PropagationMap.insert(PairType(Call, 493 PropagationInfo(StateMap->getState(Var)))); 494 495 StateMap->setState(Var, consumed::CS_Consumed); 496 497 } else { 498 PropagationMap.insert(PairType(Call, PInfo)); 499 } 500 501 } else if (Constructor->isCopyConstructor()) { 502 MapType::iterator Entry = PropagationMap.find(Call->getArg(0)); 503 504 if (Entry != PropagationMap.end()) 505 PropagationMap.insert(PairType(Call, Entry->second)); 506 507 } else { 508 PropagationMap.insert(PairType(Call, 509 PropagationInfo(consumed::CS_Unconsumed))); 510 } 511 } 512} 513 514void ConsumedStmtVisitor::VisitCXXMemberCallExpr( 515 const CXXMemberCallExpr *Call) { 516 517 VisitCallExpr(Call); 518 519 InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens()); 520 521 if (Entry != PropagationMap.end()) { 522 PropagationInfo PInfo = Entry->second; 523 const CXXMethodDecl *MethodDecl = Call->getMethodDecl(); 524 525 checkCallability(PInfo, MethodDecl, Call); 526 527 if (PInfo.isVar()) { 528 if (isTestingFunction(MethodDecl)) 529 handleTestingFunctionCall(Call, PInfo.getVar()); 530 else if (MethodDecl->hasAttr<ConsumesAttr>()) 531 StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); 532 else if (!MethodDecl->isConst()) 533 StateMap->setState(PInfo.getVar(), consumed::CS_Unknown); 534 } 535 } 536} 537 538void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( 539 const CXXOperatorCallExpr *Call) { 540 541 const FunctionDecl *FunDecl = 542 dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee()); 543 544 if (!FunDecl) return; 545 546 if (isa<CXXMethodDecl>(FunDecl) && 547 isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) { 548 549 InfoEntry LEntry = PropagationMap.find(Call->getArg(0)); 550 InfoEntry REntry = PropagationMap.find(Call->getArg(1)); 551 552 PropagationInfo LPInfo, RPInfo; 553 554 if (LEntry != PropagationMap.end() && 555 REntry != PropagationMap.end()) { 556 557 LPInfo = LEntry->second; 558 RPInfo = REntry->second; 559 560 if (LPInfo.isVar() && RPInfo.isVar()) { 561 StateMap->setState(LPInfo.getVar(), 562 StateMap->getState(RPInfo.getVar())); 563 564 StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed); 565 566 PropagationMap.insert(PairType(Call, LPInfo)); 567 568 } else if (LPInfo.isVar() && !RPInfo.isVar()) { 569 StateMap->setState(LPInfo.getVar(), RPInfo.getState()); 570 571 PropagationMap.insert(PairType(Call, LPInfo)); 572 573 } else if (!LPInfo.isVar() && RPInfo.isVar()) { 574 PropagationMap.insert(PairType(Call, 575 PropagationInfo(StateMap->getState(RPInfo.getVar())))); 576 577 StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed); 578 579 } else { 580 PropagationMap.insert(PairType(Call, RPInfo)); 581 } 582 583 } else if (LEntry != PropagationMap.end() && 584 REntry == PropagationMap.end()) { 585 586 LPInfo = LEntry->second; 587 588 if (LPInfo.isVar()) { 589 StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown); 590 591 PropagationMap.insert(PairType(Call, LPInfo)); 592 593 } else { 594 PropagationMap.insert(PairType(Call, 595 PropagationInfo(consumed::CS_Unknown))); 596 } 597 598 } else if (LEntry == PropagationMap.end() && 599 REntry != PropagationMap.end()) { 600 601 RPInfo = REntry->second; 602 603 if (RPInfo.isVar()) { 604 const VarDecl *Var = RPInfo.getVar(); 605 606 PropagationMap.insert(PairType(Call, 607 PropagationInfo(StateMap->getState(Var)))); 608 609 StateMap->setState(Var, consumed::CS_Consumed); 610 611 } else { 612 PropagationMap.insert(PairType(Call, RPInfo)); 613 } 614 } 615 616 } else { 617 618 VisitCallExpr(Call); 619 620 InfoEntry Entry = PropagationMap.find(Call->getArg(0)); 621 622 if (Entry != PropagationMap.end()) { 623 PropagationInfo PInfo = Entry->second; 624 625 checkCallability(PInfo, FunDecl, Call); 626 627 if (PInfo.isVar()) { 628 if (isTestingFunction(FunDecl)) { 629 handleTestingFunctionCall(Call, PInfo.getVar()); 630 631 } else if (FunDecl->hasAttr<ConsumesAttr>()) { 632 StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); 633 634 } else if (const CXXMethodDecl *MethodDecl = 635 dyn_cast_or_null<CXXMethodDecl>(FunDecl)) { 636 637 if (!MethodDecl->isConst()) 638 StateMap->setState(PInfo.getVar(), consumed::CS_Unknown); 639 } 640 } 641 } 642 } 643} 644 645void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) { 646 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) 647 if (StateMap->getState(Var) != consumed::CS_None) 648 PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var))); 649} 650 651void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) { 652 for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(), 653 DE = DeclS->decl_end(); DI != DE; ++DI) { 654 655 if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI)); 656 } 657 658 if (DeclS->isSingleDecl()) 659 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl())) 660 PropagationMap.insert(PairType(DeclS, PropagationInfo(Var))); 661} 662 663void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr( 664 const MaterializeTemporaryExpr *Temp) { 665 666 InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr()); 667 668 if (Entry != PropagationMap.end()) 669 PropagationMap.insert(PairType(Temp, Entry->second)); 670} 671 672void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) { 673 forwardInfo(MExpr->getBase(), MExpr); 674} 675 676void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { 677 InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens()); 678 if (Entry == PropagationMap.end()) return; 679 680 switch (UOp->getOpcode()) { 681 case UO_AddrOf: 682 PropagationMap.insert(PairType(UOp, Entry->second)); 683 break; 684 685 case UO_LNot: 686 if (Entry->second.isTest() || Entry->second.isBinTest()) 687 PropagationMap.insert(PairType(UOp, Entry->second.invertTest())); 688 break; 689 690 default: 691 break; 692 } 693} 694 695void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) { 696 if (Analyzer.isConsumableType(Var->getType())) { 697 PropagationInfo PInfo = 698 PropagationMap.find(Var->getInit())->second; 699 700 StateMap->setState(Var, PInfo.isVar() ? 701 StateMap->getState(PInfo.getVar()) : PInfo.getState()); 702 } 703} 704}} // end clang::consumed::ConsumedStmtVisitor 705 706namespace clang { 707namespace consumed { 708 709void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test, 710 ConsumedStateMap *ThenStates, 711 ConsumedStateMap *ElseStates) { 712 713 ConsumedState VarState = ThenStates->getState(Test.Var); 714 715 if (VarState == CS_Unknown) { 716 ThenStates->setState(Test.Var, Test.TestsFor); 717 if (ElseStates) 718 ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); 719 720 } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) { 721 ThenStates->markUnreachable(); 722 723 } else if (VarState == Test.TestsFor && ElseStates) { 724 ElseStates->markUnreachable(); 725 } 726} 727 728void splitVarStateForIfBinOp(const PropagationInfo &PInfo, 729 ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) { 730 731 const VarTestResult <est = PInfo.getLTest(), 732 &RTest = PInfo.getRTest(); 733 734 ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None, 735 RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None; 736 737 if (LTest.Var) { 738 if (PInfo.testEffectiveOp() == EO_And) { 739 if (LState == CS_Unknown) { 740 ThenStates->setState(LTest.Var, LTest.TestsFor); 741 742 } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) { 743 ThenStates->markUnreachable(); 744 745 } else if (LState == LTest.TestsFor && isKnownState(RState)) { 746 if (RState == RTest.TestsFor) { 747 if (ElseStates) 748 ElseStates->markUnreachable(); 749 } else { 750 ThenStates->markUnreachable(); 751 } 752 } 753 754 } else { 755 if (LState == CS_Unknown && ElseStates) { 756 ElseStates->setState(LTest.Var, 757 invertConsumedUnconsumed(LTest.TestsFor)); 758 759 } else if (LState == LTest.TestsFor && ElseStates) { 760 ElseStates->markUnreachable(); 761 762 } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) && 763 isKnownState(RState)) { 764 765 if (RState == RTest.TestsFor) { 766 if (ElseStates) 767 ElseStates->markUnreachable(); 768 } else { 769 ThenStates->markUnreachable(); 770 } 771 } 772 } 773 } 774 775 if (RTest.Var) { 776 if (PInfo.testEffectiveOp() == EO_And) { 777 if (RState == CS_Unknown) 778 ThenStates->setState(RTest.Var, RTest.TestsFor); 779 else if (RState == invertConsumedUnconsumed(RTest.TestsFor)) 780 ThenStates->markUnreachable(); 781 782 } else if (ElseStates) { 783 if (RState == CS_Unknown) 784 ElseStates->setState(RTest.Var, 785 invertConsumedUnconsumed(RTest.TestsFor)); 786 else if (RState == RTest.TestsFor) 787 ElseStates->markUnreachable(); 788 } 789 } 790} 791 792void ConsumedBlockInfo::addInfo(const CFGBlock *Block, 793 ConsumedStateMap *StateMap, 794 bool &AlreadyOwned) { 795 796 if (VisitedBlocks.alreadySet(Block)) return; 797 798 ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()]; 799 800 if (Entry) { 801 Entry->intersect(StateMap); 802 803 } else if (AlreadyOwned) { 804 StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap); 805 806 } else { 807 StateMapsArray[Block->getBlockID()] = StateMap; 808 AlreadyOwned = true; 809 } 810} 811 812void ConsumedBlockInfo::addInfo(const CFGBlock *Block, 813 ConsumedStateMap *StateMap) { 814 815 if (VisitedBlocks.alreadySet(Block)) { 816 delete StateMap; 817 return; 818 } 819 820 ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()]; 821 822 if (Entry) { 823 Entry->intersect(StateMap); 824 delete StateMap; 825 826 } else { 827 StateMapsArray[Block->getBlockID()] = StateMap; 828 } 829} 830 831ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) { 832 return StateMapsArray[Block->getBlockID()]; 833} 834 835void ConsumedBlockInfo::markVisited(const CFGBlock *Block) { 836 VisitedBlocks.insert(Block); 837} 838 839ConsumedState ConsumedStateMap::getState(const VarDecl *Var) { 840 MapType::const_iterator Entry = Map.find(Var); 841 842 if (Entry != Map.end()) { 843 return Entry->second; 844 845 } else { 846 return CS_None; 847 } 848} 849 850void ConsumedStateMap::intersect(const ConsumedStateMap *Other) { 851 ConsumedState LocalState; 852 853 if (this->From && this->From == Other->From && !Other->Reachable) { 854 this->markUnreachable(); 855 return; 856 } 857 858 for (MapType::const_iterator DMI = Other->Map.begin(), 859 DME = Other->Map.end(); DMI != DME; ++DMI) { 860 861 LocalState = this->getState(DMI->first); 862 863 if (LocalState == CS_None) 864 continue; 865 866 if (LocalState != DMI->second) 867 Map[DMI->first] = CS_Unknown; 868 } 869} 870 871void ConsumedStateMap::markUnreachable() { 872 this->Reachable = false; 873 Map.clear(); 874} 875 876void ConsumedStateMap::makeUnknown() { 877 for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME; 878 ++DMI) { 879 880 Map[DMI->first] = CS_Unknown; 881 } 882} 883 884void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) { 885 Map[Var] = State; 886} 887 888void ConsumedStateMap::remove(const VarDecl *Var) { 889 Map.erase(Var); 890} 891 892bool ConsumedAnalyzer::isConsumableType(QualType Type) { 893 const CXXRecordDecl *RD = 894 dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl()); 895 896 if (!RD) return false; 897 898 std::pair<CacheMapType::iterator, bool> Entry = 899 ConsumableTypeCache.insert(std::make_pair(RD, false)); 900 901 if (Entry.second) 902 Entry.first->second = hasConsumableAttributes(RD); 903 904 return Entry.first->second; 905} 906 907// TODO: Walk the base classes to see if any of them are unique types. 908// (Deferred) 909bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) { 910 for (CXXRecordDecl::method_iterator MI = RD->method_begin(), 911 ME = RD->method_end(); MI != ME; ++MI) { 912 913 for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end(); 914 AI != AE; ++AI) { 915 916 switch ((*AI)->getKind()) { 917 case attr::CallableWhenUnconsumed: 918 case attr::TestsUnconsumed: 919 return true; 920 921 default: 922 break; 923 } 924 } 925 } 926 927 return false; 928} 929 930bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, 931 const ConsumedStmtVisitor &Visitor) { 932 933 ConsumedStateMap *FalseStates = new ConsumedStateMap(*CurrStates); 934 PropagationInfo PInfo; 935 936 if (const IfStmt *IfNode = 937 dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) { 938 939 bool HasElse = IfNode->getElse() != NULL; 940 const Stmt *Cond = IfNode->getCond(); 941 942 PInfo = Visitor.getInfo(Cond); 943 if (!PInfo.isValid() && isa<BinaryOperator>(Cond)) 944 PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS()); 945 946 if (PInfo.isTest()) { 947 CurrStates->setSource(Cond); 948 FalseStates->setSource(Cond); 949 950 splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates, 951 HasElse ? FalseStates : NULL); 952 953 } else if (PInfo.isBinTest()) { 954 CurrStates->setSource(PInfo.testSourceNode()); 955 FalseStates->setSource(PInfo.testSourceNode()); 956 957 splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL); 958 959 } else { 960 delete FalseStates; 961 return false; 962 } 963 964 } else if (const BinaryOperator *BinOp = 965 dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) { 966 967 PInfo = Visitor.getInfo(BinOp->getLHS()); 968 if (!PInfo.isTest()) { 969 if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) { 970 PInfo = Visitor.getInfo(BinOp->getRHS()); 971 972 if (!PInfo.isTest()) { 973 delete FalseStates; 974 return false; 975 } 976 977 } else { 978 delete FalseStates; 979 return false; 980 } 981 } 982 983 CurrStates->setSource(BinOp); 984 FalseStates->setSource(BinOp); 985 986 const VarTestResult &Test = PInfo.getTest(); 987 ConsumedState VarState = CurrStates->getState(Test.Var); 988 989 if (BinOp->getOpcode() == BO_LAnd) { 990 if (VarState == CS_Unknown) 991 CurrStates->setState(Test.Var, Test.TestsFor); 992 else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) 993 CurrStates->markUnreachable(); 994 995 } else if (BinOp->getOpcode() == BO_LOr) { 996 if (VarState == CS_Unknown) 997 FalseStates->setState(Test.Var, 998 invertConsumedUnconsumed(Test.TestsFor)); 999 else if (VarState == Test.TestsFor) 1000 FalseStates->markUnreachable(); 1001 } 1002 1003 } else { 1004 delete FalseStates; 1005 return false; 1006 } 1007 1008 CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(); 1009 1010 if (*SI) 1011 BlockInfo.addInfo(*SI, CurrStates); 1012 else 1013 delete CurrStates; 1014 1015 if (*++SI) 1016 BlockInfo.addInfo(*SI, FalseStates); 1017 else 1018 delete FalseStates; 1019 1020 CurrStates = NULL; 1021 return true; 1022} 1023 1024void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { 1025 const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl()); 1026 1027 if (!D) return; 1028 1029 BlockInfo = ConsumedBlockInfo(AC.getCFG()); 1030 1031 PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>(); 1032 1033 CurrStates = new ConsumedStateMap(); 1034 ConsumedStmtVisitor Visitor(AC, *this); 1035 1036 // Visit all of the function's basic blocks. 1037 for (PostOrderCFGView::iterator I = SortedGraph->begin(), 1038 E = SortedGraph->end(); I != E; ++I) { 1039 1040 const CFGBlock *CurrBlock = *I; 1041 BlockInfo.markVisited(CurrBlock); 1042 1043 if (CurrStates == NULL) 1044 CurrStates = BlockInfo.getInfo(CurrBlock); 1045 1046 if (!CurrStates) { 1047 continue; 1048 1049 } else if (!CurrStates->isReachable()) { 1050 delete CurrStates; 1051 CurrStates = NULL; 1052 continue; 1053 } 1054 1055 Visitor.reset(CurrStates); 1056 1057 // Visit all of the basic block's statements. 1058 for (CFGBlock::const_iterator BI = CurrBlock->begin(), 1059 BE = CurrBlock->end(); BI != BE; ++BI) { 1060 1061 switch (BI->getKind()) { 1062 case CFGElement::Statement: 1063 Visitor.Visit(BI->castAs<CFGStmt>().getStmt()); 1064 break; 1065 case CFGElement::AutomaticObjectDtor: 1066 CurrStates->remove(BI->castAs<CFGAutomaticObjDtor>().getVarDecl()); 1067 default: 1068 break; 1069 } 1070 } 1071 1072 // TODO: Handle other forms of branching with precision, including while- 1073 // and for-loops. (Deferred) 1074 if (!splitState(CurrBlock, Visitor)) { 1075 CurrStates->setSource(NULL); 1076 1077 if (CurrBlock->succ_size() > 1) { 1078 CurrStates->makeUnknown(); 1079 1080 bool OwnershipTaken = false; 1081 1082 for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), 1083 SE = CurrBlock->succ_end(); SI != SE; ++SI) { 1084 1085 if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken); 1086 } 1087 1088 if (!OwnershipTaken) 1089 delete CurrStates; 1090 1091 CurrStates = NULL; 1092 1093 } else if (CurrBlock->succ_size() == 1 && 1094 (*CurrBlock->succ_begin())->pred_size() > 1) { 1095 1096 BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates); 1097 CurrStates = NULL; 1098 } 1099 } 1100 } // End of block iterator. 1101 1102 // Delete the last existing state map. 1103 delete CurrStates; 1104 1105 WarningsHandler.emitDiagnostics(); 1106} 1107}} // end namespace clang::consumed 1108