teak-llvm/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
Artem Dergachev f119bf99e5 [analyzer] UndefinedAssignmentChecker: Better warning message in implicit ctors.
When a class forgets to initialize a field in the constructor, and then gets
copied around, a warning is emitted that the value assigned to a specific field
is undefined.

When the copy/move constructor is implicit (not written out in the code) but not
trivial (is not a trivial memory copy, eg. because members have an explicit copy
constructor), the body of such constructor is auto-generated in the AST.
In this case the checker's warning message is squeezed at the top of
the class declaration, and it gets hard to guess which field is at fault.

Fix the warning message to include the name of the field.

Differential Revision: https://reviews.llvm.org/D43798

llvm-svn: 326258
2018-02-27 22:05:55 +00:00

123 lines
3.6 KiB
C++

//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that
// checks for assigning undefined values.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class UndefinedAssignmentChecker
: public Checker<check::Bind> {
mutable std::unique_ptr<BugType> BT;
public:
void checkBind(SVal location, SVal val, const Stmt *S,
CheckerContext &C) const;
};
}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
const Stmt *StoreE,
CheckerContext &C) const {
if (!val.isUndef())
return;
// Do not report assignments of uninitialized values inside swap functions.
// This should allow to swap partially uninitialized structs
// (radar://14129997)
if (const FunctionDecl *EnclosingFunctionDecl =
dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
return;
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
static const char *const DefaultMsg =
"Assigned value is garbage or undefined";
if (!BT)
BT.reset(new BuiltinBug(this, DefaultMsg));
// Generate a report for this bug.
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
const Expr *ex = nullptr;
while (StoreE) {
if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
OS << "The expression is an uninitialized value. "
"The computed value will also be garbage";
ex = U->getSubExpr();
break;
}
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
if (B->isCompoundAssignmentOp()) {
if (C.getSVal(B->getLHS()).isUndef()) {
OS << "The left expression of the compound assignment is an "
"uninitialized value. The computed value will also be garbage";
ex = B->getLHS();
break;
}
}
ex = B->getRHS();
break;
}
if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
ex = VD->getInit();
}
if (const auto *CD =
dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
if (CD->isImplicit()) {
for (auto I : CD->inits()) {
if (I->getInit()->IgnoreImpCasts() == StoreE) {
OS << "Value assigned to field '" << I->getMember()->getName()
<< "' in implicit constructor is garbage or undefined";
break;
}
}
}
}
break;
}
if (OS.str().empty())
OS << DefaultMsg;
auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
if (ex) {
R->addRange(ex->getSourceRange());
bugreporter::trackNullOrUndefValue(N, ex, *R);
}
C.emitReport(std::move(R));
}
void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefinedAssignmentChecker>();
}