summaryrefslogtreecommitdiffstats
path: root/lib/Sema/SemaChecking.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/SemaChecking.cpp')
-rw-r--r--lib/Sema/SemaChecking.cpp541
1 files changed, 500 insertions, 41 deletions
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 05c257a..690a29d 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
@@ -319,12 +320,41 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
}
// Memset/memcpy/memmove handling
- if (FDecl->getLinkage() == ExternalLinkage &&
- (!getLangOptions().CPlusPlus || FDecl->isExternC())) {
- if (FnInfo->isStr("memset") || FnInfo->isStr("memcpy") ||
- FnInfo->isStr("memmove"))
- CheckMemsetcpymoveArguments(TheCall, FnInfo);
+ int CMF = -1;
+ switch (FDecl->getBuiltinID()) {
+ case Builtin::BI__builtin_memset:
+ case Builtin::BI__builtin___memset_chk:
+ case Builtin::BImemset:
+ CMF = CMF_Memset;
+ break;
+
+ case Builtin::BI__builtin_memcpy:
+ case Builtin::BI__builtin___memcpy_chk:
+ case Builtin::BImemcpy:
+ CMF = CMF_Memcpy;
+ break;
+
+ case Builtin::BI__builtin_memmove:
+ case Builtin::BI__builtin___memmove_chk:
+ case Builtin::BImemmove:
+ CMF = CMF_Memmove;
+ break;
+
+ default:
+ if (FDecl->getLinkage() == ExternalLinkage &&
+ (!getLangOptions().CPlusPlus || FDecl->isExternC())) {
+ if (FnInfo->isStr("memset"))
+ CMF = CMF_Memset;
+ else if (FnInfo->isStr("memcpy"))
+ CMF = CMF_Memcpy;
+ else if (FnInfo->isStr("memmove"))
+ CMF = CMF_Memmove;
+ }
+ break;
}
+
+ if (CMF != -1)
+ CheckMemsetcpymoveArguments(TheCall, CheckedMemoryFunction(CMF), FnInfo);
return false;
}
@@ -382,14 +412,14 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
// casts here.
// FIXME: We don't allow floating point scalars as input.
Expr *FirstArg = TheCall->getArg(0);
- if (!FirstArg->getType()->isPointerType()) {
+ const PointerType *pointerType = FirstArg->getType()->getAs<PointerType>();
+ if (!pointerType) {
Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer)
<< FirstArg->getType() << FirstArg->getSourceRange();
return ExprError();
}
- QualType ValType =
- FirstArg->getType()->getAs<PointerType>()->getPointeeType();
+ QualType ValType = pointerType->getPointeeType();
if (!ValType->isIntegerType() && !ValType->isAnyPointerType() &&
!ValType->isBlockPointerType()) {
Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer_intptr)
@@ -397,6 +427,20 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
return ExprError();
}
+ switch (ValType.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ // okay
+ break;
+
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Autoreleasing:
+ Diag(DRE->getLocStart(), diag::err_arc_atomic_ownership)
+ << ValType << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
// The majority of builtins return a value, but a few have special return
// types, so allow them to override appropriately below.
QualType ResultType = ValType;
@@ -518,7 +562,8 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
CastKind Kind = CK_Invalid;
ExprValueKind VK = VK_RValue;
CXXCastPath BasePath;
- Arg = CheckCastTypes(Arg.get()->getSourceRange(), ValType, Arg.take(), Kind, VK, BasePath);
+ Arg = CheckCastTypes(Arg.get()->getLocStart(), Arg.get()->getSourceRange(),
+ ValType, Arg.take(), Kind, VK, BasePath);
if (Arg.isInvalid())
return ExprError();
@@ -1812,6 +1857,27 @@ static bool isDynamicClassType(QualType T) {
return false;
}
+/// \brief If E is a sizeof expression, returns its argument expression,
+/// otherwise returns NULL.
+static const Expr *getSizeOfExprArg(const Expr* E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf =
+ dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (SizeOf->getKind() == clang::UETT_SizeOf && !SizeOf->isArgumentType())
+ return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
+
+ return 0;
+}
+
+/// \brief If E is a sizeof expression, returns its argument type.
+static QualType getSizeOfArgType(const Expr* E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf =
+ dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (SizeOf->getKind() == clang::UETT_SizeOf)
+ return SizeOf->getTypeOfArgument();
+
+ return QualType();
+}
+
/// \brief Check for dangerous or invalid arguments to memset().
///
/// This issues warnings on known problematic, dangerous or unspecified
@@ -1819,35 +1885,95 @@ static bool isDynamicClassType(QualType T) {
///
/// \param Call The call expression to diagnose.
void Sema::CheckMemsetcpymoveArguments(const CallExpr *Call,
- const IdentifierInfo *FnName) {
+ CheckedMemoryFunction CMF,
+ IdentifierInfo *FnName) {
// It is possible to have a non-standard definition of memset. Validate
- // we have the proper number of arguments, and if not, abort further
- // checking.
- if (Call->getNumArgs() != 3)
+ // we have enough arguments, and if not, abort further checking.
+ if (Call->getNumArgs() < 3)
return;
- unsigned LastArg = FnName->isStr("memset")? 1 : 2;
+ unsigned LastArg = (CMF == CMF_Memset? 1 : 2);
+ const Expr *LenExpr = Call->getArg(2)->IgnoreParenImpCasts();
+
+ // We have special checking when the length is a sizeof expression.
+ QualType SizeOfArgTy = getSizeOfArgType(LenExpr);
+ const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
+ llvm::FoldingSetNodeID SizeOfArgID;
+
for (unsigned ArgIdx = 0; ArgIdx != LastArg; ++ArgIdx) {
const Expr *Dest = Call->getArg(ArgIdx)->IgnoreParenImpCasts();
+ SourceRange ArgRange = Call->getArg(ArgIdx)->getSourceRange();
QualType DestTy = Dest->getType();
if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
QualType PointeeTy = DestPtrTy->getPointeeType();
+
+ // Never warn about void type pointers. This can be used to suppress
+ // false positives.
if (PointeeTy->isVoidType())
continue;
+ // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
+ // actually comparing the expressions for equality. Because computing the
+ // expression IDs can be expensive, we only do this if the diagnostic is
+ // enabled.
+ if (SizeOfArg &&
+ Diags.getDiagnosticLevel(diag::warn_sizeof_pointer_expr_memaccess,
+ SizeOfArg->getExprLoc())) {
+ // We only compute IDs for expressions if the warning is enabled, and
+ // cache the sizeof arg's ID.
+ if (SizeOfArgID == llvm::FoldingSetNodeID())
+ SizeOfArg->Profile(SizeOfArgID, Context, true);
+ llvm::FoldingSetNodeID DestID;
+ Dest->Profile(DestID, Context, true);
+ if (DestID == SizeOfArgID) {
+ unsigned ActionIdx = 0; // Default is to suggest dereferencing.
+ if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
+ if (UnaryOp->getOpcode() == UO_AddrOf)
+ ActionIdx = 1; // If its an address-of operator, just remove it.
+ if (Context.getTypeSize(PointeeTy) == Context.getCharWidth())
+ ActionIdx = 2; // If the pointee's size is sizeof(char),
+ // suggest an explicit length.
+ DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess)
+ << FnName << ArgIdx << ActionIdx
+ << Dest->getSourceRange()
+ << SizeOfArg->getSourceRange());
+ break;
+ }
+ }
+
+ // Also check for cases where the sizeof argument is the exact same
+ // type as the memory argument, and where it points to a user-defined
+ // record type.
+ if (SizeOfArgTy != QualType()) {
+ if (PointeeTy->isRecordType() &&
+ Context.typesAreCompatible(SizeOfArgTy, DestTy)) {
+ DiagRuntimeBehavior(LenExpr->getExprLoc(), Dest,
+ PDiag(diag::warn_sizeof_pointer_type_memaccess)
+ << FnName << SizeOfArgTy << ArgIdx
+ << PointeeTy << Dest->getSourceRange()
+ << LenExpr->getSourceRange());
+ break;
+ }
+ }
+
+ unsigned DiagID;
+
// Always complain about dynamic classes.
- if (isDynamicClassType(PointeeTy)) {
- DiagRuntimeBehavior(
- Dest->getExprLoc(), Dest,
- PDiag(diag::warn_dyn_class_memaccess)
- << ArgIdx << FnName << PointeeTy
- << Call->getCallee()->getSourceRange());
- } else {
+ if (isDynamicClassType(PointeeTy))
+ DiagID = diag::warn_dyn_class_memaccess;
+ else if (PointeeTy.hasNonTrivialObjCLifetime() && CMF != CMF_Memset)
+ DiagID = diag::warn_arc_object_memaccess;
+ else
continue;
- }
- SourceRange ArgRange = Call->getArg(0)->getSourceRange();
+ DiagRuntimeBehavior(
+ Dest->getExprLoc(), Dest,
+ PDiag(DiagID)
+ << ArgIdx << FnName << PointeeTy
+ << Call->getCallee()->getSourceRange());
+
DiagRuntimeBehavior(
Dest->getExprLoc(), Dest,
PDiag(diag::note_bad_memaccess_silence)
@@ -1873,7 +1999,8 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType,
// Perform checking for returned stack addresses, local blocks,
// label addresses or references to temporaries.
- if (lhsType->isPointerType() || lhsType->isBlockPointerType()) {
+ if (lhsType->isPointerType() ||
+ (!getLangOptions().ObjCAutoRefCount && lhsType->isBlockPointerType())) {
stackE = EvalAddr(RetValExp, refVars);
} else if (lhsType->isReferenceType()) {
stackE = EvalVal(RetValExp, refVars);
@@ -2044,7 +2171,8 @@ static Expr *EvalAddr(Expr *E, llvm::SmallVectorImpl<DeclRefExpr *> &refVars) {
// pointer values, and pointer-to-pointer conversions.
case Stmt::ImplicitCastExprClass:
case Stmt::CStyleCastExprClass:
- case Stmt::CXXFunctionalCastExprClass: {
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::ObjCBridgedCastExprClass: {
Expr* SubExpr = cast<CastExpr>(E)->getSubExpr();
QualType T = SubExpr->getType();
@@ -2077,6 +2205,14 @@ static Expr *EvalAddr(Expr *E, llvm::SmallVectorImpl<DeclRefExpr *> &refVars) {
return NULL;
}
+ case Stmt::MaterializeTemporaryExprClass:
+ if (Expr *Result = EvalAddr(
+ cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
+ refVars))
+ return Result;
+
+ return E;
+
// Everything else: we simply don't reason about them.
default:
return NULL;
@@ -2178,6 +2314,14 @@ do {
return EvalVal(M->getBase(), refVars);
}
+ case Stmt::MaterializeTemporaryExprClass:
+ if (Expr *Result = EvalVal(
+ cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
+ refVars))
+ return Result;
+
+ return E;
+
default:
// Check that we don't return or take the address of a reference to a
// temporary. This is only useful in C++.
@@ -2442,15 +2586,24 @@ IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) {
case BO_NE:
return IntRange::forBoolType();
- // The type of these compound assignments is the type of the LHS,
- // so the RHS is not necessarily an integer.
+ // The type of the assignments is the type of the LHS, so the RHS
+ // is not necessarily the same type.
case BO_MulAssign:
case BO_DivAssign:
case BO_RemAssign:
case BO_AddAssign:
case BO_SubAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ // TODO: bitfields?
return IntRange::forValueOfType(C, E->getType());
+ // Simple assignments just pass through the RHS, which will have
+ // been coerced to the LHS type.
+ case BO_Assign:
+ // TODO: bitfields?
+ return GetExprRange(C, BO->getRHS(), MaxWidth);
+
// Operations with opaque sources are black-listed.
case BO_PtrMemD:
case BO_PtrMemI:
@@ -2506,14 +2659,54 @@ IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) {
case BO_Sub:
if (BO->getLHS()->getType()->isPointerType())
return IntRange::forValueOfType(C, E->getType());
- // fallthrough
+ break;
- default:
+ // The width of a division result is mostly determined by the size
+ // of the LHS.
+ case BO_Div: {
+ // Don't 'pre-truncate' the operands.
+ unsigned opWidth = C.getIntWidth(E->getType());
+ IntRange L = GetExprRange(C, BO->getLHS(), opWidth);
+
+ // If the divisor is constant, use that.
+ llvm::APSInt divisor;
+ if (BO->getRHS()->isIntegerConstantExpr(divisor, C)) {
+ unsigned log2 = divisor.logBase2(); // floor(log_2(divisor))
+ if (log2 >= L.Width)
+ L.Width = (L.NonNegative ? 0 : 1);
+ else
+ L.Width = std::min(L.Width - log2, MaxWidth);
+ return L;
+ }
+
+ // Otherwise, just use the LHS's width.
+ IntRange R = GetExprRange(C, BO->getRHS(), opWidth);
+ return IntRange(L.Width, L.NonNegative && R.NonNegative);
+ }
+
+ // The result of a remainder can't be larger than the result of
+ // either side.
+ case BO_Rem: {
+ // Don't 'pre-truncate' the operands.
+ unsigned opWidth = C.getIntWidth(E->getType());
+ IntRange L = GetExprRange(C, BO->getLHS(), opWidth);
+ IntRange R = GetExprRange(C, BO->getRHS(), opWidth);
+
+ IntRange meet = IntRange::meet(L, R);
+ meet.Width = std::min(meet.Width, MaxWidth);
+ return meet;
+ }
+
+ // The default behavior is okay for these.
+ case BO_Mul:
+ case BO_Add:
+ case BO_Xor:
+ case BO_Or:
break;
}
- // Treat every other operator as if it were closed on the
- // narrowest type that encompasses both operands.
+ // The default case is to treat the operation as if it were closed
+ // on the narrowest type that encompasses both operands.
IntRange L = GetExprRange(C, BO->getLHS(), MaxWidth);
IntRange R = GetExprRange(C, BO->getRHS(), MaxWidth);
return IntRange::join(L, R);
@@ -2837,18 +3030,16 @@ void DiagnoseFloatingLiteralImpCast(Sema &S, FloatingLiteral *FL, QualType T,
if (&Value.getSemantics() == &llvm::APFloat::PPCDoubleDouble)
return;
- // Try to convert this exactly to an 64-bit integer. FIXME: It would be
- // nice to support arbitrarily large integers here.
+ // Try to convert this exactly to an integer.
bool isExact = false;
- uint64_t IntegerPart;
- if (Value.convertToInteger(&IntegerPart, 64, /*isSigned=*/true,
+ llvm::APSInt IntegerValue(S.Context.getIntWidth(T),
+ T->hasUnsignedIntegerRepresentation());
+ if (Value.convertToInteger(IntegerValue,
llvm::APFloat::rmTowardZero, &isExact)
!= llvm::APFloat::opOK || !isExact)
return;
- llvm::APInt IntegerValue(64, IntegerPart, /*isSigned=*/true);
-
- std::string LiteralValue = IntegerValue.toString(10, /*isSigned=*/true);
+ std::string LiteralValue = IntegerValue.toString(10);
S.Diag(FL->getExprLoc(), diag::note_fix_integral_float_as_integer)
<< FixItHint::CreateReplacement(FL->getSourceRange(), LiteralValue);
}
@@ -2895,6 +3086,11 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
return;
return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_vector_scalar);
}
+
+ // If the vector cast is cast between two vectors of the same size, it is
+ // a bitcast, not a conversion.
+ if (S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target))
+ return;
Source = cast<VectorType>(Source)->getElementType().getTypePtr();
Target = cast<VectorType>(Target)->getElementType().getTypePtr();
@@ -2989,9 +3185,7 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
return;
}
- // People want to build with -Wshorten-64-to-32 and not -Wconversion
- // and by god we'll let them.
-
+ // People want to build with -Wshorten-64-to-32 and not -Wconversion.
if (isFromSystemMacro(S, CC))
return;
@@ -3356,3 +3550,268 @@ void Sema::CheckArrayAccess(const Expr *expr) {
}
}
}
+
+//===--- CHECK: Objective-C retain cycles ----------------------------------//
+
+namespace {
+ struct RetainCycleOwner {
+ RetainCycleOwner() : Variable(0), Indirect(false) {}
+ VarDecl *Variable;
+ SourceRange Range;
+ SourceLocation Loc;
+ bool Indirect;
+
+ void setLocsFrom(Expr *e) {
+ Loc = e->getExprLoc();
+ Range = e->getSourceRange();
+ }
+ };
+}
+
+/// Consider whether capturing the given variable can possibly lead to
+/// a retain cycle.
+static bool considerVariable(VarDecl *var, Expr *ref, RetainCycleOwner &owner) {
+ // In ARC, it's captured strongly iff the variable has __strong
+ // lifetime. In MRR, it's captured strongly if the variable is
+ // __block and has an appropriate type.
+ if (var->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
+ return false;
+
+ owner.Variable = var;
+ owner.setLocsFrom(ref);
+ return true;
+}
+
+static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) {
+ while (true) {
+ e = e->IgnoreParens();
+ if (CastExpr *cast = dyn_cast<CastExpr>(e)) {
+ switch (cast->getCastKind()) {
+ case CK_BitCast:
+ case CK_LValueBitCast:
+ case CK_LValueToRValue:
+ case CK_ObjCReclaimReturnedObject:
+ e = cast->getSubExpr();
+ continue;
+
+ case CK_GetObjCProperty: {
+ // Bail out if this isn't a strong explicit property.
+ const ObjCPropertyRefExpr *pre = cast->getSubExpr()->getObjCProperty();
+ if (pre->isImplicitProperty()) return false;
+ ObjCPropertyDecl *property = pre->getExplicitProperty();
+ if (!(property->getPropertyAttributes() &
+ (ObjCPropertyDecl::OBJC_PR_retain |
+ ObjCPropertyDecl::OBJC_PR_copy |
+ ObjCPropertyDecl::OBJC_PR_strong)) &&
+ !(property->getPropertyIvarDecl() &&
+ property->getPropertyIvarDecl()->getType()
+ .getObjCLifetime() == Qualifiers::OCL_Strong))
+ return false;
+
+ owner.Indirect = true;
+ e = const_cast<Expr*>(pre->getBase());
+ continue;
+ }
+
+ default:
+ return false;
+ }
+ }
+
+ if (ObjCIvarRefExpr *ref = dyn_cast<ObjCIvarRefExpr>(e)) {
+ ObjCIvarDecl *ivar = ref->getDecl();
+ if (ivar->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
+ return false;
+
+ // Try to find a retain cycle in the base.
+ if (!findRetainCycleOwner(ref->getBase(), owner))
+ return false;
+
+ if (ref->isFreeIvar()) owner.setLocsFrom(ref);
+ owner.Indirect = true;
+ return true;
+ }
+
+ if (DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) {
+ VarDecl *var = dyn_cast<VarDecl>(ref->getDecl());
+ if (!var) return false;
+ return considerVariable(var, ref, owner);
+ }
+
+ if (BlockDeclRefExpr *ref = dyn_cast<BlockDeclRefExpr>(e)) {
+ owner.Variable = ref->getDecl();
+ owner.setLocsFrom(ref);
+ return true;
+ }
+
+ if (MemberExpr *member = dyn_cast<MemberExpr>(e)) {
+ if (member->isArrow()) return false;
+
+ // Don't count this as an indirect ownership.
+ e = member->getBase();
+ continue;
+ }
+
+ // Array ivars?
+
+ return false;
+ }
+}
+
+namespace {
+ struct FindCaptureVisitor : EvaluatedExprVisitor<FindCaptureVisitor> {
+ FindCaptureVisitor(ASTContext &Context, VarDecl *variable)
+ : EvaluatedExprVisitor<FindCaptureVisitor>(Context),
+ Variable(variable), Capturer(0) {}
+
+ VarDecl *Variable;
+ Expr *Capturer;
+
+ void VisitDeclRefExpr(DeclRefExpr *ref) {
+ if (ref->getDecl() == Variable && !Capturer)
+ Capturer = ref;
+ }
+
+ void VisitBlockDeclRefExpr(BlockDeclRefExpr *ref) {
+ if (ref->getDecl() == Variable && !Capturer)
+ Capturer = ref;
+ }
+
+ void VisitObjCIvarRefExpr(ObjCIvarRefExpr *ref) {
+ if (Capturer) return;
+ Visit(ref->getBase());
+ if (Capturer && ref->isFreeIvar())
+ Capturer = ref;
+ }
+
+ void VisitBlockExpr(BlockExpr *block) {
+ // Look inside nested blocks
+ if (block->getBlockDecl()->capturesVariable(Variable))
+ Visit(block->getBlockDecl()->getBody());
+ }
+ };
+}
+
+/// Check whether the given argument is a block which captures a
+/// variable.
+static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) {
+ assert(owner.Variable && owner.Loc.isValid());
+
+ e = e->IgnoreParenCasts();
+ BlockExpr *block = dyn_cast<BlockExpr>(e);
+ if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable))
+ return 0;
+
+ FindCaptureVisitor visitor(S.Context, owner.Variable);
+ visitor.Visit(block->getBlockDecl()->getBody());
+ return visitor.Capturer;
+}
+
+static void diagnoseRetainCycle(Sema &S, Expr *capturer,
+ RetainCycleOwner &owner) {
+ assert(capturer);
+ assert(owner.Variable && owner.Loc.isValid());
+
+ S.Diag(capturer->getExprLoc(), diag::warn_arc_retain_cycle)
+ << owner.Variable << capturer->getSourceRange();
+ S.Diag(owner.Loc, diag::note_arc_retain_cycle_owner)
+ << owner.Indirect << owner.Range;
+}
+
+/// Check for a keyword selector that starts with the word 'add' or
+/// 'set'.
+static bool isSetterLikeSelector(Selector sel) {
+ if (sel.isUnarySelector()) return false;
+
+ llvm::StringRef str = sel.getNameForSlot(0);
+ while (!str.empty() && str.front() == '_') str = str.substr(1);
+ if (str.startswith("set") || str.startswith("add"))
+ str = str.substr(3);
+ else
+ return false;
+
+ if (str.empty()) return true;
+ return !islower(str.front());
+}
+
+/// Check a message send to see if it's likely to cause a retain cycle.
+void Sema::checkRetainCycles(ObjCMessageExpr *msg) {
+ // Only check instance methods whose selector looks like a setter.
+ if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector()))
+ return;
+
+ // Try to find a variable that the receiver is strongly owned by.
+ RetainCycleOwner owner;
+ if (msg->getReceiverKind() == ObjCMessageExpr::Instance) {
+ if (!findRetainCycleOwner(msg->getInstanceReceiver(), owner))
+ return;
+ } else {
+ assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
+ owner.Variable = getCurMethodDecl()->getSelfDecl();
+ owner.Loc = msg->getSuperLoc();
+ owner.Range = msg->getSuperLoc();
+ }
+
+ // Check whether the receiver is captured by any of the arguments.
+ for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i)
+ if (Expr *capturer = findCapturingExpr(*this, msg->getArg(i), owner))
+ return diagnoseRetainCycle(*this, capturer, owner);
+}
+
+/// Check a property assign to see if it's likely to cause a retain cycle.
+void Sema::checkRetainCycles(Expr *receiver, Expr *argument) {
+ RetainCycleOwner owner;
+ if (!findRetainCycleOwner(receiver, owner))
+ return;
+
+ if (Expr *capturer = findCapturingExpr(*this, argument, owner))
+ diagnoseRetainCycle(*this, capturer, owner);
+}
+
+bool Sema::checkUnsafeAssigns(SourceLocation Loc,
+ QualType LHS, Expr *RHS) {
+ Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime();
+ if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone)
+ return false;
+ // strip off any implicit cast added to get to the one arc-specific
+ while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
+ if (cast->getCastKind() == CK_ObjCConsumeObject) {
+ Diag(Loc, diag::warn_arc_retained_assign)
+ << (LT == Qualifiers::OCL_ExplicitNone)
+ << RHS->getSourceRange();
+ return true;
+ }
+ RHS = cast->getSubExpr();
+ }
+ return false;
+}
+
+void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
+ Expr *LHS, Expr *RHS) {
+ QualType LHSType = LHS->getType();
+ if (checkUnsafeAssigns(Loc, LHSType, RHS))
+ return;
+ Qualifiers::ObjCLifetime LT = LHSType.getObjCLifetime();
+ // FIXME. Check for other life times.
+ if (LT != Qualifiers::OCL_None)
+ return;
+
+ if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(LHS)) {
+ if (PRE->isImplicitProperty())
+ return;
+ const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
+ if (!PD)
+ return;
+
+ unsigned Attributes = PD->getPropertyAttributes();
+ if (Attributes & ObjCPropertyDecl::OBJC_PR_assign)
+ while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
+ if (cast->getCastKind() == CK_ObjCConsumeObject) {
+ Diag(Loc, diag::warn_arc_retained_property_assign)
+ << RHS->getSourceRange();
+ return;
+ }
+ RHS = cast->getSubExpr();
+ }
+ }
+}
OpenPOWER on IntegriCloud