from types import MappingProxyType from typing import List from .problem import Problem from ..parser import parser from . import basic_checks, class_checks, design_checks, format_checks, name_checks PASCAL_CASE = r"([A-Z][a-z0-9]*)+" SNAKE_CASE = r"[a-z][a-z0-9]*(_[a-z0-9]+)*" PRIVATE_SNAKE_CASE = r"_?{}".format(SNAKE_CASE) UPPER_SNAKE_CASE = r"[A-Z][A-Z0-9]*(_[A-Z0-9]+)*" DEFAULT_CONFIG = MappingProxyType( { # check control "disable": [], # name checks "function-name": r"(_on_{}(_[a-z0-9]+)*|{})".format( PASCAL_CASE, PRIVATE_SNAKE_CASE ), "class-name": PASCAL_CASE, "sub-class-name": r"_?{}".format(PASCAL_CASE), "signal-name": SNAKE_CASE, "class-variable-name": PRIVATE_SNAKE_CASE, "class-load-variable-name": r"({}|{})".format(PASCAL_CASE, PRIVATE_SNAKE_CASE), "function-variable-name": SNAKE_CASE, "function-load-variable-name": PASCAL_CASE, "function-argument-name": PRIVATE_SNAKE_CASE, "loop-variable-name": PRIVATE_SNAKE_CASE, "enum-name": PASCAL_CASE, "enum-element-name": UPPER_SNAKE_CASE, "constant-name": UPPER_SNAKE_CASE, "load-constant-name": r"({}|{})".format(PASCAL_CASE, UPPER_SNAKE_CASE), # basic checks "duplicated-load": None, "expression-not-assigned": None, "unnecessary-pass": None, "unused-argument": None, "comparison-with-itself": None, # not-in-loop (break/continue) # check in godot # duplicate-argument-name # check in godot # self-assigning-variable # check in godot # comparison-with-callable # duplicate-key # check in godot # unreachable # check in godot # using-constant-test # check in godot # class checks "private-method-call": None, "class-definitions-order": [ "tools", "classnames", "extends", "signals", "enums", "consts", "exports", "pubvars", "prvvars", "onreadypubvars", "onreadyprvvars", "others", ], # useless-super-delegation # design checks # max-locals # max-returns # max-branches # max-statements # max-attributes "max-public-methods": 20, # max-nested-blocks "function-arguments-number": 10, # format checks "max-file-lines": 1000, "trailing-whitespace": None, "max-line-length": 100, "mixed-tabs-and-spaces": None, # misc # no-else-return # never-returning-function # for non-void, typed functions # simplify-boolean-expression # consider-using-in # inconsistent-return-statements # redefined-argument-from-local # chained-comparison # unused-variable # pointless-statement # magic values # misc-redundant-expression # https://clang.llvm.org/extra/clang-tidy/checks/misc-redundant-expression.html # readability-magic-numbers # https://clang.llvm.org/extra/clang-tidy/checks/readability-magic-numbers.html # bugprone-virtual-near-miss # ~ https://clang.llvm.org/extra/clang-tidy/checks/list.html } ) def lint_code( gdscript_code: str, config: MappingProxyType = DEFAULT_CONFIG ) -> List[Problem]: parse_tree = parser.parse(gdscript_code, gather_metadata=True) problems = design_checks.lint(parse_tree, config) problems += format_checks.lint(gdscript_code, config) problems += name_checks.lint(parse_tree, config) problems += class_checks.lint(parse_tree, config) problems += basic_checks.lint(parse_tree, config) return problems