From: Jeff Sharkey Date: Wed, 11 Apr 2018 16:05:44 +0000 (-0600) Subject: Lint to identify "deprecated at birth" APIs. X-Git-Tag: android-x86-9.0-r1~142^2~32^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=8b141b9db00aa95a9610770b1a7757fb155d097c;p=android-x86%2Fframeworks-base.git Lint to identify "deprecated at birth" APIs. When API council requests changes, teams regularly perform the requested refactoring, but simply mark the old APIs as @Deprecated without @removed, due to internal users. As part of finalizing an SDK, we should ensure that no new APIs are marked @Deprecated, since they're typically cleanup that someone forgot to finish. This extension to the lint script makes it easy to identify these cases. $ python tools/apilint/apilint.py --show-deprecations-at-birth \ api/current.txt ../../prebuilts/sdk/api/28.txt $ python tools/apilint/apilint.py --show-deprecations-at-birth \ api/system-current.txt ../../prebuilts/sdk/system-api/28.txt Bug: 77588754 Test: manual inspection Change-Id: Ie9658006bb08f780bee0e503481d3bafec1038a1 --- diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index 26248e51b884..70a47cf42755 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -50,6 +50,18 @@ def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): return "\033[%sm" % (";".join(codes)) +def ident(raw): + """Strips superficial signature changes, giving us a strong key that + can be used to identify members across API levels.""" + raw = raw.replace(" deprecated ", " ") + raw = raw.replace(" synchronized ", " ") + raw = raw.replace(" final ", " ") + raw = re.sub("<.+?>", "", raw) + if " throws " in raw: + raw = raw[:raw.index(" throws ")] + return raw + + class Field(): def __init__(self, clazz, line, raw, blame): self.clazz = clazz @@ -69,8 +81,7 @@ class Field(): self.value = raw[3].strip(';"') else: self.value = None - - self.ident = self.raw.replace(" deprecated ", " ") + self.ident = ident(self.raw) def __hash__(self): return hash(self.raw) @@ -105,15 +116,7 @@ class Method(): for r in raw[2:]: if r == "throws": target = self.throws else: target.append(r) - - # identity for compat purposes - ident = self.raw - ident = ident.replace(" deprecated ", " ") - ident = ident.replace(" synchronized ", " ") - ident = re.sub("<.+?>", "", ident) - if " throws " in ident: - ident = ident[:ident.index(" throws ")] - self.ident = ident + self.ident = ident(self.raw) def __hash__(self): return hash(self.raw) @@ -1469,6 +1472,40 @@ def verify_compat(cur, prev): return failures +def show_deprecations_at_birth(cur, prev): + """Show API deprecations at birth.""" + global failures + + # Remove all existing things so we're left with new + for prev_clazz in prev.values(): + cur_clazz = cur[prev_clazz.fullname] + + sigs = { i.ident: i for i in prev_clazz.ctors } + cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ] + sigs = { i.ident: i for i in prev_clazz.methods } + cur_clazz.methods = [ i for i in cur_clazz.methods if i.ident not in sigs ] + sigs = { i.ident: i for i in prev_clazz.fields } + cur_clazz.fields = [ i for i in cur_clazz.fields if i.ident not in sigs ] + + # Forget about class entirely when nothing new + if len(cur_clazz.ctors) == 0 and len(cur_clazz.methods) == 0 and len(cur_clazz.fields) == 0: + del cur[prev_clazz.fullname] + + for clazz in cur.values(): + if " deprecated " in clazz.raw and not clazz.fullname in prev: + error(clazz, None, None, "Found API deprecation at birth") + + for i in clazz.ctors + clazz.methods + clazz.fields: + if " deprecated " in i.raw: + error(clazz, i, None, "Found API deprecation at birth") + + print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), + format(reset=True))) + for f in sorted(failures): + print failures[f] + print + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Enforces common Android public API design \ patterns. It ignores lint messages from a previous API level, if provided.") @@ -1481,6 +1518,8 @@ if __name__ == "__main__": help="Allow references to Google") parser.add_argument("--show-noticed", action='store_const', const=True, help="Show API changes noticed") + parser.add_argument("--show-deprecations-at-birth", action='store_const', const=True, + help="Show API deprecations at birth") args = vars(parser.parse_args()) if args['no_color']: @@ -1492,6 +1531,14 @@ if __name__ == "__main__": current_file = args['current.txt'] previous_file = args['previous.txt'] + if args['show_deprecations_at_birth']: + with current_file as f: + cur = _parse_stream(f) + with previous_file as f: + prev = _parse_stream(f) + show_deprecations_at_birth(cur, prev) + sys.exit() + with current_file as f: cur_fail, cur_noticed = examine_stream(f) if not previous_file is None: