blob: 8494f7600ebbe947df781daf35436d100ff641f6 [file] [edit]
//===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defined the Environment and EnvironmentManager classes.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
using namespace clang;
using namespace ento;
static const Expr *ignoreTransparentExprs(const Expr *E) {
E = E->IgnoreParens();
switch (E->getStmtClass()) {
case Stmt::OpaqueValueExprClass:
if (const Expr *SE = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
E = SE;
break;
}
return E;
case Stmt::ExprWithCleanupsClass:
E = cast<ExprWithCleanups>(E)->getSubExpr();
break;
case Stmt::ConstantExprClass:
E = cast<ConstantExpr>(E)->getSubExpr();
break;
case Stmt::CXXBindTemporaryExprClass:
E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
break;
case Stmt::SubstNonTypeTemplateParmExprClass:
E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement();
break;
default:
// This is the base case: we can't look through more than we already have.
return E;
}
return ignoreTransparentExprs(E);
}
EnvironmentEntry::EnvironmentEntry(const Expr *E, const StackFrame *SF)
: std::pair<const Expr *, const StackFrame *>(ignoreTransparentExprs(E),
SF) {}
SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
const SVal* X = ExprBindings.lookup(E);
if (X) {
SVal V = *X;
return V;
}
return UnknownVal();
}
SVal Environment::getSVal(const EnvironmentEntry &Entry,
SValBuilder& svalBuilder) const {
const Expr *Ex = Entry.getExpr();
const StackFrame *SF = Entry.getStackFrame();
switch (Ex->getStmtClass()) {
case Stmt::CXXBindTemporaryExprClass:
case Stmt::ExprWithCleanupsClass:
case Stmt::GenericSelectionExprClass:
case Stmt::ConstantExprClass:
case Stmt::ParenExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
llvm_unreachable("Should have been handled by ignoreTransparentExprs");
case Stmt::AddrLabelExprClass:
case Stmt::CharacterLiteralClass:
case Stmt::CXXBoolLiteralExprClass:
case Stmt::CXXScalarValueInitExprClass:
case Stmt::ImplicitValueInitExprClass:
case Stmt::IntegerLiteralClass:
case Stmt::ObjCBoolLiteralExprClass:
case Stmt::CXXNullPtrLiteralExprClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::StringLiteralClass:
case Stmt::TypeTraitExprClass:
case Stmt::SizeOfPackExprClass:
case Stmt::PredefinedExprClass:
// Known constants; defer to SValBuilder.
return *svalBuilder.getConstantVal(Ex);
// Handle all other Expr* using a lookup.
default:
return lookupExpr(EnvironmentEntry(Ex, SF));
}
}
Environment EnvironmentManager::bindExpr(Environment Env,
const EnvironmentEntry &E,
SVal V,
bool Invalidate) {
if (V.isUnknown()) {
if (Invalidate)
return Environment(F.remove(Env.ExprBindings, E));
else
return Env;
}
return Environment(F.add(Env.ExprBindings, E, V));
}
namespace {
class MarkLiveCallback final : public SymbolVisitor {
SymbolReaper &SymReaper;
public:
MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {}
bool VisitSymbol(SymbolRef sym) override {
SymReaper.markLive(sym);
return true;
}
bool VisitMemRegion(const MemRegion *R) override {
SymReaper.markLive(R);
return true;
}
};
} // namespace
// removeDeadBindings:
// - Remove subexpression bindings.
// - Remove dead block expression bindings.
// - Keep live block expression bindings:
// - Mark their reachable symbols live in SymbolReaper,
// see ScanReachableSymbols.
// - Mark the region in DRoots if the binding is a loc::MemRegionVal.
Environment
EnvironmentManager::removeDeadBindings(Environment Env,
SymbolReaper &SymReaper,
ProgramStateRef ST) {
// We construct a new Environment object entirely, as this is cheaper than
// individually removing all the subexpression bindings (which will greatly
// outnumber block-level expression bindings).
Environment NewEnv = getInitialEnvironment();
MarkLiveCallback CB(SymReaper);
ScanReachableSymbols RSScaner(ST, CB);
llvm::ImmutableMapRef<EnvironmentEntry, SVal>
EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
F.getTreeFactory());
// Iterate over the block-expr bindings.
for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) {
const EnvironmentEntry &BlkExpr = I.getKey();
SVal X = I.getData();
if (SymReaper.isLive(BlkExpr.getExpr(), BlkExpr.getStackFrame())) {
// Copy the binding to the new map.
EBMapRef = EBMapRef.add(BlkExpr, X);
// Mark all symbols in the block expr's value live.
RSScaner.scan(X);
}
}
NewEnv.ExprBindings = EBMapRef.asImmutableMap();
return NewEnv;
}
void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx,
const StackFrame *SF, const char *NL,
unsigned int Space, bool IsDot) const {
Indent(Out, Space, IsDot) << "\"environment\": ";
if (ExprBindings.isEmpty()) {
Out << "null," << NL;
return;
}
++Space;
if (!SF) {
// Find the freshest stack frame.
llvm::SmallPtrSet<const StackFrame *, 16> FoundStackFrames;
for (const auto &I : *this) {
const StackFrame *CurrentSF = I.first.getStackFrame();
if (FoundStackFrames.count(CurrentSF) == 0) {
// This stack frame is fresher than all other stack frames so far.
SF = CurrentSF;
for (const StackFrame *SFI = CurrentSF; SFI; SFI = SFI->getParent())
FoundStackFrames.insert(SFI);
}
}
}
assert(SF);
Out << "{ \"pointer\": \"" << (const void *)SF << "\", \"items\": [" << NL;
PrintingPolicy PP = Ctx.getPrintingPolicy();
SF->printJson(Out, NL, Space, IsDot, [&](const StackFrame *SF) {
// SF items begin
bool HasItem = false;
unsigned int InnerSpace = Space + 1;
// Store the last ExprBinding which we will print.
BindingsTy::iterator LastI = ExprBindings.end();
for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
++I) {
if (I->first.getStackFrame() != SF)
continue;
if (!HasItem) {
HasItem = true;
Out << '[' << NL;
}
const Expr *Ex = I->first.getExpr();
(void)Ex;
assert(Ex != nullptr && "Expected non-null Expr");
LastI = I;
}
for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
++I) {
if (I->first.getStackFrame() != SF)
continue;
const Expr *Ex = I->first.getExpr();
Indent(Out, InnerSpace, IsDot)
<< "{ \"stmt_id\": " << Ex->getID(Ctx) << ", \"kind\": \""
<< Ex->getStmtClassName() << "\", \"pretty\": ";
Ex->printJson(Out, nullptr, PP, /*AddQuotes=*/true);
Out << ", \"value\": ";
I->second.printJson(Out, /*AddQuotes=*/true);
Out << " }";
if (I != LastI)
Out << ',';
Out << NL;
}
if (HasItem)
Indent(Out, --InnerSpace, IsDot) << ']';
else
Out << "null ";
});
Indent(Out, --Space, IsDot) << "]}," << NL;
}