llvm-project/clang/lib/Analysis/BodyFarm.cpp

774 lines
26 KiB
C++
Raw Normal View History

//== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// BodyFarm is a factory for creating faux implementations for functions/methods
// for analysis purposes.
//
//===----------------------------------------------------------------------===//
#include "BodyFarm.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/Analysis/CodeInjector.h"
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "body-farm"
using namespace clang;
//===----------------------------------------------------------------------===//
// Helper creation functions for constructing faux ASTs.
//===----------------------------------------------------------------------===//
static bool isDispatchBlock(QualType Ty) {
// Is it a block pointer?
const BlockPointerType *BPT = Ty->getAs<BlockPointerType>();
if (!BPT)
return false;
// Check if the block pointer type takes no arguments and
// returns void.
const FunctionProtoType *FT =
BPT->getPointeeType()->getAs<FunctionProtoType>();
return FT && FT->getReturnType()->isVoidType() && FT->getNumParams() == 0;
}
namespace {
class ASTMaker {
public:
ASTMaker(ASTContext &C) : C(C) {}
/// Create a new BinaryOperator representing a simple assignment.
BinaryOperator *makeAssignment(const Expr *LHS, const Expr *RHS, QualType Ty);
/// Create a new BinaryOperator representing a comparison.
BinaryOperator *makeComparison(const Expr *LHS, const Expr *RHS,
BinaryOperator::Opcode Op);
/// Create a new compound stmt using the provided statements.
CompoundStmt *makeCompound(ArrayRef<Stmt*>);
/// Create a new DeclRefExpr for the referenced variable.
DeclRefExpr *makeDeclRefExpr(const VarDecl *D,
bool RefersToEnclosingVariableOrCapture = false,
bool GetNonReferenceType = false);
/// Create a new UnaryOperator representing a dereference.
UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);
/// Create an implicit cast for an integer conversion.
Expr *makeIntegralCast(const Expr *Arg, QualType Ty);
/// Create an implicit cast to a builtin boolean type.
ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg);
/// Create an implicit cast for lvalue-to-rvaluate conversions.
ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);
/// Make RValue out of variable declaration, creating a temporary
/// DeclRefExpr in the process.
ImplicitCastExpr *
makeLvalueToRvalue(const VarDecl *Decl,
bool RefersToEnclosingVariableOrCapture = false,
bool GetNonReferenceType = false);
/// Create an implicit cast of the given type.
ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,
CastKind CK = CK_LValueToRValue);
/// Create an Objective-C bool literal.
ObjCBoolLiteralExpr *makeObjCBool(bool Val);
/// Create an Objective-C ivar reference.
ObjCIvarRefExpr *makeObjCIvarRef(const Expr *Base, const ObjCIvarDecl *IVar);
/// Create a Return statement.
ReturnStmt *makeReturn(const Expr *RetVal);
/// Create an integer literal.
IntegerLiteral *makeIntegerLiteral(uint64_t value);
/// Create a member expression.
MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
bool IsArrow = false,
ExprValueKind ValueKind = VK_LValue);
/// Returns a *first* member field of a record declaration with a given name.
/// \return an nullptr if no member with such a name exists.
NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);
private:
ASTContext &C;
};
}
BinaryOperator *ASTMaker::makeAssignment(const Expr *LHS, const Expr *RHS,
QualType Ty) {
return new (C) BinaryOperator(const_cast<Expr*>(LHS), const_cast<Expr*>(RHS),
BO_Assign, Ty, VK_RValue,
OK_Ordinary, SourceLocation(), FPOptions());
}
BinaryOperator *ASTMaker::makeComparison(const Expr *LHS, const Expr *RHS,
BinaryOperator::Opcode Op) {
assert(BinaryOperator::isLogicalOp(Op) ||
BinaryOperator::isComparisonOp(Op));
return new (C) BinaryOperator(const_cast<Expr*>(LHS),
const_cast<Expr*>(RHS),
Op,
C.getLogicalOperationType(),
VK_RValue,
OK_Ordinary, SourceLocation(), FPOptions());
}
CompoundStmt *ASTMaker::makeCompound(ArrayRef<Stmt *> Stmts) {
return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation());
}
DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D,
bool RefersToEnclosingVariableOrCapture,
bool GetNonReferenceType) {
auto Type = D->getType();
if (GetNonReferenceType)
Type = Type.getNonReferenceType();
DeclRefExpr *DR = DeclRefExpr::Create(
C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl *>(D),
RefersToEnclosingVariableOrCapture, SourceLocation(), Type, VK_LValue);
return DR;
}
UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) {
return new (C) UnaryOperator(const_cast<Expr*>(Arg), UO_Deref, Ty,
VK_LValue, OK_Ordinary, SourceLocation());
}
ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) {
return makeImplicitCast(Arg, Ty, CK_LValueToRValue);
}
ImplicitCastExpr *
ASTMaker::makeLvalueToRvalue(const VarDecl *Arg,
bool RefersToEnclosingVariableOrCapture,
bool GetNonReferenceType) {
auto Type = Arg->getType();
if (GetNonReferenceType)
Type = Type.getNonReferenceType();
return makeLvalueToRvalue(makeDeclRefExpr(Arg,
RefersToEnclosingVariableOrCapture,
GetNonReferenceType),
Type);
}
ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType Ty,
CastKind CK) {
return ImplicitCastExpr::Create(C, Ty,
/* CastKind= */ CK,
/* Expr= */ const_cast<Expr *>(Arg),
/* CXXCastPath= */ nullptr,
/* ExprValueKind= */ VK_RValue);
}
Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {
if (Arg->getType() == Ty)
return const_cast<Expr*>(Arg);
return ImplicitCastExpr::Create(C, Ty, CK_IntegralCast,
const_cast<Expr*>(Arg), nullptr, VK_RValue);
}
ImplicitCastExpr *ASTMaker::makeIntegralCastToBoolean(const Expr *Arg) {
return ImplicitCastExpr::Create(C, C.BoolTy, CK_IntegralToBoolean,
const_cast<Expr*>(Arg), nullptr, VK_RValue);
}
ObjCBoolLiteralExpr *ASTMaker::makeObjCBool(bool Val) {
QualType Ty = C.getBOOLDecl() ? C.getBOOLType() : C.ObjCBuiltinBoolTy;
return new (C) ObjCBoolLiteralExpr(Val, Ty, SourceLocation());
}
ObjCIvarRefExpr *ASTMaker::makeObjCIvarRef(const Expr *Base,
const ObjCIvarDecl *IVar) {
return new (C) ObjCIvarRefExpr(const_cast<ObjCIvarDecl*>(IVar),
IVar->getType(), SourceLocation(),
SourceLocation(), const_cast<Expr*>(Base),
/*arrow=*/true, /*free=*/false);
}
ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) {
return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal),
nullptr);
}
IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t value) {
return IntegerLiteral::Create(C,
llvm::APInt(
/*numBits=*/C.getTypeSize(C.IntTy), value),
/*QualType=*/C.IntTy, SourceLocation());
}
MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
bool IsArrow,
ExprValueKind ValueKind) {
DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl, AS_public);
return MemberExpr::Create(
C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),
SourceLocation(), MemberDecl, FoundDecl,
DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),
/* TemplateArgumentListInfo= */ nullptr, MemberDecl->getType(), ValueKind,
OK_Ordinary);
}
NamedDecl *ASTMaker::findMemberField(const CXXRecordDecl *RD, StringRef Name) {
CXXBasePaths Paths(
/* FindAmbiguities=*/false,
/* RecordPaths=*/false,
/* DetectVirtual= */ false);
const IdentifierInfo &II = C.Idents.get(Name);
DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II);
DeclContextLookupResult Decls = RD->lookup(DeclName);
for (NamedDecl *FoundDecl : Decls)
if (!FoundDecl->getDeclContext()->isFunctionOrMethod())
return FoundDecl;
return nullptr;
}
//===----------------------------------------------------------------------===//
// Creation functions for faux ASTs.
//===----------------------------------------------------------------------===//
typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
const ParmVarDecl *Callback,
ArrayRef<Expr *> CallArgs) {
return new (C) CallExpr(
/*ASTContext=*/C,
/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Callback),
/*args=*/CallArgs,
/*QualType=*/C.VoidTy,
/*ExprValueType=*/VK_RValue,
/*SourceLocation=*/SourceLocation());
}
static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
const ParmVarDecl *Callback,
QualType CallbackType,
ArrayRef<Expr *> CallArgs) {
CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
assert(CallbackDecl != nullptr);
assert(CallbackDecl->isLambda());
FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();
assert(callOperatorDecl != nullptr);
DeclRefExpr *callOperatorDeclRef =
DeclRefExpr::Create(/* Ctx = */ C,
/* QualifierLoc = */ NestedNameSpecifierLoc(),
/* TemplateKWLoc = */ SourceLocation(),
const_cast<FunctionDecl *>(callOperatorDecl),
/* RefersToEnclosingVariableOrCapture= */ false,
/* NameLoc = */ SourceLocation(),
/* T = */ callOperatorDecl->getType(),
/* VK = */ VK_LValue);
return new (C)
CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef,
/*args=*/CallArgs,
/*QualType=*/C.VoidTy,
/*ExprValueType=*/VK_RValue,
/*SourceLocation=*/SourceLocation(), FPOptions());
}
/// Create a fake body for std::call_once.
/// Emulates the following function body:
///
/// \code
/// typedef struct once_flag_s {
/// unsigned long __state = 0;
/// } once_flag;
/// template<class Callable>
/// void call_once(once_flag& o, Callable func) {
/// if (!o.__state) {
/// func();
/// }
/// o.__state = 1;
/// }
/// \endcode
static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
DEBUG(llvm::dbgs() << "Generating body for call_once\n");
// We need at least two parameters.
if (D->param_size() < 2)
return nullptr;
ASTMaker M(C);
const ParmVarDecl *Flag = D->getParamDecl(0);
const ParmVarDecl *Callback = D->getParamDecl(1);
QualType CallbackType = Callback->getType().getNonReferenceType();
QualType FlagType = Flag->getType().getNonReferenceType();
CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl();
if (!FlagCXXDecl) {
DEBUG(llvm::dbgs() << "Flag field is not a CXX record: "
<< "unknown std::call_once implementation."
<< "Ignoring the call.\n");
return nullptr;
}
// Note: here we are assuming libc++ implementation of call_once,
// which has a struct with a field `__state_`.
// Body farming might not work for other `call_once` implementations.
NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");
ValueDecl *FieldDecl;
if (FoundDecl) {
FieldDecl = dyn_cast<ValueDecl>(FoundDecl);
} else {
DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "
<< "unable to synthesize call_once body, ignoring "
<< "the call.\n");
return nullptr;
}
bool isLambdaCall = CallbackType->getAsCXXRecordDecl() &&
CallbackType->getAsCXXRecordDecl()->isLambda();
SmallVector<Expr *, 5> CallArgs;
if (isLambdaCall)
// Lambda requires callback itself inserted as a first parameter.
CallArgs.push_back(
M.makeDeclRefExpr(Callback,
/* RefersToEnclosingVariableOrCapture= */ true,
/* GetNonReferenceType= */ true));
// All arguments past first two ones are passed to the callback.
for (unsigned int i = 2; i < D->getNumParams(); i++)
CallArgs.push_back(M.makeLvalueToRvalue(D->getParamDecl(i)));
CallExpr *CallbackCall;
if (isLambdaCall) {
CallbackCall =
create_call_once_lambda_call(C, M, Callback, CallbackType, CallArgs);
} else {
// Function pointer case.
CallbackCall = create_call_once_funcptr_call(C, M, Callback, CallArgs);
}
DeclRefExpr *FlagDecl =
M.makeDeclRefExpr(Flag,
/* RefersToEnclosingVariableOrCapture=*/true,
/* GetNonReferenceType=*/true);
MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl);
assert(Deref->isLValue());
QualType DerefType = Deref->getType();
// Negation predicate.
UnaryOperator *FlagCheck = new (C) UnaryOperator(
/* input= */
M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType), DerefType,
CK_IntegralToBoolean),
/* opc= */ UO_LNot,
/* QualType= */ C.IntTy,
/* ExprValueKind= */ VK_RValue,
/* ExprObjectKind= */ OK_Ordinary, SourceLocation());
// Create assignment.
BinaryOperator *FlagAssignment = M.makeAssignment(
Deref, M.makeIntegralCast(M.makeIntegerLiteral(1), DerefType), DerefType);
IfStmt *Out = new (C)
IfStmt(C, SourceLocation(),
/* IsConstexpr= */ false,
/* init= */ nullptr,
/* var= */ nullptr,
/* cond= */ FlagCheck,
/* then= */ M.makeCompound({CallbackCall, FlagAssignment}));
return Out;
}
/// Create a fake body for dispatch_once.
static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
// Check if we have at least two parameters.
if (D->param_size() != 2)
return nullptr;
// Check if the first parameter is a pointer to integer type.
const ParmVarDecl *Predicate = D->getParamDecl(0);
QualType PredicateQPtrTy = Predicate->getType();
const PointerType *PredicatePtrTy = PredicateQPtrTy->getAs<PointerType>();
if (!PredicatePtrTy)
return nullptr;
QualType PredicateTy = PredicatePtrTy->getPointeeType();
if (!PredicateTy->isIntegerType())
return nullptr;
// Check if the second parameter is the proper block type.
const ParmVarDecl *Block = D->getParamDecl(1);
QualType Ty = Block->getType();
if (!isDispatchBlock(Ty))
return nullptr;
// Everything checks out. Create a fakse body that checks the predicate,
// sets it, and calls the block. Basically, an AST dump of:
//
// void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) {
// if (!*predicate) {
// *predicate = 1;
// block();
// }
// }
ASTMaker M(C);
// (1) Create the call.
CallExpr *CE = new (C) CallExpr(
/*ASTContext=*/C,
/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
/*args=*/None,
/*QualType=*/C.VoidTy,
/*ExprValueType=*/VK_RValue,
/*SourceLocation=*/SourceLocation());
// (2) Create the assignment to the predicate.
IntegerLiteral *IL = M.makeIntegerLiteral(1);
BinaryOperator *B =
M.makeAssignment(
M.makeDereference(
M.makeLvalueToRvalue(
M.makeDeclRefExpr(Predicate), PredicateQPtrTy),
PredicateTy),
M.makeIntegralCast(IL, PredicateTy),
PredicateTy);
// (3) Create the compound statement.
Stmt *Stmts[] = { B, CE };
CompoundStmt *CS = M.makeCompound(Stmts);
// (4) Create the 'if' condition.
ImplicitCastExpr *LValToRval =
M.makeLvalueToRvalue(
M.makeDereference(
M.makeLvalueToRvalue(
M.makeDeclRefExpr(Predicate),
PredicateQPtrTy),
PredicateTy),
PredicateTy);
UnaryOperator *UO = new (C) UnaryOperator(
/* input= */ LValToRval,
/* opc= */ UO_LNot,
/* QualType= */ C.IntTy,
/* ExprValueKind= */ VK_RValue,
/* ExprObjectKind= */ OK_Ordinary, SourceLocation());
// (5) Create the 'if' statement.
IfStmt *If = new (C) IfStmt(C, SourceLocation(),
/* IsConstexpr= */ false,
/* init= */ nullptr,
/* var= */ nullptr,
/* cond= */ UO,
/* then= */ CS);
return If;
}
/// Create a fake body for dispatch_sync.
static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) {
// Check if we have at least two parameters.
if (D->param_size() != 2)
return nullptr;
// Check if the second parameter is a block.
const ParmVarDecl *PV = D->getParamDecl(1);
QualType Ty = PV->getType();
if (!isDispatchBlock(Ty))
return nullptr;
// Everything checks out. Create a fake body that just calls the block.
// This is basically just an AST dump of:
//
// void dispatch_sync(dispatch_queue_t queue, void (^block)(void)) {
// block();
// }
//
ASTMaker M(C);
DeclRefExpr *DR = M.makeDeclRefExpr(PV);
ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
SourceLocation());
return CE;
}
static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
{
// There are exactly 3 arguments.
if (D->param_size() != 3)
return nullptr;
// Signature:
// _Bool OSAtomicCompareAndSwapPtr(void *__oldValue,
// void *__newValue,
// void * volatile *__theValue)
// Generate body:
// if (oldValue == *theValue) {
// *theValue = newValue;
// return YES;
// }
// else return NO;
QualType ResultTy = D->getReturnType();
bool isBoolean = ResultTy->isBooleanType();
if (!isBoolean && !ResultTy->isIntegralType(C))
return nullptr;
const ParmVarDecl *OldValue = D->getParamDecl(0);
QualType OldValueTy = OldValue->getType();
const ParmVarDecl *NewValue = D->getParamDecl(1);
QualType NewValueTy = NewValue->getType();
assert(OldValueTy == NewValueTy);
const ParmVarDecl *TheValue = D->getParamDecl(2);
QualType TheValueTy = TheValue->getType();
const PointerType *PT = TheValueTy->getAs<PointerType>();
if (!PT)
return nullptr;
QualType PointeeTy = PT->getPointeeType();
ASTMaker M(C);
// Construct the comparison.
Expr *Comparison =
M.makeComparison(
M.makeLvalueToRvalue(M.makeDeclRefExpr(OldValue), OldValueTy),
M.makeLvalueToRvalue(
M.makeDereference(
M.makeLvalueToRvalue(M.makeDeclRefExpr(TheValue), TheValueTy),
PointeeTy),
PointeeTy),
BO_EQ);
// Construct the body of the IfStmt.
Stmt *Stmts[2];
Stmts[0] =
M.makeAssignment(
M.makeDereference(
M.makeLvalueToRvalue(M.makeDeclRefExpr(TheValue), TheValueTy),
PointeeTy),
M.makeLvalueToRvalue(M.makeDeclRefExpr(NewValue), NewValueTy),
NewValueTy);
Expr *BoolVal = M.makeObjCBool(true);
Expr *RetVal = isBoolean ? M.makeIntegralCastToBoolean(BoolVal)
: M.makeIntegralCast(BoolVal, ResultTy);
Stmts[1] = M.makeReturn(RetVal);
CompoundStmt *Body = M.makeCompound(Stmts);
// Construct the else clause.
BoolVal = M.makeObjCBool(false);
RetVal = isBoolean ? M.makeIntegralCastToBoolean(BoolVal)
: M.makeIntegralCast(BoolVal, ResultTy);
Stmt *Else = M.makeReturn(RetVal);
/// Construct the If.
Stmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,
Comparison, Body, SourceLocation(), Else);
return If;
}
Stmt *BodyFarm::getBody(const FunctionDecl *D) {
D = D->getCanonicalDecl();
Optional<Stmt *> &Val = Bodies[D];
if (Val.hasValue())
return Val.getValue();
Val = nullptr;
if (D->getIdentifier() == nullptr)
return nullptr;
StringRef Name = D->getName();
if (Name.empty())
return nullptr;
FunctionFarmer FF;
if (Name.startswith("OSAtomicCompareAndSwap") ||
Name.startswith("objc_atomicCompareAndSwap")) {
FF = create_OSAtomicCompareAndSwap;
} else if (Name == "call_once" && D->getDeclContext()->isStdNamespace()) {
FF = create_call_once;
} else {
FF = llvm::StringSwitch<FunctionFarmer>(Name)
.Case("dispatch_sync", create_dispatch_sync)
.Case("dispatch_once", create_dispatch_once)
.Default(nullptr);
}
if (FF) { Val = FF(C, D); }
Add support for the static analyzer to synthesize function implementations from external model files. Currently the analyzer lazily models some functions using 'BodyFarm', which constructs a fake function implementation that the analyzer can simulate that approximates the semantics of the function when it is called. BodyFarm does this by constructing the AST for such definitions on-the-fly. One strength of BodyFarm is that all symbols and types referenced by synthesized function bodies are contextual adapted to the containing translation unit. The downside is that these ASTs are hardcoded in Clang's own source code. A more scalable model is to allow these models to be defined as source code in separate "model" files and have the analyzer use those definitions lazily when a function body is needed. Among other things, it will allow more customization of the analyzer for specific APIs and platforms. This patch provides the initial infrastructure for this feature. It extends BodyFarm to use an abstract API 'CodeInjector' that can be used to synthesize function bodies. That 'CodeInjector' is implemented using a new 'ModelInjector' in libFrontend, which lazily parses a model file and injects the ASTs into the current translation unit. Models are currently found by specifying a 'model-path' as an analyzer option; if no path is specified the CodeInjector is not used, thus defaulting to the current behavior in the analyzer. Models currently contain a single function definition, and can be found by finding the file <function name>.model. This is an initial starting point for something more rich, but it bootstraps this feature for future evolution. This patch was contributed by Gábor Horváth as part of his Google Summer of Code project. Some notes: - This introduces the notion of a "model file" into FrontendAction and the Preprocessor. This nomenclature is specific to the static analyzer, but possibly could be generalized. Essentially these are sources pulled in exogenously from the principal translation. Preprocessor gets a 'InitializeForModelFile' and 'FinalizeForModelFile' which could possibly be hoisted out of Preprocessor if Preprocessor exposed a new API to change the PragmaHandlers and some other internal pieces. This can be revisited. FrontendAction gets a 'isModelParsingAction()' predicate function used to allow a new FrontendAction to recycle the Preprocessor and ASTContext. This name could probably be made something more general (i.e., not tied to 'model files') at the expense of losing the intent of why it exists. This can be revisited. - This is a moderate sized patch; it has gone through some amount of offline code review. Most of the changes to the non-analyzer parts are fairly small, and would make little sense without the analyzer changes. - Most of the analyzer changes are plumbing, with the interesting behavior being introduced by ModelInjector.cpp and ModelConsumer.cpp. - The new functionality introduced by this change is off-by-default. It requires an analyzer config option to enable. llvm-svn: 216550
2014-08-27 23:14:15 +08:00
else if (Injector) { Val = Injector->getBody(D); }
return Val.getValue();
}
static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) {
const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl();
if (IVar)
return IVar;
// When a readonly property is shadowed in a class extensions with a
// a readwrite property, the instance variable belongs to the shadowing
// property rather than the shadowed property. If there is no instance
// variable on a readonly property, check to see whether the property is
// shadowed and if so try to get the instance variable from shadowing
// property.
if (!Prop->isReadOnly())
return nullptr;
auto *Container = cast<ObjCContainerDecl>(Prop->getDeclContext());
const ObjCInterfaceDecl *PrimaryInterface = nullptr;
if (auto *InterfaceDecl = dyn_cast<ObjCInterfaceDecl>(Container)) {
PrimaryInterface = InterfaceDecl;
} else if (auto *CategoryDecl = dyn_cast<ObjCCategoryDecl>(Container)) {
PrimaryInterface = CategoryDecl->getClassInterface();
} else if (auto *ImplDecl = dyn_cast<ObjCImplDecl>(Container)) {
PrimaryInterface = ImplDecl->getClassInterface();
} else {
return nullptr;
}
// FindPropertyVisibleInPrimaryClass() looks first in class extensions, so it
// is guaranteed to find the shadowing property, if it exists, rather than
// the shadowed property.
auto *ShadowingProp = PrimaryInterface->FindPropertyVisibleInPrimaryClass(
Prop->getIdentifier(), Prop->getQueryKind());
if (ShadowingProp && ShadowingProp != Prop) {
IVar = ShadowingProp->getPropertyIvarDecl();
}
return IVar;
}
static Stmt *createObjCPropertyGetter(ASTContext &Ctx,
const ObjCPropertyDecl *Prop) {
// First, find the backing ivar.
const ObjCIvarDecl *IVar = findBackingIvar(Prop);
if (!IVar)
return nullptr;
// Ignore weak variables, which have special behavior.
if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)
return nullptr;
// Look to see if Sema has synthesized a body for us. This happens in
// Objective-C++ because the return value may be a C++ class type with a
// non-trivial copy constructor. We can only do this if we can find the
// @synthesize for this property, though (or if we know it's been auto-
// synthesized).
const ObjCImplementationDecl *ImplDecl =
IVar->getContainingInterface()->getImplementation();
if (ImplDecl) {
for (const auto *I : ImplDecl->property_impls()) {
if (I->getPropertyDecl() != Prop)
continue;
if (I->getGetterCXXConstructor()) {
ASTMaker M(Ctx);
return M.makeReturn(I->getGetterCXXConstructor());
}
}
}
// Sanity check that the property is the same type as the ivar, or a
// reference to it, and that it is either an object pointer or trivially
// copyable.
if (!Ctx.hasSameUnqualifiedType(IVar->getType(),
Prop->getType().getNonReferenceType()))
return nullptr;
if (!IVar->getType()->isObjCLifetimeType() &&
!IVar->getType().isTriviallyCopyableType(Ctx))
return nullptr;
// Generate our body:
// return self->_ivar;
ASTMaker M(Ctx);
const VarDecl *selfVar = Prop->getGetterMethodDecl()->getSelfDecl();
if (!selfVar)
return nullptr;
Expr *loadedIVar =
M.makeObjCIvarRef(
M.makeLvalueToRvalue(
M.makeDeclRefExpr(selfVar),
selfVar->getType()),
IVar);
if (!Prop->getType()->isReferenceType())
loadedIVar = M.makeLvalueToRvalue(loadedIVar, IVar->getType());
return M.makeReturn(loadedIVar);
}
Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
// We currently only know how to synthesize property accessors.
if (!D->isPropertyAccessor())
return nullptr;
D = D->getCanonicalDecl();
Optional<Stmt *> &Val = Bodies[D];
if (Val.hasValue())
return Val.getValue();
Val = nullptr;
const ObjCPropertyDecl *Prop = D->findPropertyDecl();
if (!Prop)
return nullptr;
// For now, we only synthesize getters.
// Synthesizing setters would cause false negatives in the
// RetainCountChecker because the method body would bind the parameter
// to an instance variable, causing it to escape. This would prevent
// warning in the following common scenario:
//
// id foo = [[NSObject alloc] init];
// self.foo = foo; // We should warn that foo leaks here.
//
if (D->param_size() != 0)
return nullptr;
Val = createObjCPropertyGetter(C, Prop);
return Val.getValue();
}