docs: protected boolean needsPropertyChangeSupport(ClassNode declaringClass, SourceUnit sourceUnit) {
          boolean foundAdd = false, foundRemove = false, foundFire = false;
          ClassNode consideredClass = declaringClass;
          while (consideredClass!= null) {
              for (MethodNode method : consideredClass.getMethods()) {
                  // just check length, MOP will match it up
                  foundAdd = foundAdd || method.getName().equals("addPropertyChangeListener") && method.getParameters().length == 1;
                  foundRemove = foundRemove || method.getName().equals("removePropertyChangeListener") && method.getParameters().length == 1;
                  foundFire = foundFire || method.getName().equals("firePropertyChange") && method.getParameters().length == 3;
                  if (foundAdd && foundRemove && foundFire) {
                      return false;
                  }
              }
              consideredClass = consideredClass.getSuperClass();
          }
          // check if a super class has @Bindable annotations
          consideredClass = declaringClass.getSuperClass();
          while (consideredClass!=null) {
              if (hasBindableAnnotation(consideredClass)) return false;
              for (FieldNode field : consideredClass.getFields()) {
                  if (hasBindableAnnotation(field)) return false;
              }
              consideredClass = consideredClass.getSuperClass();
          }
          if (foundAdd || foundRemove || foundFire) {
              sourceUnit.getErrorCollector().addErrorAndContinue(
                  new SimpleMessage("@Bindable cannot be processed on "
                      + declaringClass.getName()
                      + " because some but not all of addPropertyChangeListener, removePropertyChange, and firePropertyChange were declared in the current or super classes.",
                  sourceUnit)
              );
              return false;
          }
          return true;
      }