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 948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]: 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 2628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 2648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "static" in f.split and "final" in f.split: 2658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.match("[A-Z0-9_]+", f.name) is None: 26690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C2", "Constant field names must be FOO_NAME") 2678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2698190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_enums(clazz): 2708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Enums are bad, mmkay?""" 2718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends java.lang.Enum" in clazz.raw: 2729f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "F5", "Enums are not allowed") 2738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2758190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_class_names(clazz): 2768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Try catching malformed class names like myMtp or MTPUser.""" 2771498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 2781498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.renderscript"): return 2791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("android\.R\.[a-z]+", clazz.fullname): return 2801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 2818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.search("[A-Z]{2,}", clazz.name) is not None: 28290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, None, "S1", "Class names with acronyms should be Mtp not MTP") 2838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.match("[^A-Z]", clazz.name): 2849f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "S1", "Class must start with uppercase char") 2858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2878190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_method_names(clazz): 2888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Try catching malformed method names, like Foo() or getMTU().""" 2891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 2901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.renderscript"): return 2911498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.system.OsConstants": return 2928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 2948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.search("[A-Z]{2,}", m.name) is not None: 29590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, "S1", "Method names with acronyms should be getMtu() instead of getMTU()") 2968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.match("[^a-z]", m.name): 2979f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "S1", "Method name must start with lowercase char") 2988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3008190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_callbacks(clazz): 3018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify Callback classes. 3028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All callback classes must be abstract. 3038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All methods must follow onFoo() naming style.""" 3041498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.speech.tts.SynthesisCallback": return 3058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Callbacks"): 30790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, "L1", "Callback class names should be singular") 3088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Observer"): 3099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, None, "L1", "Class should be named FooCallback") 3108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Callback"): 3128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "interface" in clazz.split: 31390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels") 3148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 3168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not re.match("on[A-Z][a-z]*", m.name): 3179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L1", "Callback method names must be onFoo() style") 3188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3208190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_listeners(clazz): 3218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify Listener classes. 3228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All Listener classes must be interface. 3238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All methods must follow onFoo() naming style. 3248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey If only a single method, it must match class name: 3258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey interface OnFooListener { void onFoo() }""" 3268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Listener"): 3288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if " abstract class " in clazz.raw: 32990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback") 3308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 3328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not re.match("on[A-Z][a-z]*", m.name): 3339f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L1", "Listener method names must be onFoo() style") 3348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if len(clazz.methods) == 1 and clazz.name.startswith("On"): 3368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey m = clazz.methods[0] 3378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if (m.name + "Listener").lower() != clazz.name.lower(): 33890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "L1", "Single listener method name must match class name") 3398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3418190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_actions(clazz): 3428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify intent actions. 3438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All action names must be named ACTION_FOO. 3448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All action values must be scoped by package and match name: 3458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey package android.foo { 3468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey String ACTION_BAR = "android.foo.action.BAR"; 3478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey }""" 3488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 3498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value is None: continue 3508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.name.startswith("EXTRA_"): continue 3511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.name == "SERVICE_INTERFACE" or f.name == "PROVIDER_INTERFACE": continue 3528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": 3548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower(): 3558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not f.name.startswith("ACTION_"): 3569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C3", "Intent action constant name must be ACTION_FOO") 3578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.content.Intent": 3598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = "android.intent.action" 3601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname == "android.provider.Settings": 3618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = "android.settings" 3621498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname == "android.app.admin.DevicePolicyManager" or clazz.fullname == "android.app.admin.DeviceAdminReceiver": 3631498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey prefix = "android.app.action" 3648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = clazz.pkg.name + ".action" 3668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey expected = prefix + "." + f.name[7:] 3678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value != expected: 3689f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected)) 3698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3718190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_extras(clazz): 3728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify intent extras. 3738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All extra names must be named EXTRA_FOO. 3748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All extra values must be scoped by package and match name: 3758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey package android.foo { 3768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey String EXTRA_BAR = "android.foo.extra.BAR"; 3778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey }""" 3781498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.app.Notification": return 3791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.appwidget.AppWidgetManager": return 3801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 3818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 3828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value is None: continue 3838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.name.startswith("ACTION_"): continue 3848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": 3868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower(): 3878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not f.name.startswith("EXTRA_"): 3889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C3", "Intent extra must be EXTRA_FOO") 3898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.pkg.name == "android.content" and clazz.name == "Intent": 3918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = "android.intent.extra" 3921498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.pkg.name == "android.app.admin": 3931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey prefix = "android.app.extra" 3948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = clazz.pkg.name + ".extra" 3968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey expected = prefix + "." + f.name[6:] 3978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value != expected: 3989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected)) 3998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4018190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_equals(clazz): 4028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that equals() and hashCode() must be overridden together.""" 4038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey methods = [ m.name for m in clazz.methods ] 4048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey eq = "equals" in methods 4058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey hc = "hashCode" in methods 4068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if eq != hc: 4079f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "M8", "Must override both equals and hashCode; missing one") 4088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4108190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_parcelable(clazz): 4118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that Parcelable objects aren't hiding required bits.""" 4128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "implements android.os.Parcelable" in clazz.raw: 4138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey creator = [ i for i in clazz.fields if i.name == "CREATOR" ] 4148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey write = [ i for i in clazz.methods if i.name == "writeToParcel" ] 4158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey describe = [ i for i in clazz.methods if i.name == "describeContents" ] 4168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if len(creator) == 0 or len(write) == 0 or len(describe) == 0: 4189f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one") 4198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4218190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_protected(clazz): 422b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verify that no protected methods or fields are allowed.""" 4238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 4248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "protected" in m.split: 42590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M7", "Protected methods not allowed; must be public") 4268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 4278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "protected" in f.split: 42890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "M7", "Protected fields not allowed; must be public") 4298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4318190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_fields(clazz): 4328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that all exposed fields are final. 4338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Exposed fields must follow myName style. 4348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Catch internal mFoo objects being exposed.""" 4351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 4361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey IGNORE_BARE_FIELDS = [ 4371498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.app.ActivityManager.RecentTaskInfo", 4381498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.app.Notification", 4391498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.ActivityInfo", 4401498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.ApplicationInfo", 4411498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.FeatureGroupInfo", 4421498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.InstrumentationInfo", 4431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.PackageInfo", 4441498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.PackageItemInfo", 4451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.os.Message", 4461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.system.StructPollfd", 4471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey ] 4481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 4498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 4508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not "final" in f.split: 4511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname in IGNORE_BARE_FIELDS: 4521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey pass 4531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname.endswith("LayoutParams"): 4541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey pass 4551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname.startswith("android.util.Mutable"): 4561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey pass 4571498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey else: 4589f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable") 4598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not "static" in f.split: 4618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not re.match("[a-z]([a-zA-Z]+)?", f.name): 46290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "S1", "Non-static fields must be named using myField style") 4638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("[ms][A-Z]", f.name): 46590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "F1", "Internal objects must not be exposed") 4668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("[A-Z_]+", f.name): 4681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if "static" not in f.split or "final" not in f.split: 4699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C2", "Constants must be marked static final") 4701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 4718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4728190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_register(clazz): 4738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify parity of registration methods. 4748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Callback objects use register/unregister methods. 4758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Listener objects use add/remove methods.""" 4768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey methods = [ m.name for m in clazz.methods ] 4778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 4788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "Callback" in m.raw: 4798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("register"): 4808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "unregister" + m.name[8:] 4818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 4829f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing unregister method") 4838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("unregister"): 4848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "register" + m.name[10:] 4858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 4869f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing register method") 4878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("add") or m.name.startswith("remove"): 4899f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L3", "Callback methods should be named register/unregister") 4908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "Listener" in m.raw: 4928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("add"): 4938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "remove" + m.name[3:] 4948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 4959f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing remove method") 4968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("remove") and not m.name.startswith("removeAll"): 4978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "add" + m.name[6:] 4988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 4999f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing add method") 5008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("register") or m.name.startswith("unregister"): 5029f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L3", "Listener methods should be named add/remove") 5038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5058190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_sync(clazz): 5068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify synchronized methods aren't exposed.""" 5078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 5088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "synchronized" in m.split: 50990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M5", "Internal locks must not be exposed") 5108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5128190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_intent_builder(clazz): 5138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that Intent builders are createFooIntent() style.""" 5148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name == "Intent": return 5158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 5178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.typ == "android.content.Intent": 5188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("create") and m.name.endswith("Intent"): 5198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey pass 5208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 521539ea12810f6373eed926d07b856ab76a8ae8355Adam Powell warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()") 5228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5248190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_helper_classes(clazz): 525294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey """Verify that helper classes are named consistently with what they extend. 526294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey All developer extendable methods should be named onFoo().""" 527294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = False 5288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends android.app.Service" in clazz.raw: 529294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 5308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Service"): 5319f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooService") 5321498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5331498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = False 5341498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey for f in clazz.fields: 5351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.name == "SERVICE_INTERFACE": 5361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = True 5371498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.value != clazz.fullname: 5389f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname)) 5391498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends android.content.ContentProvider" in clazz.raw: 541294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 5428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Provider"): 5439f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider") 5441498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = False 5461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey for f in clazz.fields: 5471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.name == "PROVIDER_INTERFACE": 5481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = True 5491498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.value != clazz.fullname: 55090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname)) 5511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends android.content.BroadcastReceiver" in clazz.raw: 553294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 5548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Receiver"): 5559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver") 5561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 557294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if "extends android.app.Activity" in clazz.raw: 558294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 559294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if not clazz.name.endswith("Activity"): 5609f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity") 561294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 562294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if test_methods: 563294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for m in clazz.methods: 564294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if "final" in m.split: continue 565294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if not re.match("on[A-Z]", m.name): 5661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if "abstract" in m.split: 567b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey warn(clazz, m, None, "Methods implemented by developers should be named onFoo()") 5681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey else: 5699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final") 5708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5728190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_builder(clazz): 5738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify builder classes. 5748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Methods should return the builder to enable chaining.""" 5758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if " extends " in clazz.raw: return 5768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Builder"): return 5778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name != "Builder": 5799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, None, None, "Builder should be defined as inner class") 5808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey has_build = False 5828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 5838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name == "build": 5848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey has_build = True 5858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey continue 5868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("get"): continue 5888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("clear"): continue 5898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 590294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if m.name.startswith("with"): 59190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, None, "Builder methods names should use setFoo() style") 592294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 593294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if m.name.startswith("set"): 594294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if not m.typ.endswith(clazz.fullname): 59590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, "M4", "Methods must return the builder object") 5968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not has_build: 5989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, None, None, "Missing build() method") 5998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6018190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_aidl(clazz): 6028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Catch people exposing raw AIDL.""" 603932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw: 60490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, None, "Raw AIDL interfaces must not be exposed") 6058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 607932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_internal(clazz): 608932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey """Catch people exposing internal classes.""" 609932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if clazz.pkg.name.startswith("com.android"): 61090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, None, "Internal classes must not be exposed") 611932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 612932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 613932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_layering(clazz): 614932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey """Catch package layering violations. 615932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey For example, something in android.os depending on android.app.""" 616932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ranking = [ 617932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], 618932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.app", 619932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.widget", 620932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.view", 621932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.animation", 622932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.provider", 6231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey ["android.content","android.graphics.drawable"], 624932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.database", 625932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.graphics", 626932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.text", 627932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.os", 628932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.util" 629932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ] 630932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 631932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey def rank(p): 632932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for i in range(len(ranking)): 633932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if isinstance(ranking[i], list): 634932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for j in ranking[i]: 635932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if p.startswith(j): return i 636932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey else: 637932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if p.startswith(ranking[i]): return i 638932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 639932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey cr = rank(clazz.pkg.name) 640932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if cr is None: return 641932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 642932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for f in clazz.fields: 643932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ir = rank(f.typ) 644932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if ir and ir < cr: 6459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, f, "FW6", "Field type violates package layering") 646932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 647932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for m in clazz.methods: 648932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ir = rank(m.typ) 649932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if ir and ir < cr: 6509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, m, "FW6", "Method return type violates package layering") 651932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for arg in m.args: 652932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ir = rank(arg) 653932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if ir and ir < cr: 6549f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, m, "FW6", "Method argument type violates package layering") 655932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 656932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 657a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef verify_boolean(clazz): 65890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey """Verifies that boolean accessors are named correctly. 65990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey For example, hasFoo() and setHasFoo().""" 6601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 66190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey def is_get(m): return len(m.args) == 0 and m.typ == "boolean" 66290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey def is_set(m): return len(m.args) == 1 and m.args[0] == "boolean" 66390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 66490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey gets = [ m for m in clazz.methods if is_get(m) ] 66590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey sets = [ m for m in clazz.methods if is_set(m) ] 6661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 66790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey def error_if_exists(methods, trigger, expected, actual): 66890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for m in methods: 66990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if m.name == actual: 67090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M6", "Symmetric method for %s must be named %s" % (trigger, expected)) 6711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 672294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for m in clazz.methods: 67390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if is_get(m): 67490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("is[A-Z]", m.name): 67590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[2:] 67690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "setIs" + target 67790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setHas" + target) 67890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey elif re.match("has[A-Z]", m.name): 67990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[3:] 68090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "setHas" + target 68190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setIs" + target) 68290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "set" + target) 68390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey elif re.match("get[A-Z]", m.name): 68490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[3:] 68590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "set" + target 68690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setIs" + target) 68790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setHas" + target) 68890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 68990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if is_set(m): 69090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("set[A-Z]", m.name): 69190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[3:] 69290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "get" + target 69390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "is" + target) 69490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "has" + target) 695294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 696294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 697294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_collections(clazz): 698294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey """Verifies that collection types are interfaces.""" 6991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.os.Bundle": return 7001498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 701294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack", 702294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"] 703294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for m in clazz.methods: 7041498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if m.typ in bad: 70590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "CL2", "Return type is concrete collection; must be higher-level interface") 706294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for arg in m.args: 7071498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if arg in bad: 70890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface") 709294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 710294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 711294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_flags(clazz): 712294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey """Verifies that flags are non-overlapping.""" 713294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey known = collections.defaultdict(int) 714294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for f in clazz.fields: 715294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if "FLAG_" in f.name: 716294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey try: 717294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey val = int(f.value) 718294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey except: 719294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey continue 720294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 721294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey scope = f.name[0:f.name.index("FLAG_")] 722294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if val & known[scope]: 723b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey warn(clazz, f, "C1", "Found overlapping flag constant value") 724294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey known[scope] |= val 725294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 726294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 7279f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_exception(clazz): 7289f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that methods don't throw generic exceptions.""" 7299f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 7309f64d5c6c456863489ffb8583d611211e7c2f88fJeff 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: 7319f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "S1", "Methods must not throw generic exceptions") 7329f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7339f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7349f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_google(clazz): 7359f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that APIs never reference Google.""" 7369f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7379f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if re.search("google", clazz.raw, re.IGNORECASE): 7389f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, None, "Must never reference Google") 7399f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7409f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test = [] 7419f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test.extend(clazz.ctors) 7429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test.extend(clazz.fields) 7439f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test.extend(clazz.methods) 7449f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for t in test: 7469f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if re.search("google", t.raw, re.IGNORECASE): 7479f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, t, None, "Must never reference Google") 7489f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_bitset(clazz): 7519f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that we avoid using heavy BitSet.""" 7529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7539f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for f in clazz.fields: 7549f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if f.typ == "java.util.BitSet": 7559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, None, "Field type must not be heavy BitSet") 7569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7579f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 7589f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if m.typ == "java.util.BitSet": 7599f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, None, "Return type must not be heavy BitSet") 7609f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for arg in m.args: 7619f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if arg == "java.util.BitSet": 7629f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, None, "Argument type must not be heavy BitSet") 7639f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7649f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7659f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_manager(clazz): 7669f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that FooManager is only obtained from Context.""" 7679f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7689f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if not clazz.name.endswith("Manager"): return 7699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7709f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for c in clazz.ctors: 771047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey error(clazz, c, None, "Managers must always be obtained from Context; no direct constructors") 7729f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7739f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7749f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_boxed(clazz): 7759f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that methods avoid boxed primitives.""" 7769f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 777b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey boxed = ["java.lang.Number","java.lang.Byte","java.lang.Double","java.lang.Float","java.lang.Integer","java.lang.Long","java.lang.Short"] 7789f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for c in clazz.ctors: 7809f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for arg in c.args: 7819f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if arg in boxed: 78290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, c, "M11", "Must avoid boxed primitives") 7839f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7849f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for f in clazz.fields: 7859f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if f.typ in boxed: 78690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "M11", "Must avoid boxed primitives") 7879f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 7899f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if m.typ in boxed: 79090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M11", "Must avoid boxed primitives") 7919f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for arg in m.args: 7929f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if arg in boxed: 79390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M11", "Must avoid boxed primitives") 7949f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7959f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7969f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_static_utils(clazz): 7979f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that helper classes can't be constructed.""" 7989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 7999f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if re.match("android\.R\.[a-z]+", clazz.fullname): return 8009f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8019f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if len(clazz.fields) > 0: return 8029f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if len(clazz.methods) == 0: return 8039f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8049f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 8059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if "static" not in m.split: 8069f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey return 8079f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8089f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey # At this point, we have no fields, and all methods are static 8099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if len(clazz.ctors) > 0: 8109f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, None, "Fully-static utility classes must not have constructor") 8119f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8129f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 813b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_overload_args(clazz): 814b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verifies that method overloads add new arguments at the end.""" 815b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 816b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 817b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey overloads = collections.defaultdict(list) 818b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in clazz.methods: 819b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if "deprecated" in m.split: continue 820b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey overloads[m.name].append(m) 821b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 822a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey for name, methods in overloads.items(): 823b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if len(methods) <= 1: continue 824b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 825b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey # Look for arguments common across all overloads 826b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey def cluster(args): 827b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey count = collections.defaultdict(int) 828b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey res = set() 829b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for i in range(len(args)): 830b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey a = args[i] 831b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey res.add("%s#%d" % (a, count[a])) 832b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey count[a] += 1 833b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey return res 834b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 835b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey common_args = cluster(methods[0].args) 836b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in methods: 837b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey common_args = common_args & cluster(m.args) 838b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 839b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if len(common_args) == 0: continue 840b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 841b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey # Require that all common arguments are present at start of signature 842b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey locked_sig = None 843b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in methods: 844b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey sig = m.args[0:len(common_args)] 845b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if not common_args.issubset(cluster(sig)): 846b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey warn(clazz, m, "M2", "Expected common arguments [%s] at beginning of overloaded method" % (", ".join(common_args))) 847b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey elif not locked_sig: 848b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey locked_sig = sig 849b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey elif locked_sig != sig: 850b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig))) 851b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 852b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 853b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_callback_handlers(clazz): 854b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verifies that methods adding listener/callback have overload 855b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for specifying delivery thread.""" 856b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 857047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey # Ignore UI packages which assume main thread 858b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey skip = [ 859047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "animation", 860047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "view", 861047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "graphics", 862047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "transition", 863047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "widget", 864047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "webkit", 865b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey ] 866b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for s in skip: 867047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.pkg.name_path: return 868047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.extends_path: return 869047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey 870047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey # Ignore UI classes which assume main thread 871047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if "app" in clazz.pkg.name_path or "app" in clazz.extends_path: 872047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey for s in ["ActionBar","Dialog","Application","Activity","Fragment","Loader"]: 873047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.fullname: return 874047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if "content" in clazz.pkg.name_path or "content" in clazz.extends_path: 875047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey for s in ["Loader"]: 876047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.fullname: return 877b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 878b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey found = {} 879b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey by_name = collections.defaultdict(list) 880b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in clazz.methods: 881b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if m.name.startswith("unregister"): continue 882b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if m.name.startswith("remove"): continue 883b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if re.match("on[A-Z]+", m.name): continue 884b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 885b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey by_name[m.name].append(m) 886b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 887b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for a in m.args: 888b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if a.endswith("Listener") or a.endswith("Callback") or a.endswith("Callbacks"): 889b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey found[m.name] = m 890b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 891b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for f in found.values(): 892b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey takes_handler = False 893b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in by_name[f.name]: 894b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if "android.os.Handler" in m.args: 895b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey takes_handler = True 896b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if not takes_handler: 89790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler") 898b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 899b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 900b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_context_first(clazz): 901b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verifies that methods accepting a Context keep it the first argument.""" 90290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey examine = clazz.ctors + clazz.methods 90390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for m in examine: 90490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if len(m.args) > 1 and m.args[0] != "android.content.Context": 905b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if "android.content.Context" in m.args[1:]: 906b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey error(clazz, m, "M3", "Context is distinct, so it must be the first argument") 907b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 90890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 90990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_listener_last(clazz): 91090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey """Verifies that methods accepting a Listener or Callback keep them as last arguments.""" 91190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey examine = clazz.ctors + clazz.methods 91290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for m in examine: 91390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if "Listener" in m.name or "Callback" in m.name: continue 91490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey found = False 91590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for a in m.args: 91690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"): 91790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey found = True 91890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey elif found and a != "android.os.Handler": 91990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, "M3", "Listeners should always be at end of argument list") 92090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 92190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 92290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_resource_names(clazz): 92390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey """Verifies that resource names have consistent case.""" 92490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if not re.match("android\.R\.[a-z]+", clazz.fullname): return 92590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 92690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey # Resources defined by files are foo_bar_baz 92790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]: 92890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for f in clazz.fields: 92990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("[a-z1-9_]+$", f.name): continue 93090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style") 93190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 93290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey # Resources defined inside files are fooBarBaz 93390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if clazz.name in ["array","attr","id","bool","fraction","integer"]: 93490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for f in clazz.fields: 93590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue 93690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("layout_[a-z][a-zA-Z1-9]*$", f.name): continue 93790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("state_[a-z_]*$", f.name): continue 93890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 93990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("[a-z][a-zA-Z1-9]*$", f.name): continue 94090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C7", "Expected resource name in this class to be fooBarBaz style") 94190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 94290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey # Styles are FooBar_Baz 94390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if clazz.name in ["style"]: 94490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for f in clazz.fields: 94590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("[A-Z][A-Za-z1-9]+(_[A-Z][A-Za-z1-9]+?)*$", f.name): continue 94690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style") 947b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 948b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 949a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_clazz(clazz): 950a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """Find all style issues in the given class.""" 951a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("java"): return 952a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("junit"): return 953a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.apache"): return 954a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.xml"): return 955a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.json"): return 956a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.w3c"): return 957a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 958a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_constants(clazz) 959a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_enums(clazz) 960a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_class_names(clazz) 961a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_method_names(clazz) 962a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_callbacks(clazz) 963a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_listeners(clazz) 964a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_actions(clazz) 965a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_extras(clazz) 966a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_equals(clazz) 967a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_parcelable(clazz) 968a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_protected(clazz) 969a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_fields(clazz) 970a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_register(clazz) 971a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_sync(clazz) 972a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_intent_builder(clazz) 973a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_helper_classes(clazz) 974a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_builder(clazz) 975a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_aidl(clazz) 976a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_internal(clazz) 977a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_layering(clazz) 978a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_boolean(clazz) 979a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_collections(clazz) 980a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_flags(clazz) 981a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_exception(clazz) 9821c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if not ALLOW_GOOGLE: verify_google(clazz) 983a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_bitset(clazz) 984a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_manager(clazz) 985a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_boxed(clazz) 986a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_static_utils(clazz) 987a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_overload_args(clazz) 988a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_callback_handlers(clazz) 989a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_context_first(clazz) 990a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_listener_last(clazz) 991a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_resource_names(clazz) 992a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 993a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 994a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_stream(stream): 995a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """Find all style issues in the given API stream.""" 9968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey global failures 997a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey failures = {} 998a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey _parse_stream(stream, examine_clazz) 999a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey return failures 1000a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 10018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1002a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_api(api): 1003a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """Find all style issues in the given parsed API.""" 1004a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey global failures 10051498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey failures = {} 10061498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey for key in sorted(api.keys()): 1007a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey examine_clazz(api[key]) 10088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return failures 10098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 10108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1011037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeydef verify_compat(cur, prev): 1012037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey """Find any incompatible API changes between two levels.""" 1013037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey global failures 1014037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1015037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def class_exists(api, test): 1016037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return test.fullname in api 1017037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1018037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def ctor_exists(api, clazz, test): 1019037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for m in clazz.ctors: 1020037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if m.ident == test.ident: return True 1021037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return False 1022037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1023037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def all_methods(api, clazz): 1024037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods = list(clazz.methods) 1025037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if clazz.extends is not None: 1026037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods.extend(all_methods(api, api[clazz.extends])) 1027037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return methods 1028037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1029037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def method_exists(api, clazz, test): 1030037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods = all_methods(api, clazz) 1031037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for m in methods: 1032037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if m.ident == test.ident: return True 1033037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return False 1034037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1035037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def field_exists(api, clazz, test): 1036037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for f in clazz.fields: 1037037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if f.ident == test.ident: return True 1038037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return False 1039037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1040037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey failures = {} 1041037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for key in sorted(prev.keys()): 1042037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey prev_clazz = prev[key] 1043037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1044037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not class_exists(cur, prev_clazz): 10459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, None, None, "Class removed or incompatible change") 1046037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey continue 1047037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1048037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey cur_clazz = cur[key] 1049037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1050037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for test in prev_clazz.ctors: 1051037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not ctor_exists(cur, cur_clazz, test): 10529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, prev_ctor, None, "Constructor removed or incompatible change") 1053037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1054037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods = all_methods(prev, prev_clazz) 1055037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for test in methods: 1056037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not method_exists(cur, cur_clazz, test): 10579f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, test, None, "Method removed or incompatible change") 1058037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1059037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for test in prev_clazz.fields: 1060037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not field_exists(cur, cur_clazz, test): 10619f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, test, None, "Field removed or incompatible change") 1062037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1063037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return failures 1064037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1065037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1066ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyif __name__ == "__main__": 10671c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser = argparse.ArgumentParser(description="Enforces common Android public API design \ 10681c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf patterns. It ignores lint messages from a previous API level, if provided.") 10691c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt") 10701c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None, 10711c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf help="previous.txt") 10721c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("--no-color", action='store_const', const=True, 10731c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf help="Disable terminal colors") 10741c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("--allow-google", action='store_const', const=True, 10751c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf help="Allow references to Google") 10761c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf args = vars(parser.parse_args()) 10771c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 10781c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if args['no_color']: 10791c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf USE_COLOR = False 10801c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 10811c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if args['allow_google']: 10821c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf ALLOW_GOOGLE = True 10831c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 10841c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf current_file = args['current.txt'] 10851c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf previous_file = args['previous.txt'] 10861c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 10871c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf with current_file as f: 1088a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey cur_fail = examine_stream(f) 10891c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if not previous_file is None: 10901c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf with previous_file as f: 1091a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey prev_fail = examine_stream(f) 10928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1093ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey # ignore errors from previous API level 1094ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey for p in prev_fail: 1095ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if p in cur_fail: 1096ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey del cur_fail[p] 10978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1098a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """ 1099a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey # NOTE: disabled because of memory pressure 1100ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey # look for compatibility issues 1101ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey compat_fail = verify_compat(cur, prev) 1102037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1103ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) 1104ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey for f in sorted(compat_fail): 1105ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print compat_fail[f] 1106ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print 1107a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """ 11088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1109ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) 1110ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey for f in sorted(cur_fail): 1111ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print cur_fail[f] 1112ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print 1113