bin/find-unneeded-includes | 78 ++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 22 deletions(-)
New commits: commit cff2f182e4b30055661ef202e08f30a0c0af29ec Author: Gabor Kelemen <gabor.kelemen.ext...@allotropia.de> AuthorDate: Sun Dec 15 15:37:06 2024 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Jan 2 08:23:09 2025 +0100 Revert "find-unneded-includes: don't suggest removal of fwd decls" This reverts commit cee2e455605bbb921e1e32201cc27af973cb775c. Improve the forward declaration removal algorithm: run IWYU without --no_fwd_decls but ignore the header removal suggestions This way only the obsolete forward declarations are listed Previously we did not have a way for telling apart needed and unneeded forward declarations, now these can be cleaned out too Put this mode behind a new command line switch: --fwdecl Change-Id: I1b5659fd44253627adbaa7866178ddc000b1b5eb Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178966 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de> diff --git a/bin/find-unneeded-includes b/bin/find-unneeded-includes index aa1d6301ecce..e93d62f38b8b 100755 --- a/bin/find-unneeded-includes +++ b/bin/find-unneeded-includes @@ -153,7 +153,7 @@ def unwrapInclude(include): return include[1:-1] -def processIWYUOutput(iwyuOutput, moduleRules, fileName, noexclude, checknamespaces, finderrors): +def processIWYUOutput(iwyuOutput, moduleRules, fileName, noexclude, checknamespaces, finderrors, removefwdd): inAdd = False toAdd = [] inRemove = False @@ -213,15 +213,26 @@ def processIWYUOutput(iwyuOutput, moduleRules, fileName, noexclude, checknamespa toAdd.append(line) if inRemove and not checknamespaces: - match = re.match("- #include (.*) // lines (.*)-.*", line) - if match: - # Only suggest removals for now. Removing fwd decls is more complex: they may be - # indeed unused or they may removed to be replaced with an include. And we want to - # avoid the later. - include = unwrapInclude(match.group(1)) - lineno = match.group(2) - if not ignoreRemoval(include, toAdd, currentFileName, moduleRules, noexclude): - toRemove.append("%s:%s: %s" % (currentFileName, lineno, include)) + if not removefwdd: + match = re.match("- #include (.*) // lines (.*)-.*", line) + if match: + include = unwrapInclude(match.group(1)) + lineno = match.group(2) + if not ignoreRemoval(include, toAdd, currentFileName, moduleRules, noexclude): + toRemove.append("%s:%s: %s" % (currentFileName, lineno, include)) + continue + else: + # Search for obsolete forward declarations, but not header -> fwdecl replacements + match = re.match("- (.*;(?: })*)* // lines (.*)-.*", line) + if match: + fwdDecl = match.group(1) + if fwdDecl.endswith(";"): + # Remove trailing semicolon. + fwdDecl = fwdDecl[:-1] + lineno = match.group(2) + if not ignoreRemoval(fwdDecl, toAdd, currentFileName, moduleRules, noexclude): + toRemove.append("%s:%s: %s" % (currentFileName, lineno, fwdDecl)) + continue if inFull: if checknamespaces: @@ -398,25 +409,32 @@ def processIWYUOutput(iwyuOutput, moduleRules, fileName, noexclude, checknamespa # Get the row number, normal IWYU output does not contain this info subprocess.run(["git", "grep", "-n", "namespace.*[^a-zA-Z]"+nameSpace+" *;", fileName]) - for remove in sorted(toRemove): - print("ERROR: %s: remove not needed include" % remove) + if removefwdd: + for remove in sorted(toRemove): + print("ERROR: %s: remove not needed forward declaration" % remove) + else: + for remove in sorted(toRemove): + print("ERROR: %s: remove not needed include" % remove) return len(toRemove) -def run_tool(task_queue, failed_files, dontstop, noexclude, checknamespaces, finderrors): +def run_tool(task_queue, failed_files, dontstop, noexclude, checknamespaces, finderrors, removefwdd): while True: invocation, moduleRules = task_queue.get() if not len(failed_files): print("[IWYU] " + invocation.split(' ')[-1]) p = subprocess.Popen(invocation, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - retcode = processIWYUOutput(p.communicate()[0].decode('utf-8').splitlines(), moduleRules, invocation.split(' ')[-1], noexclude, checknamespaces, finderrors) + retcode = processIWYUOutput(p.communicate()[0].decode('utf-8').splitlines(), moduleRules, invocation.split(' ')[-1], noexclude, checknamespaces, finderrors, removefwdd) if finderrors: if p.returncode == 1: print("Running the IWYU process returned error code: " + invocation) - if retcode == -1 and not checknamespaces: + if retcode == -1 and not checknamespaces and not removefwdd: print("ERROR: A file is probably not self contained, check this commands output: " + invocation) elif retcode > 0: - print("ERROR: The following command found unused includes: " + invocation) + if not removefwdd: + print("ERROR: The following command found unused includes: " + invocation) + else: + print("ERROR: The following command found unused forward declarations: " + invocation) if not dontstop: failed_files.append(invocation) task_queue.task_done() @@ -438,7 +456,7 @@ def isInUnoIncludeFile(path): or path.startswith("include/uno/") -def tidy(compileCommands, paths, dontstop, noexclude, checknamespaces, finderrors): +def tidy(compileCommands, paths, dontstop, noexclude, checknamespaces, finderrors, removefwdd): return_code = 0 try: @@ -446,7 +464,7 @@ def tidy(compileCommands, paths, dontstop, noexclude, checknamespaces, finderror task_queue = queue.Queue(max_task) failed_files = [] for _ in range(max_task): - t = threading.Thread(target=run_tool, args=(task_queue, failed_files, dontstop, noexclude, checknamespaces, finderrors)) + t = threading.Thread(target=run_tool, args=(task_queue, failed_files, dontstop, noexclude, checknamespaces, finderrors, removefwdd)) t.daemon = True t.start() @@ -488,7 +506,15 @@ def tidy(compileCommands, paths, dontstop, noexclude, checknamespaces, finderror if assume: args = args.replace(assumeAbs, "-x c++ " + pathAbs) - invocation = "include-what-you-use -Xiwyu --no_fwd_decls -Xiwyu --max_line_length=200 " + args + if not removefwdd: + invocation = "include-what-you-use -Xiwyu --no_fwd_decls -Xiwyu --max_line_length=200 " + args + # In --fwdecl mode we ask for fw declaration removal suggestions. + # In this mode obsolete fw declarations are suggested for removal. + # Later we ignore the header removal suggestions, which may be + # there because of possibility of replacement with fw declarations + # but those and our CI are not reliable enough yet for use + else: + invocation = "include-what-you-use -Xiwyu --cxx17ns -Xiwyu --max_line_length=200 " + args task_queue.put((invocation, moduleRules)) task_queue.join() @@ -521,6 +547,8 @@ def main(argv): parser.add_argument('--finderrors', action='store_true', help='Report IWYU failures when it returns with -1 error code. ' 'Use only for debugging this script!') + parser.add_argument('--fwdecl', action='store_true', + help='Suggest removal of obsolete forward declarations') args = parser.parse_args() @@ -533,8 +561,14 @@ def main(argv): for root, dirs, files in os.walk(args.recursive[0]): for file in files: if args.headers: - if (file.endswith(".hxx") or file.endswith(".hrc") or file.endswith(".h")): - list_of_files.append(os.path.join(root,file)) + if not args.fwdecl: + if (file.endswith(".hxx") or file.endswith(".hrc") or file.endswith(".h")): + list_of_files.append(os.path.join(root,file)) + else: + # In fwdecl mode don't check hrc files as they contain a lot of fw declarations + # used in defines and iwyu (0.21 at least) can not yet understand those properly + if (file.endswith(".hxx") or file.endswith(".h")): + list_of_files.append(os.path.join(root,file)) else: if (file.endswith(".cxx") or file.endswith(".c")): list_of_files.append(os.path.join(root,file)) @@ -569,7 +603,7 @@ def main(argv): if not file.exists(): print("WARNING: File listed in " + rulePath + " no longer exists: " + pathname) - tidy(compileCommands, paths=list_of_files, dontstop=vars(args)["continue"], noexclude=args.noexclude, checknamespaces=args.ns, finderrors=args.finderrors) + tidy(compileCommands, paths=list_of_files, dontstop=vars(args)["continue"], noexclude=args.noexclude, checknamespaces=args.ns, finderrors=args.finderrors, removefwdd=args.fwdecl) if __name__ == '__main__': main(sys.argv[1:])