Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eae9a40 authored by Adrian Roos's avatar Adrian Roos Committed by android-build-merger
Browse files

Merge "apilint: decorator-based verifier registration" am: 6e16eb10

am: b3662ec2

Change-Id: I0463569c258ecf63c61c4eefacb257d5219df3d5
parents 4895861d b3662ec2
Loading
Loading
Loading
Loading
+70 −56
Original line number Diff line number Diff line
@@ -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):