Teach apicheck about interface methods.
If a method is declared by an interface implemented directly by a class, or indirectly by any of its superclasses, removing a duplicate declaration is not an API change. (This fixes the master build, which is currently broken because getOption and setOption were removed from DatagramSocketImpl because they were duplicate declarations of the same methods in the implemented interface SocketOptions.)
This commit is contained in:
@@ -27,7 +27,18 @@ public class ApiInfo {
|
|||||||
mAllClasses = new HashMap<String, ClassInfo>();
|
mAllClasses = new HashMap<String, ClassInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClassInfo findClass(String name) {
|
||||||
|
return mAllClasses.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolveInterfaces() {
|
||||||
|
for (ClassInfo c : mAllClasses.values()) {
|
||||||
|
c.resolveInterfaces(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isConsistent(ApiInfo otherApi) {
|
public boolean isConsistent(ApiInfo otherApi) {
|
||||||
|
resolveInterfaces();
|
||||||
boolean consistent = true;
|
boolean consistent = true;
|
||||||
for (PackageInfo pInfo : mPackages.values()) {
|
for (PackageInfo pInfo : mPackages.values()) {
|
||||||
if (otherApi.getPackages().containsKey(pInfo.name())) {
|
if (otherApi.getPackages().containsKey(pInfo.name())) {
|
||||||
|
@@ -26,7 +26,8 @@ public class ClassInfo {
|
|||||||
private boolean mIsFinal;
|
private boolean mIsFinal;
|
||||||
private String mDeprecated;
|
private String mDeprecated;
|
||||||
private String mScope;
|
private String mScope;
|
||||||
private List<String> mInterfaces;
|
private List<String> mInterfaceNames;
|
||||||
|
private List<ClassInfo> mInterfaces;
|
||||||
private HashMap<String, MethodInfo> mMethods;
|
private HashMap<String, MethodInfo> mMethods;
|
||||||
private HashMap<String, FieldInfo> mFields;
|
private HashMap<String, FieldInfo> mFields;
|
||||||
private HashMap<String, ConstructorInfo> mConstructors;
|
private HashMap<String, ConstructorInfo> mConstructors;
|
||||||
@@ -48,7 +49,8 @@ public class ClassInfo {
|
|||||||
mIsFinal = isFinal;
|
mIsFinal = isFinal;
|
||||||
mDeprecated = deprecated;
|
mDeprecated = deprecated;
|
||||||
mScope = visibility;
|
mScope = visibility;
|
||||||
mInterfaces = new ArrayList<String>();
|
mInterfaceNames = new ArrayList<String>();
|
||||||
|
mInterfaces = new ArrayList<ClassInfo>();
|
||||||
mMethods = new HashMap<String, MethodInfo>();
|
mMethods = new HashMap<String, MethodInfo>();
|
||||||
mFields = new HashMap<String, FieldInfo>();
|
mFields = new HashMap<String, FieldInfo>();
|
||||||
mConstructors = new HashMap<String, ConstructorInfo>();
|
mConstructors = new HashMap<String, ConstructorInfo>();
|
||||||
@@ -109,6 +111,18 @@ public class ClassInfo {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find a superinterface declaration of the given method.
|
||||||
|
public MethodInfo interfaceMethod(MethodInfo candidate) {
|
||||||
|
for (ClassInfo interfaceInfo : mInterfaces) {
|
||||||
|
for (MethodInfo mi : interfaceInfo.mMethods.values()) {
|
||||||
|
if (mi.matches(candidate)) {
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (mSuperClass != null) ? mSuperClass.interfaceMethod(candidate) : null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isConsistent(ClassInfo cl) {
|
public boolean isConsistent(ClassInfo cl) {
|
||||||
cl.mExistsInBoth = true;
|
cl.mExistsInBoth = true;
|
||||||
mExistsInBoth = true;
|
mExistsInBoth = true;
|
||||||
@@ -120,18 +134,18 @@ public class ClassInfo {
|
|||||||
+ " changed class/interface declaration");
|
+ " changed class/interface declaration");
|
||||||
consistent = false;
|
consistent = false;
|
||||||
}
|
}
|
||||||
for (String iface : mInterfaces) {
|
for (String iface : mInterfaceNames) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (ClassInfo c = cl; c != null && !found; c = c.mSuperClass) {
|
for (ClassInfo c = cl; c != null && !found; c = c.mSuperClass) {
|
||||||
found = c.mInterfaces.contains(iface);
|
found = c.mInterfaceNames.contains(iface);
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
Errors.error(Errors.REMOVED_INTERFACE, cl.position(),
|
Errors.error(Errors.REMOVED_INTERFACE, cl.position(),
|
||||||
"Class " + qualifiedName() + " no longer implements " + iface);
|
"Class " + qualifiedName() + " no longer implements " + iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (String iface : cl.mInterfaces) {
|
for (String iface : cl.mInterfaceNames) {
|
||||||
if (!mInterfaces.contains(iface)) {
|
if (!mInterfaceNames.contains(iface)) {
|
||||||
Errors.error(Errors.ADDED_INTERFACE, cl.position(),
|
Errors.error(Errors.ADDED_INTERFACE, cl.position(),
|
||||||
"Added interface " + iface + " to class "
|
"Added interface " + iface + " to class "
|
||||||
+ qualifiedName());
|
+ qualifiedName());
|
||||||
@@ -150,6 +164,9 @@ public class ClassInfo {
|
|||||||
* fulfills the API requirement.
|
* fulfills the API requirement.
|
||||||
*/
|
*/
|
||||||
MethodInfo mi = mInfo.containingClass().overriddenMethod(mInfo);
|
MethodInfo mi = mInfo.containingClass().overriddenMethod(mInfo);
|
||||||
|
if (mi == null) {
|
||||||
|
mi = mInfo.containingClass().interfaceMethod(mInfo);
|
||||||
|
}
|
||||||
if (mi == null) {
|
if (mi == null) {
|
||||||
Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
|
Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
|
||||||
"Removed public method " + mInfo.qualifiedName());
|
"Removed public method " + mInfo.qualifiedName());
|
||||||
@@ -257,8 +274,14 @@ public class ClassInfo {
|
|||||||
return consistent;
|
return consistent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resolveInterfaces(ApiInfo apiInfo) {
|
||||||
|
for (String interfaceName : mInterfaceNames) {
|
||||||
|
mInterfaces.add(apiInfo.findClass(interfaceName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addInterface(String name) {
|
public void addInterface(String name) {
|
||||||
mInterfaces.add(name);
|
mInterfaceNames.add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMethod(MethodInfo mInfo) {
|
public void addMethod(MethodInfo mInfo) {
|
||||||
|
Reference in New Issue
Block a user