2018-02-28 09:10:04 +08:00
|
|
|
//===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===//
|
2008-07-09 05:46:56 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2008-07-09 05:46:56 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defined the Environment and EnvironmentManager classes.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
2010-03-28 05:19:47 +08:00
|
|
|
|
2018-02-28 09:10:04 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
|
|
|
|
#include "clang/AST/Expr.h"
|
2012-01-28 20:06:22 +08:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
2018-02-28 09:10:04 +08:00
|
|
|
#include "clang/AST/PrettyPrinter.h"
|
|
|
|
#include "clang/AST/Stmt.h"
|
2017-09-07 05:45:03 +08:00
|
|
|
#include "clang/Analysis/AnalysisDeclContext.h"
|
2018-02-28 09:10:04 +08:00
|
|
|
#include "clang/Basic/LLVM.h"
|
|
|
|
#include "clang/Basic/LangOptions.h"
|
2019-05-29 23:36:58 +08:00
|
|
|
#include "clang/Basic/JsonSupport.h"
|
2011-08-16 06:09:50 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
|
2018-02-28 09:10:04 +08:00
|
|
|
#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/Casting.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2012-12-02 01:12:56 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2018-02-28 09:10:04 +08:00
|
|
|
#include <cassert>
|
2008-07-09 05:46:56 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2008-07-09 05:46:56 +08:00
|
|
|
|
2012-10-18 03:35:44 +08:00
|
|
|
static const Expr *ignoreTransparentExprs(const Expr *E) {
|
|
|
|
E = E->IgnoreParens();
|
|
|
|
|
|
|
|
switch (E->getStmtClass()) {
|
|
|
|
case Stmt::OpaqueValueExprClass:
|
|
|
|
E = cast<OpaqueValueExpr>(E)->getSourceExpr();
|
|
|
|
break;
|
|
|
|
case Stmt::ExprWithCleanupsClass:
|
|
|
|
E = cast<ExprWithCleanups>(E)->getSubExpr();
|
|
|
|
break;
|
2018-10-31 11:48:47 +08:00
|
|
|
case Stmt::ConstantExprClass:
|
|
|
|
E = cast<ConstantExpr>(E)->getSubExpr();
|
|
|
|
break;
|
2012-10-18 03:35:44 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const Stmt *ignoreTransparentExprs(const Stmt *S) {
|
2018-02-28 09:10:04 +08:00
|
|
|
if (const auto *E = dyn_cast<Expr>(S))
|
2012-10-18 03:35:44 +08:00
|
|
|
return ignoreTransparentExprs(E);
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L)
|
2018-02-28 09:10:04 +08:00
|
|
|
: std::pair<const Stmt *,
|
|
|
|
const StackFrameContext *>(ignoreTransparentExprs(S),
|
2018-06-27 09:51:55 +08:00
|
|
|
L ? L->getStackFrame()
|
2018-02-28 09:10:04 +08:00
|
|
|
: nullptr) {}
|
2012-10-18 03:35:44 +08:00
|
|
|
|
2012-01-07 06:09:28 +08:00
|
|
|
SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
|
2010-12-06 07:36:15 +08:00
|
|
|
const SVal* X = ExprBindings.lookup(E);
|
|
|
|
if (X) {
|
|
|
|
SVal V = *X;
|
|
|
|
return V;
|
|
|
|
}
|
|
|
|
return UnknownVal();
|
|
|
|
}
|
|
|
|
|
2012-01-07 06:09:28 +08:00
|
|
|
SVal Environment::getSVal(const EnvironmentEntry &Entry,
|
2012-10-13 13:05:20 +08:00
|
|
|
SValBuilder& svalBuilder) const {
|
2012-10-18 03:35:44 +08:00
|
|
|
const Stmt *S = Entry.getStmt();
|
2012-01-07 06:09:28 +08:00
|
|
|
const LocationContext *LCtx = Entry.getLocationContext();
|
2012-10-18 03:35:44 +08:00
|
|
|
|
|
|
|
switch (S->getStmtClass()) {
|
|
|
|
case Stmt::CXXBindTemporaryExprClass:
|
|
|
|
case Stmt::ExprWithCleanupsClass:
|
|
|
|
case Stmt::GenericSelectionExprClass:
|
|
|
|
case Stmt::OpaqueValueExprClass:
|
2018-11-09 08:41:36 +08:00
|
|
|
case Stmt::ConstantExprClass:
|
2012-10-18 03:35:44 +08:00
|
|
|
case Stmt::ParenExprClass:
|
|
|
|
case Stmt::SubstNonTypeTemplateParmExprClass:
|
|
|
|
llvm_unreachable("Should have been handled by ignoreTransparentExprs");
|
|
|
|
|
|
|
|
case Stmt::AddrLabelExprClass:
|
[analyzer] Consolidate constant evaluation logic in SValBuilder.
Previously, this was scattered across Environment (literal expressions),
ExprEngine (default arguments), and RegionStore (global constants). The
former special-cased several kinds of simple constant expressions, while
the latter two deferred to the AST's constant evaluator.
Now, these are all unified as SValBuilder::getConstantVal(). To keep
Environment fast, the special cases for simple constant expressions have
been left in, but the main benefits are that (a) unusual constants like
ObjCStringLiterals now work as default arguments and global constant
initializers, and (b) we're not duplicating code between ExprEngine and
RegionStore.
This actually caught a bug in our test suite, which is awesome: we stop
tracking allocated memory if it's passed as an argument along with some
kind of callback, but not if the callback is 0. We were testing this in
a case where the callback parameter had a default value, but that value
was 0. After this change, the analyzer now (correctly) flags that as a
leak!
<rdar://problem/13773117>
llvm-svn: 180894
2013-05-02 07:10:44 +08:00
|
|
|
case Stmt::CharacterLiteralClass:
|
2012-10-18 03:35:44 +08:00
|
|
|
case Stmt::CXXBoolLiteralExprClass:
|
|
|
|
case Stmt::CXXScalarValueInitExprClass:
|
[analyzer] Consolidate constant evaluation logic in SValBuilder.
Previously, this was scattered across Environment (literal expressions),
ExprEngine (default arguments), and RegionStore (global constants). The
former special-cased several kinds of simple constant expressions, while
the latter two deferred to the AST's constant evaluator.
Now, these are all unified as SValBuilder::getConstantVal(). To keep
Environment fast, the special cases for simple constant expressions have
been left in, but the main benefits are that (a) unusual constants like
ObjCStringLiterals now work as default arguments and global constant
initializers, and (b) we're not duplicating code between ExprEngine and
RegionStore.
This actually caught a bug in our test suite, which is awesome: we stop
tracking allocated memory if it's passed as an argument along with some
kind of callback, but not if the callback is 0. We were testing this in
a case where the callback parameter had a default value, but that value
was 0. After this change, the analyzer now (correctly) flags that as a
leak!
<rdar://problem/13773117>
llvm-svn: 180894
2013-05-02 07:10:44 +08:00
|
|
|
case Stmt::ImplicitValueInitExprClass:
|
2012-10-18 03:35:44 +08:00
|
|
|
case Stmt::IntegerLiteralClass:
|
|
|
|
case Stmt::ObjCBoolLiteralExprClass:
|
|
|
|
case Stmt::CXXNullPtrLiteralExprClass:
|
[analyzer] Consolidate constant evaluation logic in SValBuilder.
Previously, this was scattered across Environment (literal expressions),
ExprEngine (default arguments), and RegionStore (global constants). The
former special-cased several kinds of simple constant expressions, while
the latter two deferred to the AST's constant evaluator.
Now, these are all unified as SValBuilder::getConstantVal(). To keep
Environment fast, the special cases for simple constant expressions have
been left in, but the main benefits are that (a) unusual constants like
ObjCStringLiterals now work as default arguments and global constant
initializers, and (b) we're not duplicating code between ExprEngine and
RegionStore.
This actually caught a bug in our test suite, which is awesome: we stop
tracking allocated memory if it's passed as an argument along with some
kind of callback, but not if the callback is 0. We were testing this in
a case where the callback parameter had a default value, but that value
was 0. After this change, the analyzer now (correctly) flags that as a
leak!
<rdar://problem/13773117>
llvm-svn: 180894
2013-05-02 07:10:44 +08:00
|
|
|
case Stmt::ObjCStringLiteralClass:
|
|
|
|
case Stmt::StringLiteralClass:
|
2015-09-23 03:33:15 +08:00
|
|
|
case Stmt::TypeTraitExprClass:
|
2019-08-29 02:44:35 +08:00
|
|
|
case Stmt::SizeOfPackExprClass:
|
[analyzer] Consolidate constant evaluation logic in SValBuilder.
Previously, this was scattered across Environment (literal expressions),
ExprEngine (default arguments), and RegionStore (global constants). The
former special-cased several kinds of simple constant expressions, while
the latter two deferred to the AST's constant evaluator.
Now, these are all unified as SValBuilder::getConstantVal(). To keep
Environment fast, the special cases for simple constant expressions have
been left in, but the main benefits are that (a) unusual constants like
ObjCStringLiterals now work as default arguments and global constant
initializers, and (b) we're not duplicating code between ExprEngine and
RegionStore.
This actually caught a bug in our test suite, which is awesome: we stop
tracking allocated memory if it's passed as an argument along with some
kind of callback, but not if the callback is 0. We were testing this in
a case where the callback parameter had a default value, but that value
was 0. After this change, the analyzer now (correctly) flags that as a
leak!
<rdar://problem/13773117>
llvm-svn: 180894
2013-05-02 07:10:44 +08:00
|
|
|
// Known constants; defer to SValBuilder.
|
|
|
|
return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
|
2012-10-18 03:35:44 +08:00
|
|
|
|
|
|
|
case Stmt::ReturnStmtClass: {
|
2018-02-28 09:10:04 +08:00
|
|
|
const auto *RS = cast<ReturnStmt>(S);
|
2012-10-18 03:35:44 +08:00
|
|
|
if (const Expr *RE = RS->getRetValue())
|
|
|
|
return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
|
2015-09-08 11:50:52 +08:00
|
|
|
return UndefinedVal();
|
2012-10-18 03:35:44 +08:00
|
|
|
}
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2012-10-18 03:35:44 +08:00
|
|
|
// Handle all other Stmt* using a lookup.
|
|
|
|
default:
|
[analyzer] Consolidate constant evaluation logic in SValBuilder.
Previously, this was scattered across Environment (literal expressions),
ExprEngine (default arguments), and RegionStore (global constants). The
former special-cased several kinds of simple constant expressions, while
the latter two deferred to the AST's constant evaluator.
Now, these are all unified as SValBuilder::getConstantVal(). To keep
Environment fast, the special cases for simple constant expressions have
been left in, but the main benefits are that (a) unusual constants like
ObjCStringLiterals now work as default arguments and global constant
initializers, and (b) we're not duplicating code between ExprEngine and
RegionStore.
This actually caught a bug in our test suite, which is awesome: we stop
tracking allocated memory if it's passed as an argument along with some
kind of callback, but not if the callback is 0. We were testing this in
a case where the callback parameter had a default value, but that value
was 0. After this change, the analyzer now (correctly) flags that as a
leak!
<rdar://problem/13773117>
llvm-svn: 180894
2013-05-02 07:10:44 +08:00
|
|
|
return lookupExpr(EnvironmentEntry(S, LCtx));
|
2008-07-11 01:19:18 +08:00
|
|
|
}
|
|
|
|
}
|
2008-07-09 05:46:56 +08:00
|
|
|
|
2012-01-07 06:09:28 +08:00
|
|
|
Environment EnvironmentManager::bindExpr(Environment Env,
|
|
|
|
const EnvironmentEntry &E,
|
|
|
|
SVal V,
|
|
|
|
bool Invalidate) {
|
2009-09-09 23:08:12 +08:00
|
|
|
if (V.isUnknown()) {
|
2008-07-11 01:19:18 +08:00
|
|
|
if (Invalidate)
|
2012-01-07 06:09:28 +08:00
|
|
|
return Environment(F.remove(Env.ExprBindings, E));
|
2008-07-11 01:19:18 +08:00
|
|
|
else
|
|
|
|
return Env;
|
|
|
|
}
|
2012-01-07 06:09:28 +08:00
|
|
|
return Environment(F.add(Env.ExprBindings, E, V));
|
2008-07-11 01:19:18 +08:00
|
|
|
}
|
2008-08-21 01:08:29 +08:00
|
|
|
|
2009-02-14 11:16:10 +08:00
|
|
|
namespace {
|
2018-02-28 09:10:04 +08:00
|
|
|
|
2015-08-14 06:50:09 +08:00
|
|
|
class MarkLiveCallback final : public SymbolVisitor {
|
2009-02-14 11:16:10 +08:00
|
|
|
SymbolReaper &SymReaper;
|
2018-02-28 09:10:04 +08:00
|
|
|
|
2009-02-14 11:16:10 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {}
|
2018-02-28 09:10:04 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
bool VisitSymbol(SymbolRef sym) override {
|
2012-02-21 08:46:29 +08:00
|
|
|
SymReaper.markLive(sym);
|
|
|
|
return true;
|
|
|
|
}
|
2018-02-28 09:10:04 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
bool VisitMemRegion(const MemRegion *R) override {
|
2012-02-21 08:46:29 +08:00
|
|
|
SymReaper.markLive(R);
|
|
|
|
return true;
|
|
|
|
}
|
2009-02-14 11:16:10 +08:00
|
|
|
};
|
2018-02-28 09:10:04 +08:00
|
|
|
|
|
|
|
} // namespace
|
2009-02-14 11:16:10 +08:00
|
|
|
|
2011-01-15 04:34:15 +08:00
|
|
|
// removeDeadBindings:
|
2009-03-12 15:54:17 +08:00
|
|
|
// - Remove subexpression bindings.
|
|
|
|
// - Remove dead block expression bindings.
|
|
|
|
// - Keep live block expression bindings:
|
2009-09-09 23:08:12 +08:00
|
|
|
// - Mark their reachable symbols live in SymbolReaper,
|
2009-03-12 15:54:17 +08:00
|
|
|
// see ScanReachableSymbols.
|
|
|
|
// - Mark the region in DRoots if the binding is a loc::MemRegionVal.
|
2009-09-09 23:08:12 +08:00
|
|
|
Environment
|
2011-01-15 04:34:15 +08:00
|
|
|
EnvironmentManager::removeDeadBindings(Environment Env,
|
2009-08-27 09:39:13 +08:00
|
|
|
SymbolReaper &SymReaper,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef ST) {
|
2009-08-27 09:39:13 +08:00
|
|
|
// 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).
|
2010-03-05 12:45:36 +08:00
|
|
|
Environment NewEnv = getInitialEnvironment();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-09-23 02:10:41 +08:00
|
|
|
MarkLiveCallback CB(SymReaper);
|
|
|
|
ScanReachableSymbols RSScaner(ST, CB);
|
|
|
|
|
2018-02-28 09:10:04 +08:00
|
|
|
llvm::ImmutableMapRef<EnvironmentEntry, SVal>
|
2011-09-24 03:14:09 +08:00
|
|
|
EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
|
|
|
|
F.getTreeFactory());
|
|
|
|
|
2008-08-21 01:08:29 +08:00
|
|
|
// Iterate over the block-expr bindings.
|
2020-07-25 01:10:50 +08:00
|
|
|
for (Environment::iterator I = Env.begin(), E = Env.end();
|
|
|
|
I != E; ++I) {
|
2012-01-07 06:09:28 +08:00
|
|
|
const EnvironmentEntry &BlkExpr = I.getKey();
|
2010-04-05 21:16:29 +08:00
|
|
|
const SVal &X = I.getData();
|
|
|
|
|
2020-07-25 01:10:50 +08:00
|
|
|
if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) {
|
2009-08-27 09:39:13 +08:00
|
|
|
// Copy the binding to the new map.
|
2011-09-24 03:14:09 +08:00
|
|
|
EBMapRef = EBMapRef.add(BlkExpr, X);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-14 11:16:10 +08:00
|
|
|
// Mark all symbols in the block expr's value live.
|
2011-09-23 02:10:41 +08:00
|
|
|
RSScaner.scan(X);
|
2008-08-21 01:08:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-24 03:14:09 +08:00
|
|
|
NewEnv.ExprBindings = EBMapRef.asImmutableMap();
|
2009-08-27 09:39:13 +08:00
|
|
|
return NewEnv;
|
2008-08-21 01:08:29 +08:00
|
|
|
}
|
2012-01-07 06:09:28 +08:00
|
|
|
|
2019-05-29 23:36:58 +08:00
|
|
|
void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx,
|
|
|
|
const LocationContext *LCtx, const char *NL,
|
|
|
|
unsigned int Space, bool IsDot) const {
|
|
|
|
Indent(Out, Space, IsDot) << "\"environment\": ";
|
|
|
|
|
|
|
|
if (ExprBindings.isEmpty()) {
|
|
|
|
Out << "null," << NL;
|
2018-02-09 06:24:38 +08:00
|
|
|
return;
|
2019-05-29 23:36:58 +08:00
|
|
|
}
|
2018-02-09 06:24:38 +08:00
|
|
|
|
2019-06-25 11:17:55 +08:00
|
|
|
++Space;
|
2019-05-29 23:36:58 +08:00
|
|
|
if (!LCtx) {
|
2018-02-09 06:24:38 +08:00
|
|
|
// Find the freshest location context.
|
|
|
|
llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts;
|
2019-05-29 23:36:58 +08:00
|
|
|
for (const auto &I : *this) {
|
2018-02-09 06:24:38 +08:00
|
|
|
const LocationContext *LC = I.first.getLocationContext();
|
|
|
|
if (FoundContexts.count(LC) == 0) {
|
|
|
|
// This context is fresher than all other contexts so far.
|
2019-05-29 23:36:58 +08:00
|
|
|
LCtx = LC;
|
2018-02-09 06:24:38 +08:00
|
|
|
for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent())
|
|
|
|
FoundContexts.insert(LCI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-07 06:09:28 +08:00
|
|
|
|
2019-05-29 23:36:58 +08:00
|
|
|
assert(LCtx);
|
|
|
|
|
2019-06-25 11:17:55 +08:00
|
|
|
Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame()
|
|
|
|
<< "\", \"items\": [" << NL;
|
2019-05-29 23:36:58 +08:00
|
|
|
PrintingPolicy PP = Ctx.getPrintingPolicy();
|
|
|
|
|
|
|
|
LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
|
|
|
|
// LCtx items begin
|
|
|
|
bool HasItem = false;
|
|
|
|
unsigned int InnerSpace = Space + 1;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2019-05-29 23:36:58 +08:00
|
|
|
// 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.getLocationContext() != LC)
|
2018-02-09 06:24:38 +08:00
|
|
|
continue;
|
2014-06-10 06:53:25 +08:00
|
|
|
|
2019-05-29 23:36:58 +08:00
|
|
|
if (!HasItem) {
|
|
|
|
HasItem = true;
|
|
|
|
Out << '[' << NL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Stmt *S = I->first.getStmt();
|
2019-05-30 02:36:54 +08:00
|
|
|
(void)S;
|
2018-02-09 06:24:38 +08:00
|
|
|
assert(S != nullptr && "Expected non-null Stmt");
|
|
|
|
|
2019-05-29 23:36:58 +08:00
|
|
|
LastI = I;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
|
|
|
|
++I) {
|
|
|
|
if (I->first.getLocationContext() != LC)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const Stmt *S = I->first.getStmt();
|
|
|
|
Indent(Out, InnerSpace, IsDot)
|
2019-06-20 07:33:48 +08:00
|
|
|
<< "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": ";
|
[analyzer][AST] print() JSONify: Stmt implementation
Summary:
This patch also adds a function called `JsonFormat()` which:
- Flattens the string so removes the new-lines.
- Escapes double quotes.
Reviewers: NoQ, xazax.hun, ravikandhadai, baloghadamsoftware, Szelethus
Reviewed By: NoQ
Subscribers: cfe-commits, szepet, rnkovacs, a.sidorin, mikhail.ramalho,
donat.nagy, dkrupp
Tags: #clang
Differential Revision: https://reviews.llvm.org/D62494
llvm-svn: 362000
2019-05-30 02:17:18 +08:00
|
|
|
S->printJson(Out, nullptr, PP, /*AddQuotes=*/true);
|
2019-05-29 23:36:58 +08:00
|
|
|
|
[analyzer] print() JSONify: SVal implementation
Summary: -
Reviewers: NoQ, xazax.hun, ravikandhadai, baloghadamsoftware, Szelethus
Reviewed By: NoQ
Subscribers: cfe-commits, szepet, rnkovacs, a.sidorin, mikhail.ramalho,
Szelethus, donat.nagy, dkrupp
Tags: #clang
Differential Revision: https://reviews.llvm.org/D62497
llvm-svn: 362008
2019-05-30 02:38:52 +08:00
|
|
|
Out << ", \"value\": ";
|
|
|
|
I->second.printJson(Out, /*AddQuotes=*/true);
|
|
|
|
|
|
|
|
Out << " }";
|
2019-05-29 23:36:58 +08:00
|
|
|
|
|
|
|
if (I != LastI)
|
|
|
|
Out << ',';
|
|
|
|
Out << NL;
|
2018-02-09 06:24:38 +08:00
|
|
|
}
|
2019-05-29 23:36:58 +08:00
|
|
|
|
|
|
|
if (HasItem)
|
|
|
|
Indent(Out, --InnerSpace, IsDot) << ']';
|
|
|
|
else
|
|
|
|
Out << "null ";
|
2018-02-09 06:24:38 +08:00
|
|
|
});
|
2019-05-29 23:36:58 +08:00
|
|
|
|
2019-06-25 11:17:55 +08:00
|
|
|
Indent(Out, --Space, IsDot) << "]}," << NL;
|
2012-01-07 06:09:28 +08:00
|
|
|
}
|