https://github.com/PatJay1703 created https://github.com/llvm/llvm-project/pull/138469
None >From 8f7b6fc86234162d9c88547c543552693c8b5c79 Mon Sep 17 00:00:00 2001 From: Jay Satish Kumar Patel <kumar...@pe28vega.hpc.amslabs.hpecorp.net> Date: Sun, 4 May 2025 09:49:01 -0500 Subject: [PATCH 1/3] changed config file --- .clang-format | 246 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 9b3aa8b7213b2..b52a604773bc2 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1,245 @@ -BasedOnStyle: LLVM +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + >From d95afeaa58b4049cec51e6a4ecef22a854907b0f Mon Sep 17 00:00:00 2001 From: blazie2004 <jayvc1...@gmail.com> Date: Sun, 4 May 2025 15:04:33 -0500 Subject: [PATCH 2/3] added all scripts to repo main --- clang-format-diff.py | 183 +++++++++++++++ clang-tidy-diff.py | 424 +++++++++++++++++++++++++++++++++++ config.yaml | 14 ++ remote_clang_format.py | 52 +++++ remote_clang_tidy.py | 52 +++++ remote_class_check.py | 73 ++++++ remote_header_check.py | 69 ++++++ remote_naming_conventions.py | 145 ++++++++++++ 8 files changed, 1012 insertions(+) create mode 100644 clang-format-diff.py create mode 100644 clang-tidy-diff.py create mode 100644 config.yaml create mode 100644 remote_clang_format.py create mode 100644 remote_clang_tidy.py create mode 100644 remote_class_check.py create mode 100644 remote_header_check.py create mode 100644 remote_naming_conventions.py diff --git a/clang-format-diff.py b/clang-format-diff.py new file mode 100644 index 0000000000000..c4aa7951e85de --- /dev/null +++ b/clang-format-diff.py @@ -0,0 +1,183 @@ + +#!/usr/bin/env python3 +# +# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===------------------------------------------------------------------------===# +""" +This script reads input from a unified diff and reformats all the changed +lines. This is useful to reformat all the lines touched by a specific patch. +Example usage for git/svn users: + git diff -U0 --no-color --relative HEAD^ | {clang_format_diff} -p1 -i + svn diff --diff-cmd=diff -x-U0 | {clang_format_diff} -i +It should be noted that the filename contained in the diff is used unmodified +to determine the source file to update. Users calling this script directly +should be careful to ensure that the path in the diff is correct relative to the +current working directory. +""" +from __future__ import absolute_import, division, print_function +import argparse +import difflib +import re +import subprocess +import sys +if sys.version_info.major >= 3: + from io import StringIO +else: + from io import BytesIO as StringIO + +def main(): + parser = argparse.ArgumentParser( + description=__doc__.format(clang_format_diff="%(prog)s"), + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "-i", + action="store_true", + default=False, + help="apply edits to files instead of displaying a diff", + ) + parser.add_argument( + "-p", + metavar="NUM", + default=0, + help="strip the smallest prefix containing P slashes", + ) + parser.add_argument( + "-regex", + metavar="PATTERN", + default=None, + help="custom pattern selecting file paths to reformat " + "(case sensitive, overrides -iregex)", + ) + parser.add_argument( + "-iregex", + metavar="PATTERN", + default=r".*\.(?:cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp" + r"|hxx|m|mm|inc|js|ts|proto|protodevel|java|cs|json|ipynb|s?vh?)", + help="custom pattern selecting file paths to reformat " + "(case insensitive, overridden by -regex)", + ) + parser.add_argument( + "-sort-includes", + action="store_true", + default=False, + help="let clang-format sort include blocks", + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="be more verbose, ineffective without -i", + ) + parser.add_argument( + "-style", + help="formatting style to apply (LLVM, GNU, Google, Chromium, " + "Microsoft, Mozilla, WebKit)", + ) + parser.add_argument( + "-fallback-style", + help="The name of the predefined style used as a" + "fallback in case clang-format is invoked with" + "-style=file, but can not find the .clang-format" + "file to use.", + ) + parser.add_argument( + "-binary", + default="clang-format", + help="location of binary to use for clang-format", + ) + args = parser.parse_args() + # Extract changed lines for each file. + filename = None + lines_by_file = {} + for line in sys.stdin: + match = re.search(r"^\+\+\+\ (.*?/){%s}(\S*)" % args.p, line) + if match: + filename = match.group(2) + if filename is None: + continue + if args.regex is not None: + if not re.match("^%s$" % args.regex, filename): + continue + else: + if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE): + continue + match = re.search(r"^@@.*\+(\d+)(?:,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(2): + line_count = int(match.group(2)) + # The input is something like + # + # @@ -1, +0,0 @@ + # + # which means no lines were added. + if line_count == 0: + continue + # Also format lines range if line_count is 0 in case of deleting + # surrounding statements. + end_line = start_line + if line_count != 0: + end_line += line_count - 1 + lines_by_file.setdefault(filename, []).extend( + ["--lines", str(start_line) + ":" + str(end_line)] + ) + # Reformat files containing changes in place. + has_diff = False + for filename, lines in lines_by_file.items(): + if args.i and args.verbose: + print("Formatting {}".format(filename)) + command = [args.binary, filename] + if args.i: + command.append("-i") + if args.sort_includes: + command.append("--sort-includes") + command.extend(lines) + if args.style: + command.extend(["--style", args.style]) + if args.fallback_style: + command.extend(["--fallback-style", args.fallback_style]) + try: + p = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=None, + stdin=subprocess.PIPE, + universal_newlines=True, + ) + except OSError as e: + # Give the user more context when clang-format isn't + # found/isn't executable, etc. + raise RuntimeError( + 'Failed to run "%s" - %s"' % (" ".join(command), e.strerror) + ) + stdout, _stderr = p.communicate() + if p.returncode != 0: + return p.returncode + if not args.i: + with open(filename) as f: + code = f.readlines() + formatted_code = StringIO(stdout).readlines() + diff = difflib.unified_diff( + code, + formatted_code, + filename, + filename, + "(before formatting)", + "(after formatting)", + ) + diff_string = "".join(diff) + if len(diff_string) > 0: + has_diff = True + sys.stdout.write(diff_string) + if has_diff: + return 1 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/clang-tidy-diff.py b/clang-tidy-diff.py new file mode 100644 index 0000000000000..6f157c64613d8 --- /dev/null +++ b/clang-tidy-diff.py @@ -0,0 +1,424 @@ + +#!/usr/bin/env python3 +# +# ===- clang-tidy-diff.py - ClangTidy Diff Checker -----------*- python -*--===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===-----------------------------------------------------------------------===# + +r""" +ClangTidy Diff Checker +====================== + +This script reads input from a unified diff, runs clang-tidy on all changed +files and outputs clang-tidy warnings in changed lines only. This is useful to +detect clang-tidy regressions in the lines touched by a specific patch. +Example usage for git/svn users: + + git diff -U0 HEAD^ | clang-tidy-diff.py -p1 + svn diff --diff-cmd=diff -x-U0 | \ + clang-tidy-diff.py -fix -checks=-*,modernize-use-override + +""" + +import argparse +import glob +import json +import multiprocessing +import os +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import traceback +from pathlib import Path + +try: + import yaml +except ImportError: + yaml = None + +is_py2 = sys.version[0] == "2" + +if is_py2: + import Queue as queue +else: + import queue as queue + + +def run_tidy(task_queue, lock, timeout, failed_files): + watchdog = None + while True: + command = task_queue.get() + try: + proc = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + if timeout is not None: + watchdog = threading.Timer(timeout, proc.kill) + watchdog.start() + + stdout, stderr = proc.communicate() + if proc.returncode != 0: + if proc.returncode < 0: + msg = "Terminated by signal %d : %s\n" % ( + -proc.returncode, + " ".join(command), + ) + stderr += msg.encode("utf-8") + failed_files.append(command) + + with lock: + sys.stdout.write(stdout.decode("utf-8") + "\n") + sys.stdout.flush() + if stderr: + sys.stderr.write(stderr.decode("utf-8") + "\n") + sys.stderr.flush() + except Exception as e: + with lock: + sys.stderr.write("Failed: " + str(e) + ": ".join(command) + "\n") + finally: + with lock: + if not (timeout is None or watchdog is None): + if not watchdog.is_alive(): + sys.stderr.write( + "Terminated by timeout: " + " ".join(command) + "\n" + ) + watchdog.cancel() + task_queue.task_done() + + +def start_workers(max_tasks, tidy_caller, arguments): + for _ in range(max_tasks): + t = threading.Thread(target=tidy_caller, args=arguments) + t.daemon = True + t.start() + + +def merge_replacement_files(tmpdir, mergefile): + """Merge all replacement files in a directory into a single file""" + # The fixes suggested by clang-tidy >= 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey = "Diagnostics" + merged = [] + for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")): + content = yaml.safe_load(open(replacefile, "r")) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = {"MainSourceFile": "", mergekey: merged} + with open(mergefile, "w") as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, "w").close() + + +def get_compiling_files(args): + """Read a compile_commands.json database and return a set of file paths""" + current_dir = Path.cwd() + compile_commands_json = ( + (current_dir / args.build_path) if args.build_path else current_dir + ) + compile_commands_json = compile_commands_json / "compile_commands.json" + files = set() + with open(compile_commands_json) as db_file: + db_json = json.load(db_file) + for entry in db_json: + if "file" not in entry: + continue + files.add(Path(entry["file"])) + return files + + +def main(): + parser = argparse.ArgumentParser( + description="Run clang-tidy against changed files, and " + "output diagnostics only for modified " + "lines." + ) + parser.add_argument( + "-clang-tidy-binary", + metavar="PATH", + default="clang-tidy", + help="path to clang-tidy binary", + ) + parser.add_argument( + "-p", + metavar="NUM", + default=0, + help="strip the smallest prefix containing P slashes", + ) + parser.add_argument( + "-regex", + metavar="PATTERN", + default=None, + help="custom pattern selecting file paths to check " + "(case sensitive, overrides -iregex)", + ) + parser.add_argument( + "-iregex", + metavar="PATTERN", + default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)", + help="custom pattern selecting file paths to check " + "(case insensitive, overridden by -regex)", + ) + parser.add_argument( + "-j", + type=int, + default=1, + help="number of tidy instances to be run in parallel.", + ) + parser.add_argument( + "-timeout", type=int, default=None, help="timeout per each file in seconds." + ) + parser.add_argument( + "-fix", action="store_true", default=False, help="apply suggested fixes" + ) + parser.add_argument( + "-checks", + help="checks filter, when not specified, use clang-tidy " "default", + default="", + ) + parser.add_argument( + "-config-file", + dest="config_file", + help="Specify the path of .clang-tidy or custom config file", + default="", + ) + parser.add_argument("-use-color", action="store_true", help="Use colors in output") + parser.add_argument( + "-path", dest="build_path", help="Path used to read a compile command database." + ) + if yaml: + parser.add_argument( + "-export-fixes", + metavar="FILE_OR_DIRECTORY", + dest="export_fixes", + help="A directory or a yaml file to store suggested fixes in, " + "which can be applied with clang-apply-replacements. If the " + "parameter is a directory, the fixes of each compilation unit are " + "stored in individual yaml files in the directory.", + ) + else: + parser.add_argument( + "-export-fixes", + metavar="DIRECTORY", + dest="export_fixes", + help="A directory to store suggested fixes in, which can be applied " + "with clang-apply-replacements. The fixes of each compilation unit are " + "stored in individual yaml files in the directory.", + ) + parser.add_argument( + "-extra-arg", + dest="extra_arg", + action="append", + default=[], + help="Additional argument to append to the compiler " "command line.", + ) + parser.add_argument( + "-extra-arg-before", + dest="extra_arg_before", + action="append", + default=[], + help="Additional argument to prepend to the compiler " "command line.", + ) + parser.add_argument( + "-quiet", + action="store_true", + default=False, + help="Run clang-tidy in quiet mode", + ) + parser.add_argument( + "-load", + dest="plugins", + action="append", + default=[], + help="Load the specified plugin in clang-tidy.", + ) + parser.add_argument( + "-allow-no-checks", + action="store_true", + help="Allow empty enabled checks.", + ) + parser.add_argument( + "-only-check-in-db", + dest="skip_non_compiling", + default=False, + action="store_true", + help="Only check files in the compilation database", + ) + + clang_tidy_args = [] + argv = sys.argv[1:] + if "--" in argv: + clang_tidy_args.extend(argv[argv.index("--") :]) + argv = argv[: argv.index("--")] + + args = parser.parse_args(argv) + + compiling_files = get_compiling_files(args) if args.skip_non_compiling else None + + # Extract changed lines for each file. + filename = None + lines_by_file = {} + for line in sys.stdin: + match = re.search(r'^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line) + if match: + filename = match.group(2) + if filename is None: + continue + + if args.regex is not None: + if not re.match("^%s$" % args.regex, filename): + continue + else: + if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE): + continue + + # Skip any files not in the compiling list + if ( + compiling_files is not None + and (Path.cwd() / filename) not in compiling_files + ): + continue + + match = re.search(r"^@@.*\+(\d+)(,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).append([start_line, end_line]) + + if not any(lines_by_file): + print("No relevant changes found.") + sys.exit(0) + + max_task_count = args.j + if max_task_count == 0: + max_task_count = multiprocessing.cpu_count() + max_task_count = min(len(lines_by_file), max_task_count) + + combine_fixes = False + export_fixes_dir = None + delete_fixes_dir = False + if args.export_fixes is not None: + # if a directory is given, create it if it does not exist + if args.export_fixes.endswith(os.path.sep) and not os.path.isdir( + args.export_fixes + ): + os.makedirs(args.export_fixes) + + if not os.path.isdir(args.export_fixes): + if not yaml: + raise RuntimeError( + "Cannot combine fixes in one yaml file. Either install PyYAML or specify an output directory." + ) + + combine_fixes = True + + if os.path.isdir(args.export_fixes): + export_fixes_dir = args.export_fixes + + if combine_fixes: + export_fixes_dir = tempfile.mkdtemp() + delete_fixes_dir = True + + # Tasks for clang-tidy. + task_queue = queue.Queue(max_task_count) + # A lock for console output. + lock = threading.Lock() + + # List of files with a non-zero return code. + failed_files = [] + + # Run a pool of clang-tidy workers. + start_workers( + max_task_count, run_tidy, (task_queue, lock, args.timeout, failed_files) + ) + + # Form the common args list. + common_clang_tidy_args = [] + if args.fix: + common_clang_tidy_args.append("-fix") + if args.checks != "": + common_clang_tidy_args.append("-checks=" + args.checks) + if args.config_file != "": + common_clang_tidy_args.append("-config-file=" + args.config_file) + if args.quiet: + common_clang_tidy_args.append("-quiet") + if args.build_path is not None: + common_clang_tidy_args.append("-p=%s" % args.build_path) + if args.use_color: + common_clang_tidy_args.append("--use-color") + if args.allow_no_checks: + common_clang_tidy_args.append("--allow-no-checks") + for arg in args.extra_arg: + common_clang_tidy_args.append("-extra-arg=%s" % arg) + for arg in args.extra_arg_before: + common_clang_tidy_args.append("-extra-arg-before=%s" % arg) + for plugin in args.plugins: + common_clang_tidy_args.append("-load=%s" % plugin) + + for name in lines_by_file: + line_filter_json = json.dumps( + [{"name": name, "lines": lines_by_file[name]}], separators=(",", ":") + ) + + # Run clang-tidy on files containing changes. + command = [args.clang_tidy_binary] + command.append("-line-filter=" + line_filter_json) + if args.export_fixes is not None: + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, tmp_name) = tempfile.mkstemp(suffix=".yaml", dir=export_fixes_dir) + os.close(handle) + command.append("-export-fixes=" + tmp_name) + command.extend(common_clang_tidy_args) + command.append(name) + command.extend(clang_tidy_args) + + task_queue.put(command) + + # Application return code + return_code = 0 + + # Wait for all threads to be done. + task_queue.join() + # Application return code + return_code = 0 + if failed_files: + return_code = 1 + + if combine_fixes: + print("Writing fixes to " + args.export_fixes + " ...") + try: + merge_replacement_files(export_fixes_dir, args.export_fixes) + except: + sys.stderr.write("Error exporting fixes.\n") + traceback.print_exc() + return_code = 1 + + if delete_fixes_dir: + shutil.rmtree(export_fixes_dir) + sys.exit(return_code) + + +if __name__ == "__main__": + main() diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000000000..930c79c95b606 --- /dev/null +++ b/config.yaml @@ -0,0 +1,14 @@ + +project: + name: blazie2004 + owner: blazie2004 + repo: llvm-project-checks + pr_number: 2 # Change this as needed +scripts: + run: + - check_remote_class_check.py + - check_remote_llvmheader.py + - check_remote_naming_conventions.py + - check_remote_pr_format.py + - check_remote_tidy_format.py + skip: \ No newline at end of file diff --git a/remote_clang_format.py b/remote_clang_format.py new file mode 100644 index 0000000000000..a3dfef8ddc340 --- /dev/null +++ b/remote_clang_format.py @@ -0,0 +1,52 @@ + +import subprocess +import requests +import yaml +import sys + +def load_config(): + with open("config.yaml") as f: + return yaml.safe_load(f)["project"] + +def fetch_diff(owner, repo, pr_number): + url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}.diff" + headers = {"Accept": "application/vnd.github.v3.diff"} + print(f"๐ฅ Fetching diff from {url}") + resp = requests.get(url, headers=headers) + if resp.status_code != 200: + print(f"โ Failed to fetch diff: {resp.status_code}") + sys.exit(1) + return resp.text + +def run_clang_format_diff(diff): + print("๐ฏ Running clang-format-diff.py on diff...") + try: + result = subprocess.run( + ["./clang-format-diff.py", "-p1", "-binary", "/ptmp/jay/new/llvm-project-jay/build/bin/clang-format"], + input=diff.encode("utf-8"), + capture_output=True, + check=True + ) + return result.stdout.decode() + except subprocess.CalledProcessError as e: + print("โ clang-format-diff.py failed!") + print("๐ค STDOUT:") + print(e.stdout.decode()) + print("๐ฅ STDERR:") + print(e.stderr.decode()) + sys.exit(1) + +def main(): + config = load_config() + diff_text = fetch_diff(config["owner"], config["repo"], config["pr_number"]) + + formatted_output = run_clang_format_diff(diff_text) + + if not formatted_output.strip(): + print("โ No formatting issues found.") + else: + print("\n๐งผ Suggested clang-format changes:\n") + print(formatted_output) + +if __name__ == "__main__": + main() diff --git a/remote_clang_tidy.py b/remote_clang_tidy.py new file mode 100644 index 0000000000000..026bb125c0a44 --- /dev/null +++ b/remote_clang_tidy.py @@ -0,0 +1,52 @@ + +import subprocess +import sys +import re +import requests +import yaml +# === Load config.yaml === +with open("config.yaml", "r") as f: + config = yaml.safe_load(f) +# === Configuration === +PR_NUMBER = str(config["project"]["pr_number"]) +OWNER = config["project"]["owner"] +REPO = config["project"]["repo"] +headers = { + "Accept": "application/vnd.github.v3.diff" +} +# === Fetch PR Diff === +url = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{PR_NUMBER}" +diff_url = f"{url}.diff" +print(f"๐ฅ Fetching diff from {diff_url}") +resp = requests.get(diff_url, headers=headers) +if resp.status_code != 200: + print(f"โ Failed to fetch PR diff: {resp.status_code} {resp.text}") + sys.exit(1) +diff_text = resp.text +if not diff_text.strip(): + print("โ No changes in the PR.") + sys.exit(0) +# === Run clang-tidy-diff.py on diff from stdin === +print("๐งผ Running clang-tidy-diff.py on PR diff...") +# Adjust if clang-tidy-diff.py is in a different path +clang_tidy_diff_path = "clang-tidy-diff.py" +result = subprocess.run( + ["python3", clang_tidy_diff_path, "-p1"], + input=diff_text, + text=True, + capture_output=True +) +# === Output Results === +if result.returncode == 0 and not result.stdout.strip(): + print("โ No clang-tidy issues detected!") + sys.exit(0) +else: + print("๐จ Issues detected:") + print("\n================= Diff Before clang-tidy =================") + print(diff_text) + print("\n================= Suggested Fixes =================") + print(result.stdout) + if result.stderr: + print("\nโ ๏ธ Error while running clang-tidy:") + print(result.stderr) + sys.exit(1) diff --git a/remote_class_check.py b/remote_class_check.py new file mode 100644 index 0000000000000..00171d0bcc3c1 --- /dev/null +++ b/remote_class_check.py @@ -0,0 +1,73 @@ + +import subprocess +import sys +import re +import requests +import yaml +with open("config.yaml", "r") as f: + config = yaml.safe_load(f) + + + +PR_NUMBER = str(config["project"]["pr_number"]) +OWNER = config["project"]["owner"] +REPO = config["project"]["repo"] +# GitHub API to fetch the PR diff +url = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{PR_NUMBER}" +diff_url = f"{url}.diff" +# Fetch the diff from GitHub API +print(f"๐ฅ Fetching PR diff for PR #{PR_NUMBER} from GitHub...") +response = requests.get(diff_url, headers={"Accept": "application/vnd.github.v3.diff"}) +if response.status_code != 200: + print(f"โ Failed to fetch PR diff or no changes found. Status Code: {response.status_code}") + sys.exit(1) +# Get the diff content (only .cpp and .h files) +diff_text = response.text +if not diff_text.strip(): + print("โ No changes in the PR.") + sys.exit(0) +# Get the list of modified .cpp and .h files in the PR +pr_files = [line.split(" ")[1][2:] for line in diff_text.splitlines() if line.startswith("+++")] +pr_files = [file for file in pr_files if file.endswith(".cpp") or file.endswith(".h")] +if not pr_files: + print("โ No relevant .cpp or .h files to check in PR #$PR_NUMBER.") + sys.exit(0) +# Initialize a list to store missing documentation info +missing_docs = [] +# Process each file in the diff +for file in pr_files: + # Check if the file is a .cpp or .h file + if file.endswith(".cpp") or file.endswith(".h"): + # Get the diff for the modified file + file_diff = "\n".join( + [line[1:] for line in diff_text.splitlines() if line.startswith(('+', '-')) and line[2:].startswith(file)] + ) + # Loop through each modified line in the file + for line in file_diff.splitlines(): + # Check if the line creates a class (i.e., contains "class ") + if "class " in line: + # Check the previous line to see if it has Doxygen documentation + prev_line = None + lines = file_diff.splitlines() + idx = lines.index(line) + if idx > 0: + prev_line = lines[idx - 1] + # If the previous line is not a Doxygen comment, it's missing documentation + if prev_line and not prev_line.strip().startswith("/**"): + missing_docs.append((file, line)) + print(f"The following class is missing documentation: {file}") + print(f"Before: {prev_line}") + print(f"After: {line}") + print("Action: Please add a Doxygen comment above this class explaining its purpose and functionality.") + print("Example:") + print(" /**") + print(" * @brief Class description: What this class does.") + print(" * @details More detailed explanation if needed.") + print(" */") + print() +# If missing documentation was found, exit with status 1 +if missing_docs: + sys.exit(1) +else: + print("All modified classes are properly documented.") +print("LLVM CLASS CHECK COMPLETE") diff --git a/remote_header_check.py b/remote_header_check.py new file mode 100644 index 0000000000000..a2ed743a98d39 --- /dev/null +++ b/remote_header_check.py @@ -0,0 +1,69 @@ + +import sys +import subprocess +import re +import yaml +import requests +# === Load config.yaml === +with open("config.yaml", "r") as f: + config = yaml.safe_load(f) +# === Configuration === +PR_NUMBER = str(config["project"]["pr_number"]) +OWNER = config["project"]["owner"] +REPO = config["project"]["repo"] +LLVM_HEADER_TEMPLATE = "//===----------------------------------------------------------------------===//" +LLVM_LICENSE = "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception" +FILE_EXTENSIONS = (".cpp", ".h") +# === GitHub PR Diff URL === +DIFF_URL = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{PR_NUMBER}.diff" +# === Fetch PR Diff === +print(f"๐ฅ Fetching PR diff for PR #{PR_NUMBER}...") +response = requests.get(DIFF_URL, headers={"Accept": "application/vnd.github.v3.diff"}) +if response.status_code != 200: + print(f"โ Failed to fetch PR diff. Status: {response.status_code}") + sys.exit(1) +diff_text = response.text +if not diff_text.strip(): + print("โ No changes in the PR.") + sys.exit(0) +# === Extract Modified .cpp/.h Files === +pr_files = [ + line.split(" ")[1][2:] for line in diff_text.splitlines() + if line.startswith("+++ b/") and line.endswith(FILE_EXTENSIONS) +] +if not pr_files: + print("โ No .cpp or .h files modified in this PR.") + sys.exit(0) +print("\n๐ Checking headers in the following modified files:") +for file in pr_files: + print(" โข", file) +# === Check Each File for LLVM Header in Modified Lines === +missing_header_files = [] +for file in pr_files: + raw_url = f"https://raw.githubusercontent.com/{OWNER}/{REPO}/pull/{PR_NUMBER}/head/{file}" + file_response = requests.get(raw_url) + if file_response.status_code != 200: + continue # Skip without printing a message if file is not found in PR head + content = file_response.text + # Extract modified lines from the diff + modified_lines = [ + line[1:] for line in diff_text.splitlines() if line.startswith("+") and line[1:] not in content + ] + + # Check only the modified lines for the header + header_found = any( + LLVM_HEADER_TEMPLATE in line or LLVM_LICENSE in line for line in modified_lines + ) + if not header_found: + print(f"\nโ Missing or incorrect LLVM-style header in the modified lines of: {file}") + print("Expected header must include:") + print(f" {LLVM_HEADER_TEMPLATE}") + print(f" {LLVM_LICENSE}") + missing_header_files.append(file) +# === Final Report === +if missing_header_files: + print(f"\nโ {len(missing_header_files)} file(s) missing proper LLVM-style headers in modified lines.") + sys.exit(1) +else: + print("\nโ All modified files contain correct LLVM-style headers in modified lines!") + sys.exit(0) diff --git a/remote_naming_conventions.py b/remote_naming_conventions.py new file mode 100644 index 0000000000000..280bc8cd306cf --- /dev/null +++ b/remote_naming_conventions.py @@ -0,0 +1,145 @@ + +import sys +import requests +import re +import yaml +# === Load Configuration === +with open("config.yaml", "r") as f: + config = yaml.safe_load(f) +PR_NUMBER = str(config["project"]["pr_number"]) +OWNER = config["project"]["owner"] +REPO = config["project"]["repo"] +EXTENSIONS = (".cpp", ".h") +DIFF_URL = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{PR_NUMBER}.diff" +# === Fetch PR Diff === +print(f"๐ฅ Fetching PR diff for PR #{PR_NUMBER}...") +response = requests.get(DIFF_URL, headers={"Accept": "application/vnd.github.v3.diff"}) +if response.status_code != 200: + print(f"โ Failed to fetch PR diff. Status: {response.status_code}") + sys.exit(1) +diff_text = response.text +if not diff_text.strip(): + print("โ No changes in the PR.") + sys.exit(0) +# === Regex Patterns === +class_pattern = re.compile(r"\bclass\s+([a-z]\w*)") +var_pattern = re.compile(r"\b(?:int|float|double|char|bool)\s+([A-Z]\w*)") +func_pattern = re.compile(r"\bvoid\s+([A-Z]\w*)\s*\(") +enum_pattern = re.compile(r"\benum\s+([a-z]\w*)") +enum_kind_pattern = re.compile(r"\benum\s+(?!.*Kind\b)(\w+)\b") +# === Exempted Names === +EXEMPT_NAMES = {'RecursiveASTVisitor'} # Add any class or function names that you want to exempt +violations = [] +current_file = None +line_number = 0 +# === Process Diff === +for line in diff_text.splitlines(): + if line.startswith("+++ b/") and line.endswith(EXTENSIONS): + current_file = line[6:] + line_number = 0 + continue + if not current_file: + continue + if line.startswith("@@"): + match = re.search(r"\+(\d+)", line) + if match: + line_number = int(match.group(1)) - 1 + continue + if line.startswith("+") and not line.startswith("+++"): + line_number += 1 + code_line = line[1:] + if (m := class_pattern.search(code_line)): + class_name = m.group(1) + # Skip if the class is in the exempted list + if class_name not in EXEMPT_NAMES: + violations.append((current_file, line_number, code_line, f"Class '{class_name}' should start with an uppercase letter.")) + if (m := var_pattern.search(code_line)): + var_name = m.group(1) + # Skip if the variable is in the exempted list + if var_name not in EXEMPT_NAMES: + violations.append((current_file, line_number, code_line, f"Variable '{var_name}' should start with a lowercase letter in camelCase.")) + if (m := func_pattern.search(code_line)): + func_name = m.group(1) + # Skip if the function is inimport sys +import requests +import re +import yaml +# === Load Configuration === +with open("config.yaml", "r") as f: + config = yaml.safe_load(f) +PR_NUMBER = str(config["project"]["pr_number"]) +OWNER = config["project"]["owner"] +REPO = config["project"]["repo"] +EXTENSIONS = (".cpp", ".h") +DIFF_URL = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{PR_NUMBER}.diff" +# === Fetch PR Diff === +print(f"๐ฅ Fetching PR diff for PR #{PR_NUMBER}...") +response = requests.get(DIFF_URL, headers={"Accept": "application/vnd.github.v3.diff"}) +if response.status_code != 200: + print(f"โ Failed to fetch PR diff. Status: {response.status_code}") + sys.exit(1) +diff_text = response.text +if not diff_text.strip(): + print("โ No changes in the PR.") + sys.exit(0) +# === Regex Patterns === +class_pattern = re.compile(r"\bclass\s+([a-z]\w*)") +var_pattern = re.compile(r"\b(?:int|float|double|char|bool)\s+([A-Z]\w*)") +func_pattern = re.compile(r"\bvoid\s+([A-Z]\w*)\s*\(") +enum_pattern = re.compile(r"\benum\s+([a-z]\w*)") +enum_kind_pattern = re.compile(r"\benum\s+(?!.*Kind\b)(\w+)\b") +# === Exempted Names === +EXEMPT_NAMES = {'RecursiveASTVisitor'} # Add any class or function names that you want to exempt +violations = [] +current_file = None +line_number = 0 +# === Process Diff === +for line in diff_text.splitlines(): + if line.startswith("+++ b/") and line.endswith(EXTENSIONS): + current_file = line[6:] + line_number = 0 + continue + if not current_file: + continue + if line.startswith("@@"): + match = re.search(r"\+(\d+)", line) + if match: + line_number = int(match.group(1)) - 1 + continue + if line.startswith("+") and not line.startswith("+++"): + line_number += 1 + code_line = line[1:] + if (m := class_pattern.search(code_line)): + class_name = m.group(1) + # Skip if the class is in the exempted list + if class_name not in EXEMPT_NAMES: + violations.append((current_file, line_number, code_line, f"Class '{class_name}' should start with an uppercase letter.")) + if (m := var_pattern.search(code_line)): + var_name = m.group(1) + # Skip if the variable is in the exempted list + if var_name not in EXEMPT_NAMES: + violations.append((current_file, line_number, code_line, f"Variable '{var_name}' should start with a lowercase letter in camelCase.")) + if (m := func_pattern.search(code_line)): + func_name = m.group(1) + # Skip if the function is in the exempted list + if func_name not in EXEMPT_NAMES: + violations.append((current_file, line_number, code_line, f"Function '{func_name}' should start with a lowercase letter in camelCase.")) + if (m := enum_pattern.search(code_line)): + enum_name = m.group(1) + violations.append((current_file, line_number, code_line, f"Enum '{enum_name}' should start with an uppercase letter.")) + if (m := enum_kind_pattern.search(code_line)): + enum_kind_name = m.group(1) + violations.append((current_file, line_number, code_line, f"Enum type '{enum_kind_name}' should end with 'Kind' if used as a discriminator.")) + elif line.startswith("-") or line.startswith(" "): + line_number += 1 +# === Report Violations === +if violations: + print("\nโ Naming convention violations found:\n") + for file, line, code, message in violations: + print(f"๐ธ File: {file}, Line: {line}") + print(f"๐น Code: {code.strip()}") + print(f"โ ๏ธ {message}\n") + sys.exit(1) +else: + print("\nโ All modified lines follow naming conventions.") + sys.exit(0) >From fadfa46f1db9c7d569f74759ff327137b969a19e Mon Sep 17 00:00:00 2001 From: blazie2004 <jayvc1...@gmail.com> Date: Sun, 4 May 2025 15:17:09 -0500 Subject: [PATCH 3/3] added changes in clang/lib/ast/aststructequi.cpp --- clang/lib/AST/ASTStructuralEquivalence.cpp | 29 +++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 499854a75abc6..a155385ef9017 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -250,23 +250,22 @@ class StmtComparer { return E1->isExact() == E2->isExact() && E1->getValue() == E2->getValue(); } - bool IsStmtEquivalent(const GenericSelectionExpr *E1, - const GenericSelectionExpr *E2) { - for (auto Pair : zip_longest(E1->getAssocTypeSourceInfos(), - E2->getAssocTypeSourceInfos())) { - std::optional<TypeSourceInfo *> Child1 = std::get<0>(Pair); - std::optional<TypeSourceInfo *> Child2 = std::get<1>(Pair); - // Skip this case if there are a different number of associated types. - if (!Child1 || !Child2) - return false; + bool IsStmtEquivalent ( const GenericSelectionExpr *E1, + const GenericSelectionExpr *E2 ) { +for ( auto Pair : zip_longest( E1->getAssocTypeSourceInfos(), + E2->getAssocTypeSourceInfos() ) ) { +std::optional<TypeSourceInfo *> Child1 = std::get<0>( Pair ); +std::optional<TypeSourceInfo *> Child2 = std::get<1>( Pair ); +// Skip this case if there are a different number of associated types. +if ( !Child1 || !Child2 ) return false; + +if ( !IsStructurallyEquivalent( Context, ( *Child1 )->getType(), + ( *Child2 )->getType() ) ) return false; +} - if (!IsStructurallyEquivalent(Context, (*Child1)->getType(), - (*Child2)->getType())) - return false; - } +return true; +} - return true; - } bool IsStmtEquivalent(const ImplicitCastExpr *CastE1, const ImplicitCastExpr *CastE2) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits