diff --git a/.ci/compute_projects.py b/.ci/compute_projects.py
index 2dc5629328457..8567552fa25a6 100644
--- a/.ci/compute_projects.py
+++ b/.ci/compute_projects.py
@@ -49,8 +49,7 @@
"flang",
},
"lld": {"bolt", "cross-project-tests"},
- # TODO(issues/132795): LLDB should be enabled on clang changes.
- "clang": {"clang-tools-extra", "cross-project-tests"},
+ "clang": {"clang-tools-extra", "cross-project-tests", "lldb"},
"mlir": {"flang"},
# Test everything if ci scripts are changed.
".ci": {
@@ -80,6 +79,7 @@
"clang": {"compiler-rt"},
"clang-tools-extra": {"libc"},
"libc": {"libc"},
+ "compiler-rt": {"compiler-rt"},
".ci": {"compiler-rt", "libc"},
}
DEPENDENT_RUNTIMES_TO_TEST_NEEDS_RECONFIG = {
@@ -100,6 +100,9 @@
"libc", # No Windows Support.
"lldb", # TODO(issues/132800): Needs environment setup.
"bolt", # No Windows Support.
+ "libcxx",
+ "libcxxabi",
+ "libunwind",
}
# These are projects that we should test if the project itself is changed but
@@ -118,6 +121,9 @@
"lldb",
"openmp",
"polly",
+ "libcxx",
+ "libcxxabi",
+ "libunwind",
}
PROJECT_CHECK_TARGETS = {
diff --git a/.ci/compute_projects_test.py b/.ci/compute_projects_test.py
index bd761198300a8..7d780b51ca5d1 100644
--- a/.ci/compute_projects_test.py
+++ b/.ci/compute_projects_test.py
@@ -45,16 +45,14 @@ def test_llvm_windows(self):
env_variables["project_check_targets"],
"check-clang check-clang-tools check-lld check-llvm check-mlir check-polly",
)
- self.assertEqual(
- env_variables["runtimes_to_build"], "libcxx;libcxxabi;libunwind"
- )
+ self.assertEqual(env_variables["runtimes_to_build"], "")
self.assertEqual(
env_variables["runtimes_check_targets"],
"",
)
self.assertEqual(
env_variables["runtimes_check_targets_needs_reconfig"],
- "check-cxx check-cxxabi check-unwind",
+ "",
)
def test_llvm_mac(self):
@@ -69,16 +67,14 @@ def test_llvm_mac(self):
env_variables["project_check_targets"],
"check-clang check-clang-tools check-lld check-llvm check-mlir",
)
- self.assertEqual(
- env_variables["runtimes_to_build"], "libcxx;libcxxabi;libunwind"
- )
+ self.assertEqual(env_variables["runtimes_to_build"], "")
self.assertEqual(
env_variables["runtimes_check_targets"],
"",
)
self.assertEqual(
env_variables["runtimes_check_targets_needs_reconfig"],
- "check-cxx check-cxxabi check-unwind",
+ "",
)
def test_clang(self):
@@ -87,11 +83,11 @@ def test_clang(self):
)
self.assertEqual(
env_variables["projects_to_build"],
- "clang;clang-tools-extra;lld;llvm",
+ "clang;clang-tools-extra;lld;lldb;llvm",
)
self.assertEqual(
env_variables["project_check_targets"],
- "check-clang check-clang-tools",
+ "check-clang check-clang-tools check-lldb",
)
self.assertEqual(
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
@@ -119,30 +115,54 @@ def test_clang_windows(self):
self.assertEqual(
env_variables["project_check_targets"], "check-clang check-clang-tools"
)
- self.assertEqual(
- env_variables["runtimes_to_build"], "libcxx;libcxxabi;libunwind"
- )
+ self.assertEqual(env_variables["runtimes_to_build"], "")
self.assertEqual(
env_variables["runtimes_check_targets"],
"",
)
self.assertEqual(
env_variables["runtimes_check_targets_needs_reconfig"],
- "check-cxx check-cxxabi check-unwind",
+ "",
)
self.assertEqual(env_variables["enable_cir"], "OFF")
+ def test_compiler_rt(self):
+ env_variables = compute_projects.get_env_variables(
+ ["compiler-rt/lib/asan/asan_allocator.cpp"], "Linux"
+ )
+ self.assertEqual(
+ env_variables["projects_to_build"],
+ "clang;lld",
+ )
+ self.assertEqual(
+ env_variables["project_check_targets"],
+ "",
+ )
+ self.assertEqual(env_variables["runtimes_to_build"], "compiler-rt")
+ self.assertEqual(
+ env_variables["runtimes_check_targets"],
+ "check-compiler-rt",
+ )
+ self.assertEqual(
+ env_variables["runtimes_check_targets_needs_reconfig"],
+ "",
+ )
+ self.assertEqual(
+ env_variables["enable_cir"],
+ "OFF",
+ )
+
def test_cir(self):
env_variables = compute_projects.get_env_variables(
["clang/lib/CIR/CMakeLists.txt"], "Linux"
)
self.assertEqual(
env_variables["projects_to_build"],
- "clang;clang-tools-extra;lld;llvm;mlir",
+ "clang;clang-tools-extra;lld;lldb;llvm;mlir",
)
self.assertEqual(
env_variables["project_check_targets"],
- "check-clang check-clang-cir check-clang-tools",
+ "check-clang check-clang-cir check-clang-tools check-lldb",
)
self.assertEqual(
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
@@ -298,18 +318,15 @@ def test_windows_ci(self):
)
self.assertEqual(
env_variables["runtimes_to_build"],
- "libcxx;libcxxabi;libunwind",
+ "",
)
self.assertEqual(
env_variables["runtimes_check_targets"],
"",
)
- # TODO(boomanaiden154): We should not be emitting these on Windows.
- # It does not currently impact anything because we do not build the
- # runtimes on Windows though.
self.assertEqual(
env_variables["runtimes_check_targets_needs_reconfig"],
- "check-cxx check-cxxabi check-unwind",
+ "",
)
def test_lldb(self):
diff --git a/.ci/generate_test_report_github.py b/.ci/generate_test_report_github.py
index be43c4b4f693f..7242264723cbf 100644
--- a/.ci/generate_test_report_github.py
+++ b/.ci/generate_test_report_github.py
@@ -4,20 +4,25 @@
"""Script to generate a build report for Github."""
import argparse
+import platform
import generate_test_report_lib
+PLATFORM_TITLES = {
+ "Windows": ":window: Windows x64 Test Results",
+ "Linux": ":penguin: Linux x64 Test Results",
+}
+
if __name__ == "__main__":
parser = argparse.ArgumentParser()
+ parser.add_argument("return_code", help="The build's return code.", type=int)
parser.add_argument(
- "title", help="Title of the test report, without Markdown formatting."
+ "build_test_logs", help="Paths to JUnit report files and ninja logs.", nargs="*"
)
- parser.add_argument("return_code", help="The build's return code.", type=int)
- parser.add_argument("junit_files", help="Paths to JUnit report files.", nargs="*")
args = parser.parse_args()
report = generate_test_report_lib.generate_report_from_files(
- args.title, args.return_code, args.junit_files
+ PLATFORM_TITLES[platform.system()], args.return_code, args.build_test_logs
)
print(report)
diff --git a/.ci/generate_test_report_lib.py b/.ci/generate_test_report_lib.py
index 25d810f1c6d17..d868c08ab69ef 100644
--- a/.ci/generate_test_report_lib.py
+++ b/.ci/generate_test_report_lib.py
@@ -12,6 +12,84 @@
"https://github.com/llvm/llvm-project/issues and add the "
"`infrastructure` label."
)
+# The maximum number of lines to pull from a ninja failure.
+NINJA_LOG_SIZE_THRESHOLD = 500
+
+
+def _parse_ninja_log(ninja_log: list[str]) -> list[tuple[str, str]]:
+ """Parses an individual ninja log."""
+ failures = []
+ index = 0
+ while index < len(ninja_log):
+ while index < len(ninja_log) and not ninja_log[index].startswith("FAILED:"):
+ index += 1
+ if index == len(ninja_log):
+ # We hit the end of the log without finding a build failure, go to
+ # the next log.
+ return failures
+ # We are trying to parse cases like the following:
+ #
+ # [4/5] test/4.stamp
+ # FAILED: touch test/4.stamp
+ # touch test/4.stamp
+ #
+ # index will point to the line that starts with Failed:. The progress
+ # indicator is the line before this ([4/5] test/4.stamp) and contains a pretty
+ # printed version of the target being built (test/4.stamp). We use this line
+ # and remove the progress information to get a succinct name for the target.
+ failing_action = ninja_log[index - 1].split("] ")[1]
+ failure_log = []
+ while (
+ index < len(ninja_log)
+ and not ninja_log[index].startswith("[")
+ and not ninja_log[index].startswith("ninja: build stopped:")
+ and len(failure_log) < NINJA_LOG_SIZE_THRESHOLD
+ ):
+ failure_log.append(ninja_log[index])
+ index += 1
+ failures.append((failing_action, "\n".join(failure_log)))
+ return failures
+
+
+def find_failure_in_ninja_logs(ninja_logs: list[list[str]]) -> list[tuple[str, str]]:
+ """Extracts failure messages from ninja output.
+
+ This function takes stdout/stderr from ninja in the form of a list of files
+ represented as a list of lines. This function then returns tuples containing
+ the name of the target and the error message.
+
+ Args:
+ ninja_logs: A list of files in the form of a list of lines representing the log
+ files captured from ninja.
+
+ Returns:
+ A list of tuples. The first string is the name of the target that failed. The
+ second string is the error message.
+ """
+ failures = []
+ for ninja_log in ninja_logs:
+ log_failures = _parse_ninja_log(ninja_log)
+ failures.extend(log_failures)
+ return failures
+
+
+def _format_ninja_failures(ninja_failures: list[tuple[str, str]]) -> list[str]:
+ """Formats ninja failures into summary views for the report."""
+ output = []
+ for build_failure in ninja_failures:
+ failed_action, failure_message = build_failure
+ output.extend(
+ [
+ "",
+ f"{failed_action}
",
+ "",
+ "```",
+ failure_message,
+ "```",
+ " ",
+ ]
+ )
+ return output
# Set size_limit to limit the byte size of the report. The default is 1MB as this
@@ -24,6 +102,7 @@ def generate_report(
title,
return_code,
junit_objects,
+ ninja_logs: list[list[str]],
size_limit=1024 * 1024,
list_failures=True,
):
@@ -61,15 +140,34 @@ def generate_report(
]
)
else:
- report.extend(
- [
- "The build failed before running any tests.",
- "",
- SEE_BUILD_FILE_STR,
- "",
- UNRELATED_FAILURES_STR,
- ]
- )
+ ninja_failures = find_failure_in_ninja_logs(ninja_logs)
+ if not ninja_failures:
+ report.extend(
+ [
+ "The build failed before running any tests. Detailed "
+ "information about the build failure could not be "
+ "automatically obtained.",
+ "",
+ SEE_BUILD_FILE_STR,
+ "",
+ UNRELATED_FAILURES_STR,
+ ]
+ )
+ else:
+ report.extend(
+ [
+ "The build failed before running any tests. Click on a "
+ "failure below to see the details.",
+ "",
+ ]
+ )
+ report.extend(_format_ninja_failures(ninja_failures))
+ report.extend(
+ [
+ "",
+ UNRELATED_FAILURES_STR,
+ ]
+ )
return "\n".join(report)
tests_passed = tests_run - tests_skipped - tests_failed
@@ -114,14 +212,28 @@ def plural(num_tests):
elif return_code != 0:
# No tests failed but the build was in a failed state. Bring this to the user's
# attention.
- report.extend(
- [
- "",
- "All tests passed but another part of the build **failed**.",
- "",
- SEE_BUILD_FILE_STR,
- ]
- )
+ ninja_failures = find_failure_in_ninja_logs(ninja_logs)
+ if not ninja_failures:
+ report.extend(
+ [
+ "",
+ "All tests passed but another part of the build **failed**. "
+ "Information about the build failure could not be automatically "
+ "obtained.",
+ "",
+ SEE_BUILD_FILE_STR,
+ ]
+ )
+ else:
+ report.extend(
+ [
+ "",
+ "All tests passed but another part of the build **failed**. Click on "
+ "a failure below to see the details.",
+ "",
+ ]
+ )
+ report.extend(_format_ninja_failures(ninja_failures))
if failures or return_code != 0:
report.extend(["", UNRELATED_FAILURES_STR])
@@ -139,9 +251,19 @@ def plural(num_tests):
return report
-def generate_report_from_files(title, return_code, junit_files):
+def generate_report_from_files(title, return_code, build_log_files):
+ junit_files = [
+ junit_file for junit_file in build_log_files if junit_file.endswith(".xml")
+ ]
+ ninja_log_files = [
+ ninja_log for ninja_log in build_log_files if ninja_log.endswith(".log")
+ ]
+ ninja_logs = []
+ for ninja_log_file in ninja_log_files:
+ with open(ninja_log_file, "r") as ninja_log_file_handle:
+ ninja_logs.append(
+ [log_line.strip() for log_line in ninja_log_file_handle.readlines()]
+ )
return generate_report(
- title,
- return_code,
- [JUnitXml.fromfile(p) for p in junit_files],
+ title, return_code, [JUnitXml.fromfile(p) for p in junit_files], ninja_logs
)
diff --git a/.ci/generate_test_report_lib_test.py b/.ci/generate_test_report_lib_test.py
index eda76ead19b9d..466a8234776dc 100644
--- a/.ci/generate_test_report_lib_test.py
+++ b/.ci/generate_test_report_lib_test.py
@@ -8,6 +8,8 @@
import unittest
from io import StringIO
from textwrap import dedent
+import tempfile
+import os
from junitparser import JUnitXml
@@ -19,9 +21,114 @@ def junit_from_xml(xml):
class TestReports(unittest.TestCase):
+ def test_find_failure_ninja_logs(self):
+ failures = generate_test_report_lib.find_failure_in_ninja_logs(
+ [
+ [
+ "[1/5] test/1.stamp",
+ "[2/5] test/2.stamp",
+ "[3/5] test/3.stamp",
+ "[4/5] test/4.stamp",
+ "FAILED: touch test/4.stamp",
+ "Wow! This system is really broken!",
+ "[5/5] test/5.stamp",
+ ],
+ ]
+ )
+ self.assertEqual(len(failures), 1)
+ self.assertEqual(
+ failures[0],
+ (
+ "test/4.stamp",
+ dedent(
+ """\
+ FAILED: touch test/4.stamp
+ Wow! This system is really broken!"""
+ ),
+ ),
+ )
+
+ def test_no_failure_ninja_log(self):
+ failures = generate_test_report_lib.find_failure_in_ninja_logs(
+ [
+ [
+ "[1/3] test/1.stamp",
+ "[2/3] test/2.stamp",
+ "[3/3] test/3.stamp",
+ ]
+ ]
+ )
+ self.assertEqual(failures, [])
+
+ def test_ninja_log_end(self):
+ failures = generate_test_report_lib.find_failure_in_ninja_logs(
+ [
+ [
+ "[1/3] test/1.stamp",
+ "[2/3] test/2.stamp",
+ "[3/3] test/3.stamp",
+ "FAILED: touch test/3.stamp",
+ "Wow! This system is really broken!",
+ "ninja: build stopped: subcommand failed.",
+ ]
+ ]
+ )
+ self.assertEqual(len(failures), 1)
+ self.assertEqual(
+ failures[0],
+ (
+ "test/3.stamp",
+ dedent(
+ """\
+ FAILED: touch test/3.stamp
+ Wow! This system is really broken!"""
+ ),
+ ),
+ )
+
+ def test_ninja_log_multiple_failures(self):
+ failures = generate_test_report_lib.find_failure_in_ninja_logs(
+ [
+ [
+ "[1/5] test/1.stamp",
+ "[2/5] test/2.stamp",
+ "FAILED: touch test/2.stamp",
+ "Wow! This system is really broken!",
+ "[3/5] test/3.stamp",
+ "[4/5] test/4.stamp",
+ "FAILED: touch test/4.stamp",
+ "Wow! This system is maybe broken!",
+ "[5/5] test/5.stamp",
+ ]
+ ]
+ )
+ self.assertEqual(len(failures), 2)
+ self.assertEqual(
+ failures[0],
+ (
+ "test/2.stamp",
+ dedent(
+ """\
+ FAILED: touch test/2.stamp
+ Wow! This system is really broken!"""
+ ),
+ ),
+ )
+ self.assertEqual(
+ failures[1],
+ (
+ "test/4.stamp",
+ dedent(
+ """\
+ FAILED: touch test/4.stamp
+ Wow! This system is maybe broken!"""
+ ),
+ ),
+ )
+
def test_title_only(self):
self.assertEqual(
- generate_test_report_lib.generate_report("Foo", 0, []),
+ generate_test_report_lib.generate_report("Foo", 0, [], []),
dedent(
"""\
# Foo
@@ -32,12 +139,12 @@ def test_title_only(self):
def test_title_only_failure(self):
self.assertEqual(
- generate_test_report_lib.generate_report("Foo", 1, []),
+ generate_test_report_lib.generate_report("Foo", 1, [], []),
dedent(
"""\
# Foo
- The build failed before running any tests.
+ The build failed before running any tests. Detailed information about the build failure could not be automatically obtained.
Download the build's log file to see the details.
@@ -45,6 +152,45 @@ def test_title_only_failure(self):
),
)
+ def test_title_only_failure_ninja_log(self):
+ self.assertEqual(
+ generate_test_report_lib.generate_report(
+ "Foo",
+ 1,
+ [],
+ [
+ [
+ "[1/5] test/1.stamp",
+ "[2/5] test/2.stamp",
+ "[3/5] test/3.stamp",
+ "[4/5] test/4.stamp",
+ "FAILED: test/4.stamp",
+ "touch test/4.stamp",
+ "Wow! Risk!",
+ "[5/5] test/5.stamp",
+ ]
+ ],
+ ),
+ dedent(
+ """\
+ # Foo
+
+ The build failed before running any tests. Click on a failure below to see the details.
+
+
+ test/4.stamp
+
+ ```
+ FAILED: test/4.stamp
+ touch test/4.stamp
+ Wow! Risk!
+ ```
+
+
+ If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the `infrastructure` label."""
+ ),
+ )
+
def test_no_tests_in_testsuite(self):
self.assertEqual(
generate_test_report_lib.generate_report(
@@ -62,12 +208,13 @@ def test_no_tests_in_testsuite(self):
)
)
],
+ [],
),
dedent(
"""\
# Foo
- The build failed before running any tests.
+ The build failed before running any tests. Detailed information about the build failure could not be automatically obtained.
Download the build's log file to see the details.
@@ -93,6 +240,7 @@ def test_no_failures(self):
)
)
],
+ [],
),
(
dedent(
@@ -122,6 +270,7 @@ def test_no_failures_build_failed(self):
)
)
],
+ [],
),
(
dedent(
@@ -130,7 +279,7 @@ def test_no_failures_build_failed(self):
* 1 test passed
- All tests passed but another part of the build **failed**.
+ All tests passed but another part of the build **failed**. Information about the build failure could not be automatically obtained.
Download the build's log file to see the details.
@@ -139,6 +288,155 @@ def test_no_failures_build_failed(self):
),
)
+ def test_no_failures_build_failed_ninja_log(self):
+ self.assertEqual(
+ generate_test_report_lib.generate_report(
+ "Foo",
+ 1,
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ [
+ [
+ "[1/5] test/1.stamp",
+ "[2/5] test/2.stamp",
+ "[3/5] test/3.stamp",
+ "[4/5] test/4.stamp",
+ "FAILED: test/4.stamp",
+ "touch test/4.stamp",
+ "Wow! Close To You!",
+ "[5/5] test/5.stamp",
+ ]
+ ],
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test passed
+
+ All tests passed but another part of the build **failed**. Click on a failure below to see the details.
+
+
+ test/4.stamp
+
+ ```
+ FAILED: test/4.stamp
+ touch test/4.stamp
+ Wow! Close To You!
+ ```
+
+
+ If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the `infrastructure` label."""
+ )
+ ),
+ )
+
+ def test_no_failures_multiple_build_failed_ninja_log(self):
+ test = generate_test_report_lib.generate_report(
+ "Foo",
+ 1,
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ [
+ [
+ "[1/5] test/1.stamp",
+ "[2/5] test/2.stamp",
+ "FAILED: touch test/2.stamp",
+ "Wow! Be Kind!",
+ "[3/5] test/3.stamp",
+ "[4/5] test/4.stamp",
+ "FAILED: touch test/4.stamp",
+ "Wow! I Dare You!",
+ "[5/5] test/5.stamp",
+ ]
+ ],
+ )
+ print(test)
+ self.assertEqual(
+ generate_test_report_lib.generate_report(
+ "Foo",
+ 1,
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ [
+ [
+ "[1/5] test/1.stamp",
+ "[2/5] test/2.stamp",
+ "FAILED: touch test/2.stamp",
+ "Wow! Be Kind!",
+ "[3/5] test/3.stamp",
+ "[4/5] test/4.stamp",
+ "FAILED: touch test/4.stamp",
+ "Wow! I Dare You!",
+ "[5/5] test/5.stamp",
+ ]
+ ],
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test passed
+
+ All tests passed but another part of the build **failed**. Click on a failure below to see the details.
+
+
+ test/2.stamp
+
+ ```
+ FAILED: touch test/2.stamp
+ Wow! Be Kind!
+ ```
+
+
+ test/4.stamp
+
+ ```
+ FAILED: touch test/4.stamp
+ Wow! I Dare You!
+ ```
+
+
+ If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the `infrastructure` label."""
+ )
+ ),
+ )
+
def test_report_single_file_single_testsuite(self):
self.assertEqual(
generate_test_report_lib.generate_report(
@@ -166,6 +464,7 @@ def test_report_single_file_single_testsuite(self):
)
)
],
+ [],
),
(
dedent(
@@ -261,6 +560,7 @@ def test_report_single_file_multiple_testsuites(self):
)
)
],
+ [],
),
self.MULTI_SUITE_OUTPUT,
)
@@ -302,6 +602,7 @@ def test_report_multiple_files_multiple_testsuites(self):
)
),
],
+ [],
),
self.MULTI_SUITE_OUTPUT,
)
@@ -326,6 +627,7 @@ def test_report_dont_list_failures(self):
)
)
],
+ [],
list_failures=False,
),
(
@@ -362,6 +664,7 @@ def test_report_dont_list_failures_link_to_log(self):
)
)
],
+ [],
list_failures=False,
),
(
@@ -401,6 +704,7 @@ def test_report_size_limit(self):
)
)
],
+ [],
size_limit=512,
),
(
@@ -416,3 +720,59 @@ def test_report_size_limit(self):
)
),
)
+
+ def test_generate_report_end_to_end(self):
+ with tempfile.TemporaryDirectory() as temp_dir:
+ junit_xml_file = os.path.join(temp_dir, "junit.xml")
+ with open(junit_xml_file, "w") as junit_xml_handle:
+ junit_xml_handle.write(
+ dedent(
+ """\
+
+
+
+
+
+ """
+ )
+ )
+ ninja_log_file = os.path.join(temp_dir, "ninja.log")
+ with open(ninja_log_file, "w") as ninja_log_handle:
+ ninja_log_handle.write(
+ dedent(
+ """\
+ [1/5] test/1.stamp
+ [2/5] test/2.stamp
+ [3/5] test/3.stamp
+ [4/5] test/4.stamp
+ FAILED: test/4.stamp
+ touch test/4.stamp
+ Wow! That's so True!
+ [5/5] test/5.stamp"""
+ )
+ )
+ self.assertEqual(
+ generate_test_report_lib.generate_report_from_files(
+ "Foo", 1, [junit_xml_file, ninja_log_file]
+ ),
+ dedent(
+ """\
+ # Foo
+
+ * 1 test passed
+
+ All tests passed but another part of the build **failed**. Click on a failure below to see the details.
+
+
+ test/4.stamp
+
+ ```
+ FAILED: test/4.stamp
+ touch test/4.stamp
+ Wow! That's so True!
+ ```
+
+
+ If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the `infrastructure` label."""
+ ),
+ )
diff --git a/.ci/metrics/metrics.py b/.ci/metrics/metrics.py
index 26fdeef1913ab..0896b193b6b03 100644
--- a/.ci/metrics/metrics.py
+++ b/.ci/metrics/metrics.py
@@ -28,7 +28,10 @@
# Lists the Github workflows we want to track. Maps the Github job name to
# the metric name prefix in grafana.
# This metric name is also used as a key in the job->name map.
-GITHUB_WORKFLOW_TO_TRACK = {"CI Checks": "github_llvm_premerge_checks"}
+GITHUB_WORKFLOW_TO_TRACK = {
+ "CI Checks": "github_llvm_premerge_checks",
+ "Build and Test libc++": "github_libcxx_premerge_checks",
+}
# Lists the Github jobs to track for a given workflow. The key is the stable
# name (metric name) of the workflow (see GITHUB_WORKFLOW_TO_TRACK).
@@ -38,7 +41,12 @@
"github_llvm_premerge_checks": {
"Build and Test Linux": "premerge_linux",
"Build and Test Windows": "premerge_windows",
- }
+ },
+ "github_libcxx_premerge_checks": {
+ "stage1": "premerge_libcxx_stage1",
+ "stage2": "premerge_libcxx_stage2",
+ "stage3": "premerge_libcxx_stage3",
+ },
}
# The number of workflows to pull when sampling Github workflows.
@@ -62,13 +70,14 @@
# by trial and error).
GRAFANA_METRIC_MAX_AGE_MN = 120
-
@dataclass
class JobMetrics:
job_name: str
queue_time: int
run_time: int
status: int
+ created_at_ns: int
+ started_at_ns: int
completed_at_ns: int
workflow_id: int
workflow_name: str
@@ -81,6 +90,159 @@ class GaugeMetric:
time_ns: int
+@dataclass
+class AggregateMetric:
+ aggregate_name: str
+ aggregate_queue_time: int
+ aggregate_run_time: int
+ aggregate_status: int
+ completed_at_ns: int
+ workflow_id: int
+
+
+def _construct_aggregate(ag_name: str, job_list: list[JobMetrics]) -> AggregateMetric:
+ """Create a libc++ AggregateMetric from a list of libc++ JobMetrics
+
+ How aggregates are computed:
+ queue time: Time from when first job in group is created until last job in
+ group has started.
+ run time: Time from when first job in group starts running until last job
+ in group finishes running.
+ status: logical 'and' of all the job statuses in the group.
+
+ Args:
+ ag_name: The name for this particular AggregateMetric
+ job_list: This list of JobMetrics to be combined into the AggregateMetric.
+ The input list should contain all (and only!) the libc++ JobMetrics
+ for a particular stage and a particular workflow_id.
+
+ Returns:
+ Returns the AggregateMetric constructed from the inputs.
+ """
+
+ # Initialize the aggregate values
+ earliest_create = job_list[0].created_at_ns
+ earliest_start = job_list[0].started_at_ns
+ earliest_complete = job_list[0].completed_at_ns
+ latest_start = job_list[0].started_at_ns
+ latest_complete = job_list[0].completed_at_ns
+ ag_status = job_list[0].status
+ ag_workflow_id = job_list[0].workflow_id
+
+ # Go through rest of jobs for this workflow id, if any, updating stats
+ for job in job_list[1:]:
+ # Update the status
+ ag_status = ag_status and job.status
+ # Get the earliest & latest times
+ if job.created_at_ns < earliest_create:
+ earliest_create = job.created_at_ns
+ if job.completed_at_ns < earliest_complete:
+ earliest_complete = job.completed_at_ns
+ if job.started_at_ns > latest_start:
+ latest_start = job.started_at_ns
+ if job.started_at_ns < earliest_start:
+ earliest_start = job.started_at_ns
+ if job.completed_at_ns > latest_complete:
+ latest_complete = job.completed_at_ns
+
+ # Compute aggregate run time (in seconds, not ns)
+ ag_run_time = (latest_complete - earliest_start) / 1000000000
+ # Compute aggregate queue time (in seconds, not ns)
+ ag_queue_time = (latest_start - earliest_create) / 1000000000
+ # Append the aggregate metrics to the workflow metrics list.
+ return AggregateMetric(
+ ag_name, ag_queue_time, ag_run_time, ag_status, latest_complete, ag_workflow_id
+ )
+
+
+def create_and_append_libcxx_aggregates(workflow_metrics: list[JobMetrics]):
+ """Find libc++ JobMetric entries and create aggregate metrics for them.
+
+ Sort the libc++ JobMetric entries by workflow id, and for each workflow
+ id group them by stages. Call _construct_aggregate to reate an aggregate
+ metric for each stage for each unique workflow id. Append each aggregate
+ metric to the input workflow_metrics list.
+
+ Args:
+ workflow_metrics: A list of JobMetrics entries collected so far.
+ """
+ # Separate the jobs by workflow_id. Only look at JobMetrics entries.
+ aggregate_data = dict()
+ for job in workflow_metrics:
+ # Only want to look at JobMetrics
+ if not isinstance(job, JobMetrics):
+ continue
+ # Only want libc++ jobs.
+ if job.workflow_name != "Build and Test libc++":
+ continue
+ if job.workflow_id not in aggregate_data.keys():
+ aggregate_data[job.workflow_id] = [job]
+ else:
+ aggregate_data[job.workflow_id].append(job)
+
+ # Go through each aggregate_data list (workflow id) and find all the
+ # needed data
+ for ag_workflow_id in aggregate_data:
+ job_list = aggregate_data[ag_workflow_id]
+ stage1_jobs = list()
+ stage2_jobs = list()
+ stage3_jobs = list()
+ # sort jobs into stage1, stage2, & stage3.
+ for job in job_list:
+ if job.job_name.find("stage1") > 0:
+ stage1_jobs.append(job)
+ elif job.job_name.find("stage2") > 0:
+ stage2_jobs.append(job)
+ elif job.job_name.find("stage3") > 0:
+ stage3_jobs.append(job)
+
+ if len(stage1_jobs) > 0:
+ aggregate = _construct_aggregate(
+ "github_libcxx_premerge_checks_stage1_aggregate", stage1_jobs
+ )
+ workflow_metrics.append(aggregate)
+ if len(stage2_jobs) > 0:
+ aggregate = _construct_aggregate(
+ "github_libcxx_premerge_checks_stage2_aggregate", stage2_jobs
+ )
+ workflow_metrics.append(aggregate)
+ if len(stage3_jobs) > 0:
+ aggregate = _construct_aggregate(
+ "github_libcxx_premerge_checks_stage3_aggregate", stage3_jobs
+ )
+ workflow_metrics.append(aggregate)
+
+
+def clean_up_libcxx_job_name(old_name: str) -> str:
+ """Convert libcxx job names to generically legal strings.
+
+ Args:
+ old_name: A string with the full name of the libc++ test that was run.
+
+ Returns:
+ Returns the input string with characters that might not be acceptable
+ in some indentifier strings replaced with safer characters.
+
+ Take a name like 'stage1 (generic-cxx03, clang-22, clang++-22)'
+ and convert it to 'stage1_generic_cxx03__clang_22__clangxx_22'.
+ (Remove parentheses; replace commas, hyphens and spaces with
+ underscores; replace '+' with 'x'.)
+ """
+ # Names should have exactly one set of parentheses, so break on that. If
+ # they don't have any parentheses, then don't update them at all.
+ if old_name.find("(") == -1:
+ return old_name
+ stage, remainder = old_name.split("(")
+ stage = stage.strip()
+ if remainder[-1] == ")":
+ remainder = remainder[:-1]
+ remainder = remainder.replace("-", "_")
+ remainder = remainder.replace(",", "_")
+ remainder = remainder.replace(" ", "_")
+ remainder = remainder.replace("+", "x")
+ new_name = stage + "_" + remainder
+ return new_name
+
def github_get_metrics(
github_repo: github.Repository, last_workflows_seen_as_completed: set[int]
) -> tuple[list[JobMetrics], int]:
@@ -146,6 +308,10 @@ def github_get_metrics(
if task.name not in GITHUB_WORKFLOW_TO_TRACK:
continue
+ libcxx_testing = False
+ if task.name == "Build and Test libc++":
+ libcxx_testing = True
+
if task.status == "completed":
workflow_seen_as_completed.add(task.id)
@@ -155,11 +321,19 @@ def github_get_metrics(
name_prefix = GITHUB_WORKFLOW_TO_TRACK[task.name]
for job in task.jobs():
+ if libcxx_testing:
+ # We're not running macos or windows libc++ tests on our
+ # infrastructure.
+ if job.name.find("macos") != -1 or job.name.find("windows") != -1:
+ continue
# This job is not interesting to us.
- if job.name not in GITHUB_JOB_TO_TRACK[name_prefix]:
+ elif job.name not in GITHUB_JOB_TO_TRACK[name_prefix]:
continue
- name_suffix = GITHUB_JOB_TO_TRACK[name_prefix][job.name]
+ if libcxx_testing:
+ name_suffix = clean_up_libcxx_job_name(job.name)
+ else:
+ name_suffix = GITHUB_JOB_TO_TRACK[name_prefix][job.name]
metric_name = name_prefix + "_" + name_suffix
if task.status != "completed":
@@ -208,8 +382,13 @@ def github_get_metrics(
continue
logging.info(f"Adding a job metric for job {job.id} in workflow {task.id}")
- # The timestamp associated with the event is expected by Grafana to be
- # in nanoseconds.
+ # The completed_at_ns timestamp associated with the event is
+ # expected by Grafana to be in nanoseconds. Because we do math using
+ # all three times (when creating libc++ aggregates), we need them
+ # all to be in nanoseconds, even though created_at and started_at
+ # are not returned to Grafana.
+ created_at_ns = int(created_at.timestamp()) * 10**9
+ started_at_ns = int(started_at.timestamp()) * 10**9
completed_at_ns = int(completed_at.timestamp()) * 10**9
workflow_metrics.append(
JobMetrics(
@@ -217,12 +396,18 @@ def github_get_metrics(
queue_time.seconds,
run_time.seconds,
job_result,
+ created_at_ns,
+ started_at_ns,
completed_at_ns,
task.id,
task.name,
)
)
+ # Finished collecting the JobMetrics for all jobs; now create the
+ # aggregates for any libc++ jobs.
+ create_and_append_libcxx_aggregates(workflow_metrics)
+
for name, value in queued_count.items():
workflow_metrics.append(
GaugeMetric(f"workflow_queue_size_{name}", value, time.time_ns())
@@ -278,6 +463,11 @@ def upload_metrics(workflow_metrics, metrics_userid, api_key):
metrics_batch.append(
f"{name} queue_time={workflow_metric.queue_time},run_time={workflow_metric.run_time},status={workflow_metric.status} {workflow_metric.completed_at_ns}"
)
+ elif isinstance(workflow_metric, AggregateMetric):
+ name = workflow_metric.aggregate_name.lower().replace(" ", "_")
+ metrics_batch.append(
+ f"{name} queue_time={workflow_metric.aggregate_queue_time},run_time={workflow_metric.aggregate_run_time},status={workflow_metric.aggregate_status} {workflow_metric.completed_at_ns}"
+ )
else:
raise ValueError(
f"Unsupported object type {type(workflow_metric)}: {str(workflow_metric)}"
diff --git a/.ci/metrics/metrics_test.py b/.ci/metrics/metrics_test.py
index 259e55f817939..406b68ae4d4f1 100644
--- a/.ci/metrics/metrics_test.py
+++ b/.ci/metrics/metrics_test.py
@@ -36,7 +36,9 @@ def test_upload_gauge_metric(self):
def test_upload_job_metric(self):
"""Test that we can upload a job metric correctly."""
test_metrics = [
- metrics.JobMetrics("test_job", 5, 10, 1, 1000, 7, "test_workflow")
+ metrics.JobMetrics(
+ "test_job", 5, 10, 1, 1000, 1000, 1000, 7, "test_workflow"
+ )
]
return_value = requests.Response()
return_value.status_code = 204
@@ -49,6 +51,22 @@ def test_upload_job_metric(self):
"test_job queue_time=5,run_time=10,status=1 1000",
)
+ def test_upload_aggregate_metric(self):
+ """Test that we can upload an aggregate metric correctly."""
+ test_metrics = [
+ metrics.AggregateMetric("stage1_aggregate", 211, 1124, 1, 1200, 9)
+ ]
+ return_value = requests.Response()
+ return_value.status_code = 204
+ with unittest.mock.patch(
+ "requests.post", return_value=return_value
+ ) as post_mock:
+ metrics.upload_metrics(test_metrics, "test_userid", "test_aoi_key")
+ self.assertEqual(
+ post_mock.call_args.kwargs["data"],
+ "stage1_aggregate queue_time=211,run_time=1124,status=1 1200",
+ )
+
def test_upload_unknown_metric(self):
"""Test we report an error if we encounter an unknown metric type."""
@@ -70,6 +88,326 @@ def test_bad_response_code(self):
with unittest.mock.patch("requests.post", return_value=return_value) as _:
metrics.upload_metrics(test_metrics, "test_userid", "test_api_key")
+ def test_create_and_append_aggregate_metric_1_stage(self):
+ """Test the creation of a single AggregateMetric"""
+ test_metrics = [
+ metrics.JobMetrics(
+ "libcxx_stage1_test1",
+ 8,
+ 388,
+ 1,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755697961000000000,
+ completed_at_ns=1755698349000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test2",
+ 107,
+ 357,
+ 1,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755698060000000000,
+ completed_at_ns=1755698417000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test3",
+ 8,
+ 824,
+ 1,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755697961000000000,
+ completed_at_ns=1755698785000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ ]
+ metrics.create_and_append_libcxx_aggregates(test_metrics)
+ self.assertEqual(len(test_metrics), 4)
+ self.assertTrue(isinstance(test_metrics[-1], metrics.AggregateMetric))
+ aggregate = test_metrics[-1]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage1_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 107)
+ self.assertEqual(aggregate.aggregate_run_time, 824)
+ self.assertEqual(aggregate.aggregate_status, 1)
+ self.assertEqual(aggregate.completed_at_ns, 1755698785000000000)
+ self.assertEqual(aggregate.workflow_id, 3)
+
+ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
+ """Test creation of AggregateMetric for same stage with diff workflow ids."""
+ test_metrics = [
+ metrics.JobMetrics(
+ "libcxx_stage1_test1",
+ 8,
+ 388,
+ 1,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755697961000000000,
+ completed_at_ns=1755698349000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test2",
+ 107,
+ 357,
+ 0,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755698060000000000,
+ completed_at_ns=1755698417000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test3",
+ 8,
+ 824,
+ 1,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755697961000000000,
+ completed_at_ns=1755698785000000000,
+ workflow_id=25,
+ workflow_name="Build and Test libc++",
+ ),
+ ]
+ metrics.create_and_append_libcxx_aggregates(test_metrics)
+ self.assertEqual(len(test_metrics), 5)
+ self.assertTrue(isinstance(test_metrics[3], metrics.AggregateMetric))
+ self.assertTrue(isinstance(test_metrics[4], metrics.AggregateMetric))
+ aggregate = test_metrics[3]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage1_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 107)
+ self.assertEqual(aggregate.aggregate_run_time, 456)
+ self.assertEqual(aggregate.aggregate_status, 0)
+ self.assertEqual(aggregate.completed_at_ns, 1755698417000000000)
+ self.assertEqual(aggregate.workflow_id, 3)
+
+ aggregate = test_metrics[4]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage1_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 8)
+ self.assertEqual(aggregate.aggregate_run_time, 824)
+ self.assertEqual(aggregate.aggregate_status, 1)
+ self.assertEqual(aggregate.completed_at_ns, 1755698785000000000)
+ self.assertEqual(aggregate.workflow_id, 25)
+
+ def test_create_and_append_aggregate_metric_3_stages(self):
+ """Test the creation of AggregateMetric for each of 3 stages."""
+ test_metrics = [
+ metrics.JobMetrics(
+ "libcxx_stage1_test1",
+ 124,
+ 1454,
+ 1,
+ created_at_ns=1755696929000000000,
+ started_at_ns=1755697053000000000,
+ completed_at_ns=1755698507000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test2",
+ 129,
+ 827,
+ 1,
+ created_at_ns=1755696929000000000,
+ started_at_ns=1755697058000000000,
+ completed_at_ns=1755697885000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage2_test1",
+ 6,
+ 580,
+ 1,
+ created_at_ns=1755698507000000000,
+ started_at_ns=1755698513000000000,
+ completed_at_ns=1755699093000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage2_test2",
+ 7,
+ 473,
+ 1,
+ created_at_ns=1755698507000000000,
+ started_at_ns=1755698514000000000,
+ completed_at_ns=1755698987000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage2_test3",
+ 7,
+ 820,
+ 1,
+ created_at_ns=1755698507000000000,
+ started_at_ns=1755698514000000000,
+ completed_at_ns=1755699334000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage3_test1",
+ 7,
+ 919,
+ 1,
+ created_at_ns=1755709005000000000,
+ started_at_ns=1755709012000000000,
+ completed_at_ns=1755709931000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage3_test2",
+ 141,
+ 834,
+ 1,
+ created_at_ns=1755709005000000000,
+ started_at_ns=1755709146000000000,
+ completed_at_ns=1755709980000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage3_test3",
+ 131,
+ 370,
+ 1,
+ created_at_ns=1755709005000000000,
+ started_at_ns=1755709136000000000,
+ completed_at_ns=1755709514000000000,
+ workflow_id=17,
+ workflow_name="Build and Test libc++",
+ ),
+ ]
+ metrics.create_and_append_libcxx_aggregates(test_metrics)
+ self.assertEqual(len(test_metrics), 11)
+ self.assertTrue(isinstance(test_metrics[8], metrics.AggregateMetric))
+ self.assertTrue(isinstance(test_metrics[9], metrics.AggregateMetric))
+ self.assertTrue(isinstance(test_metrics[10], metrics.AggregateMetric))
+ aggregate = test_metrics[8]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage1_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 129)
+ self.assertEqual(aggregate.aggregate_run_time, 1454)
+ self.assertEqual(aggregate.aggregate_status, 1)
+ self.assertEqual(aggregate.completed_at_ns, 1755698507000000000)
+ self.assertEqual(aggregate.workflow_id, 17)
+
+ aggregate = test_metrics[9]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage2_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 7)
+ self.assertEqual(aggregate.aggregate_run_time, 821)
+ self.assertEqual(aggregate.aggregate_status, 1)
+ self.assertEqual(aggregate.completed_at_ns, 1755699334000000000)
+ self.assertEqual(aggregate.workflow_id, 17)
+
+ aggregate = test_metrics[10]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage3_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 141)
+ self.assertEqual(aggregate.aggregate_run_time, 968)
+ self.assertEqual(aggregate.aggregate_status, 1)
+ self.assertEqual(aggregate.completed_at_ns, 1755709980000000000)
+ self.assertEqual(aggregate.workflow_id, 17)
+
+ def test_create_and_append_aggregate_metric_mixed_job_types(self):
+ """Test the creation of AggregateMetric with non-lib++ jobs thrown in."""
+ test_metrics = [
+ metrics.JobMetrics(
+ "ci_test1", 5, 10, 1, 1000, 1200, 1400, 5, "premerge_test"
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test1",
+ 8,
+ 388,
+ 1,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755697961000000000,
+ completed_at_ns=1755698349000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "ci_test2", 3, 20, 1, 2000, 2200, 2400, 37, "premerge_test"
+ ),
+ metrics.JobMetrics(
+ "libcxx_stage1_test2",
+ 107,
+ 357,
+ 0,
+ created_at_ns=1755697953000000000,
+ started_at_ns=1755698060000000000,
+ completed_at_ns=1755698417000000000,
+ workflow_id=3,
+ workflow_name="Build and Test libc++",
+ ),
+ metrics.JobMetrics(
+ "ci_test3", 7, 35, 1, 3000, 3200, 3400, 85, "premerge_test"
+ ),
+ ]
+ metrics.create_and_append_libcxx_aggregates(test_metrics)
+ self.assertEqual(len(test_metrics), 6)
+ self.assertTrue(isinstance(test_metrics[5], metrics.AggregateMetric))
+ aggregate = test_metrics[5]
+ self.assertEqual(
+ aggregate.aggregate_name, "github_libcxx_premerge_checks_stage1_aggregate"
+ )
+ self.assertEqual(aggregate.aggregate_queue_time, 107)
+ self.assertEqual(aggregate.aggregate_run_time, 456)
+ self.assertEqual(aggregate.aggregate_status, 0)
+ self.assertEqual(aggregate.completed_at_ns, 1755698417000000000)
+ self.assertEqual(aggregate.workflow_id, 3)
+
+ def test_create_and_append_aggregate_metric_no_libcxx_jobs(self):
+ """Test the creation of AggregateMetric with no libc++ jobs.
+
+ In this case, no AggregateMetric should be created, but no
+ errors or complaints should be raised.
+ """
+ test_metrics = [
+ metrics.JobMetrics(
+ "ci_test1", 5, 10, 1, 1000, 1200, 1400, 5, "premerge_test"
+ ),
+ metrics.JobMetrics(
+ "ci_test2", 3, 20, 1, 2000, 2200, 2400, 37, "premerge_test"
+ ),
+ metrics.JobMetrics(
+ "ci_test3", 7, 35, 1, 3000, 3200, 3400, 85, "premerge_test"
+ ),
+ ]
+ metrics.create_and_append_libcxx_aggregates(test_metrics)
+ self.assertEqual(len(test_metrics), 3)
+
+ def test_clean_up_libcxx_job_name(self):
+ """Test that we correctly update (or not) libcxx job names."""
+ stage1_name = "stage1 (test1, C++-test2, my-c++-test-25)"
+ stage2_name = "stage2 (generic-cxx26, clang-21, clang++21)"
+ stage3_name = "stage3 (generic-cxx26, libcxx-next-runners, junk)"
+ bad_name = "this is a bad name"
+ out_name1 = metrics.clean_up_libcxx_job_name(stage1_name)
+ self.assertEqual(out_name1, "stage1_test1__Cxx_test2__my_cxx_test_25")
+ out_name2 = metrics.clean_up_libcxx_job_name(stage2_name)
+ self.assertEqual(out_name2, "stage2_generic_cxx26__clang_21__clangxx21")
+ out_name3 = metrics.clean_up_libcxx_job_name(stage3_name)
+ self.assertEqual(out_name3, "stage3_generic_cxx26__libcxx_next_runners__junk")
+ out_name4 = metrics.clean_up_libcxx_job_name(bad_name)
+ self.assertEqual(out_name4, bad_name)
if __name__ == "__main__":
unittest.main()
diff --git a/.ci/monolithic-linux.sh b/.ci/monolithic-linux.sh
index 6db24d894eb73..3ca4d05f4c891 100755
--- a/.ci/monolithic-linux.sh
+++ b/.ci/monolithic-linux.sh
@@ -13,36 +13,15 @@
# run only the relevant tests.
#
-set -ex
-set -o pipefail
+source .ci/utils.sh
-MONOREPO_ROOT="${MONOREPO_ROOT:="$(git rev-parse --show-toplevel)"}"
-BUILD_DIR="${BUILD_DIR:=${MONOREPO_ROOT}/build}"
INSTALL_DIR="${BUILD_DIR}/install"
-rm -rf "${BUILD_DIR}"
-
-sccache --zero-stats
mkdir -p artifacts/reproducers
-# Make sure any clang reproducers will end up as artifacts.
+# Make sure any clang reproducers will end up as artifacts
export CLANG_CRASH_DIAGNOSTICS_DIR=`realpath artifacts/reproducers`
-function at-exit {
- retcode=$?
-
- sccache --show-stats > artifacts/sccache_stats.txt
- cp "${BUILD_DIR}"/.ninja_log artifacts/.ninja_log
- cp "${BUILD_DIR}"/test-results.*.xml artifacts/ || :
-
- # If building fails there will be no results files.
- shopt -s nullglob
-
- python3 "${MONOREPO_ROOT}"/.ci/generate_test_report_github.py ":penguin: Linux x64 Test Results" \
- $retcode "${BUILD_DIR}"/test-results.*.xml >> $GITHUB_STEP_SUMMARY
-}
-trap at-exit EXIT
-
projects="${1}"
targets="${2}"
runtimes="${3}"
@@ -50,9 +29,9 @@ runtime_targets="${4}"
runtime_targets_needs_reconfig="${5}"
enable_cir="${6}"
-lit_args="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests"
+lit_args="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests --succinct"
-echo "::group::cmake"
+start-group "CMake"
export PIP_BREAK_SYSTEM_PACKAGES=1
pip install -q -r "${MONOREPO_ROOT}"/.ci/all_requirements.txt
@@ -81,51 +60,44 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D MLIR_ENABLE_BINDINGS_PYTHON=ON \
-D LLDB_ENABLE_PYTHON=ON \
-D LLDB_ENFORCE_STRICT_TEST_REQUIREMENTS=ON \
- -D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
+ -D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
+ -D CMAKE_EXE_LINKER_FLAGS="-no-pie"
-echo "::endgroup::"
-echo "::group::ninja"
+start-group "ninja"
# Targets are not escaped as they are passed as separate arguments.
-ninja -C "${BUILD_DIR}" -k 0 ${targets}
-
-echo "::endgroup::"
+ninja -C "${BUILD_DIR}" -k 0 ${targets} |& tee ninja.log
if [[ "${runtime_targets}" != "" ]]; then
- echo "::group::ninja runtimes"
-
- ninja -C "${BUILD_DIR}" ${runtime_targets}
+ start-group "ninja Runtimes"
- echo "::endgroup::"
+ ninja -C "${BUILD_DIR}" ${runtime_targets} |& tee ninja_runtimes.log
fi
# Compiling runtimes with just-built Clang and running their tests
# as an additional testing for Clang.
if [[ "${runtime_targets_needs_reconfig}" != "" ]]; then
- echo "::group::cmake runtimes C++26"
+ start-group "CMake Runtimes C++26"
cmake \
-D LIBCXX_TEST_PARAMS="std=c++26" \
-D LIBCXXABI_TEST_PARAMS="std=c++26" \
"${BUILD_DIR}"
- echo "::endgroup::"
- echo "::group::ninja runtimes C++26"
+ start-group "ninja Runtimes C++26"
- ninja -C "${BUILD_DIR}" ${runtime_targets_needs_reconfig}
+ ninja -C "${BUILD_DIR}" ${runtime_targets_needs_reconfig} \
+ |& tee ninja_runtimes_needs_reconfig1.log
- echo "::endgroup::"
- echo "::group::cmake runtimes clang modules"
+ start-group "CMake Runtimes Clang Modules"
cmake \
-D LIBCXX_TEST_PARAMS="enable_modules=clang" \
-D LIBCXXABI_TEST_PARAMS="enable_modules=clang" \
"${BUILD_DIR}"
- echo "::endgroup::"
- echo "::group::ninja runtimes clang modules"
-
- ninja -C "${BUILD_DIR}" ${runtime_targets_needs_reconfig}
+ start-group "ninja Runtimes Clang Modules"
- echo "::endgroup::"
+ ninja -C "${BUILD_DIR}" ${runtime_targets_needs_reconfig} \
+ |& tee ninja_runtimes_needs_reconfig2.log
fi
diff --git a/.ci/monolithic-windows.sh b/.ci/monolithic-windows.sh
index 50a741677d734..0f3a1994a0497 100755
--- a/.ci/monolithic-windows.sh
+++ b/.ci/monolithic-windows.sh
@@ -13,35 +13,12 @@
# run only the relevant tests.
#
-set -ex
-set -o pipefail
-
-MONOREPO_ROOT="${MONOREPO_ROOT:="$(git rev-parse --show-toplevel)"}"
-BUILD_DIR="${BUILD_DIR:=${MONOREPO_ROOT}/build}"
-
-rm -rf "${BUILD_DIR}"
-
-sccache --zero-stats
-function at-exit {
- retcode=$?
-
- mkdir -p artifacts
- sccache --show-stats >> artifacts/sccache_stats.txt
- cp "${BUILD_DIR}"/.ninja_log artifacts/.ninja_log
- cp "${BUILD_DIR}"/test-results.*.xml artifacts/ || :
-
- # If building fails there will be no results files.
- shopt -s nullglob
-
- python "${MONOREPO_ROOT}"/.ci/generate_test_report_github.py ":window: Windows x64 Test Results" \
- $retcode "${BUILD_DIR}"/test-results.*.xml >> $GITHUB_STEP_SUMMARY
-}
-trap at-exit EXIT
+source .ci/utils.sh
projects="${1}"
targets="${2}"
-echo "::group::cmake"
+start-group "CMake"
pip install -q -r "${MONOREPO_ROOT}"/.ci/all_requirements.txt
export CC=cl
@@ -62,7 +39,7 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D LLVM_ENABLE_ASSERTIONS=ON \
-D LLVM_BUILD_EXAMPLES=ON \
-D COMPILER_RT_BUILD_LIBFUZZER=OFF \
- -D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests" \
+ -D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests --succinct" \
-D COMPILER_RT_BUILD_ORC=OFF \
-D CMAKE_C_COMPILER_LAUNCHER=sccache \
-D CMAKE_CXX_COMPILER_LAUNCHER=sccache \
@@ -71,10 +48,7 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D CMAKE_MODULE_LINKER_FLAGS="/MANIFEST:NO" \
-D CMAKE_SHARED_LINKER_FLAGS="/MANIFEST:NO"
-echo "::endgroup::"
-echo "::group::ninja"
+start-group "ninja"
# Targets are not escaped as they are passed as separate arguments.
-ninja -C "${BUILD_DIR}" -k 0 ${targets}
-
-echo "::endgroup"
+ninja -C "${BUILD_DIR}" -k 0 ${targets} |& tee ninja.log
diff --git a/.ci/utils.sh b/.ci/utils.sh
new file mode 100644
index 0000000000000..2a3d2426b630a
--- /dev/null
+++ b/.ci/utils.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+#===----------------------------------------------------------------------===##
+#
+# 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 performs some setup and contains some utilities used for in the
+# monolithic-linux.sh and monolithic-windows.sh scripts.
+
+set -ex
+set -o pipefail
+
+MONOREPO_ROOT="${MONOREPO_ROOT:="$(git rev-parse --show-toplevel)"}"
+BUILD_DIR="${BUILD_DIR:=${MONOREPO_ROOT}/build}"
+
+rm -rf "${BUILD_DIR}"
+
+sccache --zero-stats
+
+function at-exit {
+ retcode=$?
+
+ mkdir -p artifacts
+ sccache --show-stats
+ sccache --show-stats >> artifacts/sccache_stats.txt
+ cp "${BUILD_DIR}"/.ninja_log artifacts/.ninja_log
+ cp "${MONOREPO_ROOT}"/*.log artifacts/ || :
+ cp "${BUILD_DIR}"/test-results.*.xml artifacts/ || :
+
+ # If building fails there will be no results files.
+ shopt -s nullglob
+
+ if [[ "$GITHUB_STEP_SUMMARY" != "" ]]; then
+ python "${MONOREPO_ROOT}"/.ci/generate_test_report_github.py \
+ $retcode "${BUILD_DIR}"/test-results.*.xml "${MONOREPO_ROOT}"/ninja*.log \
+ >> $GITHUB_STEP_SUMMARY
+ fi
+}
+trap at-exit EXIT
+
+function start-group {
+ groupname=$1
+ if [[ "$GITHUB_ACTIONS" != "" ]]; then
+ echo "::endgroup"
+ echo "::group::$groupname"
+ elif [[ "$POSTCOMMIT_CI" != "" ]]; then
+ echo "@@@$STEP@@@"
+ else
+ echo "Starting $groupname"
+ fi
+}
diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml
index 8e0fa8d42d735..dab3db2616f59 100644
--- a/.github/new-prs-labeler.yml
+++ b/.github/new-prs-labeler.yml
@@ -90,9 +90,6 @@ LTO:
- llvm/lib/Transforms/*/FunctionImport*
- llvm/tools/gold/**
-mc:
- - llvm/*/MC/**
-
clang:driver:
- clang/*/Driver/**
@@ -621,6 +618,12 @@ llvm:adt:
llvm:support:
- llvm/**/Support/**
+# Skip llvm/test/MC and llvm/unittests/MC, which includes target-specific directories.
+llvm:mc:
+ - llvm/include/llvm/MC/**
+ - llvm/lib/MC/**
+ - llvm/tools/llvm-mc/**
+
llvm:transforms:
- llvm/lib/Transforms/**
- llvm/include/llvm/Transforms/**
diff --git a/.github/workflows/bazel-checks.yml b/.github/workflows/bazel-checks.yml
new file mode 100644
index 0000000000000..65d51649dd9e7
--- /dev/null
+++ b/.github/workflows/bazel-checks.yml
@@ -0,0 +1,32 @@
+name: Bazel Checks
+
+permissions:
+ contents: read
+
+on:
+ push:
+ paths:
+ - '.github/workflows/bazel-checks.yml'
+ - 'utils/bazel/**'
+ branches:
+ - main
+ pull_request:
+ paths:
+ - '.github/workflows/bazel-checks.yml'
+ - 'utils/bazel/**'
+
+jobs:
+ buildifier:
+ name: "Buildifier"
+ runs-on: ubuntu-24.04
+ if: github.repository == 'llvm/llvm-project'
+ steps:
+ - name: Fetch LLVM sources
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ - name: Setup Buildifier
+ run: |
+ sudo curl -L https://github.com/bazelbuild/buildtools/releases/download/v8.2.1/buildifier-linux-amd64 -o /usr/bin/buildifier
+ sudo chmod +x /usr/bin/buildifier
+ - name: Run Buildifier
+ run: |
+ buildifier --mode=check $(find ./utils/bazel -name *BUILD*)
diff --git a/.github/workflows/build-ci-container-windows.yml b/.github/workflows/build-ci-container-windows.yml
index f76c69f29fb30..55a269c001c2b 100644
--- a/.github/workflows/build-ci-container-windows.yml
+++ b/.github/workflows/build-ci-container-windows.yml
@@ -25,7 +25,7 @@ jobs:
container-filename: ${{ steps.vars.outputs.container-filename }}
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: .github/workflows/containers/github-action-ci-windows
- name: Write Variables
diff --git a/.github/workflows/build-ci-container.yml b/.github/workflows/build-ci-container.yml
index 7f01264af8534..3e91c49a51d19 100644
--- a/.github/workflows/build-ci-container.yml
+++ b/.github/workflows/build-ci-container.yml
@@ -30,7 +30,7 @@ jobs:
runs-on: depot-ubuntu-24.04-arm-16
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: .github/workflows/containers/github-action-ci/
# podman is not installed by default on the ARM64 images.
diff --git a/.github/workflows/build-metrics-container.yml b/.github/workflows/build-metrics-container.yml
index af4d599f76417..265fd73cc0bb7 100644
--- a/.github/workflows/build-metrics-container.yml
+++ b/.github/workflows/build-metrics-container.yml
@@ -27,7 +27,7 @@ jobs:
container-filename: ${{ steps.vars.outputs.container-filename }}
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: .ci/metrics/
- name: Write Variables
diff --git a/.github/workflows/check-ci.yml b/.github/workflows/check-ci.yml
index befea2093f908..7e8c15696e344 100644
--- a/.github/workflows/check-ci.yml
+++ b/.github/workflows/check-ci.yml
@@ -5,6 +5,8 @@ permissions:
on:
push:
+ branches:
+ - main
paths:
- '.ci/**'
- '.github/workflows/check-ci.yml'
@@ -20,7 +22,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: .ci
- name: Setup Python
diff --git a/.github/workflows/ci-post-commit-analyzer.yml b/.github/workflows/ci-post-commit-analyzer.yml
index b8074859d23a2..7d37b900d7909 100644
--- a/.github/workflows/ci-post-commit-analyzer.yml
+++ b/.github/workflows/ci-post-commit-analyzer.yml
@@ -41,7 +41,7 @@ jobs:
LLVM_VERSION: 18
steps:
- name: Checkout Source
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup ccache
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
diff --git a/.github/workflows/commit-access-greeter.yml b/.github/workflows/commit-access-greeter.yml
index a5fbbbb94e222..f31cd015642e2 100644
--- a/.github/workflows/commit-access-greeter.yml
+++ b/.github/workflows/commit-access-greeter.yml
@@ -18,7 +18,7 @@ jobs:
github.event.label.name == 'infra:commit-access-request'
runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: llvm/utils/git/
diff --git a/.github/workflows/commit-access-review.yml b/.github/workflows/commit-access-review.yml
index d401a137737c4..a7be81b0e2da5 100644
--- a/.github/workflows/commit-access-review.yml
+++ b/.github/workflows/commit-access-review.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install dependencies
run: |
diff --git a/.github/workflows/containers/github-action-ci-windows/Dockerfile b/.github/workflows/containers/github-action-ci-windows/Dockerfile
index c06fcc0580756..640d34da02532 100644
--- a/.github/workflows/containers/github-action-ci-windows/Dockerfile
+++ b/.github/workflows/containers/github-action-ci-windows/Dockerfile
@@ -90,7 +90,7 @@ RUN powershell -Command \
RUN git config --system core.longpaths true & \
git config --global core.autocrlf false
-ARG RUNNER_VERSION=2.327.1
+ARG RUNNER_VERSION=2.328.0
ENV RUNNER_VERSION=$RUNNER_VERSION
RUN powershell -Command \
diff --git a/.github/workflows/containers/github-action-ci/Dockerfile b/.github/workflows/containers/github-action-ci/Dockerfile
index 0444f617e3711..8a888f3a411c0 100644
--- a/.github/workflows/containers/github-action-ci/Dockerfile
+++ b/.github/workflows/containers/github-action-ci/Dockerfile
@@ -59,9 +59,12 @@ RUN apt-get update && \
sudo \
# These are needed by the premerge pipeline. Pip is used to install
# dependent python packages. File and tzdata are used for tests.
+ # Having a symlink from python to python3 enables code sharing between
+ # the Linux and Windows pipelines.
python3-pip \
file \
- tzdata && \
+ tzdata \
+ python-is-python3 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
@@ -78,6 +81,8 @@ RUN curl -L 'https://github.com/mozilla/sccache/releases/download/v0.10.0/sccach
ENV LLVM_SYSROOT=$LLVM_SYSROOT
ENV PATH=${LLVM_SYSROOT}/bin:${PATH}
+ENV CC=clang
+ENV CXX=clang++
# Create a new user to avoid test failures related to a lack of expected
# permissions issues in some tests. Set the user id to 1001 as that is the
@@ -94,7 +99,7 @@ WORKDIR /home/gha
FROM ci-container as ci-container-agent
-ENV GITHUB_RUNNER_VERSION=2.327.1
+ENV GITHUB_RUNNER_VERSION=2.328.0
RUN mkdir actions-runner && \
cd actions-runner && \
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 3970271e4adbd..b627803f61b27 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -55,7 +55,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
- name: Get subprojects that have doc changes
diff --git a/.github/workflows/email-check.yaml b/.github/workflows/email-check.yaml
index 904ad718f97dd..9390fba4d4e3b 100644
--- a/.github/workflows/email-check.yaml
+++ b/.github/workflows/email-check.yaml
@@ -14,7 +14,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
diff --git a/.github/workflows/hlsl-test-all.yaml b/.github/workflows/hlsl-test-all.yaml
index b6530fe11b840..72cbbe2b7dded 100644
--- a/.github/workflows/hlsl-test-all.yaml
+++ b/.github/workflows/hlsl-test-all.yaml
@@ -29,25 +29,25 @@ jobs:
runs-on: ${{ inputs.SKU }}
steps:
- name: Checkout DXC
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: Microsoft/DirectXShaderCompiler
ref: main
path: DXC
submodules: true
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ inputs.LLVM-branch }}
path: llvm-project
- name: Checkout OffloadTest
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: llvm/offload-test-suite
ref: main
path: OffloadTest
- name: Checkout Golden Images
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: llvm/offload-golden-images
ref: main
diff --git a/.github/workflows/issue-release-workflow.yml b/.github/workflows/issue-release-workflow.yml
index efd045990d013..7fd0280b2eedf 100644
--- a/.github/workflows/issue-release-workflow.yml
+++ b/.github/workflows/issue-release-workflow.yml
@@ -42,7 +42,7 @@ jobs:
contains(github.event.action == 'opened' && github.event.issue.body || github.event.comment.body, '/cherry-pick')
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: llvm/llvm-project
# GitHub stores the token used for checkout and uses it for pushes
diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml
index de1c45c944960..afcd17c757b39 100644
--- a/.github/workflows/issue-subscriber.yml
+++ b/.github/workflows/issue-subscriber.yml
@@ -14,7 +14,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Checkout Automation Script
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: llvm/utils/git/
ref: main
diff --git a/.github/workflows/issue-write.yml b/.github/workflows/issue-write.yml
index a2c4f58d6febe..3036582a64a58 100644
--- a/.github/workflows/issue-write.yml
+++ b/.github/workflows/issue-write.yml
@@ -25,7 +25,7 @@ jobs:
)
steps:
- name: Fetch Sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: |
.github/workflows/unprivileged-download-artifact/action.yml
diff --git a/.github/workflows/libc-fullbuild-tests.yml b/.github/workflows/libc-fullbuild-tests.yml
index 9ba77ff8bb845..8967cd0949c11 100644
--- a/.github/workflows/libc-fullbuild-tests.yml
+++ b/.github/workflows/libc-fullbuild-tests.yml
@@ -52,7 +52,7 @@ jobs:
# - c_compiler: gcc
# cpp_compiler: g++
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# Libc's build is relatively small comparing with other components of LLVM.
# A fresh fullbuild takes about 190MiB of uncompressed disk space, which can
diff --git a/.github/workflows/libc-overlay-tests.yml b/.github/workflows/libc-overlay-tests.yml
index e3dc4166aa26c..7154946ac5c3d 100644
--- a/.github/workflows/libc-overlay-tests.yml
+++ b/.github/workflows/libc-overlay-tests.yml
@@ -41,7 +41,7 @@ jobs:
cpp_compiler: clang++
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# Libc's build is relatively small comparing with other components of LLVM.
# A fresh linux overlay takes about 180MiB of uncompressed disk space, which can
diff --git a/.github/workflows/libclang-abi-tests.yml b/.github/workflows/libclang-abi-tests.yml
index 4d47c07f42205..3836cc56a7c22 100644
--- a/.github/workflows/libclang-abi-tests.yml
+++ b/.github/workflows/libclang-abi-tests.yml
@@ -38,7 +38,7 @@ jobs:
LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }}
steps:
- name: Checkout source
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 250
diff --git a/.github/workflows/libclang-python-tests.yml b/.github/workflows/libclang-python-tests.yml
index 50ef4acf2feb1..e168928325561 100644
--- a/.github/workflows/libclang-python-tests.yml
+++ b/.github/workflows/libclang-python-tests.yml
@@ -4,7 +4,6 @@ permissions:
contents: read
on:
- workflow_dispatch:
push:
branches:
- 'main'
@@ -13,29 +12,46 @@ on:
- 'clang/tools/libclang/**'
- 'clang/CMakeList.txt'
- '.github/workflows/libclang-python-tests.yml'
- - '.github/workflows/llvm-project-tests.yml'
pull_request:
paths:
- 'clang/bindings/python/**'
- 'clang/tools/libclang/**'
- 'clang/CMakeList.txt'
- '.github/workflows/libclang-python-tests.yml'
- - '.github/workflows/llvm-project-tests.yml'
jobs:
check-clang-python:
# Build libclang and then run the libclang Python binding's unit tests.
+ # There is an issue running on "windows-2019".
+ # See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082.
name: Build and run Python unit tests
if: github.repository == 'llvm/llvm-project'
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.13"]
- uses: ./.github/workflows/llvm-project-tests.yml
- with:
- build_target: check-clang-python
- projects: clang
- # There is an issue running on "windows-2019".
- # See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082.
- os_list: '["ubuntu-24.04"]'
- python_version: ${{ matrix.python-version }}
+ steps:
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ - name: Setup Python
+ uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Setup ccache
+ uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
+ with:
+ max-size: 2G
+ key: spirv-ubuntu-24.04
+ variant: sccache
+ - name: Build and Test
+ run: |
+ mkdir build
+ cmake -GNinja \
+ -S llvm \
+ -B build \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLLVM_ENABLE_ASSERTIONS=ON \
+ -DCMAKE_C_COMPILER_LAUNCHER=sccache \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
+ -DLLVM_ENABLE_PROJECTS=clang
+ ninja -C build check-clang-python
diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml
index 4b29c9296bf9d..2e6ff7f91b6fc 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -36,7 +36,7 @@ concurrency:
jobs:
stage1:
if: github.repository_owner == 'llvm'
- runs-on: llvm-premerge-libcxx-runners
+ runs-on: llvm-premerge-libcxx-next-runners
continue-on-error: false
strategy:
fail-fast: false
@@ -47,14 +47,14 @@ jobs:
'generic-cxx26',
'generic-modules'
]
- cc: [ 'clang-21' ]
- cxx: [ 'clang++-21' ]
+ cc: [ 'clang-22' ]
+ cxx: [ 'clang++-22' ]
include:
- config: 'generic-gcc'
cc: 'gcc-15'
cxx: 'g++-15'
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: ${{ matrix.config }}.${{ matrix.cxx }}
run: libcxx/utils/ci/run-buildbot ${{ matrix.config }}
env:
@@ -73,7 +73,7 @@ jobs:
**/crash_diagnostics/*
stage2:
if: github.repository_owner == 'llvm'
- runs-on: llvm-premerge-libcxx-runners
+ runs-on: llvm-premerge-libcxx-next-runners
needs: [ stage1 ]
continue-on-error: false
strategy:
@@ -86,20 +86,20 @@ jobs:
'generic-cxx20',
'generic-cxx23'
]
- cc: [ 'clang-21' ]
- cxx: [ 'clang++-21' ]
+ cc: [ 'clang-22' ]
+ cxx: [ 'clang++-22' ]
include:
- config: 'generic-gcc-cxx11'
cc: 'gcc-15'
cxx: 'g++-15'
+ - config: 'generic-cxx26'
+ cc: 'clang-21'
+ cxx: 'clang++-21'
- config: 'generic-cxx26'
cc: 'clang-20'
cxx: 'clang++-20'
- - config: 'generic-cxx26'
- cc: 'clang-19'
- cxx: 'clang++-19'
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: ${{ matrix.config }}
run: libcxx/utils/ci/run-buildbot ${{ matrix.config }}
env:
@@ -148,27 +148,27 @@ jobs:
'generic-static',
'bootstrapping-build'
]
- machine: [ 'llvm-premerge-libcxx-runners' ]
+ machine: [ 'llvm-premerge-libcxx-next-runners' ]
include:
- config: 'generic-cxx26'
- machine: llvm-premerge-libcxx-runners
+ machine: llvm-premerge-libcxx-next-runners
- config: 'generic-asan'
- machine: llvm-premerge-libcxx-runners
+ machine: llvm-premerge-libcxx-next-runners
- config: 'generic-tsan'
- machine: llvm-premerge-libcxx-runners
+ machine: llvm-premerge-libcxx-next-runners
- config: 'generic-ubsan'
- machine: llvm-premerge-libcxx-runners
+ machine: llvm-premerge-libcxx-next-runners
# Use a larger machine for MSAN to avoid timeout and memory allocation issues.
- config: 'generic-msan'
- machine: llvm-premerge-libcxx-runners
+ machine: llvm-premerge-libcxx-next-runners
runs-on: ${{ matrix.machine }}
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: ${{ matrix.config }}
run: libcxx/utils/ci/run-buildbot ${{ matrix.config }}
env:
- CC: clang-21
- CXX: clang++-21
+ CC: clang-22
+ CXX: clang++-22
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
if: always()
with:
@@ -211,7 +211,7 @@ jobs:
os: macos-15
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
@@ -252,7 +252,7 @@ jobs:
- { config: mingw-dll-i686, mingw: true }
- { config: mingw-incomplete-sysroot, mingw: true }
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install dependencies
run: |
choco install -y ninja
diff --git a/.github/workflows/libcxx-build-containers.yml b/.github/workflows/libcxx-build-containers.yml
index 43c446a2c02ce..c87ee8e3be250 100644
--- a/.github/workflows/libcxx-build-containers.yml
+++ b/.github/workflows/libcxx-build-containers.yml
@@ -30,7 +30,7 @@ jobs:
packages: write
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Build the Linux builder image
working-directory: libcxx/utils/ci
diff --git a/.github/workflows/libcxx-check-generated-files.yml b/.github/workflows/libcxx-check-generated-files.yml
index 0226edd7aa17a..f338bd6952779 100644
--- a/.github/workflows/libcxx-check-generated-files.yml
+++ b/.github/workflows/libcxx-check-generated-files.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install dependencies
uses: aminya/setup-cpp@17c11551771948abc5752bbf3183482567c7caf0 # v1.1.1
diff --git a/.github/workflows/llvm-project-tests.yml b/.github/workflows/llvm-project-tests.yml
deleted file mode 100644
index d40ed5babb459..0000000000000
--- a/.github/workflows/llvm-project-tests.yml
+++ /dev/null
@@ -1,149 +0,0 @@
-name: LLVM Project Tests
-
-permissions:
- contents: read
-
-on:
- workflow_dispatch:
- inputs:
- build_target:
- required: false
- projects:
- required: false
- extra_cmake_args:
- required: false
- os_list:
- required: false
- default: '["ubuntu-24.04", "windows-2019", "macOS-13"]'
- python_version:
- required: false
- type: string
- default: '3.11'
- workflow_call:
- inputs:
- build_target:
- required: false
- type: string
- default: "all"
-
- projects:
- required: true
- type: string
-
- extra_cmake_args:
- required: false
- type: string
-
- os_list:
- required: false
- type: string
- # Use windows-2019 due to:
- # https://developercommunity.visualstudio.com/t/Prev-Issue---with-__assume-isnan-/1597317
- default: '["ubuntu-24.04", "windows-2019", "macOS-13"]'
-
- python_version:
- required: false
- type: string
- default: '3.11'
-
-concurrency:
- # Skip intermediate builds: always.
- # Cancel intermediate builds: only if it is a pull request build.
- # If the group name here is the same as the group name in the workflow that includes
- # this one, then the action will try to wait on itself and get stuck.
- group: llvm-project-${{ github.workflow }}-${{ inputs.projects }}-${{ inputs.python_version }}${{ github.ref }}
- cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
-
-jobs:
- lit-tests:
- name: Lit Tests
- runs-on: ${{ matrix.os }}
- container:
- image: ${{(startsWith(matrix.os, 'ubuntu') && 'ghcr.io/llvm/ci-ubuntu-24.04:latest') || null}}
- volumes:
- - /mnt/:/mnt/
- strategy:
- fail-fast: false
- matrix:
- os: ${{ fromJSON(inputs.os_list) }}
- steps:
- - name: Setup Windows
- if: startsWith(matrix.os, 'windows')
- uses: llvm/actions/setup-windows@main
- with:
- arch: amd64
- # On Windows, starting with win19/20220814.1, cmake choose the 32-bit
- # python3.10.6 libraries instead of the 64-bit libraries when building
- # lldb. Using this setup-python action to make 3.10 the default
- # python fixes this.
- - name: Setup Python
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
- with:
- python-version: ${{ inputs.python_version }}
- - name: Install Ninja
- if: runner.os != 'Linux'
- uses: llvm/actions/install-ninja@main
- # actions/checkout deletes any existing files in the new git directory,
- # so this needs to either run before ccache-action or it has to use
- # clean: false.
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- fetch-depth: 250
- - name: Setup ccache
- uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
- with:
- # A full build of llvm, clang, lld, and lldb takes about 250MB
- # of ccache space. There's not much reason to have more than this,
- # because we usually won't need to save cache entries from older
- # builds. Also, there is an overall 10GB cache limit, and each
- # run creates a new cache entry so we want to ensure that we have
- # enough cache space for all the tests to run at once and still
- # fit under the 10 GB limit.
- # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174
- max-size: 2G
- key: ${{ matrix.os }}
- variant: sccache
- - name: Build and Test
- env:
- # Workaround for https://github.com/actions/virtual-environments/issues/5900.
- # This should be a no-op for non-mac OSes
- PKG_CONFIG_PATH: /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig//12
- shell: bash
- id: build-llvm
- run: |
- if [ "${{ runner.os }}" == "Linux" ]; then
- builddir="/mnt/build/"
- sudo mkdir -p $builddir
- sudo chown gha $builddir
- extra_cmake_args="-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang"
- else
- builddir="$(pwd)"/build
- fi
- if [ "${{ runner.os }}" == "macOS" ]; then
- # Workaround test failure on some lld tests on MacOS
- # https://github.com/llvm/llvm-project/issues/81967
- extra_cmake_args="-DLLVM_DISABLE_ASSEMBLY_FILES=ON"
- fi
- echo "llvm-builddir=$builddir" >> "$GITHUB_OUTPUT"
- cmake -G Ninja \
- -B "$builddir" \
- -S llvm \
- -DLLVM_ENABLE_PROJECTS="${{ inputs.projects }}" \
- -DCMAKE_BUILD_TYPE=Release \
- -DLLVM_ENABLE_ASSERTIONS=ON \
- -DLLDB_INCLUDE_TESTS=OFF \
- -DLIBCLC_TARGETS_TO_BUILD="amdgcn--;amdgcn--amdhsa;r600--;nvptx--;nvptx64--;nvptx--nvidiacl;nvptx64--nvidiacl" \
- -DCMAKE_C_COMPILER_LAUNCHER=sccache \
- -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
- $extra_cmake_args \
- ${{ inputs.extra_cmake_args }}
- ninja -C "$builddir" '${{ inputs.build_target }}'
-
- - name: Build and Test libclc
- if: "!startsWith(matrix.os, 'windows') && contains(inputs.projects, 'libclc')"
- env:
- LLVM_BUILDDIR: ${{ steps.build-llvm.outputs.llvm-builddir }}
- run: |
- # The libclc tests don't have a generated check target so all we can
- # do is build it.
- ninja -C "$LLVM_BUILDDIR"
diff --git a/.github/workflows/llvm-project-workflow-tests.yml b/.github/workflows/llvm-project-workflow-tests.yml
deleted file mode 100644
index a2539b279be0a..0000000000000
--- a/.github/workflows/llvm-project-workflow-tests.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# This workflow will test the llvm-project-tests workflow in PRs
-# targetting the main branch. Since this workflow doesn't normally
-# run on main PRs, we need some way to test it to ensure new updates
-# don't break it.
-
-name: LLVM Workflow Test
-
-permissions:
- contents: read
-
-on:
- pull_request:
- branches:
- - 'main'
- paths:
- - '.github/workflows/llvm-project-tests.yml'
- - '.github/workflows/llvm-project-workflow-tests.yml'
-
-concurrency:
- # Skip intermediate builds: always.
- # Cancel intermediate builds: only if it is a pull request build.
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
-
-jobs:
- llvm-test:
- if: github.repository_owner == 'llvm'
- name: Build and Test
- uses: ./.github/workflows/llvm-project-tests.yml
- with:
- build_target: check-all
- projects: clang;lld;libclc;lldb
diff --git a/.github/workflows/llvm-tests.yml b/.github/workflows/llvm-tests.yml
index a9bd8db462cf7..52b486e7e62fc 100644
--- a/.github/workflows/llvm-tests.yml
+++ b/.github/workflows/llvm-tests.yml
@@ -38,7 +38,7 @@ jobs:
LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }}
steps:
- name: Checkout source
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 250
diff --git a/.github/workflows/merged-prs.yml b/.github/workflows/merged-prs.yml
index c771736389802..107bbc51b5314 100644
--- a/.github/workflows/merged-prs.yml
+++ b/.github/workflows/merged-prs.yml
@@ -21,7 +21,7 @@ jobs:
(github.event.pull_request.merged == true)
steps:
- name: Checkout Automation Script
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: llvm/utils/git/
ref: main
diff --git a/.github/workflows/mlir-spirv-tests.yml b/.github/workflows/mlir-spirv-tests.yml
new file mode 100644
index 0000000000000..78952ccad2642
--- /dev/null
+++ b/.github/workflows/mlir-spirv-tests.yml
@@ -0,0 +1,51 @@
+name: MLIR SPIR-V Tests
+
+permissions:
+ contents: read
+
+on:
+ workflow_dispatch:
+ pull_request:
+ paths:
+ - 'mlir/include/mlir/Dialect/SPIRV/**'
+ - 'mlir/lib/Dialect/SPIRV/**'
+ - 'mlir/include/mlir/Target/SPIRV/**'
+ - 'mlir/lib/Target/SPIRV/**'
+ - 'mlir/test/Target/SPIRV/**'
+ - '.github/workflows/mlir-spirv-tests.yml'
+
+concurrency:
+ # Skip intermediate builds: always.
+ # Cancel intermediate builds: only if it is a pull request build.
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
+
+jobs:
+ check_spirv:
+ if: github.repository_owner == 'llvm'
+ name: Test MLIR SPIR-V
+ runs-on: ubuntu-24.04
+ container:
+ image: ghcr.io/llvm/ci-ubuntu-24.04:latest
+ steps:
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ - name: Setup ccache
+ uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
+ with:
+ max-size: 2G
+ key: spirv-mlir-ubuntu-24.04
+ variant: sccache
+ - name: Build and Test
+ run: |
+ mkdir build
+ cmake -GNinja \
+ -S llvm \
+ -B build \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLLVM_ENABLE_ASSERTIONS=ON \
+ -DCMAKE_C_COMPILER_LAUNCHER=sccache \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
+ -DLLVM_TARGETS_TO_BUILD="host" \
+ -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON \
+ -DLLVM_ENABLE_PROJECTS=mlir
+ ninja -C build check-mlir
diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml
index 935598e410dbb..e1f2e754c1a3d 100644
--- a/.github/workflows/new-prs.yml
+++ b/.github/workflows/new-prs.yml
@@ -35,7 +35,7 @@ jobs:
(github.event.pull_request.author_association != 'OWNER')
steps:
- name: Checkout Automation Script
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: llvm/utils/git/
ref: main
diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml
index 70bcaafbd0cf3..5540555ae05ed 100644
--- a/.github/workflows/pr-code-format.yml
+++ b/.github/workflows/pr-code-format.yml
@@ -19,7 +19,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
@@ -35,7 +35,7 @@ jobs:
# We need to pull the script from the main branch, so that we ensure
# we get the latest version of this script.
- name: Fetch code formatting utils
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: ${{ github.repository }}
ref: ${{ github.base_ref }}
@@ -70,8 +70,6 @@ jobs:
- name: Run code formatter
env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
- START_REV: ${{ github.event.pull_request.base.sha }}
- END_REV: ${{ github.event.pull_request.head.sha }}
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
# Create an empty comments file so the pr-write job doesn't fail.
run: |
diff --git a/.github/workflows/pr-request-release-note.yml b/.github/workflows/pr-request-release-note.yml
index 57425e04ec2f4..f0197d71d6aa9 100644
--- a/.github/workflows/pr-request-release-note.yml
+++ b/.github/workflows/pr-request-release-note.yml
@@ -19,7 +19,7 @@ jobs:
# We need to pull the script from the main branch, so that we ensure
# we get the latest version of this script.
- name: Checkout Scripts
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: |
llvm/utils/git/requirements.txt
diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml
index f558da8a8fe0e..23c7a679185ee 100644
--- a/.github/workflows/pr-subscriber.yml
+++ b/.github/workflows/pr-subscriber.yml
@@ -14,7 +14,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Checkout Automation Script
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: llvm/utils/git/
ref: main
diff --git a/.github/workflows/premerge.yaml b/.github/workflows/premerge.yaml
index d0518fa6879e2..9d925517a7211 100644
--- a/.github/workflows/premerge.yaml
+++ b/.github/workflows/premerge.yaml
@@ -31,13 +31,10 @@ jobs:
runs-on: llvm-premerge-linux-runners
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
- name: Build and Test
- # Mark the job as a success even if the step fails so that people do
- # not get notified while the new premerge pipeline is in an
- # experimental state.
run: |
git config --global --add safe.directory '*'
@@ -72,6 +69,11 @@ jobs:
./.ci/monolithic-linux.sh "${projects_to_build}" "${project_check_targets}" "${runtimes_to_build}" "${runtimes_check_targets}" "${runtimes_check_targets_needs_reconfig}" "${enable_cir}"
- name: Upload Artifacts
+ # In some cases, Github will fail to upload the artifact. We want to
+ # continue anyways as a failed artifact upload is an infra failure, not
+ # a checks failure.
+ # https://github.com/actions/upload-artifact/issues/569
+ continue-on-error: true
if: '!cancelled()'
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
@@ -91,7 +93,7 @@ jobs:
shell: bash
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
- name: Compute Projects
@@ -109,9 +111,6 @@ jobs:
echo "windows-projects=${projects_to_build}" >> $GITHUB_OUTPUT
echo "windows-check-targets=${project_check_targets}" >> $GITHUB_OUTPUT
- name: Build and Test
- # Mark the job as a success even if the step fails so that people do
- # not get notified while the new premerge pipeline is in an
- # experimental state.
if: ${{ steps.vars.outputs.windows-projects != '' }}
shell: cmd
run: |
@@ -120,6 +119,11 @@ jobs:
# these environment variables.
bash -c "export SCCACHE_GCS_BUCKET=$CACHE_GCS_BUCKET; export SCCACHE_GCS_RW_MODE=READ_WRITE; export SCCACHE_IDLE_TIMEOUT=0; sccache --start-server; .ci/monolithic-windows.sh \"${{ steps.vars.outputs.windows-projects }}\" \"${{ steps.vars.outputs.windows-check-targets }}\""
- name: Upload Artifacts
+ # In some cases, Github will fail to upload the artifact. We want to
+ # continue anyways as a failed artifact upload is an infra failure, not
+ # a checks failure.
+ # https://github.com/actions/upload-artifact/issues/569
+ continue-on-error: true
if: '!cancelled()'
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
@@ -138,7 +142,7 @@ jobs:
(github.event_name != 'pull_request' || github.event.action != 'closed')
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
- name: Setup ccache
diff --git a/.github/workflows/release-asset-audit.yml b/.github/workflows/release-asset-audit.yml
index 7a1f232ae7335..6546540a1b547 100644
--- a/.github/workflows/release-asset-audit.yml
+++ b/.github/workflows/release-asset-audit.yml
@@ -23,7 +23,7 @@ jobs:
if: github.repository == 'llvm/llvm-project'
steps:
- name: Checkout LLVM
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: |
.github/workflows/release-asset-audit.py
diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml
index c113b42dc8ed4..116bdfb3929d3 100644
--- a/.github/workflows/release-binaries.yml
+++ b/.github/workflows/release-binaries.yml
@@ -73,7 +73,7 @@ jobs:
python-version: '3.12'
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install Dependencies
shell: bash
@@ -195,7 +195,7 @@ jobs:
steps:
- name: Checkout Actions
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }}
sparse-checkout: |
@@ -216,7 +216,7 @@ jobs:
run: mv workflows ../workflows-main
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ needs.prepare.outputs.ref }}
@@ -286,7 +286,7 @@ jobs:
steps:
- name: Checkout Release Scripts
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: |
llvm/utils/release/github-upload-release.py
@@ -338,7 +338,7 @@ jobs:
runs-on: ${{ needs.prepare.outputs.test-runs-on }}
steps:
- name: Checkout Actions
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }}
sparse-checkout: |
diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml
index 5a0aa063d32ac..712ff1831170e 100644
--- a/.github/workflows/release-documentation.yml
+++ b/.github/workflows/release-documentation.yml
@@ -34,7 +34,7 @@ jobs:
upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }}
steps:
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Python env
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
@@ -66,7 +66,7 @@ jobs:
- name: Clone www-releases
if: env.upload
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: ${{ github.repository_owner }}/www-releases
ref: main
diff --git a/.github/workflows/release-doxygen.yml b/.github/workflows/release-doxygen.yml
index d47c4337c07b2..17c677413f744 100644
--- a/.github/workflows/release-doxygen.yml
+++ b/.github/workflows/release-doxygen.yml
@@ -40,7 +40,7 @@ jobs:
upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }}
steps:
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Python env
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
diff --git a/.github/workflows/release-lit.yml b/.github/workflows/release-lit.yml
index 9adeffb74d52a..60ec64462bc31 100644
--- a/.github/workflows/release-lit.yml
+++ b/.github/workflows/release-lit.yml
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: "llvmorg-${{ inputs.release-version }}"
diff --git a/.github/workflows/release-sources.yml b/.github/workflows/release-sources.yml
index 99438918b56f0..14cc4c4e9b94f 100644
--- a/.github/workflows/release-sources.yml
+++ b/.github/workflows/release-sources.yml
@@ -71,7 +71,7 @@ jobs:
attestations: write
steps:
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ needs.inputs.outputs.ref }}
fetch-tags: true
diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml
index c9ae7e1ce97c3..a184996968cdd 100644
--- a/.github/workflows/release-tasks.yml
+++ b/.github/workflows/release-tasks.yml
@@ -38,7 +38,7 @@ jobs:
sudo apt-get install python3-github
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Create Release
env:
@@ -129,7 +129,7 @@ jobs:
sudo apt-get install python3-github
- name: Checkout LLVM
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
sparse-checkout: llvm/utils/release/github-upload-release.py
sparse-checkout-cone-mode: false
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 6cc80fb316c67..40db5504294ef 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -31,7 +31,7 @@ jobs:
steps:
- name: "Checkout code"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
diff --git a/.github/workflows/spirv-tests.yml b/.github/workflows/spirv-tests.yml
index f15ca1cb64ba5..8708fb06d9eb8 100644
--- a/.github/workflows/spirv-tests.yml
+++ b/.github/workflows/spirv-tests.yml
@@ -4,7 +4,6 @@ permissions:
contents: read
on:
- workflow_dispatch:
pull_request:
paths:
- 'llvm/lib/Target/SPIRV/**'
@@ -21,9 +20,27 @@ jobs:
check_spirv:
if: github.repository_owner == 'llvm'
name: Test SPIR-V
- uses: ./.github/workflows/llvm-project-tests.yml
- with:
- build_target: check-llvm-codegen-spirv
- projects:
- extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="SPIRV" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON'
- os_list: '["ubuntu-24.04"]'
+ runs-on: ubuntu-24.04
+ container:
+ image: ghcr.io/llvm/ci-ubuntu-24.04:latest
+ steps:
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ - name: Setup ccache
+ uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
+ with:
+ max-size: 2G
+ key: spirv-ubuntu-24.04
+ variant: sccache
+ - name: Build and Test
+ run: |
+ mkdir build
+ cmake -GNinja \
+ -S llvm \
+ -B build \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLLVM_ENABLE_ASSERTIONS=ON \
+ -DCMAKE_C_COMPILER_LAUNCHER=sccache \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
+ -DLLVM_TARGETS_TO_BUILD="SPIRV" \
+ -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON
+ ninja -C build check-llvm-codegen-spirv
diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml
index a0a598094376f..7e451880f4cfa 100644
--- a/.github/workflows/version-check.yml
+++ b/.github/workflows/version-check.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Fetch LLVM sources
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
diff --git a/.gitignore b/.gitignore
index a84268a7f6863..860b8ea12abd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,11 @@ autoconf/autom4te.cache
# CLion project configuration
/.idea
/cmake-build*
+# Coding assistants' stuff
+/CLAUDE.md
+/.claude/
+/GEMINI.md
+/.gemini/
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
diff --git a/bolt/docs/CommandLineArgumentReference.md b/bolt/docs/CommandLineArgumentReference.md
index f3881c9a640a9..d65cf39e16b29 100644
--- a/bolt/docs/CommandLineArgumentReference.md
+++ b/bolt/docs/CommandLineArgumentReference.md
@@ -138,6 +138,12 @@
Dump function CFGs to graphviz format after each stage;enable '-print-loops'
for color-coded blocks
+- `--dump-dot-func=`
+
+ Dump function CFGs to graphviz format for specified functions only;
+ takes function name patterns (regex supported). Note: C++ function names
+ must be passed using their mangled names
+
- `--dump-linux-exceptions`
Dump Linux kernel exception table
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index ae580520b9110..b59926cc75571 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -1196,11 +1196,6 @@ class BinaryFunction {
return getSecondaryEntryPointSymbol(BB.getLabel());
}
- /// Remove a label from the secondary entry point map.
- void removeSymbolFromSecondaryEntryPointMap(const MCSymbol *Label) {
- SecondaryEntryPoints.erase(Label);
- }
-
/// Return true if the basic block is an entry point into the function
/// (either primary or secondary).
bool isEntryPoint(const BinaryBasicBlock &BB) const {
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index f902a8c43cd1d..ae04891e791f9 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -718,6 +718,20 @@ class MCPlusBuilder {
return false;
}
+ /// Returns true if Inst is a trap instruction.
+ ///
+ /// Tests if Inst is an instruction that immediately causes an abnormal
+ /// program termination, for example when a security violation is detected
+ /// by a compiler-inserted check.
+ ///
+ /// @note An implementation of this method should likely return false for
+ /// calls to library functions like abort(), as it is possible that the
+ /// execution state is partially attacker-controlled at this point.
+ virtual bool isTrap(const MCInst &Inst) const {
+ llvm_unreachable("not implemented");
+ return false;
+ }
+
virtual bool isBreakpoint(const MCInst &Inst) const {
llvm_unreachable("not implemented");
return false;
@@ -740,6 +754,10 @@ class MCPlusBuilder {
return false;
}
+ /// Return true if the hlt instruction under the x86, otherwise, default to
+ /// false.
+ virtual bool isX86HLT(const MCInst &Inst) const { return false; }
+
/// Return the width, in bytes, of the memory access performed by \p Inst, if
/// this is a pop instruction. Return zero otherwise.
virtual int getPopSize(const MCInst &Inst) const {
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index cb1b87f8d0d65..db0f6903185b7 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -502,9 +502,6 @@ class DataAggregator : public DataReader {
/// entries).
void imputeFallThroughs();
- /// Register profiled functions for lite mode.
- void registerProfiledFunctions();
-
/// Debugging dump methods
void dump() const;
void dump(const PerfBranchSample &Sample) const;
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 91d62a78de390..19dcce8205ebc 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -241,7 +241,7 @@ class RewriteInstance {
/// Adjust function sizes and set proper maximum size values after the whole
/// symbol table has been processed.
- void adjustFunctionBoundaries();
+ void adjustFunctionBoundaries(DenseMap &MarkerSyms);
/// Make .eh_frame section relocatable.
void relocateEHFrameSection();
diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h
index a75b6bf720ec4..859d6f3bf6774 100644
--- a/bolt/include/bolt/Utils/CommandLineOpts.h
+++ b/bolt/include/bolt/Utils/CommandLineOpts.h
@@ -15,6 +15,12 @@
#include "llvm/Support/CommandLine.h"
+namespace llvm {
+namespace bolt {
+class BinaryFunction;
+}
+} // namespace llvm
+
namespace opts {
enum HeatmapModeKind {
@@ -100,6 +106,9 @@ extern llvm::cl::opt Verbosity;
/// Return true if we should process all functions in the binary.
bool processAllFunctions();
+/// Return true if we should dump dot graphs for the given function.
+bool shouldDumpDot(const llvm::bolt::BinaryFunction &Function);
+
enum GadgetScannerKind { GS_PACRET, GS_PAUTH, GS_ALL };
extern llvm::cl::bits GadgetScannersToRun;
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 84f1853469709..da59a188c6b60 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -2517,7 +2517,7 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) {
// Clean-up the effect of the code emission.
for (const MCSymbol &Symbol : Assembler.symbols()) {
MCSymbol *MutableSymbol = const_cast(&Symbol);
- MutableSymbol->setUndefined();
+ MutableSymbol->setFragment(nullptr);
MutableSymbol->setIsRegistered(false);
}
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index eec68ff5a5fce..8f494f105fbba 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1915,13 +1915,9 @@ void BinaryFunction::postProcessEntryPoints() {
continue;
// If we have grabbed a wrong code label which actually points to some
- // constant island inside the function, ignore this label and remove it
- // from the secondary entry point map.
- if (isStartOfConstantIsland(Offset)) {
- BC.SymbolToFunctionMap.erase(Label);
- removeSymbolFromSecondaryEntryPointMap(Label);
+ // constant island inside the function, ignore this label.
+ if (isStartOfConstantIsland(Offset))
continue;
- }
BC.errs() << "BOLT-WARNING: reference in the middle of instruction "
"detected in function "
diff --git a/bolt/lib/Core/MCPlusBuilder.cpp b/bolt/lib/Core/MCPlusBuilder.cpp
index fa8f4d1df308b..7f962e14ea115 100644
--- a/bolt/lib/Core/MCPlusBuilder.cpp
+++ b/bolt/lib/Core/MCPlusBuilder.cpp
@@ -30,6 +30,11 @@ using namespace bolt;
using namespace MCPlus;
namespace opts {
+cl::opt
+ TerminalHLT("terminal-x86-hlt",
+ cl::desc("Assume that execution stops at x86 HLT instruction"),
+ cl::init(true), cl::Hidden, cl::cat(BoltCategory));
+
cl::opt
TerminalTrap("terminal-trap",
cl::desc("Assume that execution stops at trap instruction"),
@@ -132,8 +137,13 @@ bool MCPlusBuilder::equals(const MCSpecifierExpr &A, const MCSpecifierExpr &B,
}
bool MCPlusBuilder::isTerminator(const MCInst &Inst) const {
- return Analysis->isTerminator(Inst) ||
- (opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap());
+ if (isX86HLT(Inst))
+ return opts::TerminalHLT;
+
+ if (Info->get(Inst.getOpcode()).isTrap())
+ return opts::TerminalTrap;
+
+ return Analysis->isTerminator(Inst);
}
void MCPlusBuilder::setTailCall(MCInst &Inst) const {
diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp
index 5d44e1a1a4902..d7f02b9470030 100644
--- a/bolt/lib/Passes/BinaryPasses.cpp
+++ b/bolt/lib/Passes/BinaryPasses.cpp
@@ -662,7 +662,7 @@ Error CleanMCState::runOnFunctions(BinaryContext &BC) {
if (S->isDefined()) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Symbol \"" << S->getName()
<< "\" is already defined\n");
- const_cast(S)->setUndefined();
+ const_cast(S)->setFragment(nullptr);
}
if (S->isRegistered()) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Symbol \"" << S->getName()
diff --git a/bolt/lib/Passes/FrameOptimizer.cpp b/bolt/lib/Passes/FrameOptimizer.cpp
index 81d4d9367f58c..b0b7207feac01 100644
--- a/bolt/lib/Passes/FrameOptimizer.cpp
+++ b/bolt/lib/Passes/FrameOptimizer.cpp
@@ -224,6 +224,11 @@ Error FrameOptimizerPass::runOnFunctions(BinaryContext &BC) {
if (opts::FrameOptimization == FOP_NONE)
return Error::success();
+ if (!BC.isX86()) {
+ BC.errs() << "BOLT-ERROR: " << getName() << " is supported only on X86\n";
+ exit(1);
+ }
+
std::unique_ptr CG;
std::unique_ptr FA;
std::unique_ptr RA;
diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp
index f928dd49edb25..65c84ebc8c4f4 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -1078,6 +1078,15 @@ class DstSafetyAnalysis {
dbgs() << ")\n";
});
+ // If this instruction terminates the program immediately, no
+ // authentication oracles are possible past this point.
+ if (BC.MIB->isTrap(Point)) {
+ LLVM_DEBUG({ traceInst(BC, "Trap instruction found", Point); });
+ DstState Next(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
+ Next.CannotEscapeUnchecked.set();
+ return Next;
+ }
+
// If this instruction is reachable by the analysis, a non-empty state will
// be propagated to it sooner or later. Until then, skip computeNext().
if (Cur.empty()) {
@@ -1185,8 +1194,8 @@ class DataflowDstSafetyAnalysis
//
// A basic block without any successors, on the other hand, can be
// pessimistically initialized to everything-is-unsafe: this will naturally
- // handle both return and tail call instructions and is harmless for
- // internal indirect branch instructions (such as computed gotos).
+ // handle return, trap and tail call instructions. At the same time, it is
+ // harmless for internal indirect branch instructions, like computed gotos.
if (BB.succ_empty())
return createUnsafeState();
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index c13fa6dbe582b..3604fdd3a94b4 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -581,26 +581,6 @@ void DataAggregator::imputeFallThroughs() {
outs() << "BOLT-INFO: imputed " << InferredTraces << " traces\n";
}
-void DataAggregator::registerProfiledFunctions() {
- DenseSet Addrs;
- for (const auto &Trace : llvm::make_first_range(Traces)) {
- if (Trace.Branch != Trace::FT_ONLY &&
- Trace.Branch != Trace::FT_EXTERNAL_ORIGIN)
- Addrs.insert(Trace.Branch);
- Addrs.insert(Trace.From);
- }
-
- for (const auto [PC, _] : BasicSamples)
- Addrs.insert(PC);
-
- for (const PerfMemSample &MemSample : MemSamples)
- Addrs.insert(MemSample.PC);
-
- for (const uint64_t Addr : Addrs)
- if (BinaryFunction *Func = getBinaryFunctionContainingAddress(Addr))
- Func->setHasProfileAvailable();
-}
-
Error DataAggregator::preprocessProfile(BinaryContext &BC) {
this->BC = &BC;
@@ -623,7 +603,6 @@ Error DataAggregator::preprocessProfile(BinaryContext &BC) {
exit(0);
}
- registerProfiledFunctions();
return Error::success();
}
@@ -1368,6 +1347,10 @@ std::error_code DataAggregator::parseAggregatedLBREntry() {
}
const uint64_t FromOffset = Addr[0]->Offset;
+ BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(FromOffset);
+ if (FromFunc)
+ FromFunc->setHasProfileAvailable();
+
int64_t Count = Counters[0];
int64_t Mispreds = Counters[1];
@@ -1378,6 +1361,11 @@ std::error_code DataAggregator::parseAggregatedLBREntry() {
return std::error_code();
}
+ const uint64_t ToOffset = Addr[1]->Offset;
+ BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(ToOffset);
+ if (ToFunc)
+ ToFunc->setHasProfileAvailable();
+
/// For fall-through types, adjust locations to match Trace container.
if (Type == FT || Type == FT_EXTERNAL_ORIGIN || Type == FT_EXTERNAL_RETURN) {
Addr[2] = Location(Addr[1]->Offset); // Trace To
@@ -1625,6 +1613,9 @@ std::error_code DataAggregator::parseBranchEvents() {
Traces.reserve(TraceMap.size());
for (const auto &[Trace, Info] : TraceMap) {
Traces.emplace_back(Trace, Info);
+ for (const uint64_t Addr : {Trace.Branch, Trace.From})
+ if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Addr))
+ BF->setHasProfileAvailable();
}
clear(TraceMap);
@@ -1685,6 +1676,9 @@ std::error_code DataAggregator::parseBasicEvents() {
continue;
++NumTotalSamples;
+ if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
+ BF->setHasProfileAvailable();
+
++BasicSamples[Sample->PC];
EventNames.insert(Sample->EventName);
}
@@ -1722,6 +1716,9 @@ std::error_code DataAggregator::parseMemEvents() {
if (std::error_code EC = Sample.getError())
return EC;
+ if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
+ BF->setHasProfileAvailable();
+
MemSamples.emplace_back(std::move(Sample.get()));
}
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index 996d2e972599d..0ddb73f828878 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -52,6 +52,7 @@ namespace opts {
extern cl::opt PrintAll;
extern cl::opt PrintDynoStats;
extern cl::opt DumpDotAll;
+extern bool shouldDumpDot(const bolt::BinaryFunction &Function);
extern cl::opt AsmDump;
extern cl::opt PLT;
extern cl::opt KeepNops;
extern cl::opt Lite;
extern cl::list ReorderData;
extern cl::opt ReorderFunctions;
+extern cl::opt TerminalHLT;
extern cl::opt TerminalTrap;
extern cl::opt TimeBuild;
extern cl::opt TimeRewrite;
@@ -114,6 +115,35 @@ cl::opt DumpDotAll(
"enable '-print-loops' for color-coded blocks"),
cl::Hidden, cl::cat(BoltCategory));
+cl::list DumpDotFunc(
+ "dump-dot-func", cl::CommaSeparated,
+ cl::desc(
+ "dump function CFGs to graphviz format for specified functions only;"
+ "takes function name patterns (regex supported)"),
+ cl::value_desc("func1,func2,func3,..."), cl::Hidden, cl::cat(BoltCategory));
+
+bool shouldDumpDot(const bolt::BinaryFunction &Function) {
+ // If dump-dot-all is enabled, dump all functions
+ if (DumpDotAll)
+ return !Function.isIgnored();
+
+ // If no specific functions specified in dump-dot-func, don't dump any
+ if (DumpDotFunc.empty())
+ return false;
+
+ if (Function.isIgnored())
+ return false;
+
+ // Check if function matches any of the specified patterns
+ for (const std::string &Name : DumpDotFunc) {
+ if (Function.hasNameRegex(Name)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static cl::list
ForceFunctionNames("funcs",
cl::CommaSeparated,
@@ -880,14 +910,9 @@ void RewriteInstance::discoverFileObjects() {
// code section (see IHI0056B). $d identifies data contents.
// Compilers usually merge multiple data objects in a single $d-$x interval,
// but we need every data object to be marked with $d. Because of that we
- // create a vector of MarkerSyms with all locations of data objects.
+ // keep track of marker symbols with all locations of data objects.
- struct MarkerSym {
- uint64_t Address;
- MarkerSymType Type;
- };
-
- std::vector SortedMarkerSymbols;
+ DenseMap MarkerSymbols;
auto addExtraDataMarkerPerSymbol = [&]() {
bool IsData = false;
uint64_t LastAddr = 0;
@@ -911,14 +936,14 @@ void RewriteInstance::discoverFileObjects() {
}
if (MarkerType != MarkerSymType::NONE) {
- SortedMarkerSymbols.push_back(MarkerSym{SymInfo.Address, MarkerType});
+ MarkerSymbols[SymInfo.Address] = MarkerType;
LastAddr = SymInfo.Address;
IsData = MarkerType == MarkerSymType::DATA;
continue;
}
if (IsData) {
- SortedMarkerSymbols.push_back({SymInfo.Address, MarkerSymType::DATA});
+ MarkerSymbols[SymInfo.Address] = MarkerSymType::DATA;
LastAddr = SymInfo.Address;
}
}
@@ -1283,27 +1308,24 @@ void RewriteInstance::discoverFileObjects() {
BC->setHasSymbolsWithFileName(FileSymbols.size());
// Now that all the functions were created - adjust their boundaries.
- adjustFunctionBoundaries();
+ adjustFunctionBoundaries(MarkerSymbols);
// Annotate functions with code/data markers in AArch64
- for (auto ISym = SortedMarkerSymbols.begin();
- ISym != SortedMarkerSymbols.end(); ++ISym) {
-
- auto *BF =
- BC->getBinaryFunctionContainingAddress(ISym->Address, true, true);
+ for (auto &[Address, Type] : MarkerSymbols) {
+ auto *BF = BC->getBinaryFunctionContainingAddress(Address, true, true);
if (!BF) {
// Stray marker
continue;
}
- const auto EntryOffset = ISym->Address - BF->getAddress();
- if (ISym->Type == MarkerSymType::CODE) {
+ const auto EntryOffset = Address - BF->getAddress();
+ if (Type == MarkerSymType::CODE) {
BF->markCodeAtOffset(EntryOffset);
continue;
}
- if (ISym->Type == MarkerSymType::DATA) {
+ if (Type == MarkerSymType::DATA) {
BF->markDataAtOffset(EntryOffset);
- BC->AddressToConstantIslandMap[ISym->Address] = BF;
+ BC->AddressToConstantIslandMap[Address] = BF;
continue;
}
llvm_unreachable("Unknown marker");
@@ -1832,7 +1854,8 @@ void RewriteInstance::disassemblePLT() {
}
}
-void RewriteInstance::adjustFunctionBoundaries() {
+void RewriteInstance::adjustFunctionBoundaries(
+ DenseMap &MarkerSyms) {
for (auto BFI = BC->getBinaryFunctions().begin(),
BFE = BC->getBinaryFunctions().end();
BFI != BFE; ++BFI) {
@@ -1870,12 +1893,15 @@ void RewriteInstance::adjustFunctionBoundaries() {
continue;
}
- // This is potentially another entry point into the function.
- uint64_t EntryOffset = NextSymRefI->first - Function.getAddress();
- LLVM_DEBUG(dbgs() << "BOLT-DEBUG: adding entry point to function "
- << Function << " at offset 0x"
- << Twine::utohexstr(EntryOffset) << '\n');
- Function.addEntryPointAtOffset(EntryOffset);
+ auto It = MarkerSyms.find(NextSymRefI->first);
+ if (It == MarkerSyms.end() || It->second != MarkerSymType::DATA) {
+ // This is potentially another entry point into the function.
+ uint64_t EntryOffset = NextSymRefI->first - Function.getAddress();
+ LLVM_DEBUG(dbgs() << "BOLT-DEBUG: adding entry point to function "
+ << Function << " at offset 0x"
+ << Twine::utohexstr(EntryOffset) << '\n');
+ Function.addEntryPointAtOffset(EntryOffset);
+ }
++NextSymRefI;
}
@@ -2177,7 +2203,9 @@ void RewriteInstance::adjustCommandLineOptions() {
if (!opts::KeepNops.getNumOccurrences())
opts::KeepNops = true;
- // Linux kernel may resume execution after a trap instruction in some cases.
+ // Linux kernel may resume execution after a trap or x86 HLT instruction.
+ if (!opts::TerminalHLT.getNumOccurrences())
+ opts::TerminalHLT = false;
if (!opts::TerminalTrap.getNumOccurrences())
opts::TerminalTrap = false;
}
@@ -3570,7 +3598,7 @@ void RewriteInstance::postProcessFunctions() {
if (opts::PrintAll || opts::PrintCFG)
Function.print(BC->outs(), "after building cfg");
- if (opts::DumpDotAll)
+ if (opts::shouldDumpDot(Function))
Function.dumpGraphForPass("00_build-cfg");
if (opts::PrintLoopInfo) {
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 973261765f951..72f95cea6fa1d 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -382,10 +382,9 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
// the list of successors of this basic block as appropriate.
// Any of the above code sequences assume the fall-through basic block
- // is a dead-end BRK instruction (any immediate operand is accepted).
+ // is a dead-end trap instruction.
const BinaryBasicBlock *BreakBB = BB.getFallthrough();
- if (!BreakBB || BreakBB->empty() ||
- BreakBB->front().getOpcode() != AArch64::BRK)
+ if (!BreakBB || BreakBB->empty() || !isTrap(BreakBB->front()))
return std::nullopt;
// Iterate over the instructions of BB in reverse order, matching opcodes
@@ -1744,6 +1743,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
Inst.addOperand(MCOperand::createImm(0));
}
+ bool isTrap(const MCInst &Inst) const override {
+ if (Inst.getOpcode() != AArch64::BRK)
+ return false;
+ // Only match the immediate values that are likely to indicate this BRK
+ // instruction is emitted to terminate the program immediately and not to
+ // be handled by a SIGTRAP handler, for example.
+ switch (Inst.getOperand(0).getImm()) {
+ case 0xc470:
+ case 0xc471:
+ case 0xc472:
+ case 0xc473:
+ // Explicit Pointer Authentication check failed, see
+ // AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue().
+ return true;
+ case 0x1:
+ // __builtin_trap(), as emitted by Clang.
+ return true;
+ case 0x3e8: // decimal 1000
+ // __builtin_trap(), as emitted by GCC.
+ return true;
+ default:
+ // Some constants may indicate intentionally recoverable break-points.
+ // This is the case at least for 0xf000, which is used by
+ // __builtin_debugtrap() supported by Clang.
+ return false;
+ }
+ }
+
bool isStorePair(const MCInst &Inst) const {
const unsigned opcode = Inst.getOpcode();
diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
index a60c1a6bf156e..1842509dcc5e0 100644
--- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
+++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
@@ -223,6 +223,10 @@ class X86MCPlusBuilder : public MCPlusBuilder {
return Inst.getOpcode() == X86::ENDBR32 || Inst.getOpcode() == X86::ENDBR64;
}
+ bool isX86HLT(const MCInst &Inst) const override {
+ return Inst.getOpcode() == X86::HLT;
+ }
+
int getPopSize(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::POP16r:
diff --git a/bolt/test/AArch64/data-marker-invalidates-extra-entrypoint.s b/bolt/test/AArch64/data-marker-invalidates-extra-entrypoint.s
new file mode 100644
index 0000000000000..3bcbcbba8a385
--- /dev/null
+++ b/bolt/test/AArch64/data-marker-invalidates-extra-entrypoint.s
@@ -0,0 +1,38 @@
+# This test is to ensure that we query data marker symbols to avoid
+# misidentifying constant data island symbol as extra entry point.
+
+# RUN: %clang %cflags %s -o %t.so -Wl,-q -Wl,--init=_bar -Wl,--fini=_bar
+# RUN: llvm-bolt %t.so -o %t.instr.so
+
+ .text
+ .global _start
+ .type _start, %function
+_start:
+ ret
+
+ .text
+ .global _foo
+ .type _foo, %function
+_foo:
+ cbz x1, _foo_2
+_foo_1:
+ add x1, x2, x0
+ b _foo
+_foo_2:
+ ret
+
+# None of these constant island symbols should be identified as extra entry
+# point for function `_foo'.
+ .align 4
+_const1: .short 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80
+_const2: .short 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0
+_const3: .short 0x04, 0x08, 0x0c, 0x20, 0x60, 0x80, 0xa0, 0xc0
+
+ .text
+ .global _bar
+ .type _bar, %function
+_bar:
+ ret
+
+ # Dummy relocation to force relocation mode
+ .reloc 0, R_AARCH64_NONE
diff --git a/bolt/test/AArch64/unsupported-passes.test b/bolt/test/AArch64/unsupported-passes.test
new file mode 100644
index 0000000000000..886fc1c574dcf
--- /dev/null
+++ b/bolt/test/AArch64/unsupported-passes.test
@@ -0,0 +1,8 @@
+// Checks that non-fully supported passes on AArch64 are handled appropriately.
+
+// REQUIRES: system-linux,asserts,target=aarch64{{.*}}
+
+RUN: %clang %cflags %p/../Inputs/hello.c -o %t -Wl,-q
+RUN: not llvm-bolt %t -o %t.bolt --frame-opt=all 2>&1 | FileCheck %s
+
+CHECK: BOLT-ERROR: frame-optimizer is supported only on X86
diff --git a/bolt/test/Inputs/multi-func.cpp b/bolt/test/Inputs/multi-func.cpp
new file mode 100644
index 0000000000000..61c968fc27f98
--- /dev/null
+++ b/bolt/test/Inputs/multi-func.cpp
@@ -0,0 +1,24 @@
+#include
+
+// Multiple functions to test selective dumping
+int add(int a, int b) { return a + b; }
+
+int multiply(int a, int b) { return a * b; }
+
+int main_helper() {
+ std::cout << "Helper function" << std::endl;
+ return 42;
+}
+
+int main_secondary() { return add(5, 3); }
+
+void other_function() { std::cout << "Other function" << std::endl; }
+
+int main() {
+ int result = add(10, 20);
+ result = multiply(result, 2);
+ main_helper();
+ main_secondary();
+ other_function();
+ return result;
+}
diff --git a/bolt/test/X86/hlt-terminator.s b/bolt/test/X86/hlt-terminator.s
new file mode 100644
index 0000000000000..3f67182fdf432
--- /dev/null
+++ b/bolt/test/X86/hlt-terminator.s
@@ -0,0 +1,24 @@
+## Check that HLT instruction is handled differently depending on the flags.
+## It's a terminator in the user-level code, but the execution can resume in
+## ring 0.
+
+# RUN: %clang %cflags %s -static -o %t.exe -nostdlib
+# RUN: llvm-bolt %t.exe --print-cfg --print-only=main --terminal-x86-hlt=0 \
+# RUN: -o %t.ring0 2>&1 | FileCheck %s --check-prefix=CHECK-RING0
+# RUN: llvm-bolt %t.exe --print-cfg --print-only=main \
+# RUN: -o %t.ring3 2>&1 | FileCheck %s --check-prefix=CHECK-RING3
+# RUN: llvm-objdump -d %t.ring0 --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN
+
+# CHECK-RING0: BB Count : 1
+# CHECK-RING3: BB Count : 2
+
+# CHECK-BIN: :
+# CHECK-BIN-NEXT: f4 hlt
+# CHECK-BIN-NEXT: c3 retq
+
+.global main
+ .type main, %function
+main:
+ hlt
+ retq
+.size main, .-main
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s b/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
index 3f982ddaf6e38..74f276197923f 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
@@ -31,7 +31,7 @@ resign_xpaci_good:
xpaci x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -46,7 +46,7 @@ resign_xpacd_good:
xpacd x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc473
1:
pacda x0, x2
ret
@@ -117,7 +117,7 @@ resign_xpaci_unrelated_auth_and_check:
xpaci x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x10, x2
ret
@@ -139,7 +139,7 @@ resign_xpaci_wrong_pattern_1:
xpaci x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -157,7 +157,7 @@ resign_xpaci_wrong_pattern_2:
xpaci x0 // x0 instead of x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -174,7 +174,7 @@ resign_xpaci_wrong_pattern_3:
xpaci x16
cmp x16, x16 // x16 instead of x0
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -191,7 +191,7 @@ resign_xpaci_wrong_pattern_4:
xpaci x16
cmp x0, x0 // x0 instead of x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -208,7 +208,7 @@ resign_xpaci_wrong_pattern_5:
mov x16, x16 // replace xpaci with a no-op instruction
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -228,7 +228,7 @@ resign_xpaclri_good:
xpaclri
cmp x30, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x30, x2
@@ -246,7 +246,7 @@ xpaclri_check_keeps_lr_safe:
xpaclri // clobbers LR
cmp x30, x16
b.eq 1f
- brk 0x1234 // marks LR as trusted and safe-to-dereference
+ brk 0xc471 // marks LR as trusted and safe-to-dereference
1:
ret // not reporting non-protected return
.size xpaclri_check_keeps_lr_safe, .-xpaclri_check_keeps_lr_safe
@@ -265,7 +265,7 @@ xpaclri_check_requires_safe_lr:
xpaclri
cmp x30, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
ret
.size xpaclri_check_requires_safe_lr, .-xpaclri_check_requires_safe_lr
@@ -283,7 +283,7 @@ resign_xpaclri_wrong_reg:
xpaclri // ... but xpaclri still operates on x30
cmp x20, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x20, x2
@@ -303,7 +303,7 @@ resign_checked_not_authenticated:
xpaci x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -323,7 +323,7 @@ resign_checked_before_authenticated:
xpaci x16
cmp x0, x16
b.eq 1f
- brk 0x1234
+ brk 0xc471
1:
autib x0, x1
pacia x0, x2
@@ -339,7 +339,7 @@ resign_high_bits_tbz_good:
autib x0, x1
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -378,7 +378,7 @@ resign_high_bits_tbz_wrong_bit:
autib x0, x1
eor x16, x0, x0, lsl #1
tbz x16, #63, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -393,7 +393,7 @@ resign_high_bits_tbz_wrong_shift_amount:
autib x0, x1
eor x16, x0, x0, lsl #2
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -408,7 +408,7 @@ resign_high_bits_tbz_wrong_shift_type:
autib x0, x1
eor x16, x0, x0, lsr #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -423,7 +423,7 @@ resign_high_bits_tbz_wrong_pattern_1:
autib x0, x1
eor x16, x0, x0, lsl #1
tbz x17, #62, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -438,7 +438,7 @@ resign_high_bits_tbz_wrong_pattern_2:
autib x0, x1
eor x16, x10, x0, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -453,7 +453,7 @@ resign_high_bits_tbz_wrong_pattern_3:
autib x0, x1
eor x16, x0, x10, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc471
1:
pacia x0, x2
ret
@@ -648,7 +648,7 @@ many_checked_regs:
xpacd x16 // ...
cmp x2, x16 // ...
b.eq 2f // end of basic block
- brk 0x1234
+ brk 0xc473
2:
pacdza x0
pacdza x1
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s b/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
index c314bc7cfe5a3..f44ba21b9d484 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
@@ -79,7 +79,7 @@ good_explicit_check:
autia x0, x1
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc470
1:
ret
.size good_explicit_check, .-good_explicit_check
@@ -373,7 +373,7 @@ good_explicit_check_multi_bb:
1:
eor x16, x0, x0, lsl #1
tbz x16, #62, 2f
- brk 0x1234
+ brk 0xc470
2:
cbz x1, 3f
nop
@@ -685,8 +685,7 @@ good_address_arith_nocfg:
.globl good_explicit_check_unrelated_reg
.type good_explicit_check_unrelated_reg,@function
good_explicit_check_unrelated_reg:
-// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_explicit_check_unrelated_reg, basic block {{[^,]+}}, at address
- // FIXME: The below instruction is not an authentication oracle
+// CHECK-NOT: good_explicit_check_unrelated_reg
autia x2, x3 // One of possible execution paths after this instruction
// ends at BRK below, thus BRK used as a trap instruction
// should formally "check everything" not to introduce
@@ -694,7 +693,7 @@ good_explicit_check_unrelated_reg:
autia x0, x1
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc470
1:
ldr x4, [x2] // Right before this instruction X2 is checked - this
// should be propagated to the basic block ending with
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s b/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
index 3a4d383ec5bc6..4d4bb7b0fb251 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
@@ -57,7 +57,7 @@ good_sign_auted_checked_brk:
autda x0, x2
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc472
1:
pacda x0, x1
ret
@@ -351,7 +351,7 @@ good_sign_auted_checked_brk_multi_bb:
1:
eor x16, x0, x0, lsl #1
tbz x16, #62, 2f
- brk 0x1234
+ brk 0xc472
2:
cbz x4, 3f
nop
@@ -705,7 +705,7 @@ good_resign_with_increment_brk:
add x0, x0, #8
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
- brk 0x1234
+ brk 0xc472
1:
mov x2, x0
pacda x2, x1
diff --git a/bolt/test/binary-analysis/AArch64/trap-instructions.s b/bolt/test/binary-analysis/AArch64/trap-instructions.s
new file mode 100644
index 0000000000000..7810b2d3c3626
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/trap-instructions.s
@@ -0,0 +1,213 @@
+// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe -Wl,--emit-relocs
+// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s
+
+// Test what instructions can be used to terminate the program abnormally
+// on security violation.
+//
+// All test cases have the same structure:
+//
+// cbz x0, 1f // [a], ensures [c] is never reported as unreachable
+// autia x2, x3
+// cbz x1, 2f // [b]
+// [instruction under test]
+// 1:
+// ret // [c]
+// 2:
+// ldr x0, [x2]
+// ret
+//
+// This is to handle three possible cases: the instruction under test may be
+// considered by BOLT as
+// * trapping (and thus no-return): after being authenticated, x2 is ether
+// checked by LDR (if [b] is taken) or the program is terminated
+// immediately without leaking x2 (if [b] falls through to the trapping
+// instruction under test). Nothing is reported.
+// * non-trapping, but no-return (such as calling abort()): x2 is leaked if [b]
+// falls through. Authentication oracle is reported.
+// * non-trapping and falling-through (i.e. a regular instruction):
+// x2 is leaked by [c]. Authentication oracle is reported.
+
+ .text
+
+ .globl brk_key_ia
+ .type brk_key_ia,@function
+brk_key_ia:
+// CHECK-NOT: brk_key_ia
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0xc470
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_key_ia, .-brk_key_ia
+
+ .globl brk_key_ib
+ .type brk_key_ib,@function
+brk_key_ib:
+// CHECK-NOT: brk_key_ib
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0xc471
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_key_ib, .-brk_key_ib
+
+ .globl brk_key_da
+ .type brk_key_da,@function
+brk_key_da:
+// CHECK-NOT: brk_key_da
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0xc472
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_key_da, .-brk_key_da
+
+ .globl brk_key_db
+ .type brk_key_db,@function
+brk_key_db:
+// CHECK-NOT: brk_key_db
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0xc473
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_key_db, .-brk_key_db
+
+// The immediate operand of BRK instruction may indicate whether the instruction
+// is intended to be a non-recoverable trap: for example, for this code
+//
+// int test_trap(void) {
+// __builtin_trap();
+// return 42;
+// }
+// int test_debugtrap(void) {
+// __builtin_debugtrap();
+// return 42;
+// }
+//
+// Clang produces the following assembly:
+//
+// test_trap:
+// brk #0x1
+// test_debugtrap:
+// brk #0xf000
+// mov w0, #42
+// ret
+//
+// In GCC, __builtin_trap() uses "brk 0x3e8" (i.e. decimal 1000) and
+// __builtin_debugtrap() is not supported.
+//
+// At the time of writing these test cases, any BRK instruction is considered
+// no-return by BOLT, thus it ends its basic block and prevents falling through
+// to the next BB.
+// FIXME: Make BOLT handle __builtin_debugtrap() properly from the CFG point
+// of view.
+
+ .globl brk_gcc_builtin_trap
+ .type brk_gcc_builtin_trap,@function
+brk_gcc_builtin_trap:
+// CHECK-NOT: brk_gcc_builtin_trap
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0x3e8 // __builtin_trap()
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_gcc_builtin_trap, .-brk_gcc_builtin_trap
+
+ .globl brk_clang_builtin_trap
+ .type brk_clang_builtin_trap,@function
+brk_clang_builtin_trap:
+// CHECK-NOT: brk_clang_builtin_trap
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0x1 // __builtin_trap()
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_clang_builtin_trap, .-brk_clang_builtin_trap
+
+ .globl brk_clang_builtin_debugtrap
+ .type brk_clang_builtin_debugtrap,@function
+brk_clang_builtin_debugtrap:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function brk_clang_builtin_debugtrap, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3
+// CHECK-NEXT: The 0 instructions that leak the affected registers are:
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0xf000 // __builtin_debugtrap()
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_clang_builtin_debugtrap, .-brk_clang_builtin_debugtrap
+
+// Conservatively assume BRK with an unknown immediate operand as not suitable
+// for terminating the program on security violation.
+ .globl brk_unknown_imm
+ .type brk_unknown_imm,@function
+brk_unknown_imm:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function brk_unknown_imm, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3
+// CHECK-NEXT: The 0 instructions that leak the affected registers are:
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ brk 0x3572
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size brk_unknown_imm, .-brk_unknown_imm
+
+// Conservatively assume calling the abort() function may be an unsafe way to
+// terminate the program, as there is some amount of instructions that would
+// be executed when the program state is already tampered with.
+ .globl call_abort_fn
+ .type call_abort_fn,@function
+call_abort_fn:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function call_abort_fn, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3
+// CHECK-NEXT: The 0 instructions that leak the affected registers are:
+ cbz x0, 1f
+ autia x2, x3
+ cbz x1, 2f
+ b abort // a no-return tail call to abort()
+1:
+ ret
+2:
+ ldr x0, [x2]
+ ret
+ .size call_abort_fn, .-call_abort_fn
+
+ .globl main
+ .type main,@function
+main:
+ mov x0, 0
+ ret
+ .size main, .-main
diff --git a/bolt/test/dump-dot-func.test b/bolt/test/dump-dot-func.test
new file mode 100644
index 0000000000000..510713dde6167
--- /dev/null
+++ b/bolt/test/dump-dot-func.test
@@ -0,0 +1,52 @@
+# Test the --dump-dot-func option with multiple functions
+# (includes tests for both mangled/unmangled names)
+
+RUN: %clang++ %p/Inputs/multi-func.cpp -o %t.exe -Wl,-q
+
+# Test 1: --dump-dot-func with specific function name (mangled)
+RUN: llvm-bolt %t.exe -o %t.bolt1 --dump-dot-func=_Z3addii -v=1 2>&1 | FileCheck %s --check-prefix=ADD
+
+# Test 2: --dump-dot-func with regex pattern (main.*)
+RUN: llvm-bolt %t.exe -o %t.bolt2 --dump-dot-func="main.*" -v=1 2>&1 | FileCheck %s --check-prefix=MAIN-REGEX
+
+# Test 3: --dump-dot-func with multiple specific functions (mangled names)
+RUN: llvm-bolt %t.exe -o %t.bolt3 --dump-dot-func=_Z3addii,_Z8multiplyii -v=1 2>&1 | FileCheck %s --check-prefix=MULTI
+
+# Test 4: No option specified should create no dot files
+RUN: llvm-bolt %t.exe -o %t.bolt4 2>&1 | FileCheck %s --check-prefix=NONE
+
+# Test 5: --dump-dot-func with non-existent function
+RUN: llvm-bolt %t.exe -o %t.bolt5 --dump-dot-func=nonexistent -v=1 2>&1 | FileCheck %s --check-prefix=NONEXISTENT
+
+# Test 6: Backward compatibility - --dump-dot-all should still work
+RUN: llvm-bolt %t.exe -o %t.bolt6 --dump-dot-all -v=1 2>&1 | FileCheck %s --check-prefix=ALL
+
+# Test 7: Test with unmangled function name (main function)
+RUN: llvm-bolt %t.exe -o %t.bolt7 --dump-dot-func=main -v=1 2>&1 | FileCheck %s --check-prefix=MAIN-UNMANGLED
+
+# Check that specific functions are dumped
+ADD: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
+ADD-NOT: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
+ADD-NOT: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
+ADD-NOT: BOLT-INFO: dumping CFG to _Z11main_helperv-00_build-cfg.dot
+
+MAIN-REGEX-DAG: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
+MAIN-REGEX-NOT: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
+MAIN-REGEX-NOT: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
+
+MULTI-DAG: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
+MULTI-DAG: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
+MULTI-NOT: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
+MULTI-NOT: BOLT-INFO: dumping CFG to _Z11main_helperv-00_build-cfg.dot
+
+# Should be no dumping messages when no option is specified
+NONE-NOT: BOLT-INFO: dumping CFG
+
+# Should be no dumping messages for non-existent function
+NONEXISTENT-NOT: BOLT-INFO: dumping CFG
+
+ALL: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
+
+MAIN-UNMANGLED: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
+MAIN-UNMANGLED-NOT: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
+MAIN-UNMANGLED-NOT: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
\ No newline at end of file
diff --git a/bolt/unittests/Core/CMakeLists.txt b/bolt/unittests/Core/CMakeLists.txt
index 54e8ea10cda12..f10b0d9472067 100644
--- a/bolt/unittests/Core/CMakeLists.txt
+++ b/bolt/unittests/Core/CMakeLists.txt
@@ -11,6 +11,11 @@ add_bolt_unittest(CoreTests
MemoryMaps.cpp
DynoStats.cpp
+ # FIXME CoreTests uses `llvm::detail::TakeError(llvm::Error)`, but linking
+ # to LLVMTestingSupport introduces a transitive dependency on the
+ # dynamic LLVM library when LLVM_LINK_LLVM_DYLIB is ON.
+ ${LLVM_MAIN_SRC_DIR}/lib/Testing/Support/Error.cpp
+
DISABLE_LLVM_LINK_LLVM_DYLIB
)
@@ -20,7 +25,6 @@ target_link_libraries(CoreTests
LLVMBOLTRewrite
LLVMBOLTProfile
LLVMBOLTUtils
- LLVMTestingSupport
)
foreach (tgt ${BOLT_TARGETS_TO_BUILD})
diff --git a/bolt/unittests/Profile/CMakeLists.txt b/bolt/unittests/Profile/CMakeLists.txt
index ce01c6c4b949e..7b3cbd2cad724 100644
--- a/bolt/unittests/Profile/CMakeLists.txt
+++ b/bolt/unittests/Profile/CMakeLists.txt
@@ -16,7 +16,6 @@ target_link_libraries(ProfileTests
LLVMBOLTCore
LLVMBOLTProfile
LLVMTargetParser
- LLVMTestingSupport
)
foreach (tgt ${BOLT_TARGETS_TO_BUILD})
diff --git a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
index 3e367ab1a5558..471ca45fb5a53 100644
--- a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
+++ b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
@@ -31,24 +31,9 @@ llvm::SmallVector splitSymbolName(llvm::StringRef Name) {
return Splitted;
}
-SourceLocation startLocationForType(TypeLoc TLoc) {
- // For elaborated types (e.g. `struct a::A`) we want the portion after the
- // `struct` but including the namespace qualifier, `a::`.
- if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
- NestedNameSpecifierLoc NestedNameSpecifier =
- TLoc.castAs().getQualifierLoc();
- if (NestedNameSpecifier.getNestedNameSpecifier())
- return NestedNameSpecifier.getBeginLoc();
- TLoc = TLoc.getNextTypeLoc();
- }
- return TLoc.getBeginLoc();
-}
-
SourceLocation endLocationForType(TypeLoc TLoc) {
- // Dig past any namespace or keyword qualifications.
- while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
- TLoc.getTypeLocClass() == TypeLoc::Qualified)
- TLoc = TLoc.getNextTypeLoc();
+ if (auto QTL = TLoc.getAs())
+ TLoc = QTL.getUnqualifiedLoc();
// The location for template specializations (e.g. Foo) includes the
// templated types in its location range. We want to restrict this to just
@@ -550,8 +535,8 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs(
"nested_specifier_loc")) {
SourceLocation Start = Specifier->getBeginLoc();
- SourceLocation End = endLocationForType(Specifier->getTypeLoc());
- fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
+ SourceLocation End = endLocationForType(Specifier->castAsTypeLoc());
+ fixTypeLoc(Result, Start, End, Specifier->castAsTypeLoc());
} else if (const auto *BaseInitializer =
Result.Nodes.getNodeAs(
"base_initializer")) {
@@ -562,19 +547,16 @@ void ChangeNamespaceTool::run(
// filtered by matchers in some cases, e.g. the type is templated. We should
// handle the record type qualifier instead.
TypeLoc Loc = *TLoc;
- while (Loc.getTypeLocClass() == TypeLoc::Qualified)
- Loc = Loc.getNextTypeLoc();
- if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
- NestedNameSpecifierLoc NestedNameSpecifier =
- Loc.castAs().getQualifierLoc();
- // FIXME: avoid changing injected class names.
- if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) {
- const Type *SpecifierType = NNS->getAsType();
- if (SpecifierType && SpecifierType->isRecordType())
- return;
- }
- }
- fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
+ if (auto QTL = Loc.getAs())
+ Loc = QTL.getUnqualifiedLoc();
+ // FIXME: avoid changing injected class names.
+ if (NestedNameSpecifier NestedNameSpecifier =
+ Loc.getPrefix().getNestedNameSpecifier();
+ NestedNameSpecifier.getKind() == NestedNameSpecifier::Kind::Type &&
+ NestedNameSpecifier.getAsType()->isRecordType())
+ return;
+ fixTypeLoc(Result, Loc.getNonElaboratedBeginLoc(), endLocationForType(Loc),
+ Loc);
} else if (const auto *VarRef =
Result.Nodes.getNodeAs("var_ref")) {
const auto *Var = Result.Nodes.getNodeAs("var_decl");
@@ -588,10 +570,9 @@ void ChangeNamespaceTool::run(
} else if (const auto *EnumConstRef =
Result.Nodes.getNodeAs("enum_const_ref")) {
// Do not rename the reference if it is already scoped by the EnumDecl name.
- if (EnumConstRef->hasQualifier() &&
- EnumConstRef->getQualifier()->getKind() ==
- NestedNameSpecifier::SpecifierKind::TypeSpec &&
- EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
+ if (NestedNameSpecifier Qualifier = EnumConstRef->getQualifier();
+ Qualifier.getKind() == NestedNameSpecifier::Kind::Type &&
+ Qualifier.getAsType()->isEnumeralType())
return;
const auto *EnumConstDecl =
Result.Nodes.getNodeAs("enum_const_decl");
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index a64cb5ea26a79..1ab40aacbfe09 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -144,17 +144,22 @@ Error MustacheHTMLGenerator::generateDocs(
} else
return JSONGenerator.takeError();
}
+ SmallString<128> JSONPath;
+ sys::path::native(RootDir.str() + "/json", JSONPath);
StringMap JSONFileMap;
{
llvm::TimeTraceScope TS("Iterate JSON files");
std::error_code EC;
- sys::fs::directory_iterator JSONIter(RootDir, EC);
+ sys::fs::directory_iterator JSONIter(JSONPath, EC);
std::vector JSONFiles;
JSONFiles.reserve(Infos.size());
if (EC)
return createStringError("Failed to create directory iterator.");
+ SmallString<128> HTMLDirPath(RootDir.str() + "/html/");
+ if (auto EC = sys::fs::create_directories(HTMLDirPath))
+ return createFileError(HTMLDirPath, EC);
while (JSONIter != sys::fs::directory_iterator()) {
if (EC)
return createFileError("Failed to iterate: " + JSONIter->path(), EC);
@@ -177,14 +182,15 @@ Error MustacheHTMLGenerator::generateDocs(
return Parsed.takeError();
std::error_code FileErr;
- SmallString<16> HTMLPath(Path.begin(), Path.end());
- sys::path::replace_extension(HTMLPath, "html");
- raw_fd_ostream InfoOS(HTMLPath, FileErr, sys::fs::OF_None);
+ SmallString<128> HTMLFilePath(HTMLDirPath);
+ sys::path::append(HTMLFilePath, sys::path::filename(Path));
+ sys::path::replace_extension(HTMLFilePath, "html");
+ raw_fd_ostream InfoOS(HTMLFilePath, FileErr, sys::fs::OF_None);
if (FileErr)
return createFileOpenError(Path, FileErr);
- if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLPath),
- HTMLPath, InfoOS, CDCtx))
+ if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLFilePath),
+ HTMLFilePath, InfoOS, CDCtx))
return Err;
JSONIter.increment(EC);
}
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 599b381cea60d..26794a5e34d02 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -600,7 +600,9 @@ Error JSONGenerator::generateDocs(
Info *Info = Group.getValue().get();
SmallString<128> Path;
- sys::path::native(RootDir, Path);
+ auto RootDirStr = RootDir.str() + "/json";
+ StringRef JSONDir = StringRef(RootDirStr);
+ sys::path::native(JSONDir, Path);
if (!CreatedDirs.contains(Path)) {
if (std::error_code Err = sys::fs::create_directories(Path);
Err != std::error_code())
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index de73f68b09386..bcab4f1b8a729 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -902,8 +902,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
return;
for (const CXXBaseSpecifier &B : D->bases()) {
if (const RecordType *Ty = B.getType()->getAs()) {
- if (const CXXRecordDecl *Base =
- cast_or_null(Ty->getDecl()->getDefinition())) {
+ if (const CXXRecordDecl *Base = cast_or_null(
+ Ty->getOriginalDecl()->getDefinition())) {
// Initialized without USR and name, this will be set in the following
// if-else stmt.
BaseRecordInfo BI(
diff --git a/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp b/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp
index bb48883f88815..1f30d27c0a54f 100644
--- a/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp
+++ b/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp
@@ -216,8 +216,7 @@ void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
// Uses of most types: just look at what the typeLoc refers to.
MatchFinder->addMatcher(
typeLoc(isExpansionInMainFile(),
- loc(qualType(allOf(unless(elaboratedType()),
- hasDeclaration(Types.bind("use")))))),
+ loc(qualType(hasDeclaration(Types.bind("use"))))),
this);
// Uses of typedefs: these are often transparent to hasDeclaration, so we need
// to handle them explicitly.
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 4ae2864d310d0..2064c7826da0c 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -424,6 +424,10 @@ ClangTidyASTConsumerFactory::createASTConsumer(
FinderOptions.CheckProfiling.emplace(Profiling->Records);
}
+ // Avoid processing system headers, unless the user explicitly requests it
+ if (!Context.getOptions().SystemHeaders.value_or(false))
+ FinderOptions.IgnoreSystemHeaders = true;
+
std::unique_ptr Finder(
new ast_matchers::MatchFinder(std::move(FinderOptions)));
@@ -540,7 +544,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
ArrayRef InputFiles,
llvm::IntrusiveRefCntPtr BaseFS,
bool ApplyAnyFix, bool EnableCheckProfile,
- llvm::StringRef StoreCheckProfile) {
+ llvm::StringRef StoreCheckProfile, bool Quiet) {
ClangTool Tool(Compilations, InputFiles,
std::make_shared(), BaseFS);
@@ -577,8 +581,9 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
class ActionFactory : public FrontendActionFactory {
public:
ActionFactory(ClangTidyContext &Context,
- IntrusiveRefCntPtr BaseFS)
- : ConsumerFactory(Context, std::move(BaseFS)) {}
+ IntrusiveRefCntPtr BaseFS,
+ bool Quiet)
+ : ConsumerFactory(Context, std::move(BaseFS)), Quiet(Quiet) {}
std::unique_ptr create() override {
return std::make_unique(&ConsumerFactory);
}
@@ -589,6 +594,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
DiagnosticConsumer *DiagConsumer) override {
// Explicitly ask to define __clang_analyzer__ macro.
Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;
+ if (Quiet)
+ Invocation->getDiagnosticOpts().ShowCarets = false;
return FrontendActionFactory::runInvocation(
Invocation, Files, PCHContainerOps, DiagConsumer);
}
@@ -607,9 +614,10 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
};
ClangTidyASTConsumerFactory ConsumerFactory;
+ bool Quiet;
};
- ActionFactory Factory(Context, std::move(BaseFS));
+ ActionFactory Factory(Context, std::move(BaseFS), Quiet);
Tool.run(&Factory);
return DiagConsumer.take();
}
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h
index 454261bbd6840..d37d68ec0a5b9 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.h
+++ b/clang-tools-extra/clang-tidy/ClangTidy.h
@@ -94,7 +94,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
ArrayRef InputFiles,
llvm::IntrusiveRefCntPtr BaseFS,
bool ApplyAnyFix, bool EnableCheckProfile = false,
- llvm::StringRef StoreCheckProfile = StringRef());
+ llvm::StringRef StoreCheckProfile = StringRef(),
+ bool Quiet = false);
/// Controls what kind of fixes clang-tidy is allowed to apply.
enum FixBehaviour {
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index f9d75978d0ea8..fac6e0418d163 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -533,7 +533,8 @@ void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) {
Builder << reinterpret_cast(Info.getRawArg(Index));
break;
case clang::DiagnosticsEngine::ak_nestednamespec:
- Builder << reinterpret_cast(Info.getRawArg(Index));
+ Builder << NestedNameSpecifier::getFromVoidPointer(
+ reinterpret_cast(Info.getRawArg(Index)));
break;
case clang::DiagnosticsEngine::ak_declcontext:
Builder << reinterpret_cast(Info.getRawArg(Index));
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h
index 7f6b652058cfd..ac71f34fed180 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h
@@ -22,6 +22,9 @@ class DurationAdditionCheck : public ClangTidyCheck {
public:
DurationAdditionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h
index d759e1d8bb789..65ab7a38eb289 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h
@@ -22,6 +22,9 @@ class DurationComparisonCheck : public ClangTidyCheck {
public:
DurationComparisonCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h
index fea9f703bae65..a898ba0483966 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h
@@ -22,6 +22,9 @@ class DurationConversionCastCheck : public ClangTidyCheck {
public:
DurationConversionCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h
index 6394b3f2b4675..e7c3985a7fd92 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h
@@ -24,6 +24,9 @@ class DurationFactoryFloatCheck : public ClangTidyCheck {
public:
DurationFactoryFloatCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h
index 40ffb30b4769c..f5f088c49897d 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h
@@ -24,6 +24,9 @@ class DurationFactoryScaleCheck : public ClangTidyCheck {
public:
DurationFactoryScaleCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h
index 17f7853adabd6..c865f2f842a0d 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h
@@ -22,6 +22,9 @@ class DurationSubtractionCheck : public ClangTidyCheck {
public:
DurationSubtractionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h
index aa25c5f43e0f0..fc9cf23459425 100644
--- a/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h
@@ -22,6 +22,9 @@ class DurationUnnecessaryConversionCheck : public ClangTidyCheck {
public:
DurationUnnecessaryConversionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h b/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h
index 0c64708758b46..bf22977e9d0df 100644
--- a/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h
@@ -22,6 +22,9 @@ class TimeComparisonCheck : public ClangTidyCheck {
public:
TimeComparisonCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h b/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h
index c947f6bca7f31..9e2ec1c8def20 100644
--- a/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h
+++ b/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h
@@ -22,6 +22,9 @@ class TimeSubtractionCheck : public ClangTidyCheck {
public:
TimeSubtractionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
diff --git a/clang-tools-extra/clang-tidy/add_new_check.py b/clang-tools-extra/clang-tidy/add_new_check.py
index e366f10053535..2b51a1dc40ebc 100755
--- a/clang-tools-extra/clang-tidy/add_new_check.py
+++ b/clang-tools-extra/clang-tidy/add_new_check.py
@@ -89,13 +89,9 @@ def write_header(
+ check_name_camel.upper()
+ "_H"
)
- f.write("//===--- ")
- f.write(os.path.basename(filename))
- f.write(" - clang-tidy ")
- f.write("-" * max(0, 42 - len(os.path.basename(filename))))
- f.write("*- C++ -*-===//")
f.write(
"""
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -145,13 +141,9 @@ def write_implementation(
filename = os.path.join(module_path, check_name_camel) + ".cpp"
print("Creating %s..." % filename)
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
- f.write("//===--- ")
- f.write(os.path.basename(filename))
- f.write(" - clang-tidy ")
- f.write("-" * max(0, 51 - len(os.path.basename(filename))))
- f.write("-===//")
f.write(
"""
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp
index 6565fa3f7c85b..0625468d9da88 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp
@@ -43,7 +43,8 @@ static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP,
return false;
}
- return FriendType->getType()->getAsCXXRecordDecl() == Derived;
+ return declaresSameEntity(FriendType->getType()->getAsCXXRecordDecl(),
+ Derived);
});
}
@@ -55,7 +56,8 @@ getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP,
CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) {
++Idx;
return Arg.getKind() == TemplateArgument::Type &&
- Arg.getAsType()->getAsCXXRecordDecl() == Derived;
+ declaresSameEntity(Arg.getAsType()->getAsCXXRecordDecl(),
+ Derived);
});
return AnyOf ? CRTP->getSpecializedTemplate()
diff --git a/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h b/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h
index 3044304892029..981e9b571a618 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h
@@ -13,14 +13,16 @@
namespace clang::tidy::bugprone {
-/// Detect dangling references in value handlers like
-/// std::experimental::string_view.
+/// Detect dangling references in value handlers like std::string_view.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/dangling-handle.html
class DanglingHandleCheck : public ClangTidyCheck {
public:
DanglingHandleCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
diff --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
index a179d4bf66b4d..7f1eeef8ea0fd 100644
--- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
@@ -577,7 +577,7 @@ approximateImplicitConversion(const TheCheck &Check, QualType LType,
ImplicitConversionModellingMode ImplicitMode);
static inline bool isUselessSugar(const Type *T) {
- return isa(T);
+ return isa(T);
}
namespace {
@@ -1040,7 +1040,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
const auto *ToRecord = To->getAsCXXRecordDecl();
if (isDerivedToBase(FromRecord, ToRecord)) {
LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n");
- WorkType = QualType{ToRecord->getTypeForDecl(), FastQualifiersToApply};
+ WorkType = QualType{
+ ToRecord->getASTContext().getCanonicalTagType(ToRecord)->getTypePtr(),
+ FastQualifiersToApply};
}
if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
@@ -1072,9 +1074,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
WorkType = To;
}
- if (WorkType == To) {
+ if (Ctx.hasSameType(WorkType, To)) {
LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n");
- return {WorkType};
+ return {Ctx.getCommonSugaredType(WorkType, To)};
}
LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n");
@@ -1219,7 +1221,7 @@ tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD,
if (std::optional
SelectedConversion = ConversionSet()) {
- QualType RecordType{RD->getTypeForDecl(), 0};
+ CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
ConversionSequence Result{RecordType, ToType};
// The conversion from the operator call's return type to ToType was
@@ -1270,7 +1272,7 @@ tryConvertingConstructors(const TheCheck &Check, QualType FromType,
if (std::optional
SelectedConversion = ConversionSet()) {
- QualType RecordType{RD->getTypeForDecl(), 0};
+ CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
ConversionSequence Result{FromType, RecordType};
Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
@@ -1573,6 +1575,10 @@ template
using ParamToSmallSetMap =
llvm::DenseMap>;
+template
+using ParamToSmallPtrSetMap =
+ llvm::DenseMap>;
+
/// Returns whether the sets mapped to the two elements in the map have at
/// least one element in common.
template
@@ -1697,7 +1703,7 @@ class PassedToSameFunction {
/// Implements the heuristic that marks two parameters related if the same
/// member is accessed (referred to) inside the current function's body.
class AccessedSameMemberOf {
- ParamToSmallSetMap AccessedMembers;
+ ParamToSmallPtrSetMap AccessedMembers;
public:
void setup(const FunctionDecl *FD) {
diff --git a/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h
index af8c1e50ef86c..435c440ddd29f 100644
--- a/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h
@@ -26,6 +26,9 @@ class FoldInitTypeCheck : public ClangTidyCheck {
public:
FoldInitTypeCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp
index 75ef628436738..070ed04efffc4 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp
@@ -69,10 +69,9 @@ void ForwardDeclarationNamespaceCheck::check(
// struct B { friend A; };
// \endcode
// `A` will not be marked as "referenced" in the AST.
- if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
- QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
- FriendTypes.insert(Desugared.getTypePtr());
- }
+ if (const TypeSourceInfo *Tsi = Decl->getFriendType())
+ FriendTypes.insert(
+ Tsi->getType()->getCanonicalTypeUnqualified().getTypePtr());
}
}
@@ -119,7 +118,9 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
continue; // Skip forward declarations that are used/referenced.
}
- if (FriendTypes.contains(CurDecl->getTypeForDecl())) {
+ if (FriendTypes.contains(CurDecl->getASTContext()
+ .getCanonicalTagType(CurDecl)
+ ->getTypePtr())) {
continue; // Skip forward declarations referenced as friend.
}
if (CurDecl->getLocation().isMacroID() ||
diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp
index 00e8f7e514368..10b747e17e2ad 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp
@@ -33,21 +33,17 @@ AST_MATCHER(QualType, isEnableIf) {
BaseType = BaseType->getPointeeType().getTypePtr();
}
// Case: type parameter dependent (enable_if>).
- if (const auto *Dependent = BaseType->getAs()) {
- BaseType = Dependent->getQualifier()->getAsType();
- }
+ if (const auto *Dependent = BaseType->getAs())
+ BaseType = Dependent->getQualifier().getAsType();
if (!BaseType)
return false;
if (CheckTemplate(BaseType->getAs()))
return true; // Case: enable_if_t< >.
- if (const auto *Elaborated = BaseType->getAs()) {
- if (const auto *Q = Elaborated->getQualifier())
- if (const auto *Qualifier = Q->getAsType()) {
- if (CheckTemplate(Qualifier->getAs())) {
- return true; // Case: enable_if< >::type.
- }
- }
- }
+ if (const auto *TT = BaseType->getAs())
+ if (NestedNameSpecifier Q = TT->getQualifier();
+ Q.getKind() == NestedNameSpecifier::Kind::Type)
+ if (CheckTemplate(Q.getAsType()->getAs()))
+ return true; // Case: enable_if< >::type.
return false;
}
AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
diff --git a/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp
index 75f1107904fce..07cd90d64c2a4 100644
--- a/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp
@@ -32,13 +32,10 @@ AST_MATCHER_P(TemplateTypeParmDecl, hasUnnamedDefaultArgument,
void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
templateTypeParmDecl(
- hasUnnamedDefaultArgument(
- elaboratedTypeLoc(
- hasNamedTypeLoc(templateSpecializationTypeLoc(
- loc(qualType(hasDeclaration(namedDecl(
- hasName("::std::enable_if"))))))
- .bind("enable_if_specialization")))
- .bind("elaborated")))
+ hasUnnamedDefaultArgument(templateSpecializationTypeLoc(
+ loc(qualType(hasDeclaration(namedDecl(
+ hasName("::std::enable_if"))))))
+ .bind("enable_if_specialization")))
.bind("enable_if"),
this);
}
@@ -46,13 +43,11 @@ void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) {
void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) {
const auto *EnableIf =
Result.Nodes.getNodeAs("enable_if");
- const auto *ElaboratedLoc =
- Result.Nodes.getNodeAs("elaborated");
const auto *EnableIfSpecializationLoc =
Result.Nodes.getNodeAs(
"enable_if_specialization");
- if (!EnableIf || !ElaboratedLoc || !EnableIfSpecializationLoc)
+ if (!EnableIf || !EnableIfSpecializationLoc)
return;
const SourceManager &SM = *Result.SourceManager;
@@ -62,8 +57,10 @@ void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) {
auto Diag = diag(EnableIf->getBeginLoc(),
"incorrect std::enable_if usage detected; use "
"'typename std::enable_if<...>::type'");
+ // FIXME: This should handle the enable_if specialization already having an
+ // elaborated keyword.
if (!getLangOpts().CPlusPlus20) {
- Diag << FixItHint::CreateInsertion(ElaboratedLoc->getBeginLoc(),
+ Diag << FixItHint::CreateInsertion(EnableIfSpecializationLoc->getBeginLoc(),
"typename ");
}
Diag << FixItHint::CreateInsertion(RAngleLoc.getLocWithOffset(1), "::type");
diff --git a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
index 4b495e3877000..cda9c4e7a6e58 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
@@ -188,7 +188,7 @@ static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx,
/// \return true iff all `CallExprs` visited have callees; false otherwise
/// indicating there is an unresolved indirect call.
static bool populateCallees(const Stmt *StmtNode,
- llvm::SmallSet &Callees) {
+ llvm::SmallPtrSet &Callees) {
if (const auto *Call = dyn_cast(StmtNode)) {
const Decl *Callee = Call->getDirectCallee();
@@ -212,7 +212,7 @@ static bool populateCallees(const Stmt *StmtNode,
/// returns true iff `SCC` contains `Func` and its' function set overlaps with
/// `Callees`
static bool overlap(ArrayRef SCC,
- const llvm::SmallSet &Callees,
+ const llvm::SmallPtrSet &Callees,
const Decl *Func) {
bool ContainsFunc = false, Overlap = false;
@@ -264,7 +264,7 @@ static bool hasRecursionOverStaticLoopCondVariables(const Expr *Cond,
if (!hasStaticLocalVariable(Cond))
return false;
- llvm::SmallSet CalleesInLoop;
+ llvm::SmallPtrSet CalleesInLoop;
if (!populateCallees(LoopStmt, CalleesInLoop)) {
// If there are unresolved indirect calls, we assume there could
diff --git a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
index f903e631e0be0..7d92ef301aec3 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
@@ -67,15 +67,15 @@ class FindEnumMember : public TypeVisitor {
return Visit(T->getElementType().getTypePtr());
}
bool VisitEnumType(const EnumType *T) {
- if (isCompleteAndHasNoZeroValue(T->getDecl())) {
+ if (isCompleteAndHasNoZeroValue(T->getOriginalDecl())) {
FoundEnum = T;
return true;
}
return false;
}
bool VisitRecordType(const RecordType *T) {
- const RecordDecl *RD = T->getDecl();
- if (RD->isUnion())
+ const RecordDecl *RD = T->getOriginalDecl()->getDefinition();
+ if (!RD || RD->isUnion())
return false;
auto VisitField = [this](const FieldDecl *F) {
return Visit(F->getType().getTypePtr());
@@ -125,7 +125,7 @@ void InvalidEnumDefaultInitializationCheck::check(
if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr()))
return;
InitExpr = InitList;
- Enum = Finder.FoundEnum->getDecl();
+ Enum = Finder.FoundEnum->getOriginalDecl();
}
if (!InitExpr || !Enum)
diff --git a/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h b/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h
index dab64f74aa6ca..04ba3596167e3 100644
--- a/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h
@@ -32,6 +32,9 @@ class LambdaFunctionNameCheck : public ClangTidyCheck {
using SourceRangeSet = std::set;
LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
diff --git a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp
index bfa2ab51a6d03..5dc988d6662df 100644
--- a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp
@@ -39,24 +39,31 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
// std::move(). This will hopefully prevent erroneous replacements if the
// code does unusual things (e.g. create an alias for std::move() in
// another namespace).
- NestedNameSpecifier *NNS = Callee->getQualifier();
- if (!NNS) {
+ NestedNameSpecifier NNS = Callee->getQualifier();
+ switch (NNS.getKind()) {
+ case NestedNameSpecifier::Kind::Null:
// Called as "move" (i.e. presumably the code had a "using std::move;").
// We still conservatively put a "std::" in front of the forward because
// we don't know whether the code also had a "using std::forward;".
Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
- } else if (const NamespaceBaseDecl *Namespace = NNS->getAsNamespace()) {
+ break;
+ case NestedNameSpecifier::Kind::Namespace: {
+ auto [Namespace, Prefix] = NNS.getAsNamespaceAndPrefix();
if (Namespace->getName() == "std") {
- if (!NNS->getPrefix()) {
+ if (!Prefix) {
// Called as "std::move".
Diag << FixItHint::CreateReplacement(CallRange,
"std::" + ForwardName);
- } else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) {
+ } else if (Prefix.getKind() == NestedNameSpecifier::Kind::Global) {
// Called as "::std::move".
Diag << FixItHint::CreateReplacement(CallRange,
"::std::" + ForwardName);
}
}
+ break;
+ }
+ default:
+ return;
}
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp
index b68888cb5b928..6344b4bb6271e 100644
--- a/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp
@@ -15,14 +15,12 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-namespace {
-
// Determine if the result of an expression is "stored" in some way.
// It is true if the value is stored into a variable or used as initialization
// or passed to a function or constructor.
// For this use case compound assignments are not counted as a "store" (the 'E'
// expression should have pointer type).
-bool isExprValueStored(const Expr *E, ASTContext &C) {
+static bool isExprValueStored(const Expr *E, ASTContext &C) {
E = E->IgnoreParenCasts();
// Get first non-paren, non-cast parent.
ParentMapContext &PMap = C.getParentMapContext();
@@ -49,6 +47,8 @@ bool isExprValueStored(const Expr *E, ASTContext &C) {
return isa(ParentE);
}
+namespace {
+
AST_MATCHER_P(CXXTryStmt, hasHandlerFor,
ast_matchers::internal::Matcher, InnerMatcher) {
for (unsigned NH = Node.getNumHandlers(), I = 0; I < NH; ++I) {
diff --git a/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.cpp
index 53782231b6421..249c77ca0c432 100644
--- a/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.cpp
@@ -555,15 +555,22 @@ bool NarrowingConversionsCheck::handleConditionalOperator(
// We have an expression like so: `output = cond ? lhs : rhs`
// From the point of view of narrowing conversion we treat it as two
// expressions `output = lhs` and `output = rhs`.
- handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
- *CO->getLHS());
- handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
- *CO->getRHS());
+ handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());
+ handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());
return true;
}
return false;
}
+void NarrowingConversionsCheck::handleConditionalOperatorArgument(
+ const ASTContext &Context, const Expr &Lhs, const Expr *Arg) {
+ if (const auto *ICE = llvm::dyn_cast(Arg))
+ if (!Arg->getIntegerConstantExpr(Context))
+ Arg = ICE->getSubExpr();
+
+ handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);
+}
+
void NarrowingConversionsCheck::handleImplicitCast(
const ASTContext &Context, const ImplicitCastExpr &Cast) {
if (Cast.getExprLoc().isMacroID())
diff --git a/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.h b/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.h
index 20403f920b925..116a8cba8d321 100644
--- a/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.h
@@ -85,6 +85,8 @@ class NarrowingConversionsCheck : public ClangTidyCheck {
bool handleConditionalOperator(const ASTContext &Context, const Expr &Lhs,
const Expr &Rhs);
+ void handleConditionalOperatorArgument(const ASTContext &Context,
+ const Expr &Lhs, const Expr *Arg);
void handleImplicitCast(const ASTContext &Context,
const ImplicitCastExpr &Cast);
diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h
index b044219948988..888d29fc937bd 100644
--- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h
@@ -23,6 +23,9 @@ namespace clang::tidy::bugprone {
class OptionalValueConversionCheck : public ClangTidyCheck {
public:
OptionalValueConversionCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
diff --git a/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h b/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h
index f552965e1876b..293069fd24665 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h
@@ -21,6 +21,9 @@ class ParentVirtualCallCheck : public ClangTidyCheck {
public:
ParentVirtualCallCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h b/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h
index 7227c3d106f43..f50ce99c6d4c0 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h
@@ -22,6 +22,9 @@ class SizeofContainerCheck : public ClangTidyCheck {
public:
SizeofContainerCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
index 88e048e65d4e8..8da6227e172cd 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
@@ -425,7 +425,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
"suspicious usage of 'sizeof(array)/sizeof(...)';"
" denominator differs from the size of array elements")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
- } else if (NumTy && DenomTy && NumTy == DenomTy &&
+ } else if (NumTy && DenomTy && Ctx.hasSameType(NumTy, DenomTy) &&
!NumTy->isDependentType()) {
// Dependent type should not be compared.
diag(E->getOperatorLoc(),
@@ -434,7 +434,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (!WarnOnSizeOfPointer) {
// When 'WarnOnSizeOfPointer' is enabled, these messages become redundant:
- if (PointedTy && DenomTy && PointedTy == DenomTy) {
+ if (PointedTy && DenomTy && Ctx.hasSameType(PointedTy, DenomTy)) {
diag(E->getOperatorLoc(),
"suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer "
"is divided by size of pointed type")
@@ -463,7 +463,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *SizeOfExpr =
Result.Nodes.getNodeAs("sizeof-ptr-mul-expr");
- if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
+ if (Ctx.hasSameType(LPtrTy, RPtrTy) &&
+ Ctx.hasSameType(LPtrTy, SizeofArgTy)) {
diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
"pointer arithmetic")
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc()
@@ -477,7 +478,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *SizeOfExpr =
Result.Nodes.getNodeAs("sizeof-ptr-div-expr");
- if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
+ if (Ctx.hasSameType(LPtrTy, RPtrTy) &&
+ Ctx.hasSameType(LPtrTy, SizeofArgTy)) {
diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
"pointer arithmetic")
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc()
diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp
index 81c38d07a0c7e..5b1b28dbfbadd 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp
@@ -14,10 +14,8 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-namespace {
-
-bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
- const StringLiteral *Lit) {
+static bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
+ const StringLiteral *Lit) {
// String literals surrounded by parentheses are assumed to be on purpose.
// i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] };
@@ -58,6 +56,8 @@ bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
return false;
}
+namespace {
+
AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,
MaxConcatenatedTokens) {
return Node.getNumConcatenated() > 1 &&
diff --git a/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp b/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp
index 3b59d2357fe29..95536bb1cfdb2 100644
--- a/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp
@@ -46,7 +46,9 @@ enum class ConversionKind {
ToLongDouble
};
-ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
+} // namespace
+
+static ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
return llvm::StringSwitch(FD->getName())
.Cases("atoi", "atol", ConversionKind::ToInt)
.Case("atoll", ConversionKind::ToLongInt)
@@ -54,8 +56,8 @@ ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
.Default(ConversionKind::None);
}
-ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
- const TargetInfo &TI) {
+static ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
+ const TargetInfo &TI) {
// Scan the format string for the first problematic format specifier, then
// report that as the conversion type. This will miss additional conversion
// specifiers, but that is acceptable behavior.
@@ -128,7 +130,7 @@ ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
return H.get();
}
-StringRef classifyConversionType(ConversionKind K) {
+static StringRef classifyConversionType(ConversionKind K) {
switch (K) {
case ConversionKind::None:
llvm_unreachable("Unexpected conversion kind");
@@ -148,7 +150,7 @@ StringRef classifyConversionType(ConversionKind K) {
llvm_unreachable("Unknown conversion kind");
}
-StringRef classifyReplacement(ConversionKind K) {
+static StringRef classifyReplacement(ConversionKind K) {
switch (K) {
case ConversionKind::None:
llvm_unreachable("Unexpected conversion kind");
@@ -173,7 +175,6 @@ StringRef classifyReplacement(ConversionKind K) {
}
llvm_unreachable("Unknown conversion kind");
}
-} // unnamed namespace
void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Call = Result.Nodes.getNodeAs("expr");
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
index 2fb4d7f1d7349..0abb000991859 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule STATIC
OwningMemoryCheck.cpp
PreferMemberInitializerCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp
+ ProBoundsAvoidUncheckedContainerAccess.cpp
ProBoundsConstantArrayIndexCheck.cpp
ProBoundsPointerArithmeticCheck.cpp
ProTypeConstCastCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
index 4b3b7bf963fdc..cc1ae156eef3e 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -36,6 +36,7 @@
#include "OwningMemoryCheck.h"
#include "PreferMemberInitializerCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
+#include "ProBoundsAvoidUncheckedContainerAccess.h"
#include "ProBoundsConstantArrayIndexCheck.h"
#include "ProBoundsPointerArithmeticCheck.h"
#include "ProTypeConstCastCheck.h"
@@ -107,6 +108,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
"cppcoreguidelines-prefer-member-initializer");
CheckFactories.registerCheck(
"cppcoreguidelines-pro-bounds-array-to-pointer-decay");
+ CheckFactories.registerCheck(
+ "cppcoreguidelines-pro-bounds-avoid-unchecked-container-access");
CheckFactories.registerCheck(
"cppcoreguidelines-pro-bounds-constant-array-index");
CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp
index ca293178c78b4..29470b1f725fb 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp
@@ -23,9 +23,9 @@ void NoSuspendWithLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) {
- auto LockType = elaboratedType(namesType(templateSpecializationType(
+ auto LockType = templateSpecializationType(
hasDeclaration(namedDecl(matchers::matchesAnyListedName(
- utils::options::parseStringList(LockGuards)))))));
+ utils::options::parseStringList(LockGuards)))));
StatementMatcher Lock =
declStmt(has(varDecl(hasType(LockType)).bind("lock-decl")))
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp
index 593a4f85d1309..79cd4bbcc9a60 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp
@@ -191,6 +191,9 @@ void PreferMemberInitializerCheck::check(
if (!AssignmentToMember)
continue;
const FieldDecl *Field = AssignmentToMember->Field;
+ // Skip if the field is inherited from a base class.
+ if (Field->getParent() != Class)
+ continue;
const Expr *InitValue = AssignmentToMember->Init;
updateAssignmentLevel(Field, InitValue, Ctor, AssignedFields);
if (!canAdvanceAssignment(AssignedFields[Field]))
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.cpp
new file mode 100644
index 0000000000000..35f432efa88ca
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.cpp
@@ -0,0 +1,262 @@
+//===--- ProBoundsAvoidUncheckedContainerAccess.cpp - clang-tidy ----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProBoundsAvoidUncheckedContainerAccess.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::cppcoreguidelines {
+
+static constexpr llvm::StringRef DefaultExclusionStr =
+ "::std::map;::std::unordered_map;::std::flat_map";
+
+ProBoundsAvoidUncheckedContainerAccess::ProBoundsAvoidUncheckedContainerAccess(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ ExcludedClasses(utils::options::parseStringList(
+ Options.get("ExcludeClasses", DefaultExclusionStr))),
+ FixMode(Options.get("FixMode", None)),
+ FixFunction(Options.get("FixFunction", "gsl::at")),
+ FixFunctionEmptyArgs(Options.get("FixFunctionEmptyArgs", FixFunction)) {}
+
+void ProBoundsAvoidUncheckedContainerAccess::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ExcludeClasses",
+ utils::options::serializeStringList(ExcludedClasses));
+ Options.store(Opts, "FixMode", FixMode);
+ Options.store(Opts, "FixFunction", FixFunction);
+ Options.store(Opts, "FixFunctionEmptyArgs", FixFunctionEmptyArgs);
+}
+
+// TODO: if at() is defined in another class in the class hierarchy of the class
+// that defines the operator[] we matched on, findAlternative() will not detect
+// it.
+static const CXXMethodDecl *
+findAlternativeAt(const CXXMethodDecl *MatchedOperator) {
+ const CXXRecordDecl *Parent = MatchedOperator->getParent();
+ const QualType SubscriptThisObjType =
+ MatchedOperator->getFunctionObjectParameterReferenceType();
+
+ for (const CXXMethodDecl *Method : Parent->methods()) {
+ // Require 'Method' to be as accessible as 'MatchedOperator' or more
+ if (MatchedOperator->getAccess() < Method->getAccess())
+ continue;
+
+ if (MatchedOperator->isConst() != Method->isConst())
+ continue;
+
+ const QualType AtThisObjType =
+ Method->getFunctionObjectParameterReferenceType();
+ if (SubscriptThisObjType != AtThisObjType)
+ continue;
+
+ if (!Method->getNameInfo().getName().isIdentifier() ||
+ Method->getName() != "at")
+ continue;
+
+ const bool SameReturnType =
+ Method->getReturnType() == MatchedOperator->getReturnType();
+ if (!SameReturnType)
+ continue;
+
+ const bool SameNumberOfArguments =
+ Method->getNumParams() == MatchedOperator->getNumParams();
+ if (!SameNumberOfArguments)
+ continue;
+
+ for (unsigned ArgInd = 0; ArgInd < Method->getNumParams(); ArgInd++) {
+ const bool SameArgType =
+ Method->parameters()[ArgInd]->getOriginalType() ==
+ MatchedOperator->parameters()[ArgInd]->getOriginalType();
+ if (!SameArgType)
+ continue;
+ }
+
+ return Method;
+ }
+ return nullptr;
+}
+
+void ProBoundsAvoidUncheckedContainerAccess::registerMatchers(
+ MatchFinder *Finder) {
+ Finder->addMatcher(
+ mapAnyOf(cxxOperatorCallExpr, cxxMemberCallExpr)
+ .with(callee(
+ cxxMethodDecl(
+ hasOverloadedOperatorName("[]"),
+ anyOf(parameterCountIs(0), parameterCountIs(1)),
+ unless(matchers::matchesAnyListedName(ExcludedClasses)))
+ .bind("operator")))
+ .bind("caller"),
+ this);
+}
+
+void ProBoundsAvoidUncheckedContainerAccess::check(
+ const MatchFinder::MatchResult &Result) {
+
+ const auto *MatchedExpr = Result.Nodes.getNodeAs("caller");
+
+ if (FixMode == None) {
+ diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]', consider bounds-safe alternatives")
+ << MatchedExpr->getCallee()->getSourceRange();
+ return;
+ }
+
+ if (const auto *OCE = dyn_cast(MatchedExpr)) {
+ // Case: a[i]
+ const auto LeftBracket = SourceRange(OCE->getCallee()->getBeginLoc(),
+ OCE->getCallee()->getBeginLoc());
+ const auto RightBracket =
+ SourceRange(OCE->getOperatorLoc(), OCE->getOperatorLoc());
+
+ if (FixMode == At) {
+ // Case: a[i] => a.at(i)
+ const auto *MatchedOperator =
+ Result.Nodes.getNodeAs("operator");
+ const CXXMethodDecl *Alternative = findAlternativeAt(MatchedOperator);
+
+ if (!Alternative) {
+ diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]', consider "
+ "bounds-safe alternatives")
+ << MatchedExpr->getCallee()->getSourceRange();
+ return;
+ }
+
+ diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]', consider "
+ "bounds-safe alternative 'at()'")
+ << MatchedExpr->getCallee()->getSourceRange()
+ << FixItHint::CreateReplacement(LeftBracket, ".at(")
+ << FixItHint::CreateReplacement(RightBracket, ")");
+
+ diag(Alternative->getBeginLoc(), "viable 'at()' is defined here",
+ DiagnosticIDs::Note)
+ << Alternative->getNameInfo().getSourceRange();
+
+ } else if (FixMode == Function) {
+ // Case: a[i] => f(a, i)
+ //
+ // Since C++23, the subscript operator may also be called without an
+ // argument, which makes the following distinction necessary
+ const bool EmptySubscript =
+ MatchedExpr->getDirectCallee()->getNumParams() == 0;
+
+ if (EmptySubscript) {
+ auto D = diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]'%select{, use safe "
+ "function '%1() instead|}0")
+ << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str()
+ << MatchedExpr->getCallee()->getSourceRange();
+ if (!FixFunctionEmptyArgs.empty()) {
+ D << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(),
+ FixFunctionEmptyArgs.str() + "(")
+ << FixItHint::CreateRemoval(LeftBracket)
+ << FixItHint::CreateReplacement(RightBracket, ")");
+ }
+ } else {
+ diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]', use safe function '%0()' instead")
+ << FixFunction.str() << MatchedExpr->getCallee()->getSourceRange()
+ << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(),
+ FixFunction.str() + "(")
+ << FixItHint::CreateReplacement(LeftBracket, ", ")
+ << FixItHint::CreateReplacement(RightBracket, ")");
+ }
+ }
+ } else if (const auto *MCE = dyn_cast(MatchedExpr)) {
+ // Case: a.operator[](i) or a->operator[](i)
+ const auto *Callee = dyn_cast(MCE->getCallee());
+
+ if (FixMode == At) {
+ // Cases: a.operator[](i) => a.at(i) and a->operator[](i) => a->at(i)
+
+ const auto *MatchedOperator =
+ Result.Nodes.getNodeAs("operator");
+
+ const CXXMethodDecl *Alternative = findAlternativeAt(MatchedOperator);
+ if (!Alternative) {
+ diag(Callee->getBeginLoc(), "possibly unsafe 'operator[]', consider "
+ "bounds-safe alternative 'at()'")
+ << Callee->getSourceRange();
+ return;
+ }
+ diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]', consider "
+ "bounds-safe alternative 'at()'")
+ << FixItHint::CreateReplacement(
+ SourceRange(Callee->getMemberLoc(), Callee->getEndLoc()),
+ "at");
+
+ diag(Alternative->getBeginLoc(), "viable 'at()' defined here",
+ DiagnosticIDs::Note)
+ << Alternative->getNameInfo().getSourceRange();
+
+ } else if (FixMode == Function) {
+ // Cases: a.operator[](i) => f(a, i) and a->operator[](i) => f(*a, i)
+ const auto *Callee = dyn_cast(MCE->getCallee());
+
+ const bool EmptySubscript =
+ MCE->getMethodDecl()->getNumNonObjectParams() == 0;
+
+ std::string BeginInsertion =
+ (EmptySubscript ? FixFunctionEmptyArgs.str() : FixFunction.str()) +
+ "(";
+
+ if (Callee->isArrow())
+ BeginInsertion += "*";
+
+ // Since C++23, the subscript operator may also be called without an
+ // argument, which makes the following distinction necessary
+ if (EmptySubscript) {
+ auto D = diag(MatchedExpr->getCallee()->getBeginLoc(),
+ "possibly unsafe 'operator[]'%select{, use safe "
+ "function '%1()' instead|}0")
+ << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str()
+ << Callee->getSourceRange();
+
+ if (!FixFunctionEmptyArgs.empty()) {
+ D << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(),
+ BeginInsertion)
+ << FixItHint::CreateRemoval(
+ SourceRange(Callee->getOperatorLoc(),
+ MCE->getRParenLoc().getLocWithOffset(-1)));
+ }
+ } else {
+ diag(Callee->getBeginLoc(),
+ "possibly unsafe 'operator[]', use safe function '%0()' instead")
+ << FixFunction.str() << Callee->getSourceRange()
+ << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(),
+ BeginInsertion)
+ << FixItHint::CreateReplacement(
+ SourceRange(
+ Callee->getOperatorLoc(),
+ MCE->getArg(0)->getBeginLoc().getLocWithOffset(-1)),
+ ", ");
+ }
+ }
+ }
+}
+
+} // namespace clang::tidy::cppcoreguidelines
+
+namespace clang::tidy {
+using P = cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess;
+
+llvm::ArrayRef>
+OptionEnumMapping::getEnumMapping() {
+ static constexpr std::pair Mapping[] = {
+ {P::None, "none"}, {P::At, "at"}, {P::Function, "function"}};
+ return {Mapping};
+}
+} // namespace clang::tidy
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.h
new file mode 100644
index 0000000000000..cfd52d69c0f58
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.h
@@ -0,0 +1,56 @@
+//===--- ProBoundsAvoidUncheckedContainerAccess.h - clang-tidy --*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_AVOID_UNCHECKED_CONTAINER_ACCESS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_AVOID_UNCHECKED_CONTAINER_ACCESS_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::cppcoreguidelines {
+
+/// Flags calls to operator[] in STL containers and suggests replacing it with
+/// safe alternatives.
+///
+/// See
+/// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#slcon3-avoid-bounds-errors
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.html
+class ProBoundsAvoidUncheckedContainerAccess : public ClangTidyCheck {
+public:
+ ProBoundsAvoidUncheckedContainerAccess(StringRef Name,
+ ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+ enum FixModes { None, At, Function };
+
+private:
+ // A list of class names that are excluded from the warning
+ std::vector ExcludedClasses;
+ // Setting which fix to suggest
+ FixModes FixMode;
+ llvm::StringRef FixFunction;
+ llvm::StringRef FixFunctionEmptyArgs;
+};
+} // namespace clang::tidy::cppcoreguidelines
+
+namespace clang::tidy {
+template <>
+struct OptionEnumMapping<
+ cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess::FixModes> {
+ static ArrayRef>
+ getEnumMapping();
+};
+} // namespace clang::tidy
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_AVOID_UNCHECKED_CONTAINER_ACCESS_H
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp
index 9ac7b9e057e35..51995c5f64ef6 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp
@@ -14,6 +14,18 @@ using namespace clang::ast_matchers;
namespace clang::tidy::cppcoreguidelines {
+ProBoundsPointerArithmeticCheck::ProBoundsPointerArithmeticCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AllowIncrementDecrementOperators(
+ Options.get("AllowIncrementDecrementOperators", false)) {}
+
+void ProBoundsPointerArithmeticCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AllowIncrementDecrementOperators",
+ AllowIncrementDecrementOperators);
+}
+
void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) {
const auto AllPointerTypes =
anyOf(hasType(hasUnqualifiedDesugaredType(pointerType())),
@@ -30,13 +42,14 @@ void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) {
this);
// Flag all operators ++, -- that result in a pointer
- Finder->addMatcher(
- unaryOperator(hasAnyOperatorName("++", "--"),
- hasType(hasUnqualifiedDesugaredType(pointerType())),
- unless(hasUnaryOperand(
- ignoringImpCasts(declRefExpr(to(isImplicit()))))))
- .bind("expr"),
- this);
+ if (!AllowIncrementDecrementOperators)
+ Finder->addMatcher(
+ unaryOperator(hasAnyOperatorName("++", "--"),
+ hasType(hasUnqualifiedDesugaredType(pointerType())),
+ unless(hasUnaryOperand(
+ ignoringImpCasts(declRefExpr(to(isImplicit()))))))
+ .bind("expr"),
+ this);
// Array subscript on a pointer (not an array) is also pointer arithmetic
Finder->addMatcher(
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h
index 3466c72a769e9..785f754055fb8 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h
@@ -21,13 +21,16 @@ namespace clang::tidy::cppcoreguidelines {
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic.html
class ProBoundsPointerArithmeticCheck : public ClangTidyCheck {
public:
- ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool AllowIncrementDecrementOperators;
};
} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
index b413b12cd37ab..40607597297b5 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
@@ -190,7 +190,7 @@ struct InitializerInsertion {
// Convenience utility to get a RecordDecl from a QualType.
const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
if (const auto *RT = Type.getCanonicalType()->getAs())
- return RT->getDecl();
+ return RT->getOriginalDecl();
return nullptr;
}
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp
index 76754394de760..40fd15c08f0a1 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp
@@ -92,7 +92,7 @@ void SlicingCheck::diagnoseSlicedOverriddenMethods(
for (const auto &Base : DerivedDecl.bases()) {
if (const auto *BaseRecordType = Base.getType()->getAs()) {
if (const auto *BaseRecord = cast_or_null(
- BaseRecordType->getDecl()->getDefinition()))
+ BaseRecordType->getOriginalDecl()->getDefinition()))
diagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
}
}
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h
index 02bfaf1205f40..317547f0a9c87 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h
@@ -22,6 +22,9 @@ class SlicingCheck : public ClangTidyCheck {
public:
SlicingCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
diff --git a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h
index 49d7820d04410..120dc90b2cbc0 100644
--- a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h
+++ b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h
@@ -21,6 +21,9 @@ class DefaultArgumentsCallsCheck : public ClangTidyCheck {
public:
DefaultArgumentsCallsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h
index d03d0b7a10f05..da73fa4064cbd 100644
--- a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h
+++ b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h
@@ -21,6 +21,9 @@ class DefaultArgumentsDeclarationsCheck : public ClangTidyCheck {
public:
DefaultArgumentsDeclarationsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
index 80ff97a762134..0302a5ad4957c 100644
--- a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
@@ -74,7 +74,7 @@ bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
const auto *Ty = I.getType()->getAs();
if (!Ty)
continue;
- const RecordDecl *D = Ty->getDecl()->getDefinition();
+ const RecordDecl *D = Ty->getOriginalDecl()->getDefinition();
if (!D)
continue;
const auto *Base = cast(D);
@@ -106,7 +106,8 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ty = I.getType()->getAs();
if (!Ty)
continue;
- const auto *Base = cast(Ty->getDecl()->getDefinition());
+ const auto *Base =
+ cast(Ty->getOriginalDecl()->getDefinition());
if (!isInterface(Base))
NumConcrete++;
}
@@ -117,7 +118,8 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ty = V.getType()->getAs();
if (!Ty)
continue;
- const auto *Base = cast(Ty->getDecl()->getDefinition());
+ const auto *Base =
+ cast(Ty->getOriginalDecl()->getDefinition());
if (!isInterface(Base))
NumConcrete++;
}
diff --git a/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h b/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h
index 007f410a6eac6..d26349d6e9afc 100644
--- a/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h
+++ b/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h
@@ -21,6 +21,9 @@ class OverloadedOperatorCheck : public ClangTidyCheck {
public:
OverloadedOperatorCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h b/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h
index 426d89d046a63..1bdf19f9146fb 100644
--- a/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h
+++ b/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h
@@ -21,6 +21,9 @@ class VirtualInheritanceCheck : public ClangTidyCheck {
public:
VirtualInheritanceCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
index e076b39b5d978..14e11eb0bc697 100644
--- a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
@@ -89,6 +89,30 @@ static StringRef getDestTypeString(const SourceManager &SM,
SM, LangOpts);
}
+static bool sameTypeAsWritten(QualType X, QualType Y) {
+ if (X.getCanonicalType() != Y.getCanonicalType())
+ return false;
+
+ auto TC = X->getTypeClass();
+ if (TC != Y->getTypeClass())
+ return false;
+
+ switch (TC) {
+ case Type::Typedef:
+ return declaresSameEntity(cast(X)->getDecl(),
+ cast(Y)->getDecl());
+ case Type::Pointer:
+ return sameTypeAsWritten(cast(X)->getPointeeType(),
+ cast(Y)->getPointeeType());
+ case Type::RValueReference:
+ case Type::LValueReference:
+ return sameTypeAsWritten(cast(X)->getPointeeType(),
+ cast(Y)->getPointeeType());
+ default:
+ return true;
+ }
+}
+
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
const auto *CastExpr = Result.Nodes.getNodeAs("cast");
@@ -128,12 +152,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
// case of overloaded functions, so detection of redundant casts is trickier
// in this case. Don't emit "redundant cast" warnings for function
// pointer/reference types.
- QualType Src = SourceTypeAsWritten, Dst = DestTypeAsWritten;
- if (const auto *ElTy = dyn_cast(Src))
- Src = ElTy->getNamedType();
- if (const auto *ElTy = dyn_cast(Dst))
- Dst = ElTy->getNamedType();
- if (Src == Dst) {
+ if (sameTypeAsWritten(SourceTypeAsWritten, DestTypeAsWritten)) {
diag(CastExpr->getBeginLoc(), "redundant cast to the same type")
<< FixItHint::CreateRemoval(ReplaceRange);
return;
diff --git a/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h b/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h
index baec40e440cbd..b53e6c45913d5 100644
--- a/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h
+++ b/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h
@@ -21,6 +21,9 @@ namespace clang::tidy::google::readability {
class AvoidUnderscoreInGoogletestNameCheck : public ClangTidyCheck {
public:
using ClangTidyCheck::ClangTidyCheck;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
diff --git a/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h b/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h
index a8f0b0112fb94..49d95a5acd35c 100644
--- a/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h
+++ b/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h
@@ -23,6 +23,9 @@ class DefaultArgumentsCheck : public ClangTidyCheck {
public:
DefaultArgumentsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp b/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp
index 3deea0620514b..68233ec6bd441 100644
--- a/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp
+++ b/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp
@@ -72,7 +72,7 @@ static bool isStdInitializerList(QualType Type) {
}
if (const auto *RT = Type->getAs()) {
if (const auto *Specialization =
- dyn_cast(RT->getDecl()))
+ dyn_cast(RT->getOriginalDecl()))
return declIsStdInitializerList(Specialization->getSpecializedTemplate());
}
return false;
diff --git a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp
index 805dcaf3ce402..274b8afa98bd6 100644
--- a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp
@@ -257,8 +257,13 @@ getAliasNameRange(const MatchFinder::MatchResult &Result) {
return CharSourceRange::getTokenRange(
Using->getNameInfo().getSourceRange());
}
- return CharSourceRange::getTokenRange(
- Result.Nodes.getNodeAs("typeloc")->getSourceRange());
+ TypeLoc TL = *Result.Nodes.getNodeAs("typeloc");
+ if (auto QTL = TL.getAs())
+ TL = QTL.getUnqualifiedLoc();
+
+ if (auto TTL = TL.getAs())
+ return CharSourceRange::getTokenRange(TTL.getNameLoc());
+ return CharSourceRange::getTokenRange(TL.castAs().getNameLoc());
}
void UpgradeGoogletestCaseCheck::check(const MatchFinder::MatchResult &Result) {
diff --git a/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h b/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h
index 48354c34a8581..39c45fea9aae4 100644
--- a/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h
+++ b/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h
@@ -21,6 +21,9 @@ namespace clang::tidy::hicpp {
class IgnoredRemoveResultCheck : public bugprone::UnusedReturnValueCheck {
public:
IgnoredRemoveResultCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
};
diff --git a/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h b/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h
index 1099ab0cd0e44..07e018a6fc969 100644
--- a/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h
+++ b/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h
@@ -23,6 +23,9 @@ class PreferRegisterOverUnsignedCheck : public ClangTidyCheck {
public:
PreferRegisterOverUnsignedCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h b/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h
index e1f25e530289a..b4550ecb226bf 100644
--- a/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h
+++ b/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h
@@ -19,6 +19,9 @@ class TwineLocalCheck : public ClangTidyCheck {
public:
TwineLocalCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
index fd7affd22a463..2cfee5fd10713 100644
--- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
@@ -32,6 +32,7 @@ add_clang_library(clangTidyMiscModule STATIC
NoRecursionCheck.cpp
NonCopyableObjects.cpp
NonPrivateMemberVariablesInClassesCheck.cpp
+ OverrideWithDifferentVisibilityCheck.cpp
RedundantExpressionCheck.cpp
StaticAssertCheck.cpp
ThrowByValueCatchByReferenceCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 697398a54332d..b32507d66cbac 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -98,11 +98,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
hasType(referenceType(pointee(substTemplateTypeParmType()))));
- const auto AllowedType = hasType(qualType(anyOf(
- hasDeclaration(namedDecl(matchers::matchesAnyListedName(AllowedTypes))),
- references(namedDecl(matchers::matchesAnyListedName(AllowedTypes))),
- pointerType(pointee(hasDeclaration(
- namedDecl(matchers::matchesAnyListedName(AllowedTypes))))))));
+ auto AllowedTypeDecl = namedDecl(
+ anyOf(matchers::matchesAnyListedName(AllowedTypes), usingShadowDecl()));
+
+ const auto AllowedType = hasType(qualType(
+ anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl),
+ pointerType(pointee(hasDeclaration(AllowedTypeDecl))))));
const auto AutoTemplateType = varDecl(
anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),
diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
index 6ddebcbc0e152..f675ca70deb9d 100644
--- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
@@ -22,6 +22,7 @@
#include "NoRecursionCheck.h"
#include "NonCopyableObjects.h"
#include "NonPrivateMemberVariablesInClassesCheck.h"
+#include "OverrideWithDifferentVisibilityCheck.h"
#include "RedundantExpressionCheck.h"
#include "StaticAssertCheck.h"
#include "ThrowByValueCatchByReferenceCheck.h"
@@ -81,6 +82,8 @@ class MiscModule : public ClangTidyModule {
"misc-use-anonymous-namespace");
CheckFactories.registerCheck(
"misc-use-internal-linkage");
+ CheckFactories.registerCheck(
+ "misc-override-with-different-visibility");
}
};
diff --git a/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp b/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp
index 0cdd48c13b2a6..bb64a5618620c 100644
--- a/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp
@@ -19,13 +19,13 @@ void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
pointee(anyOf(isConstQualified(), ignoringParens(functionType()))))));
Finder->addMatcher(
- valueDecl(hasType(qualType(
- isConstQualified(),
- elaboratedType(namesType(typedefType(hasDeclaration(
- anyOf(typedefDecl(NonConstAndNonFunctionPointerType)
- .bind("typedef"),
- typeAliasDecl(NonConstAndNonFunctionPointerType)
- .bind("typeAlias")))))))))
+ valueDecl(
+ hasType(qualType(isConstQualified(),
+ typedefType(hasDeclaration(anyOf(
+ typedefDecl(NonConstAndNonFunctionPointerType)
+ .bind("typedef"),
+ typeAliasDecl(NonConstAndNonFunctionPointerType)
+ .bind("typeAlias")))))))
.bind("decl"),
this);
}
diff --git a/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp b/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp
index 40808aaf7c3da..2837f40bc49b8 100644
--- a/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp
@@ -59,7 +59,9 @@ AST_MATCHER(FunctionDecl, isPlacementOverload) {
return true;
}
-OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
+} // namespace
+
+static OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
switch (FD->getOverloadedOperator()) {
default:
break;
@@ -75,7 +77,7 @@ OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
llvm_unreachable("Not an overloaded allocation operator");
}
-const char *getOperatorName(OverloadedOperatorKind K) {
+static const char *getOperatorName(OverloadedOperatorKind K) {
switch (K) {
default:
break;
@@ -91,13 +93,14 @@ const char *getOperatorName(OverloadedOperatorKind K) {
llvm_unreachable("Not an overloaded allocation operator");
}
-bool areCorrespondingOverloads(const FunctionDecl *LHS,
- const FunctionDecl *RHS) {
+static bool areCorrespondingOverloads(const FunctionDecl *LHS,
+ const FunctionDecl *RHS) {
return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS);
}
-bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
- const CXXRecordDecl *RD = nullptr) {
+static bool
+hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
+ const CXXRecordDecl *RD = nullptr) {
if (RD) {
// Check the methods in the given class and accessible to derived classes.
for (const auto *BMD : RD->methods())
@@ -124,8 +127,6 @@ bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
return false;
}
-} // anonymous namespace
-
void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) {
// Match all operator new and operator delete overloads (including the array
// forms). Do not match implicit operators, placement operators, or
diff --git a/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp
new file mode 100644
index 0000000000000..12f78affe463e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp
@@ -0,0 +1,150 @@
+//===--- OverrideWithDifferentVisibilityCheck.cpp - clang-tidy ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "OverrideWithDifferentVisibilityCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+using namespace clang;
+
+namespace {
+
+AST_MATCHER(NamedDecl, isOperatorDecl) {
+ DeclarationName::NameKind const NK = Node.getDeclName().getNameKind();
+ return NK != DeclarationName::Identifier &&
+ NK != DeclarationName::CXXConstructorName &&
+ NK != DeclarationName::CXXDestructorName;
+}
+
+} // namespace
+
+namespace clang::tidy {
+
+template <>
+struct OptionEnumMapping<
+ misc::OverrideWithDifferentVisibilityCheck::ChangeKind> {
+ static llvm::ArrayRef>
+ getEnumMapping() {
+ static constexpr std::pair<
+ misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>
+ Mapping[] = {
+ {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Any,
+ "any"},
+ {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Widening,
+ "widening"},
+ {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Narrowing,
+ "narrowing"},
+ };
+ return {Mapping};
+ }
+};
+
+namespace misc {
+
+OverrideWithDifferentVisibilityCheck::OverrideWithDifferentVisibilityCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ DetectVisibilityChange(
+ Options.get("DisallowedVisibilityChange", ChangeKind::Any)),
+ CheckDestructors(Options.get("CheckDestructors", false)),
+ CheckOperators(Options.get("CheckOperators", false)),
+ IgnoredFunctions(utils::options::parseStringList(
+ Options.get("IgnoredFunctions", ""))) {}
+
+void OverrideWithDifferentVisibilityCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "DisallowedVisibilityChange", DetectVisibilityChange);
+ Options.store(Opts, "CheckDestructors", CheckDestructors);
+ Options.store(Opts, "CheckOperators", CheckOperators);
+ Options.store(Opts, "IgnoredFunctions",
+ utils::options::serializeStringList(IgnoredFunctions));
+}
+
+void OverrideWithDifferentVisibilityCheck::registerMatchers(
+ MatchFinder *Finder) {
+ const auto IgnoredDecl =
+ namedDecl(matchers::matchesAnyListedName(IgnoredFunctions));
+ const auto FilterDestructors =
+ CheckDestructors ? decl() : decl(unless(cxxDestructorDecl()));
+ const auto FilterOperators =
+ CheckOperators ? namedDecl() : namedDecl(unless(isOperatorDecl()));
+ Finder->addMatcher(
+ cxxMethodDecl(
+ isVirtual(), FilterDestructors, FilterOperators,
+ ofClass(
+ cxxRecordDecl(unless(isExpansionInSystemHeader())).bind("class")),
+ forEachOverridden(cxxMethodDecl(ofClass(cxxRecordDecl().bind("base")),
+ unless(IgnoredDecl))
+ .bind("base_func")))
+ .bind("func"),
+ this);
+}
+
+void OverrideWithDifferentVisibilityCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *const MatchedFunction =
+ Result.Nodes.getNodeAs("func");
+ if (!MatchedFunction->isCanonicalDecl())
+ return;
+
+ const auto *const ParentClass =
+ Result.Nodes.getNodeAs("class");
+ const auto *const BaseClass = Result.Nodes.getNodeAs("base");
+ CXXBasePaths Paths;
+ if (!ParentClass->isDerivedFrom(BaseClass, Paths))
+ return;
+
+ const auto *const OverriddenFunction =
+ Result.Nodes.getNodeAs("base_func");
+ AccessSpecifier const ActualAccess = MatchedFunction->getAccess();
+ AccessSpecifier OverriddenAccess = OverriddenFunction->getAccess();
+
+ const CXXBaseSpecifier *InheritanceWithStrictVisibility = nullptr;
+ for (const CXXBasePath &Path : Paths) {
+ for (const CXXBasePathElement &Elem : Path) {
+ if (Elem.Base->getAccessSpecifier() > OverriddenAccess) {
+ OverriddenAccess = Elem.Base->getAccessSpecifier();
+ InheritanceWithStrictVisibility = Elem.Base;
+ }
+ }
+ }
+
+ if (ActualAccess != OverriddenAccess) {
+ if (DetectVisibilityChange == ChangeKind::Widening &&
+ ActualAccess > OverriddenAccess)
+ return;
+ if (DetectVisibilityChange == ChangeKind::Narrowing &&
+ ActualAccess < OverriddenAccess)
+ return;
+
+ if (InheritanceWithStrictVisibility) {
+ diag(MatchedFunction->getLocation(),
+ "visibility of function %0 is changed from %1 (through %1 "
+ "inheritance of class %2) to %3")
+ << MatchedFunction << OverriddenAccess
+ << InheritanceWithStrictVisibility->getType() << ActualAccess;
+ diag(InheritanceWithStrictVisibility->getBeginLoc(),
+ "%0 is inherited as %1 here", DiagnosticIDs::Note)
+ << InheritanceWithStrictVisibility->getType() << OverriddenAccess;
+ } else {
+ diag(MatchedFunction->getLocation(),
+ "visibility of function %0 is changed from %1 in class %2 to %3")
+ << MatchedFunction << OverriddenAccess << BaseClass << ActualAccess;
+ }
+ diag(OverriddenFunction->getLocation(), "function declared here as %0",
+ DiagnosticIDs::Note)
+ << OverriddenFunction->getAccess();
+ }
+}
+
+} // namespace misc
+
+} // namespace clang::tidy
diff --git a/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h
new file mode 100644
index 0000000000000..1f5222d99196b
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h
@@ -0,0 +1,43 @@
+//===--- OverrideWithDifferentVisibilityCheck.h - clang-tidy --*- C++ -*---===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::misc {
+
+/// Finds virtual function overrides with different visibility than the function
+/// in the base class.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc/override-with-different-visibility.html
+class OverrideWithDifferentVisibilityCheck : public ClangTidyCheck {
+public:
+ enum class ChangeKind { Any, Widening, Narrowing };
+
+ OverrideWithDifferentVisibilityCheck(StringRef Name,
+ ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+
+private:
+ ChangeKind DetectVisibilityChange;
+ bool CheckDestructors;
+ bool CheckOperators;
+ std::vector IgnoredFunctions;
+};
+
+} // namespace clang::tidy::misc
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
diff --git a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
index 9b2af2a8ca7d8..107eda2e98f27 100644
--- a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
@@ -45,14 +45,6 @@ static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) {
return Value < Result;
}
-static bool areEquivalentNameSpecifier(const NestedNameSpecifier *Left,
- const NestedNameSpecifier *Right) {
- llvm::FoldingSetNodeID LeftID, RightID;
- Left->Profile(LeftID);
- Right->Profile(RightID);
- return LeftID == RightID;
-}
-
static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
if (!Left || !Right)
return !Left && !Right;
@@ -104,9 +96,8 @@ static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
if (cast(Left)->getDeclName() !=
cast(Right)->getDeclName())
return false;
- return areEquivalentNameSpecifier(
- cast(Left)->getQualifier(),
- cast(Right)->getQualifier());
+ return cast(Left)->getQualifier() ==
+ cast(Right)->getQualifier();
case Stmt::DeclRefExprClass:
return cast(Left)->getDecl() ==
cast(Right)->getDecl();
diff --git a/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp
index 3fdaf9239f6af..8200239b982a0 100644
--- a/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp
@@ -29,11 +29,13 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
const auto HasGoodReturnType =
cxxMethodDecl(returns(hasCanonicalType(lValueReferenceType(pointee(
unless(isConstQualified()),
- anyOf(autoType(), hasDeclaration(equalsBoundNode("class"))))))));
+ anyOf(autoType(),
+ hasDeclaration(declaresSameEntityAsBoundNode("class"))))))));
const auto IsSelf = qualType(hasCanonicalType(
- anyOf(hasDeclaration(equalsBoundNode("class")),
- referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))));
+ anyOf(hasDeclaration(declaresSameEntityAsBoundNode("class")),
+ referenceType(pointee(
+ hasDeclaration(declaresSameEntityAsBoundNode("class")))))));
const auto IsAssign =
cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),
hasName("operator="), ofClass(recordDecl().bind("class")))
diff --git a/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp
index 86992cd8a141b..4fa679aa8dd88 100644
--- a/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp
@@ -35,12 +35,12 @@ void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) {
}
if (const auto *NestedName =
- Result.Nodes.getNodeAs("nns")) {
- if (const auto *AliasDecl = dyn_cast_if_present(
- NestedName->getAsNamespace())) {
+ Result.Nodes.getNodeAs("nns");
+ NestedName &&
+ NestedName->getKind() == NestedNameSpecifier::Kind::Namespace)
+ if (const auto *AliasDecl = dyn_cast(
+ NestedName->getAsNamespaceAndPrefix().Namespace))
FoundDecls[AliasDecl] = CharSourceRange();
- }
- }
}
void UnusedAliasDeclsCheck::onEndOfTranslationUnit() {
diff --git a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
index d5c5fa3364d63..8211a0ec6a5e1 100644
--- a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
@@ -71,11 +71,7 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
templateArgument().bind("used")))),
this);
Finder->addMatcher(userDefinedLiteral().bind("used"), this);
- Finder->addMatcher(
- loc(elaboratedType(unless(hasQualifier(nestedNameSpecifier())),
- hasUnqualifiedDesugaredType(
- type(asTagDecl(tagDecl().bind("used")))))),
- this);
+ Finder->addMatcher(loc(asTagDecl(tagDecl().bind("used"))), this);
// Cases where we can identify the UsingShadowDecl directly, rather than
// just its target.
// FIXME: cover more cases in this way, as the AST supports it.
@@ -136,7 +132,7 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
}
if (const auto *ECD = dyn_cast(Used)) {
if (const auto *ET = ECD->getType()->getAs())
- removeFromFoundDecls(ET->getDecl());
+ removeFromFoundDecls(ET->getOriginalDecl());
}
};
// We rely on the fact that the clang AST is walked in order, usages are only
diff --git a/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp
index f22f48d831608..2aca61021166d 100644
--- a/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp
@@ -29,8 +29,7 @@ static std::optional getReplacementType(StringRef Type) {
void DeprecatedIosBaseAliasesCheck::registerMatchers(MatchFinder *Finder) {
auto IoStateDecl = typedefDecl(hasAnyName(DeprecatedTypes)).bind("TypeDecl");
- auto IoStateType =
- qualType(hasDeclaration(IoStateDecl), unless(elaboratedType()));
+ auto IoStateType = typedefType(hasDeclaration(IoStateDecl));
Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this);
}
@@ -43,12 +42,14 @@ void DeprecatedIosBaseAliasesCheck::check(
StringRef TypeName = Typedef->getName();
auto Replacement = getReplacementType(TypeName);
- const auto *TL = Result.Nodes.getNodeAs("TypeLoc");
- SourceLocation IoStateLoc = TL->getBeginLoc();
+ TypeLoc TL = *Result.Nodes.getNodeAs("TypeLoc");
+ if (auto QTL = TL.getAs())
+ TL = QTL.getUnqualifiedLoc();
+ SourceLocation IoStateLoc = TL.castAs().getNameLoc();
// Do not generate fixits for matches depending on template arguments and
// macro expansions.
- bool Fix = Replacement && !TL->getType()->isDependentType();
+ bool Fix = Replacement && !TL.getType()->isDependentType();
if (IoStateLoc.isMacroID()) {
IoStateLoc = SM.getSpellingLoc(IoStateLoc);
Fix = false;
diff --git a/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp
index c2db858f72e32..118e96a6f34ae 100644
--- a/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp
@@ -395,16 +395,12 @@ void MacroToEnumCallbacks::Endif(SourceLocation Loc, SourceLocation IfLoc) {
--CurrentFile->ConditionScopes;
}
-namespace {
-
template
-bool textEquals(const char (&Needle)[N], const char *HayStack) {
+static bool textEquals(const char (&Needle)[N], const char *HayStack) {
return StringRef{HayStack, N - 1} == Needle;
}
-template size_t len(const char (&)[N]) { return N - 1; }
-
-} // namespace
+template static size_t len(const char (&)[N]) { return N - 1; }
void MacroToEnumCallbacks::PragmaDirective(SourceLocation Loc,
PragmaIntroducerKind Introducer) {
diff --git a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp
index deef3586628c6..cea48ce6f4564 100644
--- a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp
@@ -16,14 +16,13 @@ using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
-namespace {
+static constexpr char ConstructorCall[] = "constructorCall";
+static constexpr char ResetCall[] = "resetCall";
+static constexpr char NewExpression[] = "newExpression";
-constexpr char ConstructorCall[] = "constructorCall";
-constexpr char ResetCall[] = "resetCall";
-constexpr char NewExpression[] = "newExpression";
-
-std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM,
- const LangOptions &Lang) {
+static std::string getNewExprName(const CXXNewExpr *NewExpr,
+ const SourceManager &SM,
+ const LangOptions &Lang) {
StringRef WrittenName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
@@ -34,8 +33,6 @@ std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM,
return WrittenName.str();
}
-} // namespace
-
const char MakeSmartPtrCheck::PointerType[] = "pointerType";
MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
diff --git a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
index 1e271dfa768ce..a54d0721a5b7d 100644
--- a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
@@ -77,8 +77,7 @@ AST_MATCHER_P(CXXRecordDecl, isMoveConstructibleInBoundCXXRecordDecl, StringRef,
static TypeMatcher notTemplateSpecConstRefType() {
return lValueReferenceType(
- pointee(unless(elaboratedType(namesType(templateSpecializationType()))),
- isConstQualified()));
+ pointee(unless(templateSpecializationType()), isConstQualified()));
}
static TypeMatcher nonConstValueType() {
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
index 24674a407cb36..0c9e909fea7f9 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
@@ -19,9 +19,7 @@ using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
-namespace {
-
-bool containsEscapes(StringRef HayStack, StringRef Escapes) {
+static bool containsEscapes(StringRef HayStack, StringRef Escapes) {
size_t BackSlash = HayStack.find('\\');
if (BackSlash == StringRef::npos)
return false;
@@ -35,16 +33,16 @@ bool containsEscapes(StringRef HayStack, StringRef Escapes) {
return true;
}
-bool isRawStringLiteral(StringRef Text) {
+static bool isRawStringLiteral(StringRef Text) {
// Already a raw string literal if R comes before ".
const size_t QuotePos = Text.find('"');
assert(QuotePos != StringRef::npos);
return (QuotePos > 0) && (Text[QuotePos - 1] == 'R');
}
-bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
- const StringLiteral *Literal,
- const CharsBitSet &DisallowedChars) {
+static bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
+ const StringLiteral *Literal,
+ const CharsBitSet &DisallowedChars) {
// FIXME: Handle L"", u8"", u"" and U"" literals.
if (!Literal->isOrdinary())
return false;
@@ -64,14 +62,12 @@ bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
return containsEscapes(Text, R"('\"?x01)");
}
-bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
+static bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
return Bytes.find(Delimiter.empty()
? std::string(R"lit()")lit")
: (")" + Delimiter + R"(")")) != StringRef::npos;
}
-} // namespace
-
RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp
index 1ad31d315dc2a..f2142b810a126 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp
@@ -48,7 +48,7 @@ void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isInStdNamespace());
- auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
+ auto AutoPtrType = hasCanonicalType(recordType(hasDeclaration(AutoPtrDecl)));
// std::auto_ptr a;
// ^~~~~~~~~~~~~
@@ -58,11 +58,7 @@ void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
//
// std::auto_ptr fn(std::auto_ptr);
// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
- Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
- // Skip elaboratedType() as the named
- // type will match soon thereafter.
- unless(elaboratedType()))))
- .bind(AutoPtrTokenId),
+ Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType))).bind(AutoPtrTokenId),
this);
// using std::auto_ptr;
@@ -118,10 +114,13 @@ void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
}
SourceLocation AutoPtrLoc;
- if (const auto *TL = Result.Nodes.getNodeAs(AutoPtrTokenId)) {
+ if (const auto *PTL = Result.Nodes.getNodeAs(AutoPtrTokenId)) {
+ auto TL = *PTL;
+ if (auto QTL = TL.getAs())
+ TL = QTL.getUnqualifiedLoc();
// std::auto_ptr i;
// ^
- if (auto Loc = TL->getAs())
+ if (auto Loc = TL.getAs())
AutoPtrLoc = Loc.getTemplateNameLoc();
} else if (const auto *D =
Result.Nodes.getNodeAs(AutoPtrTokenId)) {
diff --git a/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp
index ff0b3213cb58f..472128201acc2 100644
--- a/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp
@@ -168,19 +168,6 @@ static DeclarationName getName(const DeclRefExpr &D) {
return D.getDecl()->getDeclName();
}
-static bool isNamedType(const ElaboratedTypeLoc &ETL) {
- if (const auto *TFT =
- ETL.getNamedTypeLoc().getTypePtr()->getAs()) {
- const TypedefNameDecl *Decl = TFT->getDecl();
- return Decl->getDeclName().isIdentifier() && Decl->getName() == "type";
- }
- return false;
-}
-
-static bool isNamedType(const DependentNameTypeLoc &DTL) {
- return DTL.getTypePtr()->getIdentifier()->getName() == "type";
-}
-
namespace {
AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
DeclRefExpr, DependentScopeDeclRefExpr)) {
@@ -188,25 +175,20 @@ AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
return Ident && Ident->isStr("value");
}
-AST_POLYMORPHIC_MATCHER(isType,
- AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc,
- DependentNameTypeLoc)) {
- return Node.getBeginLoc().isValid() && isNamedType(Node);
+AST_MATCHER(TypeLoc, isType) {
+ if (auto TL = Node.getAs()) {
+ const auto *TD = TL.getDecl();
+ return TD->getDeclName().isIdentifier() && TD->getName() == "type";
+ }
+ if (auto TL = Node.getAs())
+ return TL.getTypePtr()->getIdentifier()->getName() == "type";
+ return false;
}
} // namespace
static constexpr char Bind[] = "";
void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
- const ast_matchers::internal::VariadicDynCastAllOfMatcher<
- Stmt,
- DependentScopeDeclRefExpr>
- dependentScopeDeclRefExpr; // NOLINT(readability-identifier-naming)
- const ast_matchers::internal::VariadicDynCastAllOfMatcher<
- TypeLoc,
- DependentNameTypeLoc>
- dependentNameTypeLoc; // NOLINT(readability-identifier-naming)
-
// Only register matchers for trait<...>::value in c++17 mode.
if (getLangOpts().CPlusPlus17) {
Finder->addMatcher(mapAnyOf(declRefExpr, dependentScopeDeclRefExpr)
@@ -214,10 +196,7 @@ void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
.bind(Bind),
this);
}
- Finder->addMatcher(mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc)
- .with(isType())
- .bind(Bind),
- this);
+ Finder->addMatcher(typeLoc(isType()).bind(Bind), this);
}
static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
@@ -226,14 +205,11 @@ static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
Set.contains(ND->getName());
}
-static bool checkTemplatedDecl(const NestedNameSpecifier *NNS,
+static bool checkTemplatedDecl(NestedNameSpecifier NNS,
const llvm::StringSet<> &Set) {
- if (!NNS)
+ if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
return false;
- const Type *NNST = NNS->getAsType();
- if (!NNST)
- return false;
- const auto *TST = NNST->getAs();
+ const auto *TST = NNS.getAsType()->getAs();
if (!TST)
return false;
if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
@@ -250,8 +226,8 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
SourceLocation EndLoc) {
SourceLocation TemplateNameEndLoc;
- if (auto TSTL = QualLoc.getTypeLoc().getAs();
- !TSTL.isNull())
+ if (auto TSTL =
+ QualLoc.getAsTypeLoc().getAs())
TemplateNameEndLoc = Lexer::getLocForEndOfToken(
TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
Result.Context->getLangOpts());
@@ -274,8 +250,8 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
SourceLocation EndLoc,
SourceLocation TypenameLoc) {
SourceLocation TemplateNameEndLoc;
- if (auto TSTL = QualLoc.getTypeLoc().getAs();
- !TSTL.isNull())
+ if (auto TSTL =
+ QualLoc.getAsTypeLoc().getAs())
TemplateNameEndLoc = Lexer::getLocForEndOfToken(
TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
Result.Context->getLangOpts());
@@ -301,23 +277,21 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
if (!DRE->hasQualifier())
return;
if (const auto *CTSD = dyn_cast_if_present(
- DRE->getQualifier()->getAsRecordDecl())) {
+ DRE->getQualifier().getAsRecordDecl())) {
if (isNamedDeclInStdTraitsSet(CTSD, ValueTraits))
EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc());
}
return;
}
- if (const auto *ETL = Result.Nodes.getNodeAs(Bind)) {
- const NestedNameSpecifierLoc QualLoc = ETL->getQualifierLoc();
- const auto *NNS = QualLoc.getNestedNameSpecifier();
- if (!NNS)
- return;
+ if (const auto *TL = Result.Nodes.getNodeAs(Bind)) {
+ const NestedNameSpecifierLoc QualLoc = TL->getQualifierLoc();
+ NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier();
if (const auto *CTSD = dyn_cast_if_present(
- NNS->getAsRecordDecl())) {
+ NNS.getAsRecordDecl())) {
if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits))
- EmitTypeWarning(ETL->getQualifierLoc(), ETL->getEndLoc(),
- ETL->getElaboratedKeywordLoc());
+ EmitTypeWarning(TL->getQualifierLoc(), TL->getEndLoc(),
+ TL->getElaboratedKeywordLoc());
}
return;
}
diff --git a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
index f4b63087b7234..b601620633cee 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
@@ -186,16 +186,14 @@ TypeMatcher nestedIterator() {
/// declarations and which name standard iterators for standard containers.
TypeMatcher iteratorFromUsingDeclaration() {
auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
- // Types resulting from using declarations are represented by elaboratedType.
- return elaboratedType(
- // Unwrap the nested name specifier to test for one of the standard
- // containers.
- hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
- namedDecl(hasStdContainerName(), isInStdNamespace()))))),
- // the named type is what comes after the final '::' in the type. It
- // should name one of the standard iterator names.
- namesType(
- anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
+ // Unwrap the nested name specifier to test for one of the standard
+ // containers.
+ auto Qualifier = hasQualifier(specifiesType(templateSpecializationType(
+ hasDeclaration(namedDecl(hasStdContainerName(), isInStdNamespace())))));
+ // the named type is what comes after the final '::' in the type. It should
+ // name one of the standard iterator names.
+ return anyOf(typedefType(HasIteratorDecl, Qualifier),
+ recordType(HasIteratorDecl, Qualifier));
}
/// This matcher returns declaration statements that contain variable
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
index e9b96c4016af6..c4a64be537a44 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
@@ -8,6 +8,7 @@
#include "UseConstraintsCheck.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
@@ -60,9 +61,11 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
Keyword != ElaboratedTypeKeyword::None)) {
return std::nullopt;
}
- TheType = Dep.getQualifierLoc().getTypeLoc();
+ TheType = Dep.getQualifierLoc().getAsTypeLoc();
if (TheType.isNull())
return std::nullopt;
+ } else {
+ return std::nullopt;
}
if (const auto SpecializationLoc =
@@ -78,6 +81,13 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
if (!TD || TD->getName() != "enable_if")
return std::nullopt;
+ assert(!TD->getTemplateParameters()->empty() &&
+ "found template with no template parameters?");
+ const auto *FirstParam = dyn_cast(
+ TD->getTemplateParameters()->getParam(0));
+ if (!FirstParam || !FirstParam->getType()->isBooleanType())
+ return std::nullopt;
+
int NumArgs = SpecializationLoc.getNumArgs();
if (NumArgs != 1 && NumArgs != 2)
return std::nullopt;
@@ -89,9 +99,6 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
static std::optional
matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
- if (const auto Elaborated = TheType.getAs())
- TheType = Elaborated.getNamedTypeLoc();
-
if (const auto SpecializationLoc =
TheType.getAs()) {
@@ -108,6 +115,13 @@ matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
if (!Specialization->isTypeAlias())
return std::nullopt;
+ assert(!TD->getTemplateParameters()->empty() &&
+ "found template with no template parameters?");
+ const auto *FirstParam = dyn_cast(
+ TD->getTemplateParameters()->getParam(0));
+ if (!FirstParam || !FirstParam->getType()->isBooleanType())
+ return std::nullopt;
+
if (const auto *AliasedType =
dyn_cast(Specialization->getAliasedType())) {
ElaboratedTypeKeyword Keyword = AliasedType->getKeyword();
diff --git a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
index aaf24eaa33c1b..ee49d8a7cb0b0 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
@@ -164,10 +164,10 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
auto CallEmplacy = cxxMemberCallExpr(
hasDeclaration(
functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
- on(hasTypeOrPointeeType(hasCanonicalType(hasDeclaration(
- has(typedefNameDecl(hasName("value_type"),
- hasType(type(hasUnqualifiedDesugaredType(
- recordType().bind("value_type")))))))))));
+ on(hasTypeOrPointeeType(
+ hasCanonicalType(hasDeclaration(has(typedefNameDecl(
+ hasName("value_type"),
+ hasType(hasCanonicalType(recordType().bind("value_type"))))))))));
// We can't replace push_backs of smart pointer because
// if emplacement fails (f.e. bad_alloc in vector) we will have leak of
@@ -241,17 +241,16 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
auto HasConstructExprWithValueTypeType =
has(ignoringImplicit(cxxConstructExpr(
- SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType(
- type(equalsBoundNode("value_type"))))))));
-
- auto HasBracedInitListWithValueTypeType =
- anyOf(allOf(HasConstructInitListExpr,
- has(initListExpr(hasType(type(hasUnqualifiedDesugaredType(
- type(equalsBoundNode("value_type")))))))),
- has(cxxBindTemporaryExpr(
- HasConstructInitListExpr,
- has(initListExpr(hasType(type(hasUnqualifiedDesugaredType(
- type(equalsBoundNode("value_type"))))))))));
+ SoughtConstructExpr,
+ hasType(hasCanonicalType(type(equalsBoundNode("value_type")))))));
+
+ auto HasBracedInitListWithValueTypeType = anyOf(
+ allOf(HasConstructInitListExpr,
+ has(initListExpr(hasType(
+ hasCanonicalType(type(equalsBoundNode("value_type"))))))),
+ has(cxxBindTemporaryExpr(HasConstructInitListExpr,
+ has(initListExpr(hasType(hasCanonicalType(
+ type(equalsBoundNode("value_type")))))))));
auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument(
materializeTemporaryExpr(
@@ -289,19 +288,17 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
this);
Finder->addMatcher(
- traverse(
- TK_AsIs,
- cxxMemberCallExpr(
- CallEmplacy,
- on(hasType(cxxRecordDecl(has(typedefNameDecl(
- hasName("value_type"),
- hasType(type(
- hasUnqualifiedDesugaredType(recordType(hasDeclaration(
- cxxRecordDecl(hasAnyName(SmallVector(
- TupleTypes.begin(), TupleTypes.end()))))))))))))),
- has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
- unless(isInTemplateInstantiation()))
- .bind("emplacy_call")),
+ traverse(TK_AsIs,
+ cxxMemberCallExpr(
+ CallEmplacy,
+ on(hasType(cxxRecordDecl(has(typedefNameDecl(
+ hasName("value_type"),
+ hasType(hasCanonicalType(recordType(hasDeclaration(
+ cxxRecordDecl(hasAnyName(SmallVector(
+ TupleTypes.begin(), TupleTypes.end())))))))))))),
+ has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
+ unless(isInTemplateInstantiation()))
+ .bind("emplacy_call")),
this);
}
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
index 52e9a9f8d49e0..5310f2fd25381 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
@@ -29,7 +29,7 @@ static bool isLockGuardDecl(const NamedDecl *Decl) {
static bool isLockGuard(const QualType &Type) {
if (const auto *Record = Type->getAs())
- if (const RecordDecl *Decl = Record->getDecl())
+ if (const RecordDecl *Decl = Record->getOriginalDecl())
return isLockGuardDecl(Decl);
if (const auto *TemplateSpecType = Type->getAs())
@@ -89,17 +89,6 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
return LockGuardGroups;
}
-static TemplateSpecializationTypeLoc
-getTemplateLockGuardTypeLoc(const TypeSourceInfo *SourceInfo) {
- const TypeLoc Loc = SourceInfo->getTypeLoc();
-
- const auto ElaboratedLoc = Loc.getAs();
- if (!ElaboratedLoc)
- return {};
-
- return ElaboratedLoc.getNamedTypeLoc().getAs();
-}
-
// Find the exact source range of the 'lock_guard' token
static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc();
@@ -110,7 +99,7 @@ static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
// Find the exact source range of the 'lock_guard' name token
static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) {
const TemplateSpecializationTypeLoc TemplateLoc =
- getTemplateLockGuardTypeLoc(SourceInfo);
+ SourceInfo->getTypeLoc().getAs();
if (!TemplateLoc)
return {};
@@ -136,11 +125,11 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
const auto LockGuardClassDecl =
namedDecl(hasName("lock_guard"), isInStdNamespace());
- const auto LockGuardType = qualType(anyOf(
- hasUnqualifiedDesugaredType(
- recordType(hasDeclaration(LockGuardClassDecl))),
- elaboratedType(namesType(hasUnqualifiedDesugaredType(
- templateSpecializationType(hasDeclaration(LockGuardClassDecl)))))));
+ const auto LockGuardType =
+ qualType(anyOf(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(LockGuardClassDecl))),
+ hasUnqualifiedDesugaredType(templateSpecializationType(
+ hasDeclaration(LockGuardClassDecl)))));
const auto LockVarDecl = varDecl(hasType(LockGuardType));
@@ -165,18 +154,16 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
if (WarnOnUsingAndTypedef) {
// Match 'typedef std::lock_guard Lock'
Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()),
- hasUnderlyingType(LockGuardType))
+ hasType(hasUnderlyingType(LockGuardType)))
.bind("lock-guard-typedef"),
this);
// Match 'using Lock = std::lock_guard'
- Finder->addMatcher(
- typeAliasDecl(
- unless(isExpansionInSystemHeader()),
- hasType(elaboratedType(namesType(templateSpecializationType(
- hasDeclaration(LockGuardClassDecl))))))
- .bind("lock-guard-using-alias"),
- this);
+ Finder->addMatcher(typeAliasDecl(unless(isExpansionInSystemHeader()),
+ hasType(templateSpecializationType(
+ hasDeclaration(LockGuardClassDecl))))
+ .bind("lock-guard-using-alias"),
+ this);
// Match 'using std::lock_guard'
Finder->addMatcher(
@@ -288,8 +275,8 @@ void UseScopedLockCheck::diagOnSourceInfo(
const ast_matchers::MatchFinder::MatchResult &Result) {
const TypeLoc TL = LockGuardSourceInfo->getTypeLoc();
- if (const auto ElaboratedTL = TL.getAs()) {
- auto Diag = diag(ElaboratedTL.getBeginLoc(), UseScopedLockMessage);
+ if (const auto TTL = TL.getAs()) {
+ auto Diag = diag(TTL.getBeginLoc(), UseScopedLockMessage);
const SourceRange LockGuardRange =
getLockGuardNameRange(LockGuardSourceInfo);
diff --git a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
index ced4825f79a99..82f64096cbec1 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp
@@ -64,66 +64,65 @@ struct UnqualNameVisitor : public RecursiveASTVisitor {
return false;
}
- bool TraverseTypeLoc(TypeLoc TL, bool Elaborated = false) {
+ bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
if (TL.isNull())
return true;
- if (!Elaborated) {
- switch (TL.getTypeLocClass()) {
- case TypeLoc::Record:
- if (visitUnqualName(
- TL.getAs().getTypePtr()->getDecl()->getName()))
- return false;
+ switch (TL.getTypeLocClass()) {
+ case TypeLoc::InjectedClassName:
+ case TypeLoc::Record:
+ case TypeLoc::Enum: {
+ auto TTL = TL.getAs();
+ const auto *T = TTL.getTypePtr();
+ if (T->getKeyword() != ElaboratedTypeKeyword::None ||
+ TTL.getQualifierLoc())
break;
- case TypeLoc::Enum:
- if (visitUnqualName(
- TL.getAs().getTypePtr()->getDecl()->getName()))
- return false;
- break;
- case TypeLoc::TemplateSpecialization:
- if (visitUnqualName(TL.getAs()
- .getTypePtr()
- ->getTemplateName()
- .getAsTemplateDecl()
- ->getName()))
- return false;
- break;
- case TypeLoc::Typedef:
- if (visitUnqualName(
- TL.getAs().getTypePtr()->getDecl()->getName()))
- return false;
+ if (visitUnqualName(T->getOriginalDecl()->getName()))
+ return false;
+ break;
+ }
+ case TypeLoc::TemplateSpecialization: {
+ auto TTL = TL.getAs();
+ const auto *T = TTL.getTypePtr();
+ if (T->getKeyword() != ElaboratedTypeKeyword::None ||
+ TTL.getQualifierLoc())
break;
- case TypeLoc::Using:
- if (visitUnqualName(TL.getAs()
- .getTypePtr()
- ->getFoundDecl()
- ->getName()))
- return false;
+ if (visitUnqualName(T->getTemplateName().getAsTemplateDecl()->getName()))
+ return false;
+ break;
+ }
+ case TypeLoc::Typedef: {
+ auto TTL = TL.getAs();
+ const auto *T = TTL.getTypePtr();
+ if (T->getKeyword() != ElaboratedTypeKeyword::None ||
+ TTL.getQualifierLoc())
break;
- default:
+ if (visitUnqualName(T->getDecl()->getName()))
+ return false;
+ break;
+ }
+ case TypeLoc::Using: {
+ auto TTL = TL.getAs();
+ const auto *T = TTL.getTypePtr();
+ if (T->getKeyword() != ElaboratedTypeKeyword::None ||
+ TTL.getQualifierLoc())
break;
- }
+ if (visitUnqualName(T->getDecl()->getName()))
+ return false;
+ break;
+ }
+ default:
+ break;
}
- return RecursiveASTVisitor::TraverseTypeLoc(TL);
+ return RecursiveASTVisitor::TraverseTypeLoc(
+ TL, TraverseQualifier);
}
// Replace the base method in order to call our own
// TraverseTypeLoc().
- bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) {
- return TraverseTypeLoc(TL.getUnqualifiedLoc());
- }
-
- // Replace the base version to inform TraverseTypeLoc that the type is
- // elaborated.
- bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc TL) {
- if (TL.getQualifierLoc() &&
- !TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()))
- return false;
- const auto *T = TL.getTypePtr();
- return TraverseTypeLoc(TL.getNamedTypeLoc(),
- T->getKeyword() != ElaboratedTypeKeyword::None ||
- T->getQualifier());
+ bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL, bool TraverseQualifier) {
+ return TraverseTypeLoc(TL.getUnqualifiedLoc(), TraverseQualifier);
}
bool VisitDeclRefExpr(DeclRefExpr *S) {
diff --git a/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
index a053c07f95ce2..2373a26fe48b4 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
@@ -37,15 +37,13 @@ void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
// Non-transparent functor mentioned as a template parameter. FIXIT.
Finder->addMatcher(
- loc(qualType(
- unless(elaboratedType()),
- hasDeclaration(classTemplateSpecializationDecl(
- unless(hasAnyTemplateArgument(templateArgument(refersToType(
- qualType(pointsTo(qualType(isAnyCharacter()))))))),
- hasAnyTemplateArgument(
- templateArgument(refersToType(qualType(hasDeclaration(
- TransparentFunctors))))
- .bind("Functor"))))))
+ loc(qualType(hasDeclaration(classTemplateSpecializationDecl(
+ unless(hasAnyTemplateArgument(templateArgument(refersToType(
+ qualType(pointsTo(qualType(isAnyCharacter()))))))),
+ hasAnyTemplateArgument(
+ templateArgument(refersToType(qualType(
+ hasDeclaration(TransparentFunctors))))
+ .bind("Functor"))))))
.bind("FunctorParentLoc"),
this);
diff --git a/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp
index bd9bdd1701975..8e4ed41c5f501 100644
--- a/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp
@@ -29,12 +29,13 @@
using namespace clang::ast_matchers;
namespace clang::tidy::objc {
-namespace {
static constexpr StringRef WeakText = "__weak";
static constexpr StringRef StrongText = "__strong";
static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";
+namespace {
+
/// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference
/// Objective-C object (or block) variables or fields whose object lifetimes
/// are not __unsafe_unretained.
@@ -49,6 +50,8 @@ AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,
QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
}
+} // namespace
+
static std::optional
fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,
StringRef Ownership) {
@@ -93,8 +96,6 @@ fixItHintForVarDecl(const VarDecl *VD, const SourceManager &SM,
return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");
}
-} // namespace
-
void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
traverse(
diff --git a/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp b/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp
index ffbdb025848d7..01ee4d518b97c 100644
--- a/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp
+++ b/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp
@@ -27,11 +27,14 @@ enum NamingStyle {
CategoryProperty = 2,
};
+} // namespace
+
/// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
/// 'camelCase' or 'abc_camelCase'. For other cases the users need to
/// come up with a proper name by their own.
/// FIXME: provide fix for snake_case to snakeCase
-FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
+static FixItHint generateFixItHint(const ObjCPropertyDecl *Decl,
+ NamingStyle Style) {
auto Name = Decl->getName();
auto NewName = Decl->getName().str();
size_t Index = 0;
@@ -50,7 +53,7 @@ FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
return {};
}
-std::string validPropertyNameRegex(bool UsedInMatcher) {
+static std::string validPropertyNameRegex(bool UsedInMatcher) {
// Allow any of these names:
// foo
// fooBar
@@ -72,13 +75,13 @@ std::string validPropertyNameRegex(bool UsedInMatcher) {
return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
}
-bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
+static bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
auto RegexExp =
llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$");
return RegexExp.match(PropertyName);
}
-bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
+static bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
size_t Start = PropertyName.find_first_of('_');
assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
auto Prefix = PropertyName.substr(0, Start);
@@ -88,7 +91,6 @@ bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
auto RegexExp = llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));
return RegexExp.match(PropertyName.substr(Start + 1));
}
-} // namespace
void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(objcPropertyDecl(
diff --git a/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp b/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
index 7022e9d784fa7..1c018999432e3 100644
--- a/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
@@ -42,11 +42,11 @@ void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) {
// A matcher for a `DstT::DstT(const Src&)` where DstT also has a
// `DstT::DstT(Src&&)`.
const auto LValueRefCtor = cxxConstructorDecl(
- hasParameter(0,
- hasType(lValueReferenceType(pointee(type().bind("SrcT"))))),
+ hasParameter(0, hasType(hasCanonicalType(
+ lValueReferenceType(pointee(type().bind("SrcT")))))),
ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl(
- hasParameter(0, hasType(rValueReferenceType(
- pointee(type(equalsBoundNode("SrcT")))))))))));
+ hasParameter(0, hasType(hasCanonicalType(rValueReferenceType(
+ pointee(type(equalsBoundNode("SrcT"))))))))))));
// A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
// instantiation of `template DstT::DstT(U&&)`.
diff --git a/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp b/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
index 120f7fb749493..c413090b3a0a4 100644
--- a/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ b/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -17,7 +17,6 @@
#include
namespace clang::tidy::performance {
-namespace {
using namespace ::clang::ast_matchers;
using llvm::StringRef;
@@ -30,8 +29,8 @@ static constexpr StringRef MethodDeclId = "methodDecl";
static constexpr StringRef FunctionDeclId = "functionDecl";
static constexpr StringRef OldVarDeclId = "oldVarDecl";
-void recordFixes(const VarDecl &Var, ASTContext &Context,
- DiagnosticBuilder &Diagnostic) {
+static void recordFixes(const VarDecl &Var, ASTContext &Context,
+ DiagnosticBuilder &Diagnostic) {
Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
if (!Var.getType().isLocalConstQualified()) {
if (std::optional Fix = utils::fixit::addQualifierToVarDecl(
@@ -40,8 +39,8 @@ void recordFixes(const VarDecl &Var, ASTContext &Context,
}
}
-std::optional firstLocAfterNewLine(SourceLocation Loc,
- SourceManager &SM) {
+static std::optional firstLocAfterNewLine(SourceLocation Loc,
+ SourceManager &SM) {
bool Invalid = false;
const char *TextAfter = SM.getCharacterData(Loc, &Invalid);
if (Invalid) {
@@ -51,8 +50,8 @@ std::optional firstLocAfterNewLine(SourceLocation Loc,
return Loc.getLocWithOffset(TextAfter[Offset] == '\0' ? Offset : Offset + 1);
}
-void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
- DiagnosticBuilder &Diagnostic) {
+static void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
+ DiagnosticBuilder &Diagnostic) {
auto &SM = Context.getSourceManager();
// Attempt to remove trailing comments as well.
auto Tok = utils::lexer::findNextTokenSkippingComments(Stmt.getEndLoc(), SM,
@@ -74,6 +73,8 @@ void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
}
}
+namespace {
+
AST_MATCHER_FUNCTION_P(StatementMatcher,
isRefReturningMethodCallWithConstOverloads,
std::vector, ExcludedContainerTypes) {
@@ -130,6 +131,8 @@ AST_MATCHER_FUNCTION_P(StatementMatcher, initializerReturnsReferenceToConst,
hasUnaryOperand(OldVarDeclRef)))));
}
+} // namespace
+
// This checks that the variable itself is only used as const, and also makes
// sure that it does not reference another variable that could be modified in
// the BlockStmt. It does this by checking the following:
@@ -180,13 +183,13 @@ static bool isInitializingVariableImmutable(
return false;
}
-bool isVariableUnused(const VarDecl &Var, const Stmt &BlockStmt,
- ASTContext &Context) {
+static bool isVariableUnused(const VarDecl &Var, const Stmt &BlockStmt,
+ ASTContext &Context) {
return allDeclRefExprs(Var, BlockStmt, Context).empty();
}
-const SubstTemplateTypeParmType *getSubstitutedType(const QualType &Type,
- ASTContext &Context) {
+static const SubstTemplateTypeParmType *
+getSubstitutedType(const QualType &Type, ASTContext &Context) {
auto Matches = match(
qualType(anyOf(substTemplateTypeParmType().bind("subst"),
hasDescendant(substTemplateTypeParmType().bind("subst")))),
@@ -194,9 +197,9 @@ const SubstTemplateTypeParmType *getSubstitutedType(const QualType &Type,
return selectFirst("subst", Matches);
}
-bool differentReplacedTemplateParams(const QualType &VarType,
- const QualType &InitializerType,
- ASTContext &Context) {
+static bool differentReplacedTemplateParams(const QualType &VarType,
+ const QualType &InitializerType,
+ ASTContext &Context) {
if (const SubstTemplateTypeParmType *VarTmplType =
getSubstitutedType(VarType, Context)) {
if (const SubstTemplateTypeParmType *InitializerTmplType =
@@ -212,8 +215,8 @@ bool differentReplacedTemplateParams(const QualType &VarType,
return false;
}
-QualType constructorArgumentType(const VarDecl *OldVar,
- const BoundNodes &Nodes) {
+static QualType constructorArgumentType(const VarDecl *OldVar,
+ const BoundNodes &Nodes) {
if (OldVar) {
return OldVar->getType();
}
@@ -224,8 +227,6 @@ QualType constructorArgumentType(const VarDecl *OldVar,
return MethodDecl->getReturnType();
}
-} // namespace
-
UnnecessaryCopyInitialization::UnnecessaryCopyInitialization(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
diff --git a/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
index fbd2ba67805d9..c1aa52bacf99f 100644
--- a/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -21,16 +21,14 @@ using namespace clang::ast_matchers;
namespace clang::tidy::performance {
-namespace {
-
-std::string paramNameOrIndex(StringRef Name, size_t Index) {
+static std::string paramNameOrIndex(StringRef Name, size_t Index) {
return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
: llvm::Twine('\'') + Name + llvm::Twine('\''))
.str();
}
-bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
- ASTContext &Context) {
+static bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
+ ASTContext &Context) {
auto Matches = match(
traverse(TK_AsIs,
decl(forEachDescendant(declRefExpr(
@@ -41,8 +39,6 @@ bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
return Matches.empty();
}
-} // namespace
-
UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
diff --git a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp
index 3b4d65be7dfa1..5a3c9a4203eb9 100644
--- a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp
+++ b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp
@@ -15,10 +15,11 @@ namespace clang::tidy::portability {
void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) {
// Match std::allocator.
- auto AllocatorConst =
+ auto AllocatorConst = qualType(hasCanonicalType(
recordType(hasDeclaration(classTemplateSpecializationDecl(
hasName("::std::allocator"),
- hasTemplateArgument(0, refersToType(qualType(isConstQualified()))))));
+ hasTemplateArgument(0,
+ refersToType(qualType(isConstQualified()))))))));
auto HasContainerName =
hasAnyName("::std::vector", "::std::deque", "::std::list",
@@ -31,8 +32,10 @@ void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) {
// aren't caught.
Finder->addMatcher(
typeLoc(
- templateSpecializationTypeLoc(),
- loc(hasUnqualifiedDesugaredType(anyOf(
+ anyOf(templateSpecializationTypeLoc(),
+ qualifiedTypeLoc(
+ hasUnqualifiedLoc(templateSpecializationTypeLoc()))),
+ loc(qualType(anyOf(
recordType(hasDeclaration(classTemplateSpecializationDecl(
HasContainerName,
anyOf(
diff --git a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h
index d22d1aafa00af..87702af91bdb6 100644
--- a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h
+++ b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h
@@ -23,6 +23,9 @@ class StdAllocatorConstCheck : public ClangTidyCheck {
public:
StdAllocatorConstCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
index ce736a8d16023..c4dc319f23c38 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
@@ -238,9 +238,12 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
? MemberCallObject
: (Pointee ? Pointee : Result.Nodes.getNodeAs("STLObject"));
FixItHint Hint;
- std::string ReplacementText = std::string(
- Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
- *Result.SourceManager, getLangOpts()));
+ std::string ReplacementText =
+ E->isImplicitCXXThis()
+ ? ""
+ : std::string(Lexer::getSourceText(
+ CharSourceRange::getTokenRange(E->getSourceRange()),
+ *Result.SourceManager, getLangOpts()));
const auto *OpCallExpr = dyn_cast(E);
if (isBinaryOrTernary(E) || isa(E) ||
(OpCallExpr && (OpCallExpr->getOperator() == OO_Star))) {
@@ -251,6 +254,8 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
// This can happen if the object is a smart pointer. Don't add anything
// because a '->' is already there (PR#51776), just call the method.
ReplacementText += "empty()";
+ } else if (E->isImplicitCXXThis()) {
+ ReplacementText += "empty()";
} else if (E->getType()->isPointerType())
ReplacementText += "->empty()";
else
diff --git a/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h b/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h
index 79b0d39bed41b..1b12fec972998 100644
--- a/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h
+++ b/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h
@@ -23,6 +23,9 @@ class ConvertMemberFunctionsToStatic : public ClangTidyCheck {
public:
ConvertMemberFunctionsToStatic(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
diff --git a/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h b/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h
index e9e7c942d1d2c..6e746d803d3ee 100644
--- a/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h
@@ -22,6 +22,9 @@ class DeleteNullPointerCheck : public ClangTidyCheck {
public:
DeleteNullPointerCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional getCheckTraversalKind() const override {
diff --git a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
index b4a157c153bb9..9eef5c4db2d01 100644
--- a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
@@ -122,15 +122,15 @@ AST_MATCHER(EnumDecl, hasSequentialInitialValues) {
return !AllEnumeratorsArePowersOfTwo;
}
-std::string getName(const EnumDecl *Decl) {
+} // namespace
+
+static std::string getName(const EnumDecl *Decl) {
if (!Decl->getDeclName())
return "";
return Decl->getQualifiedNameAsString();
}
-} // namespace
-
EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
diff --git a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
index e1fb42b8210e2..2f59aaa86b157 100644
--- a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
@@ -144,6 +144,8 @@ struct CognitiveComplexity final {
void account(SourceLocation Loc, unsigned short Nesting, Criteria C);
};
+} // namespace
+
// All the possible messages that can be output. The choice of the message
// to use is based of the combination of the CognitiveComplexity::Criteria.
// It would be nice to have it in CognitiveComplexity struct, but then it is
@@ -163,23 +165,27 @@ static const std::array Msgs = {{
}};
// Criteria is a bitset, thus a few helpers are needed.
-CognitiveComplexity::Criteria operator|(CognitiveComplexity::Criteria LHS,
- CognitiveComplexity::Criteria RHS) {
+static CognitiveComplexity::Criteria
+operator|(CognitiveComplexity::Criteria LHS,
+ CognitiveComplexity::Criteria RHS) {
return static_cast(llvm::to_underlying(LHS) |
llvm::to_underlying(RHS));
}
-CognitiveComplexity::Criteria operator&(CognitiveComplexity::Criteria LHS,
- CognitiveComplexity::Criteria RHS) {
+static CognitiveComplexity::Criteria
+operator&(CognitiveComplexity::Criteria LHS,
+ CognitiveComplexity::Criteria RHS) {
return static_cast(llvm::to_underlying(LHS) &
llvm::to_underlying(RHS));
}
-CognitiveComplexity::Criteria &operator|=(CognitiveComplexity::Criteria &LHS,
- CognitiveComplexity::Criteria RHS) {
+static CognitiveComplexity::Criteria &
+operator|=(CognitiveComplexity::Criteria &LHS,
+ CognitiveComplexity::Criteria RHS) {
LHS = operator|(LHS, RHS);
return LHS;
}
-CognitiveComplexity::Criteria &operator&=(CognitiveComplexity::Criteria &LHS,
- CognitiveComplexity::Criteria RHS) {
+static CognitiveComplexity::Criteria &
+operator&=(CognitiveComplexity::Criteria &LHS,
+ CognitiveComplexity::Criteria RHS) {
LHS = operator&(LHS, RHS);
return LHS;
}
@@ -199,6 +205,8 @@ void CognitiveComplexity::account(SourceLocation Loc, unsigned short Nesting,
Total += Increase;
}
+namespace {
+
class FunctionASTVisitor final
: public RecursiveASTVisitor {
using Base = RecursiveASTVisitor;
diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
index 5f19706e16866..6b10e6b206a31 100644
--- a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
@@ -41,9 +41,11 @@ AST_MATCHER(Stmt, isNULLMacroExpansion) {
return isNULLMacroExpansion(&Node, Finder->getASTContext());
}
-StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
- QualType Type,
- ASTContext &Context) {
+} // namespace
+
+static StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
+ QualType Type,
+ ASTContext &Context) {
switch (CastExprKind) {
case CK_IntegralToBoolean:
return Type->isUnsignedIntegerType() ? "0u" : "0";
@@ -62,15 +64,15 @@ StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
}
}
-bool isUnaryLogicalNotOperator(const Stmt *Statement) {
+static bool isUnaryLogicalNotOperator(const Stmt *Statement) {
const auto *UnaryOperatorExpr = dyn_cast(Statement);
return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
}
-void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
- const ImplicitCastExpr *Cast, const Stmt *Parent,
- ASTContext &Context,
- bool UseUpperCaseLiteralSuffix) {
+static void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
+ const ImplicitCastExpr *Cast,
+ const Stmt *Parent, ASTContext &Context,
+ bool UseUpperCaseLiteralSuffix) {
// In case of expressions like (! integer), we should remove the redundant not
// operator and use inverted comparison (integer == 0).
bool InvertComparison =
@@ -133,8 +135,8 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
}
-StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
- ASTContext &Context) {
+static StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
+ ASTContext &Context) {
if (isNULLMacroExpansion(Expression, Context)) {
return "false";
}
@@ -161,7 +163,7 @@ StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
return {};
}
-bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
+static bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc);
StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),
@@ -173,9 +175,10 @@ bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());
}
-void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
- const ImplicitCastExpr *Cast,
- ASTContext &Context, StringRef OtherType) {
+static void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
+ const ImplicitCastExpr *Cast,
+ ASTContext &Context,
+ StringRef OtherType) {
if (!Context.getLangOpts().CPlusPlus) {
Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),
(Twine("(") + OtherType + ")").str());
@@ -200,8 +203,9 @@ void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
}
}
-StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
- QualType DestType, ASTContext &Context) {
+static StringRef
+getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
+ QualType DestType, ASTContext &Context) {
// Prior to C++11, false literal could be implicitly converted to pointer.
if (!Context.getLangOpts().CPlusPlus11 &&
(DestType->isPointerType() || DestType->isMemberPointerType()) &&
@@ -222,8 +226,8 @@ StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
return BoolLiteral->getValue() ? "1" : "0";
}
-bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
- ASTContext &Context) {
+static bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
+ ASTContext &Context) {
std::queue Q;
Q.push(Cast);
@@ -251,8 +255,6 @@ bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
return false;
}
-} // anonymous namespace
-
ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
index 561f067d471d1..44a784bc9f21a 100644
--- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
@@ -28,8 +28,11 @@ AST_MATCHER_P(QualType, hasUnqualifiedType,
enum class Qualifier { Const, Volatile, Restrict };
-std::optional findQualToken(const VarDecl *Decl, Qualifier Qual,
- const MatchFinder::MatchResult &Result) {
+} // namespace
+
+static std::optional
+findQualToken(const VarDecl *Decl, Qualifier Qual,
+ const MatchFinder::MatchResult &Result) {
// Since either of the locs can be in a macro, use `makeFileCharRange` to be
// sure that we have a consistent `CharSourceRange`, located entirely in the
// source file.
@@ -58,7 +61,7 @@ std::optional