12754fe60SDimitry Andric //===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
22754fe60SDimitry Andric //
32754fe60SDimitry Andric //                     The LLVM Compiler Infrastructure
42754fe60SDimitry Andric //
52754fe60SDimitry Andric // This file is distributed under the University of Illinois Open Source
62754fe60SDimitry Andric // License. See LICENSE.TXT for details.
72754fe60SDimitry Andric //
82754fe60SDimitry Andric //===----------------------------------------------------------------------===//
92754fe60SDimitry Andric //
103b0f4066SDimitry Andric // This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that
112754fe60SDimitry Andric // checks for assigning undefined values.
122754fe60SDimitry Andric //
132754fe60SDimitry Andric //===----------------------------------------------------------------------===//
142754fe60SDimitry Andric 
15*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16139f7f9bSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
173b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
183b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
193b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
202754fe60SDimitry Andric 
212754fe60SDimitry Andric using namespace clang;
222754fe60SDimitry Andric using namespace ento;
232754fe60SDimitry Andric 
242754fe60SDimitry Andric namespace {
252754fe60SDimitry Andric class UndefinedAssignmentChecker
263b0f4066SDimitry Andric   : public Checker<check::Bind> {
2759d1ed5bSDimitry Andric   mutable std::unique_ptr<BugType> BT;
283b0f4066SDimitry Andric 
292754fe60SDimitry Andric public:
306122f3e6SDimitry Andric   void checkBind(SVal location, SVal val, const Stmt *S,
316122f3e6SDimitry Andric                  CheckerContext &C) const;
322754fe60SDimitry Andric };
332754fe60SDimitry Andric }
342754fe60SDimitry Andric 
checkBind(SVal location,SVal val,const Stmt * StoreE,CheckerContext & C) const353b0f4066SDimitry Andric void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
366122f3e6SDimitry Andric                                            const Stmt *StoreE,
373b0f4066SDimitry Andric                                            CheckerContext &C) const {
382754fe60SDimitry Andric   if (!val.isUndef())
392754fe60SDimitry Andric     return;
402754fe60SDimitry Andric 
41f785676fSDimitry Andric   // Do not report assignments of uninitialized values inside swap functions.
42f785676fSDimitry Andric   // This should allow to swap partially uninitialized structs
43f785676fSDimitry Andric   // (radar://14129997)
44f785676fSDimitry Andric   if (const FunctionDecl *EnclosingFunctionDecl =
45f785676fSDimitry Andric       dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
46f785676fSDimitry Andric     if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
47f785676fSDimitry Andric       return;
48f785676fSDimitry Andric 
490623d748SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
502754fe60SDimitry Andric 
512754fe60SDimitry Andric   if (!N)
522754fe60SDimitry Andric     return;
532754fe60SDimitry Andric 
544ba319b5SDimitry Andric   static const char *const DefaultMsg =
554ba319b5SDimitry Andric       "Assigned value is garbage or undefined";
562754fe60SDimitry Andric   if (!BT)
574ba319b5SDimitry Andric     BT.reset(new BuiltinBug(this, DefaultMsg));
582754fe60SDimitry Andric 
592754fe60SDimitry Andric   // Generate a report for this bug.
604ba319b5SDimitry Andric   llvm::SmallString<128> Str;
614ba319b5SDimitry Andric   llvm::raw_svector_ostream OS(Str);
624ba319b5SDimitry Andric 
6359d1ed5bSDimitry Andric   const Expr *ex = nullptr;
642754fe60SDimitry Andric 
652754fe60SDimitry Andric   while (StoreE) {
669a199699SDimitry Andric     if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
674ba319b5SDimitry Andric       OS << "The expression is an uninitialized value. "
689a199699SDimitry Andric             "The computed value will also be garbage";
699a199699SDimitry Andric 
709a199699SDimitry Andric       ex = U->getSubExpr();
719a199699SDimitry Andric       break;
729a199699SDimitry Andric     }
739a199699SDimitry Andric 
742754fe60SDimitry Andric     if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
752754fe60SDimitry Andric       if (B->isCompoundAssignmentOp()) {
764ba319b5SDimitry Andric         if (C.getSVal(B->getLHS()).isUndef()) {
774ba319b5SDimitry Andric           OS << "The left expression of the compound assignment is an "
782754fe60SDimitry Andric                 "uninitialized value. The computed value will also be garbage";
792754fe60SDimitry Andric           ex = B->getLHS();
802754fe60SDimitry Andric           break;
812754fe60SDimitry Andric         }
822754fe60SDimitry Andric       }
832754fe60SDimitry Andric 
842754fe60SDimitry Andric       ex = B->getRHS();
852754fe60SDimitry Andric       break;
862754fe60SDimitry Andric     }
872754fe60SDimitry Andric 
882754fe60SDimitry Andric     if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
892754fe60SDimitry Andric       const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
902754fe60SDimitry Andric       ex = VD->getInit();
912754fe60SDimitry Andric     }
922754fe60SDimitry Andric 
934ba319b5SDimitry Andric     if (const auto *CD =
944ba319b5SDimitry Andric             dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
954ba319b5SDimitry Andric       if (CD->isImplicit()) {
964ba319b5SDimitry Andric         for (auto I : CD->inits()) {
974ba319b5SDimitry Andric           if (I->getInit()->IgnoreImpCasts() == StoreE) {
984ba319b5SDimitry Andric             OS << "Value assigned to field '" << I->getMember()->getName()
994ba319b5SDimitry Andric                << "' in implicit constructor is garbage or undefined";
1004ba319b5SDimitry Andric             break;
1014ba319b5SDimitry Andric           }
1024ba319b5SDimitry Andric         }
1034ba319b5SDimitry Andric       }
1044ba319b5SDimitry Andric     }
1054ba319b5SDimitry Andric 
1062754fe60SDimitry Andric     break;
1072754fe60SDimitry Andric   }
1082754fe60SDimitry Andric 
1094ba319b5SDimitry Andric   if (OS.str().empty())
1104ba319b5SDimitry Andric     OS << DefaultMsg;
1114ba319b5SDimitry Andric 
1124ba319b5SDimitry Andric   auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
1132754fe60SDimitry Andric   if (ex) {
1142754fe60SDimitry Andric     R->addRange(ex->getSourceRange());
115*b5893f02SDimitry Andric     bugreporter::trackExpressionValue(N, ex, *R);
1162754fe60SDimitry Andric   }
1173dac3a9bSDimitry Andric   C.emitReport(std::move(R));
1182754fe60SDimitry Andric }
1192754fe60SDimitry Andric 
registerUndefinedAssignmentChecker(CheckerManager & mgr)1203b0f4066SDimitry Andric void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
1213b0f4066SDimitry Andric   mgr.registerChecker<UndefinedAssignmentChecker>();
1223b0f4066SDimitry Andric }
123