[cfe-commits] r164868 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp test/Analysis/objc_invalidation.m

Jordan Rose jordan_rose at apple.com
Fri Sep 28 18:51:50 PDT 2012


Comments (fewer this time):

On Sep 28, 2012, at 17:20 , Anna Zaks <ganna at apple.com> wrote:

> 
>   /// Statement visitor, which walks the method body and flags the ivars
>   /// referenced in it (either directly or via property).
>   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
> +    const ObjCMethodDecl *EnclosingMethod;
> 
>     /// The set of Ivars which need to be invalidated.
>     IvarSet &IVars;
> 
> -    /// Property setter/getter to ivar mapping.
> -    MethToIvarMapTy &PropertyAccessorToIvarMap;
> +    /// Flag is set as the result of a message send to another
> +    /// invalidation method.
> +    bool &CalledAnotherInvalidationMethod;
> +
> +    /// Property setter to ivar mapping.
> +    const MethToIvarMapTy &PropertySetterToIvarMap;
> +
> +    /// Property getter to ivar mapping.
> +    const MethToIvarMapTy &PropertyGetterToIvarMap;
> +
> +    /// Property to ivar mapping.
> +    const PropToIvarMapTy &PropertyToIvarMap;
> +
> +    /// The invalidation method being currently processed.
> +    const ObjCMethodDecl *InvalidationMethod;
> +
> +    /// Peel off parents, casts, OpaqueValueExpr, and PseudoObjectExpr.
> +    const Expr *peel(const Expr *E) const;

Typo: "parens".



> +const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const {
> +  E = E->IgnoreParenCasts();
> +  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
> +    E = POE->getSyntacticForm()->IgnoreParenCasts();
> +  if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
> +    E = OVE->getSourceExpr()->IgnoreParenCasts();
> +  return E;
> +}




> +
> +bool IvarInvalidationChecker::MethodCrawler::isZero(const Expr *E) const {
> +  E = peel(E);
> +  if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E))
> +    return IL->getValue() == 0;
> +
> +  if (const CastExpr *ICE = dyn_cast<CastExpr>(E))
> +    return ICE->getCastKind() == CK_NullToPointer;
> +
> +  return false;
> +}

The more robust form of this is Expr::isNullPointerConstant.


> +void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) {
> +  E = peel(E);
> +
> +  if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
> +    E = OVE->getSourceExpr();

This should have been checked by peel already?


> +  if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
> +    checkObjCIvarRefExpr(IvarRef);
> +    return;
> +  }
> +
> +  if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
> +    checkObjCPropertyRefExpr(PropRef);
> +    return;
> +  }
> +
> +  if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
> +    checkObjCMessageExpr(MsgExpr);
> +    return;
> +  }
> +}
> +
> +void IvarInvalidationChecker::MethodCrawler::VisitBinaryOperator(
> +    const BinaryOperator *BO) {
> +  if (!BO->isAssignmentOp())
> +    return;

You could probably be even more aggressive here and look for = specifically, since += and such aren't valid on ObjC objects.

> +  // Do we assign zero?
> +  if (!isZero(BO->getRHS()))
> +    return;
> +
> +  // Check the variable we are assigning to.
> +  check(BO->getLHS());
> +
> +  VisitStmt(BO);
> +}

This VisitStmt is after two early returns. It seems like it should be first.


> +void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
> +    const ObjCMessageExpr *ME) {
> +  const ObjCMethodDecl *MD = ME->getMethodDecl();
> +  const Expr *Receiver = ME->getInstanceReceiver();
> +
> +  // Stop if we are calling '[self invalidate]'.
> +  if (Receiver && isInvalidationMethod(MD))
> +    if (const DeclRefExpr *RD =
> +          dyn_cast<DeclRefExpr>(Receiver->IgnoreParenCasts())) {
> +      if (RD->getDecl() == EnclosingMethod->getSelfDecl()) {
> +        CalledAnotherInvalidationMethod = true;
> +        return;
> +      }
> +    }
> +
> +  // Check if we call a setter and set the property to 'nil'.
> +  if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
> +    MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
> +    MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
> +    if (IvI != PropertySetterToIvarMap.end()) {
> +      markInvalidated(IvI->second);
> +      return;
> +    }
> +  }
> +
> +  // Check if we call the 'invalidation' routine on the ivar.
> +  if (Receiver) {
> +    InvalidationMethod = MD;
> +    check(Receiver->IgnoreParenCasts());
> +    InvalidationMethod = 0;

Passing state around like this does not make me happy. In particular, [[a foo] bar] will not work because the check() method calls back to VisitObjCMessageExpr. Then again, no one will write an invalidation method like this.



More information about the cfe-commits mailing list