Teach UninitializedValuesV2 to implicitly reason about C++
references by monitoring whether an access to a variable is solely to compute it's lvalue or to do an lvalue-to-rvalue conversion (i.e., a load). llvm-svn: 123777
This commit is contained in:
parent
e86a7ad01a
commit
b63931eef6
|
@ -19,6 +19,7 @@
|
||||||
#include "clang/Analysis/CFG.h"
|
#include "clang/Analysis/CFG.h"
|
||||||
#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
|
#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
|
||||||
#include "clang/Analysis/Analyses/UninitializedValuesV2.h"
|
#include "clang/Analysis/Analyses/UninitializedValuesV2.h"
|
||||||
|
#include "clang/Analysis/Support/SaveAndRestore.h"
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
|
@ -218,15 +219,17 @@ class TransferFunctions : public CFGRecStmtVisitor<TransferFunctions> {
|
||||||
CFGBlockValues &vals;
|
CFGBlockValues &vals;
|
||||||
const CFG &cfg;
|
const CFG &cfg;
|
||||||
UninitVariablesHandler *handler;
|
UninitVariablesHandler *handler;
|
||||||
|
const DeclRefExpr *currentDR;
|
||||||
public:
|
public:
|
||||||
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
|
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
|
||||||
UninitVariablesHandler *handler)
|
UninitVariablesHandler *handler)
|
||||||
: vals(vals), cfg(cfg), handler(handler) {}
|
: vals(vals), cfg(cfg), handler(handler), currentDR(0) {}
|
||||||
|
|
||||||
const CFG &getCFG() { return cfg; }
|
const CFG &getCFG() { return cfg; }
|
||||||
void reportUninit(const DeclRefExpr *ex, const VarDecl *vd);
|
void reportUninit(const DeclRefExpr *ex, const VarDecl *vd);
|
||||||
|
|
||||||
void VisitDeclStmt(DeclStmt *ds);
|
void VisitDeclStmt(DeclStmt *ds);
|
||||||
|
void VisitDeclRefExpr(DeclRefExpr *dr);
|
||||||
void VisitUnaryOperator(UnaryOperator *uo);
|
void VisitUnaryOperator(UnaryOperator *uo);
|
||||||
void VisitBinaryOperator(BinaryOperator *bo);
|
void VisitBinaryOperator(BinaryOperator *bo);
|
||||||
void VisitCastExpr(CastExpr *ce);
|
void VisitCastExpr(CastExpr *ce);
|
||||||
|
@ -249,8 +252,26 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *ds) {
|
||||||
vals[vd] = Initialized;
|
vals[vd] = Initialized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Stmt *init = vd->getInit()) {
|
||||||
|
Visit(init);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
|
||||||
|
// We assume that DeclRefExprs wrapped in an lvalue-to-rvalue cast
|
||||||
|
// cannot be block-level expressions. Therefore, we determine if
|
||||||
|
// a DeclRefExpr is involved in a "load" by comparing it to the current
|
||||||
|
// DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr.
|
||||||
|
// If a DeclRefExpr is not involved in a load, we are essentially computing
|
||||||
|
// its address, either for assignment to a reference or via the '&' operator.
|
||||||
|
// In such cases, treat the variable as being initialized, since this
|
||||||
|
// analysis isn't powerful enough to do alias tracking.
|
||||||
|
if (dr != currentDR)
|
||||||
|
if (const VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl()))
|
||||||
|
if (isTrackedVar(vd))
|
||||||
|
vals[vd] = Initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FindVarResult findBlockVarDecl(Expr* ex) {
|
static FindVarResult findBlockVarDecl(Expr* ex) {
|
||||||
|
@ -263,55 +284,86 @@ static FindVarResult findBlockVarDecl(Expr* ex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) {
|
void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) {
|
||||||
Visit(bo->getRHS());
|
|
||||||
Visit(bo->getLHS());
|
|
||||||
if (bo->isAssignmentOp()) {
|
if (bo->isAssignmentOp()) {
|
||||||
const FindVarResult &res = findBlockVarDecl(bo->getLHS());
|
const FindVarResult &res = findBlockVarDecl(bo->getLHS());
|
||||||
if (const VarDecl* vd = res.getDecl()) {
|
if (const VarDecl* vd = res.getDecl()) {
|
||||||
|
// We assume that DeclRefExprs wrapped in a BinaryOperator "assignment"
|
||||||
|
// cannot be block-level expressions. Therefore, we determine if
|
||||||
|
// a DeclRefExpr is involved in a "load" by comparing it to the current
|
||||||
|
// DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr.
|
||||||
|
SaveAndRestore<const DeclRefExpr*> lastDR(currentDR,
|
||||||
|
res.getDeclRefExpr());
|
||||||
|
Visit(bo->getRHS());
|
||||||
|
Visit(bo->getLHS());
|
||||||
|
|
||||||
llvm::BitVector::reference bit = vals[vd];
|
llvm::BitVector::reference bit = vals[vd];
|
||||||
if (bit == Uninitialized) {
|
if (bit == Uninitialized) {
|
||||||
if (bo->getOpcode() != BO_Assign)
|
if (bo->getOpcode() != BO_Assign)
|
||||||
reportUninit(res.getDeclRefExpr(), vd);
|
reportUninit(res.getDeclRefExpr(), vd);
|
||||||
bit = Initialized;
|
bit = Initialized;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Visit(bo->getRHS());
|
||||||
|
Visit(bo->getLHS());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) {
|
void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) {
|
||||||
Visit(uo->getSubExpr());
|
|
||||||
switch (uo->getOpcode()) {
|
switch (uo->getOpcode()) {
|
||||||
case clang::UO_AddrOf:
|
|
||||||
if (const VarDecl *vd = findBlockVarDecl(uo->getSubExpr()).getDecl())
|
|
||||||
vals[vd] = Initialized;
|
|
||||||
break;
|
|
||||||
case clang::UO_PostDec:
|
case clang::UO_PostDec:
|
||||||
case clang::UO_PostInc:
|
case clang::UO_PostInc:
|
||||||
case clang::UO_PreDec:
|
case clang::UO_PreDec:
|
||||||
case clang::UO_PreInc: {
|
case clang::UO_PreInc: {
|
||||||
const FindVarResult &res = findBlockVarDecl(uo->getSubExpr());
|
const FindVarResult &res = findBlockVarDecl(uo->getSubExpr());
|
||||||
if (const VarDecl *vd = res.getDecl()) {
|
if (const VarDecl *vd = res.getDecl()) {
|
||||||
|
// We assume that DeclRefExprs wrapped in a unary operator ++/--
|
||||||
|
// cannot be block-level expressions. Therefore, we determine if
|
||||||
|
// a DeclRefExpr is involved in a "load" by comparing it to the current
|
||||||
|
// DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr.
|
||||||
|
SaveAndRestore<const DeclRefExpr*> lastDR(currentDR,
|
||||||
|
res.getDeclRefExpr());
|
||||||
|
Visit(uo->getSubExpr());
|
||||||
|
|
||||||
llvm::BitVector::reference bit = vals[vd];
|
llvm::BitVector::reference bit = vals[vd];
|
||||||
if (bit == Uninitialized) {
|
if (bit == Uninitialized) {
|
||||||
reportUninit(res.getDeclRefExpr(), vd);
|
reportUninit(res.getDeclRefExpr(), vd);
|
||||||
bit = Initialized;
|
bit = Initialized;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Visit(uo->getSubExpr());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) {
|
void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) {
|
||||||
Visit(ce->getSubExpr());
|
|
||||||
if (ce->getCastKind() == CK_LValueToRValue) {
|
if (ce->getCastKind() == CK_LValueToRValue) {
|
||||||
const FindVarResult &res = findBlockVarDecl(ce->getSubExpr());
|
const FindVarResult &res = findBlockVarDecl(ce->getSubExpr());
|
||||||
if (const VarDecl *vd = res.getDecl())
|
if (const VarDecl *vd = res.getDecl()) {
|
||||||
if (vals[vd] == Uninitialized)
|
// We assume that DeclRefExprs wrapped in an lvalue-to-rvalue cast
|
||||||
|
// cannot be block-level expressions. Therefore, we determine if
|
||||||
|
// a DeclRefExpr is involved in a "load" by comparing it to the current
|
||||||
|
// DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr.
|
||||||
|
// Here we update 'currentDR' to be the one associated with this
|
||||||
|
// lvalue-to-rvalue cast. Then, when we analyze the DeclRefExpr, we
|
||||||
|
// will know that we are not computing its lvalue for other purposes
|
||||||
|
// than to perform a load.
|
||||||
|
SaveAndRestore<const DeclRefExpr*> lastDR(currentDR,
|
||||||
|
res.getDeclRefExpr());
|
||||||
|
Visit(ce->getSubExpr());
|
||||||
|
if (vals[vd] == Uninitialized) {
|
||||||
reportUninit(res.getDeclRefExpr(), vd);
|
reportUninit(res.getDeclRefExpr(), vd);
|
||||||
|
// Don't cascade warnings.
|
||||||
|
vals[vd] = Initialized;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Visit(ce->getSubExpr());
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------====//
|
//------------------------------------------------------------------------====//
|
||||||
|
|
|
@ -442,13 +442,9 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
|
||||||
|
|
||||||
if (Diags.getDiagnosticLevel(diag::warn_var_is_uninit, D->getLocStart())
|
if (Diags.getDiagnosticLevel(diag::warn_var_is_uninit, D->getLocStart())
|
||||||
!= Diagnostic::Ignored) {
|
!= Diagnostic::Ignored) {
|
||||||
if (!S.getLangOptions().CPlusPlus) {
|
if (CFG *cfg = AC.getCFG()) {
|
||||||
CFG *cfg = AC.getCFG();
|
|
||||||
if (cfg) {
|
|
||||||
UninitValsDiagReporter reporter(S);
|
UninitValsDiagReporter reporter(S);
|
||||||
runUninitializedVariablesAnalysis(*cast<DeclContext>(D), *cfg,
|
runUninitializedVariablesAnalysis(*cast<DeclContext>(D), *cfg, reporter);
|
||||||
reporter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,3 +102,12 @@ void test16() {
|
||||||
for (unsigned i = 0 ; i < 100 ; i++)
|
for (unsigned i = 0 ; i < 100 ; i++)
|
||||||
p[i] = 'a'; // no-warning
|
p[i] = 'a'; // no-warning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test17() {
|
||||||
|
// Don't warn multiple times about the same uninitialized variable
|
||||||
|
// along the same path.
|
||||||
|
int *x;
|
||||||
|
*x = 1; // expected-warning{{use of uninitialized variable 'x'}}
|
||||||
|
*x = 1; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -Wuninitialized-experimental -fsyntax-only %s -verify
|
||||||
|
|
||||||
|
int test1_aux(int &x);
|
||||||
|
int test1() {
|
||||||
|
int x;
|
||||||
|
test1_aux(x);
|
||||||
|
return x; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int test2_aux() {
|
||||||
|
int x;
|
||||||
|
int &y = x;
|
||||||
|
return x; // no-warning
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue