1*d1c7848bSJulian Sun#!/usr/bin/python3 2*d1c7848bSJulian Sun# SPDX-License-Identifier: GPL-2.0 3*d1c7848bSJulian Sun# Author: Julian Sun <[email protected]> 4*d1c7848bSJulian Sun 5*d1c7848bSJulian Sun""" Find macro definitions with unused parameters. """ 6*d1c7848bSJulian Sun 7*d1c7848bSJulian Sunimport argparse 8*d1c7848bSJulian Sunimport os 9*d1c7848bSJulian Sunimport re 10*d1c7848bSJulian Sun 11*d1c7848bSJulian Sunparser = argparse.ArgumentParser() 12*d1c7848bSJulian Sun 13*d1c7848bSJulian Sunparser.add_argument("path", type=str, help="The file or dir path that needs check") 14*d1c7848bSJulian Sunparser.add_argument("-v", "--verbose", action="store_true", 15*d1c7848bSJulian Sun help="Check conditional macros, but may lead to more false positives") 16*d1c7848bSJulian Sunargs = parser.parse_args() 17*d1c7848bSJulian Sun 18*d1c7848bSJulian Sunmacro_pattern = r"#define\s+(\w+)\(([^)]*)\)" 19*d1c7848bSJulian Sun# below vars were used to reduce false positives 20*d1c7848bSJulian Sunfp_patterns = [r"\s*do\s*\{\s*\}\s*while\s*\(\s*0\s*\)", 21*d1c7848bSJulian Sun r"\(?0\)?", r"\(?1\)?"] 22*d1c7848bSJulian Suncorrect_macros = [] 23*d1c7848bSJulian Suncond_compile_mark = "#if" 24*d1c7848bSJulian Suncond_compile_end = "#endif" 25*d1c7848bSJulian Sun 26*d1c7848bSJulian Sundef check_macro(macro_line, report): 27*d1c7848bSJulian Sun match = re.match(macro_pattern, macro_line) 28*d1c7848bSJulian Sun if match: 29*d1c7848bSJulian Sun macro_def = re.sub(macro_pattern, '', macro_line) 30*d1c7848bSJulian Sun identifier = match.group(1) 31*d1c7848bSJulian Sun content = match.group(2) 32*d1c7848bSJulian Sun arguments = [item.strip() for item in content.split(',') if item.strip()] 33*d1c7848bSJulian Sun 34*d1c7848bSJulian Sun macro_def = macro_def.strip() 35*d1c7848bSJulian Sun if not macro_def: 36*d1c7848bSJulian Sun return 37*d1c7848bSJulian Sun # used to reduce false positives, like #define endfor_nexthops(rt) } 38*d1c7848bSJulian Sun if len(macro_def) == 1: 39*d1c7848bSJulian Sun return 40*d1c7848bSJulian Sun 41*d1c7848bSJulian Sun for fp_pattern in fp_patterns: 42*d1c7848bSJulian Sun if (re.match(fp_pattern, macro_def)): 43*d1c7848bSJulian Sun return 44*d1c7848bSJulian Sun 45*d1c7848bSJulian Sun for arg in arguments: 46*d1c7848bSJulian Sun # used to reduce false positives 47*d1c7848bSJulian Sun if "..." in arg: 48*d1c7848bSJulian Sun return 49*d1c7848bSJulian Sun for arg in arguments: 50*d1c7848bSJulian Sun if not arg in macro_def and report == False: 51*d1c7848bSJulian Sun return 52*d1c7848bSJulian Sun # if there is a correct macro with the same name, do not report it. 53*d1c7848bSJulian Sun if not arg in macro_def and identifier not in correct_macros: 54*d1c7848bSJulian Sun print(f"Argument {arg} is not used in function-line macro {identifier}") 55*d1c7848bSJulian Sun return 56*d1c7848bSJulian Sun 57*d1c7848bSJulian Sun correct_macros.append(identifier) 58*d1c7848bSJulian Sun 59*d1c7848bSJulian Sun 60*d1c7848bSJulian Sun# remove comment and whitespace 61*d1c7848bSJulian Sundef macro_strip(macro): 62*d1c7848bSJulian Sun comment_pattern1 = r"\/\/*" 63*d1c7848bSJulian Sun comment_pattern2 = r"\/\**\*\/" 64*d1c7848bSJulian Sun 65*d1c7848bSJulian Sun macro = macro.strip() 66*d1c7848bSJulian Sun macro = re.sub(comment_pattern1, '', macro) 67*d1c7848bSJulian Sun macro = re.sub(comment_pattern2, '', macro) 68*d1c7848bSJulian Sun 69*d1c7848bSJulian Sun return macro 70*d1c7848bSJulian Sun 71*d1c7848bSJulian Sundef file_check_macro(file_path, report): 72*d1c7848bSJulian Sun # number of conditional compiling 73*d1c7848bSJulian Sun cond_compile = 0 74*d1c7848bSJulian Sun # only check .c and .h file 75*d1c7848bSJulian Sun if not file_path.endswith(".c") and not file_path.endswith(".h"): 76*d1c7848bSJulian Sun return 77*d1c7848bSJulian Sun 78*d1c7848bSJulian Sun with open(file_path, "r") as f: 79*d1c7848bSJulian Sun while True: 80*d1c7848bSJulian Sun line = f.readline() 81*d1c7848bSJulian Sun if not line: 82*d1c7848bSJulian Sun break 83*d1c7848bSJulian Sun line = line.strip() 84*d1c7848bSJulian Sun if line.startswith(cond_compile_mark): 85*d1c7848bSJulian Sun cond_compile += 1 86*d1c7848bSJulian Sun continue 87*d1c7848bSJulian Sun if line.startswith(cond_compile_end): 88*d1c7848bSJulian Sun cond_compile -= 1 89*d1c7848bSJulian Sun continue 90*d1c7848bSJulian Sun 91*d1c7848bSJulian Sun macro = re.match(macro_pattern, line) 92*d1c7848bSJulian Sun if macro: 93*d1c7848bSJulian Sun macro = macro_strip(macro.string) 94*d1c7848bSJulian Sun while macro[-1] == '\\': 95*d1c7848bSJulian Sun macro = macro[0:-1] 96*d1c7848bSJulian Sun macro = macro.strip() 97*d1c7848bSJulian Sun macro += f.readline() 98*d1c7848bSJulian Sun macro = macro_strip(macro) 99*d1c7848bSJulian Sun if not args.verbose: 100*d1c7848bSJulian Sun if file_path.endswith(".c") and cond_compile != 0: 101*d1c7848bSJulian Sun continue 102*d1c7848bSJulian Sun # 1 is for #ifdef xxx at the beginning of the header file 103*d1c7848bSJulian Sun if file_path.endswith(".h") and cond_compile != 1: 104*d1c7848bSJulian Sun continue 105*d1c7848bSJulian Sun check_macro(macro, report) 106*d1c7848bSJulian Sun 107*d1c7848bSJulian Sundef get_correct_macros(path): 108*d1c7848bSJulian Sun file_check_macro(path, False) 109*d1c7848bSJulian Sun 110*d1c7848bSJulian Sundef dir_check_macro(dir_path): 111*d1c7848bSJulian Sun 112*d1c7848bSJulian Sun for dentry in os.listdir(dir_path): 113*d1c7848bSJulian Sun path = os.path.join(dir_path, dentry) 114*d1c7848bSJulian Sun if os.path.isdir(path): 115*d1c7848bSJulian Sun dir_check_macro(path) 116*d1c7848bSJulian Sun elif os.path.isfile(path): 117*d1c7848bSJulian Sun get_correct_macros(path) 118*d1c7848bSJulian Sun file_check_macro(path, True) 119*d1c7848bSJulian Sun 120*d1c7848bSJulian Sun 121*d1c7848bSJulian Sundef main(): 122*d1c7848bSJulian Sun if os.path.isfile(args.path): 123*d1c7848bSJulian Sun get_correct_macros(args.path) 124*d1c7848bSJulian Sun file_check_macro(args.path, True) 125*d1c7848bSJulian Sun elif os.path.isdir(args.path): 126*d1c7848bSJulian Sun dir_check_macro(args.path) 127*d1c7848bSJulian Sun else: 128*d1c7848bSJulian Sun print(f"{args.path} doesn't exit or is neither a file nor a dir") 129*d1c7848bSJulian Sun 130*d1c7848bSJulian Sunif __name__ == "__main__": 131*d1c7848bSJulian Sun main()