stephanemoore marked 2 inline comments as done.
stephanemoore added a comment.

Thanks for the review!



================
Comment at: clang-tools-extra/clang-tidy/objc/MissingHashCheck.cpp:56
+  const auto *ID = Result.Nodes.getNodeAs<ObjCImplementationDecl>("impl");
+  diag(ID->getLocation(), "%0 implements -isEqual: without implementing -hash")
+      << ID;
----------------
aaron.ballman wrote:
> stephanemoore wrote:
> > aaron.ballman wrote:
> > > Do you think we could generate a fixit to add the `hash` method? Do you 
> > > think we could even add a default implementation that returns the pointer 
> > > to the object (assuming that's the correct default behavior)?
> > > Do you think we could generate a fixit to add the hash method?
> > 
> > I think it would be pretty tough to generate a reasonable hash method 
> > without knowing the equality and hashing semantics that the scenario calls 
> > for.
> > 
> > Here is an analogous situation presented in C++ (please excuse the hastily 
> > assembled sample code):
> > ```
> > namespace {
> > 
> > class NSObject {
> >   public:
> >     NSObject() {}
> >     virtual ~NSObject() {}
> > 
> >     virtual bool isEqual(const NSObject *other) const {
> >       return this == other;
> >     }
> >     virtual unsigned long long hash() const {
> >       return (unsigned long long)this;
> >     }
> > };
> > 
> > }
> > 
> > #include <stdio.h>
> > #include <string>
> > 
> > namespace {
> > 
> > class Movie : public virtual NSObject {
> >   private:
> >     std::string name;
> >     std::string language;
> > 
> >   public:
> >     Movie(std::string name, std::string language) : name(name), 
> > language(language) {}
> >     ~Movie() override {}
> >     bool isEqual(const NSObject *other) const override {
> >       if (auto otherMovie = dynamic_cast<const Movie *>(other)) {
> >         // Movies with the same name are considered equal
> >         // regardless of the language of the screening.
> >         return name == otherMovie->name;
> >       }
> >       return false;
> >     }
> >     unsigned long long hash() const override {
> >       return name.length();
> >     }
> > };
> > 
> > }
> > ```
> > 
> > As before, the base class uses pointer equality and the pointer as a hash. 
> > A subclass may arbitrarily add additional state but only the developer 
> > knows which added state factors into equality operations and consequently 
> > should be considered—but not necessarily required—in the hash operation. 
> > The matter can technically get even more complicated if an object stores 
> > state externally. I would hope that externally stored state would not 
> > factor into the equality operation of an object but I hesitate to make an 
> > assumption.
> > 
> > The developer is also in the best position to prioritize different 
> > properties of the hash function including performance, collision 
> > resistance, uniformity, and non-invertibility.
> > 
> > Writing effective hash functions is probably difficult independent of the 
> > programming language but it might help to consider some specific examples 
> > in Objective-C. 
> > [GPBMessage](https://github.com/protocolbuffers/protobuf/blob/ffa6bfc/objectivec/GPBMessage.m),
> >  the Objective-C base class for Google Protocol Buffer message classes, 
> > implements `-hash` but has an [extensive 
> > comment](https://github.com/protocolbuffers/protobuf/blob/ffa6bfc/objectivec/GPBMessage.m#L2749)
> >  explaining that its complex but generic implementation is not generally 
> > optimal and recommends that developers override `-hash` and `-isEqual:` to 
> > optimize for runtime performance. In contrast, the basic collection classes 
> > in Apple's Foundation framework have [surprisingly simple hash 
> > behavior](https://github.com/stephanemoore/archives/blob/master/objc/tips/hashing-basic-collections.md)
> >  that clearly indicate priority to runtime performance over uniformity and 
> > collision resistance. The former is a conservatively expensive hash 
> > function and the latter is a conservatively inexpensive hash function.
> > 
> > > Do you think we could even add a default implementation that returns the 
> > > pointer to the object (assuming that's the correct default behavior)?
> > 
> > A hash returning the object pointer is already inherited from the 
> > superclass (i..e, `-[NSObject hash]`). Defining an override that returns 
> > the object pointer would be a functional no-op for classes directly derived 
> > from `NSObject` (although the explicit override could be useful as a signal 
> > of intended behavior).
> > A hash returning the object pointer is already inherited from the 
> > superclass (i..e, -[NSObject hash]). Defining an override that returns the 
> > object pointer would be a functional no-op for classes directly derived 
> > from NSObject (although the explicit override could be useful as a signal 
> > of intended behavior).
> 
> Ah, my ObjC knowledge is weak and I was thinking that the one inherited from 
> `NSObject` would be hidden. Thank you for the detailed explanation, that 
> makes a lot of sense to me.
> Thank you for the detailed explanation, that makes a lot of sense to me.

My pleasure!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D67737/new/

https://reviews.llvm.org/D67737



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to