|
| 1 | +/** |
| 2 | + * @name Type metrics |
| 3 | + * @description Counts of various kinds of type annotations in Python code. |
| 4 | + * @kind table |
| 5 | + * @id py/type-metrics |
| 6 | + */ |
| 7 | + |
| 8 | +import python |
| 9 | + |
| 10 | +class BuiltinType extends Name { |
| 11 | + BuiltinType() { this.getId() in ["int", "float", "str", "bool", "bytes", "None"] } |
| 12 | +} |
| 13 | + |
| 14 | +newtype TAnnotatable = |
| 15 | + TAnnotatedFunction(FunctionExpr f) { exists(f.getReturns()) } or |
| 16 | + TAnnotatedParameter(Parameter p) { exists(p.getAnnotation()) } or |
| 17 | + TAnnotatedAssignment(AnnAssign a) { exists(a.getAnnotation()) } |
| 18 | + |
| 19 | +abstract class Annotatable extends TAnnotatable { |
| 20 | + string toString() { result = "Annotatable" } |
| 21 | + |
| 22 | + abstract Expr getAnnotation(); |
| 23 | +} |
| 24 | + |
| 25 | +class AnnotatedFunction extends TAnnotatedFunction, Annotatable { |
| 26 | + FunctionExpr function; |
| 27 | + |
| 28 | + AnnotatedFunction() { this = TAnnotatedFunction(function) } |
| 29 | + |
| 30 | + override Expr getAnnotation() { result = function.getReturns() } |
| 31 | +} |
| 32 | + |
| 33 | +class AnnotatedParameter extends TAnnotatedParameter, Annotatable { |
| 34 | + Parameter parameter; |
| 35 | + |
| 36 | + AnnotatedParameter() { this = TAnnotatedParameter(parameter) } |
| 37 | + |
| 38 | + override Expr getAnnotation() { result = parameter.getAnnotation() } |
| 39 | +} |
| 40 | + |
| 41 | +class AnnotatedAssignment extends TAnnotatedAssignment, Annotatable { |
| 42 | + AnnAssign assignment; |
| 43 | + |
| 44 | + AnnotatedAssignment() { this = TAnnotatedAssignment(assignment) } |
| 45 | + |
| 46 | + override Expr getAnnotation() { result = assignment.getAnnotation() } |
| 47 | +} |
| 48 | + |
| 49 | +/** Holds if `e` is a forward declaration of a type. */ |
| 50 | +predicate is_forward_declaration(Expr e) { e instanceof StringLiteral } |
| 51 | + |
| 52 | +/** Holds if `e` is a type that may be difficult to analyze. */ |
| 53 | +predicate is_complex_type(Expr e) { |
| 54 | + e instanceof Subscript and not is_optional_type(e) |
| 55 | + or |
| 56 | + e instanceof Tuple |
| 57 | + or |
| 58 | + e instanceof List |
| 59 | +} |
| 60 | + |
| 61 | +/** Holds if `e` is a type of the form `Optional[...]`. */ |
| 62 | +predicate is_optional_type(Subscript e) { e.getObject().(Name).getId() = "Optional" } |
| 63 | + |
| 64 | +/** Holds if `e` is a simple type, that is either an identifier (excluding built-in types) or an attribute of a simple type. */ |
| 65 | +predicate is_simple_type(Expr e) { |
| 66 | + e instanceof Name and not e instanceof BuiltinType |
| 67 | + or |
| 68 | + is_simple_type(e.(Attribute).getObject()) |
| 69 | +} |
| 70 | + |
| 71 | +/** Holds if `e` is a built-in type. */ |
| 72 | +predicate is_builtin_type(Expr e) { e instanceof BuiltinType } |
| 73 | + |
| 74 | +predicate type_count( |
| 75 | + string kind, int total, int built_in_count, int forward_declaration_count, int simple_type_count, |
| 76 | + int complex_type_count, int optional_type_count |
| 77 | +) { |
| 78 | + kind = "Parameter annotation" and |
| 79 | + total = count(AnnotatedParameter p) and |
| 80 | + built_in_count = count(AnnotatedParameter p | is_builtin_type(p.getAnnotation())) and |
| 81 | + forward_declaration_count = |
| 82 | + count(AnnotatedParameter p | is_forward_declaration(p.getAnnotation())) and |
| 83 | + simple_type_count = count(AnnotatedParameter p | is_simple_type(p.getAnnotation())) and |
| 84 | + complex_type_count = count(AnnotatedParameter p | is_complex_type(p.getAnnotation())) and |
| 85 | + optional_type_count = count(AnnotatedParameter p | is_optional_type(p.getAnnotation())) |
| 86 | + or |
| 87 | + kind = "Return type annotation" and |
| 88 | + total = count(AnnotatedFunction f) and |
| 89 | + built_in_count = count(AnnotatedFunction f | is_builtin_type(f.getAnnotation())) and |
| 90 | + forward_declaration_count = count(AnnotatedFunction f | is_forward_declaration(f.getAnnotation())) and |
| 91 | + simple_type_count = count(AnnotatedFunction f | is_simple_type(f.getAnnotation())) and |
| 92 | + complex_type_count = count(AnnotatedFunction f | is_complex_type(f.getAnnotation())) and |
| 93 | + optional_type_count = count(AnnotatedFunction f | is_optional_type(f.getAnnotation())) |
| 94 | + or |
| 95 | + kind = "Annotated assignment" and |
| 96 | + total = count(AnnotatedAssignment a) and |
| 97 | + built_in_count = count(AnnotatedAssignment a | is_builtin_type(a.getAnnotation())) and |
| 98 | + forward_declaration_count = |
| 99 | + count(AnnotatedAssignment a | is_forward_declaration(a.getAnnotation())) and |
| 100 | + simple_type_count = count(AnnotatedAssignment a | is_simple_type(a.getAnnotation())) and |
| 101 | + complex_type_count = count(AnnotatedAssignment a | is_complex_type(a.getAnnotation())) and |
| 102 | + optional_type_count = count(AnnotatedAssignment a | is_optional_type(a.getAnnotation())) |
| 103 | +} |
| 104 | + |
| 105 | +from |
| 106 | + string message, int total, int built_in, int forward_decl, int simple, int complex, int optional |
| 107 | +where type_count(message, total, built_in, forward_decl, simple, complex, optional) |
| 108 | +select message, total, built_in, forward_decl, simple, complex, optional |
0 commit comments