apicheck: Allow safe addition and removal of final qualifier.
Split the CHANGED_FINAL error into ADDED_FINAL,
ADDED_FINAL_UNINSTANTIABLE and REMOVED_FINAL so that the
API check scripts can more precisely target policy around
changes to the final qualifier.
ADDED_FINAL_UNINSTANTIABLE covers the case of a class
that has become final but that was previously not
instantiable. This change is safe since applications could
not have declared subclasses of their own.
Change-Id: I36a143bdbcffd04379788a55ff996b47398ba4fe
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index db5e0cf..b6a5cc3 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -624,6 +624,10 @@
return mIsVarargs;
}
+ public boolean isFinalOrBelongsToFinalClass() {
+ return mIsFinal || (containingClass() != null && containingClass().isFinal());
+ }
+
@Override
public String toString() {
return this.name();
@@ -729,17 +733,19 @@
+ " has changed 'native' qualifier");
}
- if (mIsFinal != mInfo.mIsFinal) {
- // Compiler-generated methods vary in their 'final' qual between versions of
+ if (!mIsStatic) {
+ // Compiler-generated methods vary in their 'final' qualifier between versions of
// the compiler, so this check needs to be quite narrow. A change in 'final'
// status of a method is only relevant if (a) the method is not declared 'static'
- // and (b) the method's class is not itself 'final'.
- if (!mIsStatic) {
- if ((containingClass() == null) || (!containingClass().isFinal())) {
- consistent = false;
- Errors.error(Errors.CHANGED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has changed 'final' qualifier");
- }
+ // and (b) the method is not already inferred to be 'final' by virtue of its class.
+ if (!isFinalOrBelongsToFinalClass() && mInfo.isFinalOrBelongsToFinalClass()) {
+ consistent = false;
+ Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
+ + " has added 'final' qualifier");
+ } else if (isFinalOrBelongsToFinalClass() && !mInfo.isFinalOrBelongsToFinalClass()) {
+ consistent = false;
+ Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
+ + " has removed 'final' qualifier");
}
}