Linux Audio

Check our new training course

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