From 6f85a1ae9f8eef0264635c602b823330fc8d3774 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 26 Jul 2023 15:23:21 +1000 Subject: [PATCH] Tools: fixes/improvements for 'check_struct_comments' check - Fix using forward declarations when extracting struct information as it causing this check to be ignored. - Fix extracting comments which sometimes included code fragments. - Fix extracting the outer comment for struct declarations on a single line. - Skip source files in the build directory. --- build_files/cmake/cmake_static_check_clang.py | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/build_files/cmake/cmake_static_check_clang.py b/build_files/cmake/cmake_static_check_clang.py index fd34aea703d..48004999d7b 100644 --- a/build_files/cmake/cmake_static_check_clang.py +++ b/build_files/cmake/cmake_static_check_clang.py @@ -164,16 +164,18 @@ class clang_checkers: # IPython.embed() if node.kind == CursorKind.STRUCT_DECL: - struct_type = node.spelling.strip() - if not struct_type: - # The parent may be a `typedef [..] TypeID` where `[..]` is `struct { a; b; c; }`. - # Inspect the parent. - if node_parent is not None and (node_parent.kind == CursorKind.TYPEDEF_DECL): - tokens = list(node_parent.get_tokens()) - if tokens[0].spelling == "typedef": - struct_type = tokens[-1].spelling + # Ignore forward declarations. + if next(node.get_children(), None) is not None: + struct_type = node.spelling.strip() + if not struct_type: + # The parent may be a `typedef [..] TypeID` where `[..]` is `struct { a; b; c; }`. + # Inspect the parent. + if node_parent is not None and (node_parent.kind == CursorKind.TYPEDEF_DECL): + tokens = list(node_parent.get_tokens()) + if tokens[0].spelling == "typedef": + struct_type = tokens[-1].spelling - struct_decl_map[struct_type] = node + struct_decl_map[struct_type] = node # Ignore declarations for anything defined outside this file. if str(node.location.file) == filepath: @@ -238,8 +240,31 @@ class clang_checkers: break beg = end - 1 - while beg != 0 and bytes((file_data[beg],)) != b"\n": + while beg != 0 and bytes((file_data[beg],)) not in { + b"\n", + # Needed so declarations on a single line don't detect a comment + # from an outer comment, e.g. + # SomeStruct x = { + # /*list*/ {nullptr, nullptr}, + # }; + # Would start inside the first `nullptr` and walk backwards to find `/*list*/`. + b"{" + }: beg -= 1 + + # Seek back until the comment end (in some cases this includes code). + # This occurs when the body of the declaration includes code, e.g. + # rcti x = { + # /*xmin*/ foo->bar.baz, + # ... snip ... + # }; + # Where `"xmin*/ foo->bar."` would be extracted were it not for this check. + # There might be a more elegant way to handle this, for how snipping off the last + # comment characters is sufficient. + end_test = file_data.rfind(b"*/", end + 1, beg) + if end_test != -1: + end = end_test + text = file_data[beg:end] if text.lstrip().startswith(b"/*"): if not has_newline: @@ -422,6 +447,10 @@ def source_info_filter( has_match = True if not has_match: continue + else: + # Skip files not in source (generated files from the build directory), + # these could be check but it's not all that useful (preview blend ... etc). + continue source_info_result.append(item)