18190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#!/usr/bin/env python
28190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
38190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Copyright (C) 2014 The Android Open Source Project
48190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
58190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Licensed under the Apache License, Version 2.0 (the 'License');
68190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# you may not use this file except in compliance with the License.
78190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# You may obtain a copy of the License at
88190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
98190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#      http://www.apache.org/licenses/LICENSE-2.0
108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Unless required by applicable law or agreed to in writing, software
128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# distributed under the License is distributed on an 'AS IS' BASIS,
138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# See the License for the specific language governing permissions and
158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# limitations under the License.
168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
188190f4885b3eb34231877003a583116a0e82826eJeff SharkeyEnforces common Android public API design patterns.  It ignores lint messages from
198190f4885b3eb34231877003a583116a0e82826eJeff Sharkeya previous API level, if provided.
208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
218190f4885b3eb34231877003a583116a0e82826eJeff SharkeyUsage: apilint.py current.txt
228190f4885b3eb34231877003a583116a0e82826eJeff SharkeyUsage: apilint.py current.txt previous.txt
231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
241498f9c615395de17e11204b962d7d925e5f222dJeff SharkeyYou can also splice in blame details like this:
251498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey$ git blame api/current.txt -t -e > /tmp/currentblame.txt
261498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey$ apilint.py /tmp/currentblame.txt previous.txt --no-color
278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
291c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalfimport re, sys, collections, traceback, argparse
308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
328190f4885b3eb34231877003a583116a0e82826eJeff SharkeyBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
341c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam MetcalfALLOW_GOOGLE = False
351c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam MetcalfUSE_COLOR = True
361c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
378190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
391c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if not USE_COLOR: return ""
408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    codes = []
418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if reset: codes.append("0")
428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    else:
438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not fg is None: codes.append("3%d" % (fg))
448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not bg is None:
458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not bright: codes.append("4%d" % (bg))
468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else: codes.append("10%d" % (bg))
478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if bold: codes.append("1")
488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif dim: codes.append("2")
498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else: codes.append("22")
508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return "\033[%sm" % (";".join(codes))
518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
538190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Field():
54ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, clazz, line, raw, blame):
558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
56ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1].strip(";")
688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(raw) >= 4 and raw[2] == "=":
698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = raw[3].strip(';"')
708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else:
718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = None
728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
73037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        self.ident = self.raw.replace(" deprecated ", " ")
74037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
798190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Method():
80ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, clazz, line, raw, blame):
818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
82ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
841498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        # drop generics for now
871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        raw = re.sub("<.+?>", "", raw)
888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = re.split("[\s(),;]+", raw)
908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["", ";"]:
918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
94db234ef0ac1d1a7fec7d1f00ba37bbd7435e9893Filip Pavlis        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default"]:
958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1]
998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.args = []
1008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in raw[2:]:
1018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if r == "throws": break
1028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.args.append(r)
1038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
104037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        # identity for compat purposes
105037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = self.raw
106037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = ident.replace(" deprecated ", " ")
107037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = ident.replace(" synchronized ", " ")
108037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = re.sub("<.+?>", "", ident)
109037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if " throws " in ident:
110037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            ident = ident[:ident.index(" throws ")]
111037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        self.ident = ident
112037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1178190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Class():
118ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, pkg, line, raw, blame):
1198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.pkg = pkg
120ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
1218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1221498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
1238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.ctors = []
1248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.fields = []
1258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.methods = []
1268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
1298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "class" in raw:
1308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("class")+1]
1318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif "interface" in raw:
1328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("interface")+1]
133037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        else:
134037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            raise ValueError("Funky class type %s" % (self.raw))
135037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
136037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if "extends" in raw:
137037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            self.extends = raw[raw.index("extends")+1]
138047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            self.extends_path = self.extends.split(".")
139037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        else:
140037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            self.extends = None
141047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            self.extends_path = []
1428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.fullname = self.pkg.name + "." + self.fullname
144047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        self.fullname_path = self.fullname.split(".")
145047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey
1461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.name = self.fullname[self.fullname.rindex(".")+1:]
1471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1528190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Package():
153ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __init__(self, line, raw, blame):
154ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = line
1558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
1578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[raw.index("package")+1]
160047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        self.name_path = self.name.split(".")
1618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
166a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef _parse_stream(f, clazz_cb=None):
167ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    line = 0
1681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    api = {}
1698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    pkg = None
1708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    clazz = None
1711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    blame = None
1721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
174a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    for raw in f:
175ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        line += 1
176ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        raw = raw.rstrip()
177ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        match = re_blame.match(raw)
178ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if match is not None:
179ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            blame = match.groups()[0:2]
180ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            raw = match.groups()[2]
181ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        else:
182ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            blame = None
183ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
184ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if raw.startswith("package"):
185ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            pkg = Package(line, raw, blame)
186ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("  ") and raw.endswith("{"):
187a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            # When provided with class callback, we treat as incremental
188a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            # parse and don't build up entire API
189a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            if clazz and clazz_cb:
190a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey                clazz_cb(clazz)
191ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz = Class(pkg, line, raw, blame)
192a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            if not clazz_cb:
193a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey                api[clazz.fullname] = clazz
194ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("    ctor"):
195ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz.ctors.append(Method(clazz, line, raw, blame))
196ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("    method"):
197ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz.methods.append(Method(clazz, line, raw, blame))
198ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        elif raw.startswith("    field"):
199ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            clazz.fields.append(Field(clazz, line, raw, blame))
200ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
201a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    # Handle last trailing class
202a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz and clazz_cb:
203a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        clazz_cb(clazz)
2048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
205a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    return api
2068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
207ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
208ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyclass Failure():
2099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    def __init__(self, sig, clazz, detail, error, rule, msg):
210ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.sig = sig
211ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.error = error
2129f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        self.rule = rule
213ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.msg = msg
214ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
215ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if error:
2169f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            self.head = "Error %s" % (rule) if rule else "Error"
2179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg)
218ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        else:
2199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            self.head = "Warning %s" % (rule) if rule else "Warning"
2209f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg)
221ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
222ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.line = clazz.line
223ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        blame = clazz.blame
224ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if detail is not None:
225ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            dump += "\n    in " + repr(detail)
226ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            self.line = detail.line
227ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            blame = detail.blame
228ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        dump += "\n    in " + repr(clazz)
229ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        dump += "\n    in " + repr(clazz.pkg)
230ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        dump += "\n    at line " + repr(self.line)
231ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        if blame is not None:
232ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            dump += "\n    last modified by %s in %s" % (blame[1], blame[0])
233ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
234ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        self.dump = dump
235ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
236ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    def __repr__(self):
237ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        return self.dump
2388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2401498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeyfailures = {}
241294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
2429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef _fail(clazz, detail, error, rule, msg):
2438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Records an API failure to be processed later."""
2448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
2458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
2471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    sig = sig.replace(" deprecated ", " ")
2481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
2499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
250ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey
2518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef warn(clazz, detail, rule, msg):
2539f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    _fail(clazz, detail, False, rule, msg)
2548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef error(clazz, detail, rule, msg):
2569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    _fail(clazz, detail, True, rule, msg)
2578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2598190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_constants(clazz):
2608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """All static final constants must be FOO_NAME style."""
2611498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
26224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if clazz.fullname.startswith("android.os.Build"): return
26324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if clazz.fullname == "android.system.OsConstants": return
2648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
26524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    req = ["java.lang.String","byte","short","int","long","float","double","boolean","char"]
2668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
2678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split:
2688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if re.match("[A-Z0-9_]+", f.name) is None:
26990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, f, "C2", "Constant field names must be FOO_NAME")
27024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if f.typ != "java.lang.String":
271331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey                if f.name.startswith("MIN_") or f.name.startswith("MAX_"):
272331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey                    warn(clazz, f, "C8", "If min/max could change in future, make them dynamic methods")
27324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if f.typ in req and f.value is None:
27424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey                error(clazz, f, None, "All constants must be defined at compile time")
2758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2778190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_enums(clazz):
2788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Enums are bad, mmkay?"""
2798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends java.lang.Enum" in clazz.raw:
2809f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, "F5", "Enums are not allowed")
2818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2838190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_class_names(clazz):
2848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed class names like myMtp or MTPUser."""
2851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
2861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.renderscript"): return
2871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
2881498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
2898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.search("[A-Z]{2,}", clazz.name) is not None:
29090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        warn(clazz, None, "S1", "Class names with acronyms should be Mtp not MTP")
2918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.match("[^A-Z]", clazz.name):
2929f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, "S1", "Class must start with uppercase char")
2938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2958190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_method_names(clazz):
2968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed method names, like Foo() or getMTU()."""
2971498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
2981498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.renderscript"): return
2991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.system.OsConstants": return
3008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
3028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.search("[A-Z]{2,}", m.name) is not None:
30390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            warn(clazz, m, "S1", "Method names with acronyms should be getMtu() instead of getMTU()")
3048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.match("[^a-z]", m.name):
3059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, m, "S1", "Method name must start with lowercase char")
3068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3088190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_callbacks(clazz):
3098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Callback classes.
3108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All callback classes must be abstract.
3118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style."""
3121498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.speech.tts.SynthesisCallback": return
3138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callbacks"):
31590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        error(clazz, None, "L1", "Callback class names should be singular")
3168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Observer"):
3179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        warn(clazz, None, "L1", "Class should be named FooCallback")
3188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callback"):
3208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "interface" in clazz.split:
32190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels")
3228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
3248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
3259f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L1", "Callback method names must be onFoo() style")
3268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3288190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_listeners(clazz):
3298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Listener classes.
3308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All Listener classes must be interface.
3318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style.
3328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    If only a single method, it must match class name:
3338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        interface OnFooListener { void onFoo() }"""
3348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Listener"):
3368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if " abstract class " in clazz.raw:
33790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback")
3388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
3408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
3419f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L1", "Listener method names must be onFoo() style")
3428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(clazz.methods) == 1 and clazz.name.startswith("On"):
3448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            m = clazz.methods[0]
3458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if (m.name + "Listener").lower() != clazz.name.lower():
34690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "L1", "Single listener method name must match class name")
3478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3498190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_actions(clazz):
3508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent actions.
3518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action names must be named ACTION_FOO.
3528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action values must be scoped by package and match name:
3538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
3548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String ACTION_BAR = "android.foo.action.BAR";
3558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
3568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
3588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("EXTRA_"): continue
3591498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if f.name == "SERVICE_INTERFACE" or f.name == "PROVIDER_INTERFACE": continue
36024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if "INTERACTION" in f.name: continue
3618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
3638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
3648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("ACTION_"):
3659f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, f, "C3", "Intent action constant name must be ACTION_FOO")
3668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
3671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    if clazz.fullname == "android.content.Intent":
3688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.action"
3691498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.fullname == "android.provider.Settings":
3708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.settings"
3711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.fullname == "android.app.admin.DevicePolicyManager" or clazz.fullname == "android.app.admin.DeviceAdminReceiver":
3721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        prefix = "android.app.action"
3738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
3748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".action"
3758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[7:]
3768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
3779f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                        error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected))
3788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3808190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_extras(clazz):
3818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent extras.
3828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra names must be named EXTRA_FOO.
3838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra values must be scoped by package and match name:
3848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
3858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String EXTRA_BAR = "android.foo.extra.BAR";
3868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
3871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.app.Notification": return
3881498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.appwidget.AppWidgetManager": return
3891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
3908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
3928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("ACTION_"): continue
3938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
3958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
3968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("EXTRA_"):
3979f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, f, "C3", "Intent extra must be EXTRA_FOO")
3988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
3991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    if clazz.pkg.name == "android.content" and clazz.name == "Intent":
4008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.extra"
4011498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.pkg.name == "android.app.admin":
4021498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        prefix = "android.app.extra"
4038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
4048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".extra"
4058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[6:]
4068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
4079f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                        error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected))
4088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4108190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_equals(clazz):
4118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that equals() and hashCode() must be overridden together."""
41240d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey    eq = False
41340d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey    hc = False
41440d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey    for m in clazz.methods:
41540d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey        if " static " in m.raw: continue
41640d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey        if "boolean equals(java.lang.Object)" in m.raw: eq = True
41740d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey        if "int hashCode()" in m.raw: hc = True
4188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if eq != hc:
4199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
4208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4228190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_parcelable(clazz):
4238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Parcelable objects aren't hiding required bits."""
4248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "implements android.os.Parcelable" in clazz.raw:
4258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
4268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
4278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        describe = [ i for i in clazz.methods if i.name == "describeContents" ]
4288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
4309f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
4318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
432331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey        if " final class " not in clazz.raw:
433331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            error(clazz, None, "FW8", "Parcelable classes must be final")
434331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
4358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4368190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_protected(clazz):
437b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verify that no protected methods or fields are allowed."""
4388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in m.split:
44090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "M7", "Protected methods not allowed; must be public")
4418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
4428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in f.split:
44390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "M7", "Protected fields not allowed; must be public")
4448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4468190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_fields(clazz):
4478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that all exposed fields are final.
4488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Exposed fields must follow myName style.
4498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Catch internal mFoo objects being exposed."""
4501498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    IGNORE_BARE_FIELDS = [
4521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.app.ActivityManager.RecentTaskInfo",
4531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.app.Notification",
4541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.ActivityInfo",
4551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.ApplicationInfo",
45624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "android.content.pm.ComponentInfo",
45724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "android.content.pm.ResolveInfo",
4581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.FeatureGroupInfo",
4591498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.InstrumentationInfo",
4601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.PackageInfo",
4611498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.PackageItemInfo",
46224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "android.content.res.Configuration",
46324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "android.graphics.BitmapFactory.Options",
4641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.os.Message",
4651498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.system.StructPollfd",
4661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    ]
4671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
4698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "final" in f.split:
4701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if clazz.fullname in IGNORE_BARE_FIELDS:
4711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif clazz.fullname.endswith("LayoutParams"):
4731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4741498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif clazz.fullname.startswith("android.util.Mutable"):
4751498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4761498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            else:
4779f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable")
4788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "static" in f.split:
4808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("[a-z]([a-zA-Z]+)?", f.name):
48190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, f, "S1", "Non-static fields must be named using myField style")
4828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4831498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if re.match("[ms][A-Z]", f.name):
48490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "F1", "Internal objects must not be exposed")
4858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if re.match("[A-Z_]+", f.name):
4871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if "static" not in f.split or "final" not in f.split:
4889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, f, "C2", "Constants must be marked static final")
4891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4918190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_register(clazz):
4928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify parity of registration methods.
4938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Callback objects use register/unregister methods.
4948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Listener objects use add/remove methods."""
4958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
4968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Callback" in m.raw:
4988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register"):
4998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "unregister" + m.name[8:]
5008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
5019f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing unregister method")
5028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("unregister"):
5038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "register" + m.name[10:]
5048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
5059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing register method")
5068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add") or m.name.startswith("remove"):
5089f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L3", "Callback methods should be named register/unregister")
5098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Listener" in m.raw:
5118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add"):
5128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "remove" + m.name[3:]
5138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
5149f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing remove method")
5158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("remove") and not m.name.startswith("removeAll"):
5168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "add" + m.name[6:]
5178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
5189f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, m, "L2", "Missing add method")
5198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register") or m.name.startswith("unregister"):
5219f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, "L3", "Listener methods should be named add/remove")
5228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5248190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_sync(clazz):
5258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify synchronized methods aren't exposed."""
5268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
5278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "synchronized" in m.split:
52890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "M5", "Internal locks must not be exposed")
5298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5318190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_intent_builder(clazz):
5328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Intent builders are createFooIntent() style."""
5338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name == "Intent": return
5348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
5368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.typ == "android.content.Intent":
5378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("create") and m.name.endswith("Intent"):
5388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                pass
5398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else:
540539ea12810f6373eed926d07b856ab76a8ae8355Adam Powell                warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()")
5418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5438190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_helper_classes(clazz):
544294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verify that helper classes are named consistently with what they extend.
545294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    All developer extendable methods should be named onFoo()."""
546294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    test_methods = False
5478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.app.Service" in clazz.raw:
548294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Service"):
5509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooService")
5511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        found = False
5531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        for f in clazz.fields:
5541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if f.name == "SERVICE_INTERFACE":
5551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                found = True
5561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if f.value != clazz.fullname:
5579f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
5581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.ContentProvider" in clazz.raw:
560294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Provider"):
5629f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider")
5631498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        found = False
5651498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        for f in clazz.fields:
5661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if f.name == "PROVIDER_INTERFACE":
5671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                found = True
5681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if f.value != clazz.fullname:
56990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                    error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
5701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.BroadcastReceiver" in clazz.raw:
572294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Receiver"):
5749f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver")
5751498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
576294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if "extends android.app.Activity" in clazz.raw:
577294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
578294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if not clazz.name.endswith("Activity"):
5799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity")
580294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
581294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if test_methods:
582294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for m in clazz.methods:
583294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if "final" in m.split: continue
584294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not re.match("on[A-Z]", m.name):
5851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if "abstract" in m.split:
586b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                    warn(clazz, m, None, "Methods implemented by developers should be named onFoo()")
5871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                else:
5889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                    warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
5898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5918190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_builder(clazz):
5928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify builder classes.
5938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Methods should return the builder to enable chaining."""
5948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if " extends " in clazz.raw: return
5958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not clazz.name.endswith("Builder"): return
5968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name != "Builder":
5989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        warn(clazz, None, None, "Builder should be defined as inner class")
5998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    has_build = False
6018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
6028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name == "build":
6038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            has_build = True
6048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            continue
6058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("get"): continue
6078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("clear"): continue
6088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
609294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("with"):
61090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            warn(clazz, m, None, "Builder methods names should use setFoo() style")
611294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
612294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("set"):
613294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not m.typ.endswith(clazz.fullname):
61490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                warn(clazz, m, "M4", "Methods must return the builder object")
6158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not has_build:
6179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        warn(clazz, None, None, "Missing build() method")
6188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6208190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_aidl(clazz):
6218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Catch people exposing raw AIDL."""
622932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
62390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
6248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
626932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_internal(clazz):
627932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch people exposing internal classes."""
628932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if clazz.pkg.name.startswith("com.android"):
62990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        error(clazz, None, None, "Internal classes must not be exposed")
630932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
631932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
632932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_layering(clazz):
633932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch package layering violations.
634932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    For example, something in android.os depending on android.app."""
635932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ranking = [
636932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
637932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.app",
638932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.widget",
639932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.view",
640932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.animation",
641932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.provider",
6421498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        ["android.content","android.graphics.drawable"],
643932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.database",
644932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.graphics",
645932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.text",
646932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.os",
647932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.util"
648932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ]
649932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
650932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    def rank(p):
651932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for i in range(len(ranking)):
652932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if isinstance(ranking[i], list):
653932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                for j in ranking[i]:
654932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                    if p.startswith(j): return i
655932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            else:
656932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                if p.startswith(ranking[i]): return i
657932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
658932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    cr = rank(clazz.pkg.name)
659932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if cr is None: return
660932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
661932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for f in clazz.fields:
662932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(f.typ)
663932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
6649f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            warn(clazz, f, "FW6", "Field type violates package layering")
665932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
666932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for m in clazz.methods:
667932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(m.typ)
668932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
6699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            warn(clazz, m, "FW6", "Method return type violates package layering")
670932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for arg in m.args:
671932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            ir = rank(arg)
672932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if ir and ir < cr:
6739f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                warn(clazz, m, "FW6", "Method argument type violates package layering")
674932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
675932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
676a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef verify_boolean(clazz):
67790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    """Verifies that boolean accessors are named correctly.
67890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    For example, hasFoo() and setHasFoo()."""
6791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
68090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    def is_get(m): return len(m.args) == 0 and m.typ == "boolean"
68190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    def is_set(m): return len(m.args) == 1 and m.args[0] == "boolean"
68290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
68390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    gets = [ m for m in clazz.methods if is_get(m) ]
68490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    sets = [ m for m in clazz.methods if is_set(m) ]
6851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
68690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    def error_if_exists(methods, trigger, expected, actual):
68790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for m in methods:
68890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if m.name == actual:
68990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "M6", "Symmetric method for %s must be named %s" % (trigger, expected))
6901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
691294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
69290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if is_get(m):
69390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("is[A-Z]", m.name):
69490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[2:]
69590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "setIs" + target
69690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setHas" + target)
69790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            elif re.match("has[A-Z]", m.name):
69890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[3:]
69990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "setHas" + target
70090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setIs" + target)
70190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "set" + target)
70290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            elif re.match("get[A-Z]", m.name):
70390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[3:]
70490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "set" + target
70590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setIs" + target)
70690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "setHas" + target)
70790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
70890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if is_set(m):
70990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("set[A-Z]", m.name):
71090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                target = m.name[3:]
71190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                expected = "get" + target
71290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "is" + target)
71390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error_if_exists(sets, m.name, expected, "has" + target)
714294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
715294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
716294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_collections(clazz):
717294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that collection types are interfaces."""
7181498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.os.Bundle": return
7191498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
720294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack",
721294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey           "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"]
722294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
7231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if m.typ in bad:
72490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "CL2", "Return type is concrete collection; must be higher-level interface")
725294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for arg in m.args:
7261498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if arg in bad:
72790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface")
728294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
729294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
730294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_flags(clazz):
731294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that flags are non-overlapping."""
732294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    known = collections.defaultdict(int)
733294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for f in clazz.fields:
734294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if "FLAG_" in f.name:
735294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            try:
736294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                val = int(f.value)
737294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            except:
738294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                continue
739294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
740294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            scope = f.name[0:f.name.index("FLAG_")]
741294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if val & known[scope]:
742b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                warn(clazz, f, "C1", "Found overlapping flag constant value")
743294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            known[scope] |= val
744294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
745294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
7469f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_exception(clazz):
7479f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that methods don't throw generic exceptions."""
7489f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
7499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if "throws java.lang.Exception" in m.raw or "throws java.lang.Throwable" in m.raw or "throws java.lang.Error" in m.raw:
7509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, m, "S1", "Methods must not throw generic exceptions")
7519f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
752331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey        if "throws android.os.RemoteException" in m.raw:
753331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            if clazz.name == "android.content.ContentProviderClient": continue
754331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            if clazz.name == "android.os.Binder": continue
755331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            if clazz.name == "android.os.IBinder": continue
756331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
757331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            error(clazz, m, "FW9", "Methods calling into system server should rethrow RemoteException as RuntimeException")
758331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
7599f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7609f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_google(clazz):
7619f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that APIs never reference Google."""
7629f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7639f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if re.search("google", clazz.raw, re.IGNORECASE):
7649f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, None, "Must never reference Google")
7659f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7669f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test = []
7679f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test.extend(clazz.ctors)
7689f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test.extend(clazz.fields)
7699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    test.extend(clazz.methods)
7709f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7719f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for t in test:
7729f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if re.search("google", t.raw, re.IGNORECASE):
7739f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, t, None, "Must never reference Google")
7749f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7759f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7769f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_bitset(clazz):
7779f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that we avoid using heavy BitSet."""
7789f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for f in clazz.fields:
7809f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if f.typ == "java.util.BitSet":
7819f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, f, None, "Field type must not be heavy BitSet")
7829f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7839f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
7849f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if m.typ == "java.util.BitSet":
7859f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(clazz, m, None, "Return type must not be heavy BitSet")
7869f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        for arg in m.args:
7879f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            if arg == "java.util.BitSet":
7889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(clazz, m, None, "Argument type must not be heavy BitSet")
7899f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7909f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7919f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_manager(clazz):
7929f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that FooManager is only obtained from Context."""
7939f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7949f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if not clazz.name.endswith("Manager"): return
7959f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
7969f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for c in clazz.ctors:
797047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        error(clazz, c, None, "Managers must always be obtained from Context; no direct constructors")
7989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
79924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    for m in clazz.methods:
80024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.typ == clazz.fullname:
80124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            error(clazz, m, None, "Managers must always be obtained from Context")
80224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
8039f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8049f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_boxed(clazz):
8059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that methods avoid boxed primitives."""
8069f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
807b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    boxed = ["java.lang.Number","java.lang.Byte","java.lang.Double","java.lang.Float","java.lang.Integer","java.lang.Long","java.lang.Short"]
8089f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for c in clazz.ctors:
8109f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        for arg in c.args:
8119f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            if arg in boxed:
81290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, c, "M11", "Must avoid boxed primitives")
8139f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8149f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for f in clazz.fields:
8159f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if f.typ in boxed:
81690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "M11", "Must avoid boxed primitives")
8179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8189f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    for m in clazz.methods:
8199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        if m.typ in boxed:
82090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, m, "M11", "Must avoid boxed primitives")
8219f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        for arg in m.args:
8229f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            if arg in boxed:
82390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                error(clazz, m, "M11", "Must avoid boxed primitives")
8249f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8259f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8269f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_static_utils(clazz):
8279f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    """Verifies that helper classes can't be constructed."""
8289f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
82924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if clazz.fullname.startswith("android.R"): return
8309f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
83124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    # Only care about classes with default constructors
83224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if len(clazz.ctors) == 1 and len(clazz.ctors[0].args) == 0:
83324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        test = []
83424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        test.extend(clazz.fields)
83524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        test.extend(clazz.methods)
8369f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
83724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if len(test) == 0: return
83824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        for t in test:
83924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if "static" not in t.split:
84024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey                return
8419f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey        error(clazz, None, None, "Fully-static utility classes must not have constructor")
8439f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
8449f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey
845b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_overload_args(clazz):
846b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verifies that method overloads add new arguments at the end."""
847b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
848b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
849b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    overloads = collections.defaultdict(list)
850b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for m in clazz.methods:
851b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if "deprecated" in m.split: continue
852b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        overloads[m.name].append(m)
853b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
854a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    for name, methods in overloads.items():
855b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if len(methods) <= 1: continue
856b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
857b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        # Look for arguments common across all overloads
858b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        def cluster(args):
859b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            count = collections.defaultdict(int)
860b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            res = set()
861b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            for i in range(len(args)):
862b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                a = args[i]
863b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                res.add("%s#%d" % (a, count[a]))
864b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                count[a] += 1
865b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            return res
866b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
867b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        common_args = cluster(methods[0].args)
868b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for m in methods:
869b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            common_args = common_args & cluster(m.args)
870b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
871b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if len(common_args) == 0: continue
872b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
873b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        # Require that all common arguments are present at start of signature
874b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        locked_sig = None
875b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for m in methods:
876b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            sig = m.args[0:len(common_args)]
877b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if not common_args.issubset(cluster(sig)):
878b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                warn(clazz, m, "M2", "Expected common arguments [%s] at beginning of overloaded method" % (", ".join(common_args)))
879b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            elif not locked_sig:
880b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                locked_sig = sig
881b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            elif locked_sig != sig:
882b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig)))
883b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
884b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
885b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_callback_handlers(clazz):
886b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verifies that methods adding listener/callback have overload
887b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for specifying delivery thread."""
888b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
889047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    # Ignore UI packages which assume main thread
890b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    skip = [
891047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "animation",
892047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "view",
893047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "graphics",
894047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "transition",
895047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "widget",
896047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        "webkit",
897b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    ]
898b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for s in skip:
899047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        if s in clazz.pkg.name_path: return
900047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        if s in clazz.extends_path: return
901047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey
902047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    # Ignore UI classes which assume main thread
903047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    if "app" in clazz.pkg.name_path or "app" in clazz.extends_path:
904047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        for s in ["ActionBar","Dialog","Application","Activity","Fragment","Loader"]:
905047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            if s in clazz.fullname: return
906047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey    if "content" in clazz.pkg.name_path or "content" in clazz.extends_path:
907047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey        for s in ["Loader"]:
908047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey            if s in clazz.fullname: return
909b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
910b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    found = {}
911b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    by_name = collections.defaultdict(list)
912b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for m in clazz.methods:
913b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if m.name.startswith("unregister"): continue
914b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if m.name.startswith("remove"): continue
915b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if re.match("on[A-Z]+", m.name): continue
916b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
917b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        by_name[m.name].append(m)
918b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
919b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for a in m.args:
920b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if a.endswith("Listener") or a.endswith("Callback") or a.endswith("Callbacks"):
921b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                found[m.name] = m
922b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
923b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    for f in found.values():
924b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        takes_handler = False
925b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        for m in by_name[f.name]:
926b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if "android.os.Handler" in m.args:
927b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                takes_handler = True
928b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey        if not takes_handler:
92990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler")
930b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
931b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
932b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_context_first(clazz):
933b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey    """Verifies that methods accepting a Context keep it the first argument."""
93490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    examine = clazz.ctors + clazz.methods
93590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    for m in examine:
93690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if len(m.args) > 1 and m.args[0] != "android.content.Context":
937b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey            if "android.content.Context" in m.args[1:]:
938b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey                error(clazz, m, "M3", "Context is distinct, so it must be the first argument")
93924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if len(m.args) > 1 and m.args[0] != "android.content.ContentResolver":
94024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if "android.content.ContentResolver" in m.args[1:]:
94124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey                error(clazz, m, "M3", "ContentResolver is distinct, so it must be the first argument")
942b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
94390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
94490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_listener_last(clazz):
94590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    """Verifies that methods accepting a Listener or Callback keep them as last arguments."""
94690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    examine = clazz.ctors + clazz.methods
94790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    for m in examine:
94890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        if "Listener" in m.name or "Callback" in m.name: continue
94990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        found = False
95090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for a in m.args:
95190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"):
95290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                found = True
95390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            elif found and a != "android.os.Handler":
95490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey                warn(clazz, m, "M3", "Listeners should always be at end of argument list")
95590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
95690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
95790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_resource_names(clazz):
95890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    """Verifies that resource names have consistent case."""
95990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if not re.match("android\.R\.[a-z]+", clazz.fullname): return
96090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
96190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    # Resources defined by files are foo_bar_baz
96290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]:
96390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for f in clazz.fields:
96490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("[a-z1-9_]+$", f.name): continue
96590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style")
96690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
96790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    # Resources defined inside files are fooBarBaz
96890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if clazz.name in ["array","attr","id","bool","fraction","integer"]:
96990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for f in clazz.fields:
97090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue
97190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("layout_[a-z][a-zA-Z1-9]*$", f.name): continue
97290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("state_[a-z_]*$", f.name): continue
97390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
97490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("[a-z][a-zA-Z1-9]*$", f.name): continue
97590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "C7", "Expected resource name in this class to be fooBarBaz style")
97690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey
97790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    # Styles are FooBar_Baz
97890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey    if clazz.name in ["style"]:
97990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey        for f in clazz.fields:
98090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            if re.match("[A-Z][A-Za-z1-9]+(_[A-Z][A-Za-z1-9]+?)*$", f.name): continue
98190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey            error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style")
982b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
983b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey
984331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkeydef verify_files(clazz):
985331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    """Verifies that methods accepting File also accept streams."""
986331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
987331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    has_file = set()
988331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    has_stream = set()
989331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
990331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    test = []
991331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    test.extend(clazz.ctors)
992331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    test.extend(clazz.methods)
993331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
994331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    for m in test:
995331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey        if "java.io.File" in m.args:
996331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            has_file.add(m)
997331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey        if "java.io.FileDescriptor" in m.args or "android.os.ParcelFileDescriptor" in m.args or "java.io.InputStream" in m.args or "java.io.OutputStream" in m.args:
998331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            has_stream.add(m.name)
999331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
1000331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    for m in has_file:
1001331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey        if m.name not in has_stream:
1002331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey            warn(clazz, m, "M10", "Methods accepting File should also accept FileDescriptor or streams")
1003331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
1004331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey
100570168dde6e1da06042818350fc6e258188d001aeJeff Sharkeydef verify_manager_list(clazz):
100670168dde6e1da06042818350fc6e258188d001aeJeff Sharkey    """Verifies that managers return List<? extends Parcelable> instead of arrays."""
100770168dde6e1da06042818350fc6e258188d001aeJeff Sharkey
100870168dde6e1da06042818350fc6e258188d001aeJeff Sharkey    if not clazz.name.endswith("Manager"): return
100970168dde6e1da06042818350fc6e258188d001aeJeff Sharkey
101070168dde6e1da06042818350fc6e258188d001aeJeff Sharkey    for m in clazz.methods:
101170168dde6e1da06042818350fc6e258188d001aeJeff Sharkey        if m.typ.startswith("android.") and m.typ.endswith("[]"):
101270168dde6e1da06042818350fc6e258188d001aeJeff Sharkey            warn(clazz, m, None, "Methods should return List<? extends Parcelable> instead of Parcelable[] to support ParceledListSlice under the hood")
101370168dde6e1da06042818350fc6e258188d001aeJeff Sharkey
101470168dde6e1da06042818350fc6e258188d001aeJeff Sharkey
101526c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkeydef verify_abstract_inner(clazz):
101626c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey    """Verifies that abstract inner classes are static."""
101726c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey
101826c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey    if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname):
101926c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey        if " abstract " in clazz.raw and " static " not in clazz.raw:
102026c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey            warn(clazz, None, None, "Abstract inner classes should be static to improve testability")
102126c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey
102226c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey
102324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_runtime_exceptions(clazz):
102424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    """Verifies that runtime exceptions aren't listed in throws."""
102524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
102624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    banned = [
102724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.NullPointerException",
102824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.ClassCastException",
102924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.IndexOutOfBoundsException",
103024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.reflect.UndeclaredThrowableException",
103124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.reflect.MalformedParametersException",
103224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.reflect.MalformedParameterizedTypeException",
103324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.invoke.WrongMethodTypeException",
103424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.EnumConstantNotPresentException",
103524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.IllegalMonitorStateException",
103624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.SecurityException",
103724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.UnsupportedOperationException",
103824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.annotation.AnnotationTypeMismatchException",
103924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.annotation.IncompleteAnnotationException",
104024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.TypeNotPresentException",
104124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.IllegalStateException",
104224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.ArithmeticException",
104324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.IllegalArgumentException",
104424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.ArrayStoreException",
104524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.lang.NegativeArraySizeException",
104624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.MissingResourceException",
104724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.EmptyStackException",
104824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.concurrent.CompletionException",
104924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.concurrent.RejectedExecutionException",
105024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.IllformedLocaleException",
105124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.ConcurrentModificationException",
105224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.util.NoSuchElementException",
105324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.io.UncheckedIOException",
105424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.time.DateTimeException",
105524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.security.ProviderException",
105624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.nio.BufferUnderflowException",
105724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "java.nio.BufferOverflowException",
105824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    ]
105924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
106024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    test = []
106124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    test.extend(clazz.ctors)
106224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    test.extend(clazz.methods)
106324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
106424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    for t in test:
106524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if " throws " not in t.raw: continue
106624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        throws = t.raw[t.raw.index(" throws "):]
106724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        for b in banned:
106824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if b in throws:
106924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey                error(clazz, t, None, "Methods must not mention RuntimeException subclasses in throws clauses")
107024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
107124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
107224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_error(clazz):
107324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    """Verifies that we always use Exception instead of Error."""
107424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if not clazz.extends: return
107524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if clazz.extends.endswith("Error"):
107624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        error(clazz, None, None, "Trouble must be reported through an Exception, not Error")
107724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if clazz.extends.endswith("Exception") and not clazz.name.endswith("Exception"):
107824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        error(clazz, None, None, "Exceptions must be named FooException")
107924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
108024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
108124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_units(clazz):
108224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    """Verifies that we use consistent naming for units."""
108324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
108424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    # If we find K, recommend replacing with V
108524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    bad = {
108624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Ns": "Nanos",
108724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Ms": "Millis or Micros",
108824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Sec": "Seconds", "Secs": "Seconds",
108924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Hr": "Hours", "Hrs": "Hours",
109024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Mo": "Months", "Mos": "Months",
109124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Yr": "Years", "Yrs": "Years",
109224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        "Byte": "Bytes", "Space": "Bytes",
109324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    }
109424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
109524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    for m in clazz.methods:
109624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.typ not in ["short","int","long"]: continue
109724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        for k, v in bad.iteritems():
109824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if m.name.endswith(k):
109924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey                error(clazz, m, None, "Expected method name units to be " + v)
110024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.name.endswith("Nanos") or m.name.endswith("Micros"):
110124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            warn(clazz, m, None, "Returned time values are strongly encouraged to be in milliseconds unless you need the extra precision")
110224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.name.endswith("Seconds"):
110324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            error(clazz, m, None, "Returned time values must be in milliseconds")
110424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
110524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    for m in clazz.methods:
110624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        typ = m.typ
110724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if typ == "void":
110824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            if len(m.args) != 1: continue
110924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            typ = m.args[0]
111024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
111124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.name.endswith("Fraction") and typ != "float":
111224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            error(clazz, m, None, "Fractions must use floats")
111324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.name.endswith("Percentage") and typ != "int":
111424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            error(clazz, m, None, "Percentage must use ints")
111524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
111624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
111724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_closable(clazz):
111824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    """Verifies that classes are AutoClosable."""
111924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if "implements java.lang.AutoCloseable" in clazz.raw: return
112024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    if "implements java.io.Closeable" in clazz.raw: return
112124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
112224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    for m in clazz.methods:
112324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if len(m.args) > 0: continue
112424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey        if m.name in ["close","release","destroy","finish","finalize","disconnect","shutdown","stop","free","quit"]:
112524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            warn(clazz, m, None, "Classes that release resources should implement AutoClosable and CloseGuard")
112624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey            return
112724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
112824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey
1129a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_clazz(clazz):
1130a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    """Find all style issues in the given class."""
1131a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("java"): return
1132a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("junit"): return
1133a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.apache"): return
1134a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.xml"): return
1135a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.json"): return
1136a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    if clazz.pkg.name.startswith("org.w3c"): return
1137331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    if clazz.pkg.name.startswith("android.icu."): return
1138a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
1139a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_constants(clazz)
1140a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_enums(clazz)
1141a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_class_names(clazz)
1142a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_method_names(clazz)
1143a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_callbacks(clazz)
1144a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_listeners(clazz)
1145a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_actions(clazz)
1146a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_extras(clazz)
1147a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_equals(clazz)
1148a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_parcelable(clazz)
1149a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_protected(clazz)
1150a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_fields(clazz)
1151a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_register(clazz)
1152a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_sync(clazz)
1153a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_intent_builder(clazz)
1154a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_helper_classes(clazz)
1155a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_builder(clazz)
1156a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_aidl(clazz)
1157a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_internal(clazz)
1158a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_layering(clazz)
1159a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_boolean(clazz)
1160a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_collections(clazz)
1161a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_flags(clazz)
1162a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_exception(clazz)
11631c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if not ALLOW_GOOGLE: verify_google(clazz)
1164a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_bitset(clazz)
1165a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_manager(clazz)
1166a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_boxed(clazz)
1167a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_static_utils(clazz)
1168a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_overload_args(clazz)
1169a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_callback_handlers(clazz)
1170a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_context_first(clazz)
1171a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_listener_last(clazz)
1172a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    verify_resource_names(clazz)
1173331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey    verify_files(clazz)
117470168dde6e1da06042818350fc6e258188d001aeJeff Sharkey    verify_manager_list(clazz)
117526c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey    verify_abstract_inner(clazz)
117624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    verify_runtime_exceptions(clazz)
117724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    verify_error(clazz)
117824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    verify_units(clazz)
117924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey    verify_closable(clazz)
1180a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
1181a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
1182a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_stream(stream):
1183a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    """Find all style issues in the given API stream."""
11848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
1185a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    failures = {}
1186a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    _parse_stream(stream, examine_clazz)
1187a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    return failures
1188a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey
11898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1190a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_api(api):
1191a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    """Find all style issues in the given parsed API."""
1192a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey    global failures
11931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    failures = {}
11941498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    for key in sorted(api.keys()):
1195a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        examine_clazz(api[key])
11968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return failures
11978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
11988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1199037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeydef verify_compat(cur, prev):
1200037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    """Find any incompatible API changes between two levels."""
1201037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    global failures
1202037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1203037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def class_exists(api, test):
1204037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return test.fullname in api
1205037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1206037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def ctor_exists(api, clazz, test):
1207037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for m in clazz.ctors:
1208037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if m.ident == test.ident: return True
1209037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
1210037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1211037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def all_methods(api, clazz):
1212037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = list(clazz.methods)
1213037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if clazz.extends is not None:
1214037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            methods.extend(all_methods(api, api[clazz.extends]))
1215037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return methods
1216037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1217037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def method_exists(api, clazz, test):
1218037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = all_methods(api, clazz)
1219037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for m in methods:
1220037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if m.ident == test.ident: return True
1221037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
1222037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1223037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def field_exists(api, clazz, test):
1224037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for f in clazz.fields:
1225037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if f.ident == test.ident: return True
1226037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
1227037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1228037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    failures = {}
1229037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    for key in sorted(prev.keys()):
1230037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        prev_clazz = prev[key]
1231037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1232037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if not class_exists(cur, prev_clazz):
12339f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey            error(prev_clazz, None, None, "Class removed or incompatible change")
1234037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            continue
1235037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1236037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        cur_clazz = cur[key]
1237037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1238037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in prev_clazz.ctors:
1239037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not ctor_exists(cur, cur_clazz, test):
12409f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(prev_clazz, prev_ctor, None, "Constructor removed or incompatible change")
1241037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1242037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = all_methods(prev, prev_clazz)
1243037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in methods:
1244037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not method_exists(cur, cur_clazz, test):
12459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(prev_clazz, test, None, "Method removed or incompatible change")
1246037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1247037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in prev_clazz.fields:
1248037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not field_exists(cur, cur_clazz, test):
12499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey                error(prev_clazz, test, None, "Field removed or incompatible change")
1250037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1251037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    return failures
1252037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1253037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1254ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyif __name__ == "__main__":
12551c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser = argparse.ArgumentParser(description="Enforces common Android public API design \
12561c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            patterns. It ignores lint messages from a previous API level, if provided.")
12571c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
12581c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
12591c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            help="previous.txt")
12601c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("--no-color", action='store_const', const=True,
12611c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            help="Disable terminal colors")
12621c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    parser.add_argument("--allow-google", action='store_const', const=True,
12631c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf            help="Allow references to Google")
12641c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    args = vars(parser.parse_args())
12651c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
12661c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if args['no_color']:
12671c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf        USE_COLOR = False
12681c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
12691c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if args['allow_google']:
12701c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf        ALLOW_GOOGLE = True
12711c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
12721c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    current_file = args['current.txt']
12731c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    previous_file = args['previous.txt']
12741c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf
12751c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    with current_file as f:
1276a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        cur_fail = examine_stream(f)
12771c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf    if not previous_file is None:
12781c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf        with previous_file as f:
1279a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey            prev_fail = examine_stream(f)
12808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1281ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        # ignore errors from previous API level
1282ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        for p in prev_fail:
1283ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            if p in cur_fail:
1284ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey                del cur_fail[p]
12858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1286a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        """
1287a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        # NOTE: disabled because of memory pressure
1288ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        # look for compatibility issues
1289ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        compat_fail = verify_compat(cur, prev)
1290037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1291ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
1292ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        for f in sorted(compat_fail):
1293ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            print compat_fail[f]
1294ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey            print
1295a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey        """
12968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1297ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
1298ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey    for f in sorted(cur_fail):
1299ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        print cur_fail[f]
1300ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey        print
1301