Merge "apilint: decorator-based verifier registration"

am: 6e16eb107d

Change-Id: I4eba036308797fcf892264aab6e70f7b8acf8ffc
This commit is contained in:
Adrian Roos
2019-03-05 15:19:22 -08:00
committed by android-build-merger

View File

@@ -763,6 +763,14 @@ def notice(clazz):
noticed[clazz.fullname] = hash(clazz)
verifiers = {}
def verifier(f):
verifiers[f.__name__] = f
return f
@verifier
def verify_constants(clazz):
"""All static final constants must be FOO_NAME style."""
if re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -780,13 +788,13 @@ def verify_constants(clazz):
if f.typ in req and f.value is None:
error(clazz, f, None, "All constants must be defined at compile time")
@verifier
def verify_enums(clazz):
"""Enums are bad, mmkay?"""
if clazz.extends == "java.lang.Enum" or "enum" in clazz.split:
error(clazz, None, "F5", "Enums are not allowed")
@verifier
def verify_class_names(clazz):
"""Try catching malformed class names like myMtp or MTPUser."""
if clazz.fullname.startswith("android.opengl"): return
@@ -801,6 +809,7 @@ def verify_class_names(clazz):
error(clazz, None, None, "Don't expose your implementation details")
@verifier
def verify_method_names(clazz):
"""Try catching malformed method names, like Foo() or getMTU()."""
if clazz.fullname.startswith("android.opengl"): return
@@ -814,6 +823,7 @@ def verify_method_names(clazz):
error(clazz, m, "S1", "Method name must start with lowercase char")
@verifier
def verify_callbacks(clazz):
"""Verify Callback classes.
All callback classes must be abstract.
@@ -834,6 +844,7 @@ def verify_callbacks(clazz):
error(clazz, m, "L1", "Callback method names must be onFoo() style")
@verifier
def verify_listeners(clazz):
"""Verify Listener classes.
All Listener classes must be interface.
@@ -855,6 +866,7 @@ def verify_listeners(clazz):
error(clazz, m, "L1", "Single listener method name must match class name")
@verifier
def verify_actions(clazz):
"""Verify intent actions.
All action names must be named ACTION_FOO.
@@ -886,6 +898,7 @@ def verify_actions(clazz):
error(clazz, f, "C4", "Inconsistent action value; expected '%s'" % (expected))
@verifier
def verify_extras(clazz):
"""Verify intent extras.
All extra names must be named EXTRA_FOO.
@@ -916,6 +929,7 @@ def verify_extras(clazz):
error(clazz, f, "C4", "Inconsistent extra value; expected '%s'" % (expected))
@verifier
def verify_equals(clazz):
"""Verify that equals() and hashCode() must be overridden together."""
eq = False
@@ -928,6 +942,7 @@ def verify_equals(clazz):
error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
@verifier
def verify_parcelable(clazz):
"""Verify that Parcelable objects aren't hiding required bits."""
if clazz.implements == "android.os.Parcelable":
@@ -946,6 +961,7 @@ def verify_parcelable(clazz):
error(clazz, c, "FW3", "Parcelable inflation is exposed through CREATOR, not raw constructors")
@verifier
def verify_protected(clazz):
"""Verify that no protected methods or fields are allowed."""
for m in clazz.methods:
@@ -957,6 +973,7 @@ def verify_protected(clazz):
error(clazz, f, "M7", "Protected fields not allowed; must be public")
@verifier
def verify_fields(clazz):
"""Verify that all exposed fields are final.
Exposed fields must follow myName style.
@@ -1002,6 +1019,7 @@ def verify_fields(clazz):
error(clazz, f, "C2", "Constants must be marked static final")
@verifier
def verify_register(clazz):
"""Verify parity of registration methods.
Callback objects use register/unregister methods.
@@ -1035,6 +1053,7 @@ def verify_register(clazz):
error(clazz, m, "L3", "Listener methods should be named add/remove")
@verifier
def verify_sync(clazz):
"""Verify synchronized methods aren't exposed."""
for m in clazz.methods:
@@ -1042,6 +1061,7 @@ def verify_sync(clazz):
error(clazz, m, "M5", "Internal locks must not be exposed")
@verifier
def verify_intent_builder(clazz):
"""Verify that Intent builders are createFooIntent() style."""
if clazz.name == "Intent": return
@@ -1054,6 +1074,7 @@ def verify_intent_builder(clazz):
warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()")
@verifier
def verify_helper_classes(clazz):
"""Verify that helper classes are named consistently with what they extend.
All developer extendable methods should be named onFoo()."""
@@ -1102,6 +1123,7 @@ def verify_helper_classes(clazz):
warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
@verifier
def verify_builder(clazz):
"""Verify builder classes.
Methods should return the builder to enable chaining."""
@@ -1134,12 +1156,14 @@ def verify_builder(clazz):
error(clazz, None, None, "Builder should be final")
@verifier
def verify_aidl(clazz):
"""Catch people exposing raw AIDL."""
if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface":
error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
@verifier
def verify_internal(clazz):
"""Catch people exposing internal classes."""
if clazz.pkg.name.startswith("com.android"):
@@ -1174,6 +1198,7 @@ LAYERING_PACKAGE_RANKING = layering_build_ranking([
"android.util"
])
@verifier
def verify_layering(clazz):
"""Catch package layering violations.
For example, something in android.os depending on android.app."""
@@ -1208,6 +1233,7 @@ def verify_layering(clazz):
warn(clazz, m, "FW6", "Method argument type violates package layering")
@verifier
def verify_boolean(clazz):
"""Verifies that boolean accessors are named correctly.
For example, hasFoo() and setHasFoo()."""
@@ -1248,6 +1274,7 @@ def verify_boolean(clazz):
error_if_exists(sets, m.name, expected, "has" + target)
@verifier
def verify_collections(clazz):
"""Verifies that collection types are interfaces."""
if clazz.fullname == "android.os.Bundle": return
@@ -1262,6 +1289,8 @@ def verify_collections(clazz):
if arg in bad:
error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface")
@verifier
def verify_uris(clazz):
bad = ["java.net.URL", "java.net.URI", "android.net.URL"]
@@ -1276,6 +1305,8 @@ def verify_uris(clazz):
if arg in bad:
error(clazz, m, None, "Argument must take android.net.Uri instead of " + arg)
@verifier
def verify_flags(clazz):
"""Verifies that flags are non-overlapping."""
known = collections.defaultdict(int)
@@ -1292,6 +1323,7 @@ def verify_flags(clazz):
known[scope] |= val
@verifier
def verify_exception(clazz):
"""Verifies that methods don't throw generic exceptions."""
for m in clazz.methods:
@@ -1309,8 +1341,10 @@ def verify_exception(clazz):
if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]:
warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException")
GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE)
# Not marked as @verifier, because it is only conditionally applied.
def verify_google(clazz):
"""Verifies that APIs never reference Google."""
@@ -1323,6 +1357,7 @@ def verify_google(clazz):
error(clazz, t, None, "Must never reference Google")
@verifier
def verify_bitset(clazz):
"""Verifies that we avoid using heavy BitSet."""
@@ -1338,6 +1373,7 @@ def verify_bitset(clazz):
error(clazz, m, None, "Argument type must not be heavy BitSet")
@verifier
def verify_manager(clazz):
"""Verifies that FooManager is only obtained from Context."""
@@ -1351,6 +1387,7 @@ def verify_manager(clazz):
error(clazz, m, None, "Managers must always be obtained from Context")
@verifier
def verify_boxed(clazz):
"""Verifies that methods avoid boxed primitives."""
@@ -1373,6 +1410,7 @@ def verify_boxed(clazz):
error(clazz, m, "M11", "Must avoid boxed primitives")
@verifier
def verify_static_utils(clazz):
"""Verifies that helper classes can't be constructed."""
if clazz.fullname.startswith("android.opengl"): return
@@ -1392,6 +1430,7 @@ def verify_static_utils(clazz):
error(clazz, None, None, "Fully-static utility classes must not have constructor")
# @verifier # Disabled for now
def verify_overload_args(clazz):
"""Verifies that method overloads add new arguments at the end."""
if clazz.fullname.startswith("android.opengl"): return
@@ -1432,6 +1471,7 @@ def verify_overload_args(clazz):
error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig)))
@verifier
def verify_callback_handlers(clazz):
"""Verifies that methods adding listener/callback have overload
for specifying delivery thread."""
@@ -1483,6 +1523,7 @@ def verify_callback_handlers(clazz):
warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Executor")
@verifier
def verify_context_first(clazz):
"""Verifies that methods accepting a Context keep it the first argument."""
examine = clazz.ctors + clazz.methods
@@ -1495,6 +1536,7 @@ def verify_context_first(clazz):
error(clazz, m, "M3", "ContentResolver is distinct, so it must be the first argument")
@verifier
def verify_listener_last(clazz):
"""Verifies that methods accepting a Listener or Callback keep them as last arguments."""
examine = clazz.ctors + clazz.methods
@@ -1508,6 +1550,7 @@ def verify_listener_last(clazz):
warn(clazz, m, "M3", "Listeners should always be at end of argument list")
@verifier
def verify_resource_names(clazz):
"""Verifies that resource names have consistent case."""
if not re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -1539,6 +1582,7 @@ def verify_resource_names(clazz):
error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style")
@verifier
def verify_files(clazz):
"""Verifies that methods accepting File also accept streams."""
@@ -1560,6 +1604,7 @@ def verify_files(clazz):
warn(clazz, m, "M10", "Methods accepting File should also accept FileDescriptor or streams")
@verifier
def verify_manager_list(clazz):
"""Verifies that managers return List<? extends Parcelable> instead of arrays."""
@@ -1570,6 +1615,7 @@ def verify_manager_list(clazz):
warn(clazz, m, None, "Methods should return List<? extends Parcelable> instead of Parcelable[] to support ParceledListSlice under the hood")
@verifier
def verify_abstract_inner(clazz):
"""Verifies that abstract inner classes are static."""
@@ -1578,6 +1624,7 @@ def verify_abstract_inner(clazz):
warn(clazz, None, None, "Abstract inner classes should be static to improve testability")
@verifier
def verify_runtime_exceptions(clazz):
"""Verifies that runtime exceptions aren't listed in throws."""
@@ -1622,6 +1669,7 @@ def verify_runtime_exceptions(clazz):
error(clazz, m, None, "Methods must not mention RuntimeException subclasses in throws clauses")
@verifier
def verify_error(clazz):
"""Verifies that we always use Exception instead of Error."""
if not clazz.extends: return
@@ -1631,6 +1679,7 @@ def verify_error(clazz):
error(clazz, None, None, "Exceptions must be named FooException")
@verifier
def verify_units(clazz):
"""Verifies that we use consistent naming for units."""
@@ -1667,6 +1716,7 @@ def verify_units(clazz):
error(clazz, m, None, "Percentage must use ints")
@verifier
def verify_closable(clazz):
"""Verifies that classes are AutoClosable."""
if "java.lang.AutoCloseable" in clazz.implements_all: return
@@ -1679,6 +1729,7 @@ def verify_closable(clazz):
return
@verifier
def verify_member_name_not_kotlin_keyword(clazz):
"""Prevent method names which are keywords in Kotlin."""
@@ -1704,6 +1755,7 @@ def verify_member_name_not_kotlin_keyword(clazz):
error(clazz, f, None, "Field name must not be a Kotlin keyword")
@verifier
def verify_method_name_not_kotlin_operator(clazz):
"""Warn about method names which become operators in Kotlin."""
@@ -1753,6 +1805,7 @@ def verify_method_name_not_kotlin_operator(clazz):
unique_binary_op(m, m.name[:-6]) # Remove 'Assign' suffix
@verifier
def verify_collections_over_arrays(clazz):
"""Warn that [] should be Collections."""
@@ -1768,6 +1821,7 @@ def verify_collections_over_arrays(clazz):
warn(clazz, m, None, "Method argument should be Collection<> (or subclass) instead of raw array")
@verifier
def verify_user_handle(clazz):
"""Methods taking UserHandle should be ForUser or AsUser."""
if clazz.name.endswith("Listener") or clazz.name.endswith("Callback") or clazz.name.endswith("Callbacks"): return
@@ -1792,6 +1846,7 @@ def verify_user_handle(clazz):
"or 'queryFooForUser'")
@verifier
def verify_params(clazz):
"""Parameter classes should be 'Params'."""
if clazz.name.endswith("Params"): return
@@ -1807,6 +1862,7 @@ def verify_params(clazz):
error(clazz, None, None, "Classes holding a set of parameters should be called 'FooParams'")
@verifier
def verify_services(clazz):
"""Service name should be FOO_BAR_SERVICE = 'foo_bar'."""
if clazz.fullname != "android.content.Context": return
@@ -1820,6 +1876,7 @@ def verify_services(clazz):
error(clazz, f, "C4", "Inconsistent service value; expected '%s'" % (expected))
@verifier
def verify_tense(clazz):
"""Verify tenses of method names."""
if clazz.fullname.startswith("android.opengl"): return
@@ -1829,6 +1886,7 @@ def verify_tense(clazz):
warn(clazz, m, None, "Unexpected tense; probably meant 'enabled'")
@verifier
def verify_icu(clazz):
"""Verifies that richer ICU replacements are used."""
better = {
@@ -1860,6 +1918,7 @@ def verify_icu(clazz):
warn(clazz, m, None, "Type %s should be replaced with richer ICU type %s" % (arg, better[arg]))
@verifier
def verify_clone(clazz):
"""Verify that clone() isn't implemented; see EJ page 61."""
for m in clazz.methods:
@@ -1867,6 +1926,7 @@ def verify_clone(clazz):
error(clazz, m, None, "Provide an explicit copy constructor instead of implementing clone()")
@verifier
def verify_pfd(clazz):
"""Verify that android APIs use PFD over FD."""
if clazz.fullname == "android.os.FileUtils" or clazz.fullname == "android.system.Os":
@@ -1888,6 +1948,7 @@ def verify_pfd(clazz):
error(clazz, f, "FW11", "Must use ParcelFileDescriptor")
@verifier
def verify_numbers(clazz):
"""Discourage small numbers types like short and byte."""
@@ -1909,8 +1970,10 @@ def verify_numbers(clazz):
if arg in discouraged:
warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead")
PRIMITIVES = {"void", "int", "float", "boolean", "short", "char", "byte", "long", "double"}
@verifier
def verify_nullability(clazz):
"""Catches missing nullability annotations"""
@@ -1939,6 +2002,8 @@ def verify_nullability_args(clazz, m):
def has_nullability(annotations):
return "@NonNull" in annotations or "@Nullable" in annotations
@verifier
def verify_singleton(clazz):
"""Catch singleton objects with constructors."""
@@ -1973,61 +2038,10 @@ def examine_clazz(clazz):
if not is_interesting(clazz): return
verify_constants(clazz)
verify_enums(clazz)
verify_class_names(clazz)
verify_method_names(clazz)
verify_callbacks(clazz)
verify_listeners(clazz)
verify_actions(clazz)
verify_extras(clazz)
verify_equals(clazz)
verify_parcelable(clazz)
verify_protected(clazz)
verify_fields(clazz)
verify_register(clazz)
verify_sync(clazz)
verify_intent_builder(clazz)
verify_helper_classes(clazz)
verify_builder(clazz)
verify_aidl(clazz)
verify_internal(clazz)
verify_layering(clazz)
verify_boolean(clazz)
verify_collections(clazz)
verify_uris(clazz)
verify_flags(clazz)
verify_exception(clazz)
for v in verifiers.itervalues():
v(clazz)
if not ALLOW_GOOGLE: verify_google(clazz)
verify_bitset(clazz)
verify_manager(clazz)
verify_boxed(clazz)
verify_static_utils(clazz)
# verify_overload_args(clazz)
verify_callback_handlers(clazz)
verify_context_first(clazz)
verify_listener_last(clazz)
verify_resource_names(clazz)
verify_files(clazz)
verify_manager_list(clazz)
verify_abstract_inner(clazz)
verify_runtime_exceptions(clazz)
verify_error(clazz)
verify_units(clazz)
verify_closable(clazz)
verify_member_name_not_kotlin_keyword(clazz)
verify_method_name_not_kotlin_operator(clazz)
verify_collections_over_arrays(clazz)
verify_user_handle(clazz)
verify_params(clazz)
verify_services(clazz)
verify_tense(clazz)
verify_icu(clazz)
verify_clone(clazz)
verify_pfd(clazz)
verify_numbers(clazz)
verify_singleton(clazz)
verify_nullability(clazz)
def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):