Add checks for Kotlin keywords and operator functions.
Test: Ran against current.txt and observed warnings.
Change-Id: I071d564885be3a0c97bca77d93c9a107ecde583c
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 77c1c24..2eab22e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1127,6 +1127,80 @@
return
+def verify_member_name_not_kotlin_keyword(clazz):
+ """Prevent method names which are keywords in Kotlin."""
+
+ # https://kotlinlang.org/docs/reference/keyword-reference.html#hard-keywords
+ # This list does not include Java keywords as those are already impossible to use.
+ keywords = [
+ 'as',
+ 'fun',
+ 'in',
+ 'is',
+ 'object',
+ 'typealias',
+ 'val',
+ 'var',
+ 'when',
+ ]
+
+ for m in clazz.methods:
+ if m.name in keywords:
+ error(clazz, m, None, "Method name must not be a Kotlin keyword")
+ for f in clazz.fields:
+ if f.name in keywords:
+ error(clazz, f, None, "Field name must not be a Kotlin keyword")
+
+
+def verify_method_name_not_kotlin_operator(clazz):
+ """Warn about method names which become operators in Kotlin."""
+
+ binary = set()
+
+ def unique_binary_op(m, op):
+ if op in binary:
+ error(clazz, m, None, "Only one of '{0}' and '{0}Assign' methods should be present for Kotlin".format(op))
+ binary.add(op)
+
+ for m in clazz.methods:
+ if 'static' in m.split:
+ continue
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators
+ if m.name in ['unaryPlus', 'unaryMinus', 'not'] and len(m.args) == 0:
+ warn(clazz, m, None, "Method can be invoked as a unary operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#increments-and-decrements
+ if m.name in ['inc', 'dec'] and len(m.args) == 0 and m.typ != 'void':
+ # This only applies if the return type is the same or a subtype of the enclosing class, but we have no
+ # practical way of checking that relationship here.
+ warn(clazz, m, None, "Method can be invoked as a pre/postfix inc/decrement operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#arithmetic
+ if m.name in ['plus', 'minus', 'times', 'div', 'rem', 'mod', 'rangeTo'] and len(m.args) == 1:
+ warn(clazz, m, None, "Method can be invoked as a binary operator from Kotlin")
+ unique_binary_op(m, m.name)
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#in
+ if m.name == 'contains' and len(m.args) == 1 and m.typ == 'boolean':
+ warn(clazz, m, None, "Method can be invoked as a 'in' operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#indexed
+ if (m.name == 'get' and len(m.args) > 0) or (m.name == 'set' and len(m.args) > 1):
+ warn(clazz, m, None, "Method can be invoked with an indexing operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#invoke
+ if m.name == 'invoke':
+ warn(clazz, m, None, "Method can be invoked with function call syntax from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#assignments
+ if m.name in ['plusAssign', 'minusAssign', 'timesAssign', 'divAssign', 'remAssign', 'modAssign'] \
+ and len(m.args) == 1 \
+ and m.typ == 'void':
+ warn(clazz, m, None, "Method can be invoked as a compound assignment operator from Kotlin")
+ unique_binary_op(m, m.name[:-6]) # Remove 'Assign' suffix
+
+
def examine_clazz(clazz):
"""Find all style issues in the given class."""
if clazz.pkg.name.startswith("java"): return
@@ -1178,6 +1252,8 @@
verify_error(clazz)
verify_units(clazz)
verify_closable(clazz)
+ verify_member_name_not_kotlin_keyword(clazz)
+ verify_method_name_not_kotlin_operator(clazz)
def examine_stream(stream):