Comments comments comments:

On Sep 27, 2012, at 12:45 , Anna Zaks <[email protected]> wrote:

> +static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl 
> *PD,
> +                                                   ObjCInterfaceDecl *InterD,
> +                                            ASTContext &Ctx) {
> +  // Check for synthesized ivars.
> +  ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
> +  if (ID)
> +    return ID;
> +
> +  // Check for existing "_PropName".
> +  ID = InterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
> +  if (ID)
> +    return ID;
> +
> +  // Check for existing "PropName".
> +  IdentifierInfo *PropIdent = PD->getIdentifier();
> +  ID = InterD->lookupInstanceVariable(PropIdent);
> +
> +  return ID;
> +}
> +
> +void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
> +                                       AnalysisManager& Mgr,
> +                                       BugReporter &BR) const {
> +  const ObjCInterfaceDecl *InterD = D->getClassInterface();
> +
> +
> +  IvarToPropertyMapTy IvarToPropMap;
> +
> +  // Find all properties for this class.
> +  for (ObjCInterfaceDecl::prop_iterator I = InterD->prop_begin(),
> +      E = InterD->prop_end(); I != E; ++I) {
> +    ObjCPropertyDecl *PD = *I;
> +
> +    // Find the corresponding IVar.
> +    const ObjCIvarDecl *ID = findPropertyBackingIvar(PD,
> +                               const_cast<ObjCInterfaceDecl*>(InterD),
> +                               Mgr.getASTContext());

Why the const_cast here? Why isn't the parameter just const?


> +    if (!ID)
> +      continue;
> +
> +    // Store the IVar to property mapping.
> +    IvarToPropMap[ID] = PD;
> +  }
> +
> +  if (IvarToPropMap.empty())
> +    return;
> +
> +  for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
> +      E = D->instmeth_end(); I != E; ++I) {
> +
> +    ObjCMethodDecl *M = *I;
> +    AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
> +
> +    if (M->getMethodFamily() == OMF_init ||
> +        M->getMethodFamily() == OMF_dealloc ||
> +        M->getSelector().getAsString().find("init") != StringRef::npos ||
> +        M->getSelector().getAsString().find("Init") != StringRef::npos)
> +      continue;

Converting the selector to a string isn't particularly cheap. The "best" thing 
to do here would be to check each piece of the selector, but if you do want to 
use getAsString() for simplicity/ease of maintenance it'd be nice to do it just 
once.

Also, I'd appreciate a comment to say what the string-search is trying to 
solve, because my initial thought was "but any method starting with the word 
'init' is automatically marked as OMF_init" before I figured it out.



> +    const Stmt *Body = M->getBody();
> +    if (!Body)
> +      continue;

I would go ahead and assert this: all methods in @implementation should have a 
body.


> +    MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx);
> +    MC.VisitStmt(Body);
> +  }
> +}
> +
> +void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
> +                                                    const BinaryOperator 
> *BO) {
> +  if (!BO->isAssignmentOp())
> +    return;
> +
> +  const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(BO->getLHS());

I'm not 100% sure but I think an IgnoreParenCasts is a good idea here.


> +  if (!IvarRef)
> +    return;
> +
> +  if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
> +    IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
> +    if (I != IvarToPropMap.end()) {
> +      const ObjCPropertyDecl *PD = I->second;
> +
> +      ObjCMethodDecl *GetterMethod =
> +          InterfD->getInstanceMethod(PD->getGetterName());
> +      ObjCMethodDecl *SetterMethod =
> +          InterfD->getInstanceMethod(PD->getSetterName());
> +
> +      if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
> +        return;
> +
> +      if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
> +        return;
> +
> +
> +      PathDiagnosticLocation IvarRefLocation =
> +          PathDiagnosticLocation::createBegin(IvarRef,
> +              BR.getSourceManager(), DCtx);

Same comment as last time: createBegin is unnecessary here, just use the 
constructor that takes a Stmt to highlight the whole range.


> +      BR.EmitBasicReport(MD,
> +          "Property access",
> +          categories::CoreFoundationObjectiveC,
> +          "Direct assignment to an instance variable backing a property; "
> +          "use the setter instead", IvarRefLocation);
> +    }
> +  }
> +}
> +}
> +
> +void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
> +  mgr.registerChecker<DirectIvarAssignment>();
> +}
> 
> Added: cfe/trunk/test/Analysis/objc-properties.m
> URL: 
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/objc-properties.m?rev=164790&view=auto
> ==============================================================================
> --- cfe/trunk/test/Analysis/objc-properties.m (added)
> +++ cfe/trunk/test/Analysis/objc-properties.m Thu Sep 27 14:45:15 2012
> @@ -0,0 +1,56 @@
> +// RUN: %clang_cc1 -analyze 
> -analyzer-checker=alpha.osx.cocoa.DirectIvarAssignment 
> -fobjc-default-synthesize-properties -Wno-objc-root-class -verify -fblocks %s
> +
> +@interface MyClass;
> +@end
> +@interface TestProperty {
> +  MyClass *_Z;
> +  id _nonSynth;
> +}
> +
> +  @property (assign, nonatomic) MyClass* A; // explicitely synthesized, not 
> implemented, non-default ivar name
> +
> +  @property (assign) MyClass* X;  // automatically synthesized, not 
> implemented
> +
> +  @property (assign, nonatomic) MyClass* Y; // automatically synthesized, 
> implemented
> +
> +  @property (assign, nonatomic) MyClass* Z; // non synthesized, implemented

Z's getter is still synthesized, which means this test case isn't testing the 
case where the ObjCPropertyDecl doesn't have an associated ivar.

> +  @property (readonly) id nonSynth;  // non synthesized, explicitly 
> implemented to return ivar with expected name

Ditto. You have to implement the getter for readonly and both getter and setter 
for readwrite in order to turn off ivar matching/synthesis.


> +  - (id) initWithPtr:(MyClass*) value;
> +  - (id) myInitWithPtr:(MyClass*) value;
> +  - (void) someMethod: (MyClass*)In;
> +@end
> +
> +@implementation TestProperty
> +  @synthesize A = __A;
> +  
> +  - (id) initWithPtr: (MyClass*) value {
> +    _Y = value; // no-warning
> +    return self;
> +  }
> +
> +  - (id) myInitWithPtr: (MyClass*) value {
> +    _Y = value; // no-warning
> +    return self;
> +  }
> +  
> +  - (void) setY:(MyClass*) NewValue {
> +    _Y = NewValue; // no-warning
> +  }
> +
> +  - (void) setZ:(MyClass*) NewValue {
> +    _Z = NewValue; // no-warning
> +  }
> +
> +  - (id)nonSynth {
> +      return _nonSynth;
> +  }
> +
> +  - (void) someMethod: (MyClass*)In {
> +    __A = In; // expected-warning {{Direct assignment to an instance 
> variable backing a property; use the setter instead}}
> +    _X = In; // expected-warning {{Direct assignment to an instance variable 
> backing a property; use the setter instead}}
> +    _Y = In; // expected-warning {{Direct assignment to an instance variable 
> backing a property; use the setter instead}}
> +    _Z = In; // expected-warning {{Direct assignment to an instance variable 
> backing a property; use the setter instead}}
> +    _nonSynth = 0; // expected-warning {{Direct assignment to an instance 
> variable backing a property; use the setter instead}}
> +  }
> +@end
> \ No newline at end of file
> 
> 
> _______________________________________________
> cfe-commits mailing list
> [email protected]
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to