diff --git a/.ci/buildbot/worker.py b/.ci/buildbot/worker.py index 09a6d0cb0d6c2..cbdc34a38ed1b 100644 --- a/.ci/buildbot/worker.py +++ b/.ci/buildbot/worker.py @@ -148,7 +148,7 @@ def rmtree(path): Taken from official Python docs https://docs.python.org/3/library/shutil.html#rmtree-example """ - shutil.rmtree(path, onexc=_remove_readonly) + shutil.rmtree(path, onerror=_remove_readonly) def try_delete(path): diff --git a/.ci/green-dragon/bisect.groovy b/.ci/green-dragon/bisect.groovy new file mode 100644 index 0000000000000..ee45bef7b4f3e --- /dev/null +++ b/.ci/green-dragon/bisect.groovy @@ -0,0 +1,191 @@ +branchName = 'main' + +library identifier: "zorg-shared-lib@${branchName}", + retriever: modernSCM([ + $class: 'GitSCMSource', + remote: "https://github.com/llvm/llvm-zorg.git" + ]) + +pipeline { + options { + skipDefaultCheckout() + } + + agent { + node { + label 'macos-trigger' + } + } + + parameters { + string(name: 'GOOD_COMMIT', description: 'Known good commit SHA', defaultValue: '') + string(name: 'BAD_COMMIT', description: 'Known bad commit SHA', defaultValue: '') + choice( + name: 'TEST_JOB_NAME', + choices: [ + 'clang-stage1-RA', + 'clang-stage1-RA-cmake-incremental', + 'clang-stage1-RA-expensive', + 'clang-stage1-RA-as', + 'clang-san-iossim', + 'clang-stage2-cmake-RgSan', + 'clang-stage2-cmake-RgTSan', + 'clang-stage2-Rthinlto', + ], + description: 'Job to execute for testing each commit' + ) + choice( + name: 'REPOSITORY', + choices: ['llvm-project'], + description: 'Repository to bisect' + ) + booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Run tests as part of bisection. Set to False if bisecting a build failure.') + string(name: 'SESSION_ID', description: 'Session ID to continue (optional)', defaultValue: '') + booleanParam(name: 'DRY_RUN', defaultValue: false, description: 'Dry run mode') + string(name: 'REQUESTOR', description: 'Email address of the requestor', defaultValue: '') + } + + stages { + stage('Validate Parameters') { + steps { + script { + if (!params.GOOD_COMMIT || !params.BAD_COMMIT) { + error("Both GOOD_COMMIT and BAD_COMMIT parameters are required") + } + if (!params.TEST_JOB_NAME) { + error("TEST_JOB_NAME parameter is required") + } + } + } + } + + stage('Setup Repository') { + steps { + script { + echo "📁 Setting up repository: ${params.REPOSITORY}..." + + dir(params.REPOSITORY) { + // Clone or checkout the repository + checkout([$class: 'GitSCM', branches: [ + [name: params.GOOD_COMMIT] + ], extensions: [ + [$class: 'CloneOption', + timeout: 30] + ], userRemoteConfigs: [ + [url: 'https://github.com/llvm/llvm-project.git'] + ]]) + + // Verify commits exist + sh "git cat-file -e ${params.GOOD_COMMIT}" + sh "git cat-file -e ${params.BAD_COMMIT}" + + echo "Repository setup complete" + } + } + } + } + + stage('Initialize Bisection') { + steps { + script { + bisectionManager.initializeBisection( + params.GOOD_COMMIT, + params.BAD_COMMIT, + params.TEST_JOB_NAME, + params.REPOSITORY, + params.SESSION_ID ?: null + ) + } + } + } + + stage('Execute Bisection') { + steps { + script { + def stepNumber = 1 + def maxSteps = 50 + + while (stepNumber <= maxSteps) { + // Log step and get info + def stepInfo = bisectionManager.logStepStart(stepNumber, params.REPOSITORY) + + if (stepInfo.type == 'complete') { + echo "Bisection complete! Failing commit: ${stepInfo.failing_commit}" + break + } + + // Show restart instructions + bisectionManager.showRestartInstructions(stepNumber, params.TEST_JOB_NAME, params.REPOSITORY) + + if (params.DRY_RUN) { + def simulatedResult = (stepNumber % 2 == 0) ? "SUCCESS" : "FAILURE" + echo "Simulated result: ${simulatedResult}" + bisectionManager.recordTestResult(stepInfo.commit, simulatedResult, params.REPOSITORY) + } else { + // Execute real job + def startTime = System.currentTimeMillis() + + def jobResult = build( + job: "llvm.org/bisect/${params.TEST_JOB_NAME}", + parameters: [ + string(name: 'GIT_SHA', value: stepInfo.commit), + string(name: 'BISECT_GOOD', value: stepInfo.bisection_range.current_good), + string(name: 'BISECT_BAD', value: stepInfo.bisection_range.current_bad), + booleanParam(name: 'IS_BISECT_JOB', value: true), + booleanParam(name: 'SKIP_TESTS', value: params.RUN_TESTS), + booleanParam(name: 'SKIP_TRIGGER', value: true) + ], + propagate: false, + wait: true + ) + + def duration = ((System.currentTimeMillis() - startTime) / 1000.0) as double + + // Log the job execution + bisectionManager.logJobExecution( + params.TEST_JOB_NAME, + jobResult.result, + duration, + jobResult.absoluteUrl, + jobResult.number.toString(), + params.REPOSITORY + ) + + // Record the test result + bisectionManager.recordTestResult(stepInfo.commit, jobResult.result, params.REPOSITORY) + } + + stepNumber++ + sleep(2) + } + + if (stepNumber > maxSteps) { + error("Bisection exceeded maximum steps") + } + } + } + } + + stage('Final Report') { + steps { + script { + bisectionManager.generateFinalReport(params.REPOSITORY) + } + } + } + } + + post { + always { + script { + bisectionManager.displaySummary(params.REPOSITORY) + archiveArtifacts artifacts: "bisection_state.json,bisection.log,restart_instructions.log,bisection_final_report.txt", + allowEmptyArchive: true + } + } + cleanup { + // Clean up but preserve artifacts + sh 'rm -f bisection_manager.py' + } + } +} diff --git a/.ci/green-dragon/relay-clang-stage2-sanitizers.groovy b/.ci/green-dragon/relay-clang-stage2-sanitizers.groovy index 10f958434cd2c..8c8571cf6e345 100644 --- a/.ci/green-dragon/relay-clang-stage2-sanitizers.groovy +++ b/.ci/green-dragon/relay-clang-stage2-sanitizers.groovy @@ -1,5 +1,9 @@ branchName = 'main' +properties([ + disableConcurrentBuilds() +]) + library identifier: "zorg-shared-lib@${branchName}", retriever: modernSCM([ $class: 'GitSCMSource', diff --git a/.ci/green-dragon/relay-clang-stage2-thinlto.groovy b/.ci/green-dragon/relay-clang-stage2-thinlto.groovy new file mode 100644 index 0000000000000..59ac857b4237d --- /dev/null +++ b/.ci/green-dragon/relay-clang-stage2-thinlto.groovy @@ -0,0 +1,16 @@ +branchName = 'main' + +properties([ + disableConcurrentBuilds() +]) + +library identifier: "zorg-shared-lib@${branchName}", + retriever: modernSCM([ + $class: 'GitSCMSource', + remote: "https://github.com/llvm/llvm-zorg.git", + credentialsId: scm.userRemoteConfigs[0].credentialsId + ]) + +relay.pipeline([ + "llvm.org/clang-stage2-Rthinlto" +]) \ No newline at end of file diff --git a/.ci/green-dragon/relay-lnt-ctmark.groovy b/.ci/green-dragon/relay-lnt-ctmark.groovy index 1718a12861162..54cd6b7db439e 100644 --- a/.ci/green-dragon/relay-lnt-ctmark.groovy +++ b/.ci/green-dragon/relay-lnt-ctmark.groovy @@ -1,5 +1,9 @@ branchName = 'main' +properties([ + disableConcurrentBuilds() +]) + library identifier: "zorg-shared-lib@${branchName}", retriever: modernSCM([ $class: 'GitSCMSource', diff --git a/.ci/green-dragon/relay-test-suite-verify-machineinstrs.groovy b/.ci/green-dragon/relay-test-suite-verify-machineinstrs.groovy index 6865baded518e..abf178fc8fafc 100644 --- a/.ci/green-dragon/relay-test-suite-verify-machineinstrs.groovy +++ b/.ci/green-dragon/relay-test-suite-verify-machineinstrs.groovy @@ -1,5 +1,9 @@ branchName = 'main' +properties([ + disableConcurrentBuilds() +]) + library identifier: "zorg-shared-lib@${branchName}", retriever: modernSCM([ $class: 'GitSCMSource', diff --git a/.github/workflows/build-libc-container.yml b/.github/workflows/build-libc-container.yml new file mode 100644 index 0000000000000..ab766dd8f9b9d --- /dev/null +++ b/.github/workflows/build-libc-container.yml @@ -0,0 +1,58 @@ +name: Build libc Container + +permissions: + contents: read + +on: + push: + branches: + - main + paths: + - .github/workflows/build-libc-container.yml + - '.github/workflows/containers/libc/**' + pull_request: + paths: + - .github/workflows/build-libc-container.yml + - '.github/workflows/containers/libc/**' + +jobs: + build-libc-container: + name: Build libc container + if: github.repository_owner == 'llvm' + runs-on: ${{ matrix.runs-on }} + strategy: + matrix: + runs-on: + - ubuntu-24.04 + - ubuntu-24.04-arm + steps: + - name: Checkout LLVM + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + sparse-checkout: | + .github/workflows/containers/libc/ + .github/actions/build-container + - name: Build Container + uses: ./.github/actions/build-container + with: + container-name: libc-ubuntu-24.04 + dockerfile: .github/workflows/containers/libc/Dockerfile + target: '' + test-command: ${{ matrix.test-command }} + push-libc-container: + if: github.event_name == 'push' + needs: + - build-libc-container + permissions: + packages: write + runs-on: ubuntu-24.04 + steps: + - name: Checkout LLVM + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + sparse-checkout: | + .github/actions/push-container + + - uses: ./.github/actions/push-container + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/commit-access-review.py b/.github/workflows/commit-access-review.py index 338bdcbca6607..2ac89d3959e2e 100644 --- a/.github/workflows/commit-access-review.py +++ b/.github/workflows/commit-access-review.py @@ -9,6 +9,7 @@ # # ===------------------------------------------------------------------------===# +import concurrent.futures import datetime import github import re @@ -264,6 +265,11 @@ def count_prs(gh: github.Github, triage_list: dict, start_date: datetime.datetim date_begin = date_end +# 4 because that's how many cores the default github runners have. Also, if we +# make this too high, we risk hitting some of GitHub's secondary rate limits. +THREAD_POOL_MAX_WORKERS = 4 + + def main(): token = sys.argv[1] gh = github.Github(auth=github.Auth.Token(token)) @@ -291,9 +297,16 @@ def main(): sys.exit(0) # Step 2 check for reviews - for user in list(triage_list.keys()): - review_count = get_review_count(gh, user, one_year_ago) - triage_list[user].add_reviewed(review_count) + + with concurrent.futures.ThreadPoolExecutor( + max_workers=THREAD_POOL_MAX_WORKERS + ) as executor: + executor.map( + lambda user: triage_list[user].add_reviewed( + get_review_count(gh, user, one_year_ago) + ), + list(triage_list.keys()), + ) print("After Reviews:", len(triage_list), "triagers") @@ -301,11 +314,17 @@ def main(): sys.exit(0) # Step 3 check for number of commits - for user in list(triage_list.keys()): - num_commits = get_num_commits(gh, user, one_year_ago) + with concurrent.futures.ThreadPoolExecutor( + max_workers=THREAD_POOL_MAX_WORKERS + ) as executor: # Override the total number of commits to not double count commits and # authored PRs. - triage_list[user].set_authored(num_commits) + executor.map( + lambda user: triage_list[user].set_authored( + get_num_commits(gh, user, one_year_ago) + ), + list(triage_list.keys()), + ) print("After Commits:", len(triage_list), "triagers") diff --git a/.github/workflows/commit-access-review.yml b/.github/workflows/commit-access-review.yml index 2809b1c3a7ef7..d860b0dce0c30 100644 --- a/.github/workflows/commit-access-review.yml +++ b/.github/workflows/commit-access-review.yml @@ -13,6 +13,7 @@ permissions: jobs: commit-access-review: if: github.repository_owner == 'llvm' + environment: main-branch-only runs-on: ubuntu-24.04 steps: - name: Fetch LLVM sources @@ -21,10 +22,19 @@ jobs: - name: Install dependencies run: | pip install --require-hashes -r ./llvm/utils/git/requirements.txt + + - id: app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf #v2.2.1 + with: + app-id: ${{ secrets.LLVM_TOKEN_GENERATOR_CLIENT_ID }} + private-key: ${{ secrets.LLVM_TOKEN_GENERATOR_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + permission-members: read + permission-contents: read - name: Run Script env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} run: | python3 .github/workflows/commit-access-review.py $GITHUB_TOKEN diff --git a/.github/workflows/containers/libc/Dockerfile b/.github/workflows/containers/libc/Dockerfile new file mode 100644 index 0000000000000..e5a628f7ea8ca --- /dev/null +++ b/.github/workflows/containers/libc/Dockerfile @@ -0,0 +1,42 @@ +FROM docker.io/library/ubuntu:24.04 + +# MPFR is required by some of the mathlib tests. +RUN apt-get update && \ + apt-get install -y \ + git \ + libmpfr-dev \ + libgmp-dev \ + libmpc-dev \ + ninja-build \ + sudo \ + sccache \ + wget \ + lsb-release \ + software-properties-common \ + gnupg \ + cmake \ + linux-libc-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN wget https://apt.llvm.org/llvm.sh && \ + chmod +x llvm.sh && \ + sudo ./llvm.sh 21 && \ + rm llvm.sh + +# Debian has a multilib setup, so we need to symlink the asm directory. +# For more information, see https://wiki.debian.org/Multiarch/LibraryPathOverview +RUN ln -sf /usr/include/$(uname -p)-linux-gnu/asm /usr/include/asm + +# 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 +# user id that Github Actions uses to perform the checkout action. +RUN useradd gha -u 1001 -m -s /bin/bash + +# Also add the user to passwordless sudoers so that we can install software +# later on without having to rebuild the container. +RUN adduser gha sudo +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +USER gha +WORKDIR /home/gha diff --git a/.github/workflows/libc-fullbuild-tests.yml b/.github/workflows/libc-fullbuild-tests.yml index d9445a6407ccf..c796196b2180f 100644 --- a/.github/workflows/libc-fullbuild-tests.yml +++ b/.github/workflows/libc-fullbuild-tests.yml @@ -12,6 +12,13 @@ on: jobs: build: runs-on: ${{ matrix.os }} + container: + image: ${{ (startsWith(matrix.os, 'ubuntu-24.04-arm') && 'ghcr.io/llvm/arm64v8/libc-ubuntu-24.04') || 'ghcr.io/llvm/libc-ubuntu-24.04'}} + # We need to enable privileged containers so that certain libc tests + # have the necessary permissions (like SYS_TIME). There are no security + # implications as we are already running in an isolated VM. + options: >- + --privileged strategy: fail-fast: false matrix: @@ -102,26 +109,13 @@ jobs: max-size: 1G key: libc_fullbuild_${{ matrix.c_compiler }} variant: sccache - - # Notice: - # - MPFR is required by some of the mathlib tests. - # - Debian has a multilib setup, so we need to symlink the asm directory. - # For more information, see https://wiki.debian.org/Multiarch/LibraryPathOverview - - name: Prepare dependencies (Ubuntu) - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 21 - sudo apt-get update - sudo apt-get install -y libmpfr-dev libgmp-dev libmpc-dev ninja-build linux-libc-dev - sudo ln -sf /usr/include/$(uname -p)-linux-gnu/asm /usr/include/asm - name: Set reusable strings id: strings shell: bash run: | - echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - echo "build-install-dir=${{ github.workspace }}/install" >> "$GITHUB_OUTPUT" + echo "build-output-dir=/__w/llvm-project/llvm-project/build" >> "$GITHUB_OUTPUT" + echo "build-install-dir=/__w/llvm-project/llvm-project/install" >> "$GITHUB_OUTPUT" # Configure libc fullbuild with scudo. # Use MinSizeRel to reduce the size of the build. @@ -131,7 +125,7 @@ jobs: export CMAKE_FLAGS=" -G Ninja - -S ${{ github.workspace }}/runtimes + -S /__w/llvm-project/llvm-project/runtimes -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_ASM_COMPILER=${{ matrix.c_compiler }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} @@ -153,7 +147,7 @@ jobs: case "${{ matrix.target }}" in *-none-eabi|riscv32-unknown-elf) cmake $CMAKE_FLAGS \ - -C ${{ github.workspace }}/libc/cmake/caches/${{ matrix.target }}.cmake + -C /__w/llvm-project/llvm-project/libc/cmake/caches/${{ matrix.target }}.cmake ;; *) cmake -DLLVM_RUNTIME_TARGETS=${{ matrix.target }} \ diff --git a/.github/workflows/prune-branches.yml b/.github/workflows/prune-branches.yml index 73a1e6a97bee8..12d2c39bb4a36 100644 --- a/.github/workflows/prune-branches.yml +++ b/.github/workflows/prune-branches.yml @@ -7,6 +7,7 @@ on: pull_request: paths: - .github/workflows/prune-branches.yml + - .github/workflows/prune-unused-branches.py schedule: - cron: '0 8 * * *' # Runs daily at 08:00 UTC. @@ -30,10 +31,16 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir patches - python3 .github/workflows/prune-unused-branches.py patches/ + python3 .github/workflows/prune-unused-branches.py . - name: Upload Patches uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: BranchDiffs retention-days: 90 path: patches/*.patch + - name: Upload Branch List + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: BranchList + retention-days: 90 + path: branches.txt diff --git a/.github/workflows/prune-unused-branches.py b/.github/workflows/prune-unused-branches.py index ac8d87f4241e0..a39a25fcfb987 100644 --- a/.github/workflows/prune-unused-branches.py +++ b/.github/workflows/prune-unused-branches.py @@ -1,6 +1,10 @@ import subprocess import sys import os +import logging +import zipfile +import io +import urllib.request import github @@ -22,25 +26,26 @@ def branch_filter(branch_name): return [branch.replace("remotes/origin/", "") for branch in filtered_branches] -def get_branches_from_open_prs(github_token) -> list[str]: +def query_prs(github_token, extra_query_criteria) -> list[str]: gh = github.Github(auth=github.Auth.Token(github_token)) - query = """ - query ($after: String) { - search(query: "is:pr repo:llvm/llvm-project is:open head:users/", type: ISSUE, first: 100, after: $after) { - nodes { - ... on PullRequest { - baseRefName - headRefName - isCrossRepository - number - } - } - pageInfo { - hasNextPage - endCursor - } - } - }""" + query_template = """ + query ($after: String) {{ + search(query: "is:pr repo:llvm/llvm-project is:open {query_param}", type: ISSUE, first: 100, after: $after) {{ + nodes {{ + ... on PullRequest {{ + baseRefName + headRefName + isCrossRepository + number + }} + }} + pageInfo {{ + hasNextPage + endCursor + }} + }} + }}""" + query = query_template.format(query_param=extra_query_criteria) pr_data = [] has_next_page = True variables = {"after": None} @@ -54,26 +59,62 @@ def get_branches_from_open_prs(github_token) -> list[str]: pr_data.extend(prs) print(f"Processed {len(prs)} PRs") + return pr_data + + +def get_branches_from_open_prs(github_token) -> list[str]: + pr_data = [] + pr_data.extend(query_prs(github_token, "head:users/")) + # We need to explicitly check cases where the base is a user branch to + # ensure we capture branches that are used as a diff base for cross-repo + # PRs. + pr_data.extend(query_prs(github_token, "base:users/")) + user_branches = [] for pr in pr_data: if not pr["isCrossRepository"]: if pr["baseRefName"] != "main": user_branches.append(pr["baseRefName"]) user_branches.append(pr["headRefName"]) - return user_branches + else: + # We want to skip cross-repo PRs where someone has simply used a + # users/ branch naming scheme for a branch in their fork. + if pr["baseRefName"] == "main": + continue + user_branches.append(pr["baseRefName"]) + # Convert to a set to ensure we have no duplicates. + return list(set(user_branches)) def get_user_branches_to_remove( - user_branches: list[str], user_branches_from_prs: list[str] + user_branches: list[str], + user_branches_from_prs: list[str], + previous_run_branches: list[str], ) -> list[str]: user_branches_to_remove = set(user_branches) for pr_user_branch in set(user_branches_from_prs): + if pr_user_branch not in user_branches_to_remove: + logging.warning( + f"Found branch {pr_user_branch} attached to a PR, but it " + "was not found in the repository. This is likely because " + "the PR was created after this workflow cloned the repository." + ) + continue user_branches_to_remove.remove(pr_user_branch) + for branch in list(user_branches_to_remove): + if branch not in previous_run_branches: + user_branches_to_remove.remove(branch) return list(user_branches_to_remove) def generate_patch_for_branch(branch_name: str) -> bytes: - command_vector = ["git", "diff", f"origin/main...origin/{branch_name}"] + command_vector = [ + "git", + "format-patch", + "--stdout", + "-k", + f"origin/main..origin/{branch_name}", + ] try: result = subprocess.run( command_vector, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True @@ -118,22 +159,58 @@ def delete_branches(branches_to_remove: list[str]): print(f"Deleted branch {branch}") +def get_branches_found_in_previous_run(github_token: str) -> list[str]: + gh = github.Github(auth=github.Auth.Token(github_token)) + repo = gh.get_repo("llvm/llvm-project") + workflow_run = None + for workflow_run in iter( + repo.get_workflow("prune-branches.yml").get_runs(branch="main") + ): + if workflow_run.status == "completed": + break + assert workflow_run + workflow_artifact = None + for workflow_artifact in iter(workflow_run.get_artifacts()): + if workflow_artifact.name == "BranchList": + break + assert workflow_artifact + status, headers, response = workflow_artifact._requester.requestJson( + "GET", workflow_artifact.archive_download_url + ) + # Github will always send a redirect to where the file actuall lives. + assert status == 302 + with urllib.request.urlopen(headers["location"]) as response: + raw_bytes = response.read() + with zipfile.ZipFile(io.BytesIO(raw_bytes)) as zip_file: + branch_names = zip_file.read("branches.txt").decode("utf-8").split("\n")[:-1] + return branch_names + + def main(github_token): if len(sys.argv) != 2: print( - "Invalid invocation. Correct usage: python3 prune-unused-branches.py " + "Invalid invocation. Correct usage: python3 prune-unused-branches.py " ) sys.exit(1) + previous_run_branches = get_branches_found_in_previous_run(github_token) + print( + f"{len(previous_run_branches)} branches existed the last time the workflow ran." + ) user_branches = get_branches() + output_dir = sys.argv[1] + with open(os.path.join(output_dir, "branches.txt"), "w") as branches_file: + branches_file.writelines([user_branch + "\n" for user_branch in user_branches]) user_branches_from_prs = get_branches_from_open_prs(github_token) print(f"Found {len(user_branches)} user branches in the repository") print(f"Found {len(user_branches_from_prs)} user branches associated with PRs") user_branches_to_remove = get_user_branches_to_remove( - user_branches, user_branches_from_prs + user_branches, user_branches_from_prs, previous_run_branches ) print(f"Deleting {len(user_branches_to_remove)} user branches.") - generate_patches_for_all_branches(user_branches_to_remove, sys.argv[1]) + generate_patches_for_all_branches( + user_branches_to_remove, os.path.join(output_dir, "patches") + ) delete_branches(user_branches_to_remove) diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index a8188289412a0..4001bdeec87e3 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -176,7 +176,11 @@ jobs: test_runs_on=$build_runs_on ;; windows-11-arm) - build_runs_on="windows-11-arm-16core" + if [ "$GITHUB_EVENT_NAME" = "pull_request" ] || [ "$GITHUB_EVENT_NAME" = "schedule" ]; then + build_runs_on="${{ inputs.runs-on }}" + else + build_runs_on="windows-11-arm-16core" + fi test_runs_on=$build_runs_on ;; macos-14) @@ -352,6 +356,7 @@ jobs: - name: Upload Artifacts uses: ./.github/workflows/upload-release-artifact with: + release-version: ${{ needs.prepare.outputs.release-version }} artifact-id: ${{ needs.build-release-package.outputs.artifact-id }} attestation-name: ${{ needs.prepare.outputs.attestation-name }} digest: ${{ needs.build-release-package.outputs.digest }} diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml index 1d5d1f6bc03fa..3828633ec3921 100644 --- a/.github/workflows/release-documentation.yml +++ b/.github/workflows/release-documentation.yml @@ -36,6 +36,7 @@ on: jobs: release-documentation: name: Build and Upload Release Documentation + environment: release runs-on: ubuntu-24.04 env: upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }} diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml index ce44311507a83..cfa0f9d81dcaa 100644 --- a/.github/workflows/release-tasks.yml +++ b/.github/workflows/release-tasks.yml @@ -54,10 +54,6 @@ jobs: with: release-version: ${{ needs.validate-tag.outputs.release-version }} upload: true - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - LLVMBOT_WWW_RELEASES_PUSH: ${{ secrets.LLVMBOT_WWW_RELEASES_PUSH }} - WWW_RELEASES_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }} release-doxygen: name: Build and Upload Release Doxygen diff --git a/.gitignore b/.gitignore index 9ce99993e767c..fa133b2d09834 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ autoconf/autom4te.cache /cmake-build* # Coding assistants' stuff /CLAUDE.md +/instructions.md .claude/ /GEMINI.md .gemini/ diff --git a/FOO b/FOO new file mode 100644 index 0000000000000..7d673d082777e --- /dev/null +++ b/FOO @@ -0,0 +1,4 @@ +FOO +FOOO +FOOOO +ASDF diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index 4637fe311927c..80375b1167b0c 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -400,7 +400,8 @@ class BinaryFunction { FragmentsSetTy ParentFragments; /// Indicate if the function body was folded into another function. - /// Used by ICF optimization. + /// Used by ICF optimization. Always points to the root parent function + /// (i.e., a function that is not itself folded). BinaryFunction *FoldedIntoFunction{nullptr}; /// All fragments for a parent function. diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index e571e91d85135..c8bffe741038a 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -545,6 +545,11 @@ class MCPlusBuilder { llvm_unreachable("not implemented"); } + virtual void createDirectBranch(MCInst &Inst, const MCSymbol *Target, + MCContext *Ctx) { + llvm_unreachable("not implemented"); + } + virtual MCPhysReg getX86R11() const { llvm_unreachable("not implemented"); } virtual unsigned getShortBranchOpcode(unsigned Opcode) const { @@ -1747,6 +1752,12 @@ class MCPlusBuilder { return false; } + /// AArch64 uses this to perform diagnostics in the LongJmp pass. + virtual bool isShortRangeBranch(const MCInst &Inst) const { + llvm_unreachable("not implemented"); + return false; + } + /// Receives a list of MCInst of the basic block to analyze and interpret the /// terminators of this basic block. TBB must be initialized with the original /// fall-through for this BB. @@ -1787,6 +1798,21 @@ class MCPlusBuilder { llvm_unreachable("not implemented"); } + virtual void applyBTIFixupToTarget(BinaryBasicBlock &StubBB) { + llvm_unreachable("not implemented"); + } + + virtual void applyBTIFixupToSymbol(BinaryContext &BC, const MCSymbol *Symbol, + MCInst &Call) { + llvm_unreachable("not implemented"); + } + + virtual void applyBTIFixupCommon(const MCSymbol *RealTargetSym, + BinaryFunction *TgtFunction, + BinaryBasicBlock *TgtBB, MCInst &Call) { + llvm_unreachable("not implemented"); + } + virtual bool analyzeVirtualMethodCall(InstructionIterator Begin, InstructionIterator End, std::vector &MethodFetchInsns, @@ -2378,7 +2404,7 @@ class MCPlusBuilder { virtual InstructionListType createInstrumentedIndirectCall(MCInst &&CallInst, MCSymbol *HandlerFuncAddr, - int CallSiteID, MCContext *Ctx) { + size_t CallSiteID, MCContext *Ctx) { llvm_unreachable("not implemented"); return InstructionListType(); } diff --git a/bolt/lib/Passes/Hugify.cpp b/bolt/lib/Passes/Hugify.cpp index 1ac1b08573b86..2f240b7cdeb19 100644 --- a/bolt/lib/Passes/Hugify.cpp +++ b/bolt/lib/Passes/Hugify.cpp @@ -42,8 +42,11 @@ Error HugePage::runOnFunctions(BinaryContext &BC) { BC.getBinaryFunctionAtAddress(*BC.StartFunctionAddress); assert(Start && "Entry point function not found"); const MCSymbol *StartSym = Start->getSymbol(); - createSimpleFunction("__bolt_hugify_start_program", - BC.MIB->createSymbolTrampoline(StartSym, BC.Ctx.get())); + InstructionListType Insts = + BC.MIB->createSymbolTrampoline(StartSym, BC.Ctx.get()); + createSimpleFunction("__bolt_hugify_start_program", Insts); + if (BC.usesBTI()) + BC.MIB->applyBTIFixupToSymbol(BC, StartSym, *(Insts.end() - 1)); return Error::success(); } } // namespace bolt diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp index 1c99a60bfaad3..e412bc1530219 100644 --- a/bolt/lib/Passes/IdenticalCodeFolding.cpp +++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp @@ -605,6 +605,19 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) { } while (NumFoldedLastIteration > 0); + // Flatten folded function chains so FoldedIntoFunction always points + // to the root parent. + for (auto &BFI : BC.getBinaryFunctions()) { + BinaryFunction &BF = BFI.second; + if (!BF.isFolded()) + continue; + BinaryFunction *Parent = BF.getFoldedIntoFunction(); + while (Parent->isFolded()) + Parent = Parent->getFoldedIntoFunction(); + if (Parent != BF.getFoldedIntoFunction()) + BF.setFolded(Parent); + } + LLVM_DEBUG({ // Print functions that are congruent but not identical. for (auto &CBI : CongruentBuckets) { diff --git a/bolt/lib/Passes/LongJmp.cpp b/bolt/lib/Passes/LongJmp.cpp index 798e1ba08918a..6ae3a35b84b27 100644 --- a/bolt/lib/Passes/LongJmp.cpp +++ b/bolt/lib/Passes/LongJmp.cpp @@ -43,6 +43,8 @@ static void relaxStubToShortJmp(BinaryBasicBlock &StubBB, const MCSymbol *Tgt) { BC.MIB->createShortJmp(Seq, Tgt, BC.Ctx.get()); StubBB.clear(); StubBB.addInstructions(Seq.begin(), Seq.end()); + if (BC.usesBTI()) + BC.MIB->applyBTIFixupToTarget(StubBB); } static void relaxStubToLongJmp(BinaryBasicBlock &StubBB, const MCSymbol *Tgt) { @@ -51,6 +53,8 @@ static void relaxStubToLongJmp(BinaryBasicBlock &StubBB, const MCSymbol *Tgt) { BC.MIB->createLongJmp(Seq, Tgt, BC.Ctx.get()); StubBB.clear(); StubBB.addInstructions(Seq.begin(), Seq.end()); + if (BC.usesBTI()) + BC.MIB->applyBTIFixupToTarget(StubBB); } static BinaryBasicBlock *getBBAtHotColdSplitPoint(BinaryFunction &Func) { @@ -69,6 +73,13 @@ static BinaryBasicBlock *getBBAtHotColdSplitPoint(BinaryFunction &Func) { } static bool mayNeedStub(const BinaryContext &BC, const MCInst &Inst) { + if (BC.isAArch64() && BC.MIB->isShortRangeBranch(Inst) && + !opts::CompactCodeModel) { + BC.errs() << "BOLT-ERROR: short range branch not supported" + << " outside compact code model\n"; + BC.printInstruction(BC.errs(), Inst); + exit(1); + } return (BC.MIB->isBranch(Inst) || BC.MIB->isCall(Inst)) && !BC.MIB->isIndirectBranch(Inst) && !BC.MIB->isIndirectCall(Inst); } @@ -484,62 +495,12 @@ Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) { ~((1ULL << (RangeSingleInstr - 1)) - 1); const MCSymbol *RealTargetSym = BC.MIB->getTargetSymbol(*StubBB.begin()); - BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(RealTargetSym); - BinaryFunction *TargetFunction = BC.getFunctionForSymbol(RealTargetSym); + const BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(RealTargetSym); uint64_t TgtAddress = getSymbolAddress(BC, RealTargetSym, TgtBB); uint64_t DotAddress = BBAddresses[&StubBB]; uint64_t PCRelTgtAddress = DotAddress > TgtAddress ? DotAddress - TgtAddress : TgtAddress - DotAddress; - auto applyBTIFixup = [&](BinaryFunction *TargetFunction, - BinaryBasicBlock *RealTgtBB) { - // TODO: add support for editing each type, and remove errors. - if (!TargetFunction && !RealTgtBB) { - BC.errs() << "BOLT-ERROR: Cannot add BTI to function with symbol " - << RealTargetSym->getName() << "\n"; - exit(1); - } - if (TargetFunction && TargetFunction->isPLTFunction()) { - BC.MIB->patchPLTEntryForBTI(*TargetFunction, - *StubBB.getLastNonPseudoInstr()); - return; - } - if (TargetFunction && TargetFunction->isIgnored()) { - // Includes PLT functions. - BC.errs() << "BOLT-ERROR: Cannot add BTI landing pad to ignored function " - << TargetFunction->getPrintName() << "\n"; - exit(1); - } - if (TargetFunction && !TargetFunction->hasCFG()) { - if (TargetFunction->hasInstructions()) { - auto FirstII = TargetFunction->instrs().begin(); - MCInst FirstInst = FirstII->second; - if (BC.MIB->isCallCoveredByBTI(*StubBB.getLastNonPseudoInstr(), - FirstInst)) - return; - } - BC.errs() - << "BOLT-ERROR: Cannot add BTI landing pad to function without CFG: " - << TargetFunction->getPrintName() << "\n"; - exit(1); - } - if (!RealTgtBB) - // !RealTgtBB -> TargetFunction is not a nullptr - RealTgtBB = &*TargetFunction->begin(); - if (RealTgtBB) { - if (!RealTgtBB->hasParent()) { - BC.errs() << "BOLT-ERROR: Cannot add BTI to block with no parent " - "function. Targeted symbol: " - << RealTargetSym->getName() << "\n"; - exit(1); - } - // The BR is the last inst of the StubBB. - BC.MIB->insertBTI(*RealTgtBB, *StubBB.getLastNonPseudoInstr()); - return; - } - BC.errs() << "BOLT-ERROR: unhandled case when applying BTI fixup\n"; - exit(1); - }; // If it fits in one instruction, do not relax if (!(PCRelTgtAddress & SingleInstrMask)) return Error::success(); @@ -554,8 +515,6 @@ Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) { << " RealTargetSym = " << RealTargetSym->getName() << "\n"); relaxStubToShortJmp(StubBB, RealTargetSym); - if (BC.usesBTI()) - applyBTIFixup(TargetFunction, TgtBB); StubBits[&StubBB] = RangeShortJmp; Modified = true; return Error::success(); @@ -571,8 +530,6 @@ Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) { << Twine::utohexstr(PCRelTgtAddress) << " RealTargetSym = " << RealTargetSym->getName() << "\n"); relaxStubToLongJmp(StubBB, RealTargetSym); - if (BC.usesBTI()) - applyBTIFixup(TargetFunction, TgtBB); StubBits[&StubBB] = static_cast(BC.AsmInfo->getCodePointerSize() * 8); Modified = true; return Error::success(); diff --git a/bolt/lib/Passes/PatchEntries.cpp b/bolt/lib/Passes/PatchEntries.cpp index 1b79c122c5f9b..9af9e2ca3bdee 100644 --- a/bolt/lib/Passes/PatchEntries.cpp +++ b/bolt/lib/Passes/PatchEntries.cpp @@ -120,6 +120,9 @@ Error PatchEntries::runOnFunctions(BinaryContext &BC) { BinaryFunction *PatchFunction = BC.createInstructionPatch( Patch.Address, Instructions, NameResolver::append(Patch.Symbol->getName(), ".org.0")); + if (BC.usesBTI()) + BC.MIB->applyBTIFixupToSymbol(BC, Patch.Symbol, + *(Instructions.end() - 1)); // Verify the size requirements. uint64_t HotSize, ColdSize; diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index bde326aee2617..4539b7221cb5f 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -5162,9 +5162,7 @@ void RewriteInstance::updateELFSymbolTable( auto addExtraSymbols = [&](const BinaryFunction &Function, const ELFSymTy &FunctionSymbol) { if (Function.isFolded()) { - BinaryFunction *ICFParent = Function.getFoldedIntoFunction(); - while (ICFParent->isFolded()) - ICFParent = ICFParent->getFoldedIntoFunction(); + const BinaryFunction *ICFParent = Function.getFoldedIntoFunction(); ELFSymTy ICFSymbol = FunctionSymbol; SmallVector Buf; ICFSymbol.st_name = @@ -5280,8 +5278,7 @@ void RewriteInstance::updateELFSymbolTable( // instead so that the symbol gets updated to the parent's output address. // In non-relocation mode, folded functions are emitted at their original // location, so we keep the original function reference. - // Follow the chain of folded functions to get the final parent. - while (BC->HasRelocations && Function && Function->isFolded()) + if (BC->HasRelocations && Function && Function->isFolded()) Function = Function->getFoldedIntoFunction(); // Ignore false function references, e.g. when the section address matches // the address of the function. @@ -6084,8 +6081,8 @@ uint64_t RewriteInstance::getNewFunctionAddress(uint64_t OldAddress) { return 0; // If this function was folded, its output address is 0 since it wasn't - // emitted. Follow the chain to get the parent function's address. - while (Function->isFolded()) + // emitted. Get the parent function's address. + if (Function->isFolded()) Function = Function->getFoldedIntoFunction(); return Function->getOutputAddress(); diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index aa5cf3c671cdc..75436b6625f93 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -48,14 +48,14 @@ static cl::opt NoLSEAtomics( namespace { -static void getSystemFlag(MCInst &Inst, MCPhysReg RegName) { +[[maybe_unused]] static void getSystemFlag(MCInst &Inst, MCPhysReg RegName) { Inst.setOpcode(AArch64::MRS); Inst.clear(); Inst.addOperand(MCOperand::createReg(RegName)); Inst.addOperand(MCOperand::createImm(AArch64SysReg::NZCV)); } -static void setSystemFlag(MCInst &Inst, MCPhysReg RegName) { +[[maybe_unused]] static void setSystemFlag(MCInst &Inst, MCPhysReg RegName) { Inst.setOpcode(AArch64::MSR); Inst.clear(); Inst.addOperand(MCOperand::createImm(AArch64SysReg::NZCV)); @@ -716,6 +716,55 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { return Insts; } + bool isCompAndBranch(const MCInst &Inst) const { + const unsigned Opcode = Inst.getOpcode(); + switch (Opcode) { + // Compare register with immediate and branch. + case AArch64::CBGTWri: + case AArch64::CBGTXri: + case AArch64::CBLTWri: + case AArch64::CBLTXri: + case AArch64::CBHIWri: + case AArch64::CBHIXri: + case AArch64::CBLOWri: + case AArch64::CBLOXri: + case AArch64::CBEQWri: + case AArch64::CBEQXri: + case AArch64::CBNEWri: + case AArch64::CBNEXri: + // Compare registers and branch. + case AArch64::CBGTWrr: + case AArch64::CBGTXrr: + case AArch64::CBGEWrr: + case AArch64::CBGEXrr: + case AArch64::CBHIWrr: + case AArch64::CBHIXrr: + case AArch64::CBHSWrr: + case AArch64::CBHSXrr: + case AArch64::CBEQWrr: + case AArch64::CBEQXrr: + case AArch64::CBNEWrr: + case AArch64::CBNEXrr: + // Compare bytes and branch. + case AArch64::CBBGTWrr: + case AArch64::CBBGEWrr: + case AArch64::CBBHIWrr: + case AArch64::CBBHSWrr: + case AArch64::CBBEQWrr: + case AArch64::CBBNEWrr: + // Compare halfwords and branch. + case AArch64::CBHGTWrr: + case AArch64::CBHGEWrr: + case AArch64::CBHHIWrr: + case AArch64::CBHHSWrr: + case AArch64::CBHEQWrr: + case AArch64::CBHNEWrr: + return true; + default: + return false; + } + } + bool isTB(const MCInst &Inst) const { return (Inst.getOpcode() == AArch64::TBNZW || Inst.getOpcode() == AArch64::TBNZX || @@ -1260,7 +1309,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { if (isConditionalBranch(Inst) || isADR(Inst) || isADRP(Inst) || isMOVW(Inst)) OpNum = 1; - if (isTB(Inst) || isAddXri(Inst)) + if (isTB(Inst) || isAddXri(Inst) || isCompAndBranch(Inst)) OpNum = 2; } @@ -1329,7 +1378,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { ++OI; } - if (isTB(Inst)) { + if (isTB(Inst) || isCompAndBranch(Inst)) { assert(MCPlus::getNumPrimeOperands(Inst) >= 3 && "Invalid number of operands"); OI = Inst.begin() + 2; @@ -1733,7 +1782,72 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { BC.createInstructionPatch(PLTFunction.getAddress(), NewPLTSeq); } + void applyBTIFixupToSymbol(BinaryContext &BC, const MCSymbol *TargetSymbol, + MCInst &Call) override { + BinaryFunction *TargetFunction = BC.getFunctionForSymbol(TargetSymbol); + applyBTIFixupCommon(TargetSymbol, TargetFunction, nullptr, Call); + } + + void applyBTIFixupToTarget(BinaryBasicBlock &StubBB) override { + BinaryFunction &Func = *StubBB.getFunction(); + BinaryContext &BC = Func.getBinaryContext(); + const MCSymbol *RealTargetSym = BC.MIB->getTargetSymbol(*StubBB.begin()); + BinaryFunction *TargetFunction = BC.getFunctionForSymbol(RealTargetSym); + BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(RealTargetSym); + applyBTIFixupCommon(RealTargetSym, TargetFunction, TgtBB, + *StubBB.getLastNonPseudoInstr()); + } + + void applyBTIFixupCommon(const MCSymbol *RealTargetSym, + BinaryFunction *TargetFunction, + BinaryBasicBlock *TargetBB, MCInst &Call) override { + // TODO: add support for editing each type, and remove errors. + if (!TargetFunction && !TargetBB) { + errs() << "BOLT-ERROR: Cannot add BTI to function with symbol " + << RealTargetSym->getName() << "\n"; + exit(1); + } + if (TargetFunction && TargetFunction->isPLTFunction()) { + patchPLTEntryForBTI(*TargetFunction, Call); + return; + } + if (TargetFunction && TargetFunction->isIgnored()) { + errs() << "BOLT-ERROR: Cannot add BTI landing pad to ignored function " + << TargetFunction->getPrintName() << "\n"; + exit(1); + } + if (TargetFunction && !TargetFunction->hasCFG()) { + if (TargetFunction->hasInstructions()) { + auto FirstII = TargetFunction->instrs().begin(); + MCInst FirstInst = FirstII->second; + if (isCallCoveredByBTI(Call, FirstInst)) + return; + } + errs() + << "BOLT-ERROR: Cannot add BTI landing pad to function without CFG: " + << TargetFunction->getPrintName() << "\n"; + exit(1); + } + if (!TargetBB) + // No need to check TargetFunction for nullptr, because + // !TargetBB &&!TargetFunction has already been checked. + TargetBB = &*TargetFunction->begin(); + if (TargetBB) { + if (!TargetBB->hasParent()) { + errs() << "BOLT-ERROR: Cannot add BTI to block with no parent " + "function. Targeted symbol: " + << RealTargetSym->getName() << "\n"; + exit(1); + } + insertBTI(*TargetBB, Call); + return; + } + errs() << "BOLT-ERROR: unhandled case when applying BTI fixup\n"; + exit(1); + } + unsigned getInvertedBranchOpcode(unsigned Opcode) const { + // clang-format off switch (Opcode) { default: llvm_unreachable("Failed to invert branch opcode"); @@ -1746,7 +1860,48 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { case AArch64::CBZX: return AArch64::CBNZX; case AArch64::CBNZW: return AArch64::CBZW; case AArch64::CBNZX: return AArch64::CBZX; - } + // Compare register with immediate and branch. + case AArch64::CBGTWri: return AArch64::CBLTWri; // +1 + case AArch64::CBGTXri: return AArch64::CBLTXri; // +1 + case AArch64::CBLTWri: return AArch64::CBGTWri; // -1 + case AArch64::CBLTXri: return AArch64::CBGTXri; // -1 + case AArch64::CBHIWri: return AArch64::CBLOWri; // +1 + case AArch64::CBHIXri: return AArch64::CBLOXri; // +1 + case AArch64::CBLOWri: return AArch64::CBHIWri; // -1 + case AArch64::CBLOXri: return AArch64::CBHIXri; // -1 + case AArch64::CBEQWri: return AArch64::CBNEWri; + case AArch64::CBEQXri: return AArch64::CBNEXri; + case AArch64::CBNEWri: return AArch64::CBEQWri; + case AArch64::CBNEXri: return AArch64::CBEQXri; + // Compare registers and branch. + case AArch64::CBGTWrr: return AArch64::CBGEWrr; // swap + case AArch64::CBGTXrr: return AArch64::CBGEXrr; // swap + case AArch64::CBGEWrr: return AArch64::CBGTWrr; // swap + case AArch64::CBGEXrr: return AArch64::CBGTXrr; // swap + case AArch64::CBHIWrr: return AArch64::CBHSWrr; // swap + case AArch64::CBHIXrr: return AArch64::CBHSXrr; // swap + case AArch64::CBHSWrr: return AArch64::CBHIWrr; // swap + case AArch64::CBHSXrr: return AArch64::CBHIXrr; // swap + case AArch64::CBEQWrr: return AArch64::CBNEWrr; + case AArch64::CBEQXrr: return AArch64::CBNEXrr; + case AArch64::CBNEWrr: return AArch64::CBEQWrr; + case AArch64::CBNEXrr: return AArch64::CBEQXrr; + // Compare bytes and branch. + case AArch64::CBBGTWrr: return AArch64::CBBGEWrr; // swap + case AArch64::CBBGEWrr: return AArch64::CBBGTWrr; // swap + case AArch64::CBBHIWrr: return AArch64::CBBHSWrr; // swap + case AArch64::CBBHSWrr: return AArch64::CBBHIWrr; // swap + case AArch64::CBBEQWrr: return AArch64::CBBNEWrr; + case AArch64::CBBNEWrr: return AArch64::CBBEQWrr; + // Compare halfwords and branch. + case AArch64::CBHGTWrr: return AArch64::CBHGEWrr; // swap + case AArch64::CBHGEWrr: return AArch64::CBHGTWrr; // swap + case AArch64::CBHHIWrr: return AArch64::CBHHSWrr; // swap + case AArch64::CBHHSWrr: return AArch64::CBHHIWrr; // swap + case AArch64::CBHEQWrr: return AArch64::CBHNEWrr; + case AArch64::CBHNEWrr: return AArch64::CBHEQWrr; + } + // clang-format on } unsigned getCondCode(const MCInst &Inst) const override { @@ -1766,11 +1921,89 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { } } + bool needsRegSwap(unsigned Opcode) const { + switch (Opcode) { + default: + return false; + // Compare registers and branch. + case AArch64::CBGTWrr: + case AArch64::CBGTXrr: + case AArch64::CBGEWrr: + case AArch64::CBGEXrr: + case AArch64::CBHIWrr: + case AArch64::CBHIXrr: + case AArch64::CBHSWrr: + case AArch64::CBHSXrr: + // Compare bytes and branch. + case AArch64::CBBGTWrr: + case AArch64::CBBGEWrr: + case AArch64::CBBHIWrr: + case AArch64::CBBHSWrr: + // Compare halfwords and branch. + case AArch64::CBHGTWrr: + case AArch64::CBHGEWrr: + case AArch64::CBHHIWrr: + case AArch64::CBHHSWrr: + return true; + } + } + + bool needsImmDec(unsigned Opcode) const { + switch (Opcode) { + default: + return false; + case AArch64::CBGTWri: + case AArch64::CBGTXri: + case AArch64::CBHIWri: + case AArch64::CBHIXri: + return true; + } + } + + bool needsImmInc(unsigned Opcode) const { + switch (Opcode) { + default: + return false; + case AArch64::CBLTWri: + case AArch64::CBLTXri: + case AArch64::CBLOWri: + case AArch64::CBLOXri: + return true; + } + } + + bool isReversibleBranch(const MCInst &Inst) const override { + if (isCompAndBranch(Inst)) { + unsigned InvertedOpcode = getInvertedBranchOpcode(Inst.getOpcode()); + if (needsImmDec(InvertedOpcode) && Inst.getOperand(1).getImm() == 0) + return false; + if (needsImmInc(InvertedOpcode) && Inst.getOperand(1).getImm() == 63) + return false; + } + return MCPlusBuilder::isReversibleBranch(Inst); + } + void reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB, MCContext *Ctx) const override { - if (isTB(Inst) || isCB(Inst)) { - Inst.setOpcode(getInvertedBranchOpcode(Inst.getOpcode())); + if (!isReversibleBranch(Inst)) { + errs() << "BOLT-ERROR: Cannot reverse branch " << Inst << "\n"; + exit(1); + } + + if (isTB(Inst) || isCB(Inst) || isCompAndBranch(Inst)) { + unsigned InvertedOpcode = getInvertedBranchOpcode(Inst.getOpcode()); + Inst.setOpcode(InvertedOpcode); assert(Inst.getOpcode() != 0 && "Invalid branch instruction"); + // The FEAT_CMPBR compare-and-branch instructions cannot encode all + // the possible condition codes, therefore we either have to adjust + // the immediate value by +-1, or to swap the register operands + // when reversing the branch condition. + if (needsRegSwap(InvertedOpcode)) + std::swap(Inst.getOperand(0), Inst.getOperand(1)); + else if (needsImmDec(InvertedOpcode)) + Inst.getOperand(1).setImm(Inst.getOperand(1).getImm() - 1); + else if (needsImmInc(InvertedOpcode)) + Inst.getOperand(1).setImm(Inst.getOperand(1).getImm() + 1); } else if (Inst.getOpcode() == AArch64::Bcc) { Inst.getOperand(0).setImm(AArch64CC::getInvertedCondCode( static_cast(Inst.getOperand(0).getImm()))); @@ -1785,18 +2018,16 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { } int getPCRelEncodingSize(const MCInst &Inst) const override { + if (isCompAndBranch(Inst)) + return 11; + if (isTB(Inst)) + return 16; + if (isCB(Inst)) + return 21; switch (Inst.getOpcode()) { default: llvm_unreachable("Failed to get pcrel encoding size"); return 0; - case AArch64::TBZW: return 16; - case AArch64::TBZX: return 16; - case AArch64::TBNZW: return 16; - case AArch64::TBNZX: return 16; - case AArch64::CBZW: return 21; - case AArch64::CBZX: return 21; - case AArch64::CBNZW: return 21; - case AArch64::CBNZX: return 21; case AArch64::B: return 28; case AArch64::BL: return 28; case AArch64::Bcc: return 21; @@ -2178,6 +2409,18 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { convertJmpToTailCall(Inst); } + bool isShortRangeBranch(const MCInst &Inst) const override { + return isCompAndBranch(Inst); + } + + void createDirectBranch(MCInst &Inst, const MCSymbol *Target, + MCContext *Ctx) override { + Inst.setOpcode(AArch64::B); + Inst.clear(); + Inst.addOperand(MCOperand::createExpr(getTargetExprFor( + Inst, MCSymbolRefExpr::create(Target, *Ctx), *Ctx, 0))); + } + bool analyzeBranch(InstructionIterator Begin, InstructionIterator End, const MCSymbol *&TBB, const MCSymbol *&FBB, MCInst *&CondBranch, @@ -2535,21 +2778,14 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { } InstructionListType createInstrumentedIndCallHandlerExitBB() const override { - InstructionListType Insts(5); // Code sequence for instrumented indirect call handler: - // msr nzcv, x1 - // ldp x0, x1, [sp], #16 - // ldr x16, [sp], #16 - // ldp x0, x1, [sp], #16 - // br x16 - setSystemFlag(Insts[0], AArch64::X1); - createPopRegisters(Insts[1], AArch64::X0, AArch64::X1); - // Here we load address of the next function which should be called in the - // original binary to X16 register. Writing to X16 is permitted without - // needing to restore. - loadReg(Insts[2], AArch64::X16, AArch64::SP); - createPopRegisters(Insts[3], AArch64::X0, AArch64::X1); - createIndirectBranch(Insts[4], AArch64::X16, 0); + // ret + + InstructionListType Insts; + + Insts.emplace_back(); + createReturn(Insts.back()); + return Insts; } @@ -2602,14 +2838,28 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { InstructionListType createLoadImmediate(const MCPhysReg Dest, uint64_t Imm) const override { - InstructionListType Insts(4); - int Shift = 48; - for (int I = 0; I < 4; I++, Shift -= 16) { - Insts[I].setOpcode(AArch64::MOVKXi); - Insts[I].addOperand(MCOperand::createReg(Dest)); - Insts[I].addOperand(MCOperand::createReg(Dest)); - Insts[I].addOperand(MCOperand::createImm((Imm >> Shift) & 0xFFFF)); - Insts[I].addOperand(MCOperand::createImm(Shift)); + InstructionListType Insts; + + Insts.emplace_back(); + MCInst &Inst = Insts.back(); + Inst.clear(); + Inst.setOpcode(AArch64::MOVZXi); + Inst.addOperand(MCOperand::createReg(Dest)); + Inst.addOperand(MCOperand::createImm(Imm & 0xFFFF)); + Inst.addOperand(MCOperand::createImm(0)); + + int Shift = 16; + for (int I = 0; I < 3; I++, Shift += 16) { + const uint64_t ImmVal = (Imm >> Shift) & 0xFFFF; + if (!ImmVal) + continue; + Insts.emplace_back(); + MCInst &Inst = Insts.back(); + Inst.setOpcode(AArch64::MOVKXi); + Inst.addOperand(MCOperand::createReg(Dest)); + Inst.addOperand(MCOperand::createReg(Dest)); + Inst.addOperand(MCOperand::createImm(ImmVal)); + Inst.addOperand(MCOperand::createImm(Shift)); } return Insts; } @@ -2623,41 +2873,48 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { InstructionListType createInstrumentedIndirectCall(MCInst &&CallInst, MCSymbol *HandlerFuncAddr, - int CallSiteID, + size_t CallSiteID, MCContext *Ctx) override { - InstructionListType Insts; // Code sequence used to enter indirect call instrumentation helper: - // stp x0, x1, [sp, #-16]! createPushRegisters - // mov target x0 convertIndirectCallToLoad -> orr x0 target xzr - // mov x1 CallSiteID createLoadImmediate -> - // movk x1, #0x0, lsl #48 - // movk x1, #0x0, lsl #32 - // movk x1, #0x0, lsl #16 - // movk x1, #0x0 - // stp x0, x1, [sp, #-16]! - // bl *HandlerFuncAddr createIndirectCall -> - // adr x0 *HandlerFuncAddr -> adrp + add - // blr x0 + // snippet requires 2 registers: target address and call site id + // stp CallIDReg, x30, [sp, #-16]! + // movz/k CallIDReg, CallSiteID + // stp TAReg, CallIDReg, [sp, #-16]! ; push address and id for lib + // adr + add TAReg, *HandlerFuncAddr ; __bolt_instr_ind_call_handler_func + // blr TAReg + // ldr TAReg, [sp], #16 ; restore target address + // ldp CallIDReg, x30, [sp], #16 + // blr TAReg + + const MCRegister TAReg = CallInst.getOperand(0).getReg(); + const MCRegister CallIDReg = + TAReg != AArch64::X0 ? AArch64::X0 : AArch64::X1; + + InstructionListType Insts; Insts.emplace_back(); - createPushRegisters(Insts.back(), AArch64::X0, AArch64::X1); - Insts.emplace_back(CallInst); - convertIndirectCallToLoad(Insts.back(), AArch64::X0); - InstructionListType LoadImm = - createLoadImmediate(getIntArgRegister(1), CallSiteID); + createPushRegisters(Insts.back(), CallIDReg, AArch64::LR); + + InstructionListType LoadImm = createLoadImmediate(CallIDReg, CallSiteID); Insts.insert(Insts.end(), LoadImm.begin(), LoadImm.end()); + Insts.emplace_back(); - createPushRegisters(Insts.back(), AArch64::X0, AArch64::X1); + createPushRegisters(Insts.back(), TAReg, CallIDReg); + Insts.resize(Insts.size() + 2); - InstructionListType Addr = - materializeAddress(HandlerFuncAddr, Ctx, AArch64::X0); + InstructionListType Addr = materializeAddress(HandlerFuncAddr, Ctx, TAReg); assert(Addr.size() == 2 && "Invalid Addr size"); std::copy(Addr.begin(), Addr.end(), Insts.end() - Addr.size()); + Insts.emplace_back(); - createIndirectCallInst(Insts.back(), isTailCall(CallInst), AArch64::X0); + createIndirectCallInst(Insts.back(), false, TAReg); - // Carry over metadata including tail call marker if present. - stripAnnotations(Insts.back()); - moveAnnotations(std::move(CallInst), Insts.back()); + Insts.emplace_back(); + loadReg(Insts.back(), TAReg, getStackPointer()); + + Insts.emplace_back(); + createPopRegisters(Insts.back(), CallIDReg, AArch64::LR); + + Insts.emplace_back(CallInst); return Insts; } @@ -2666,12 +2923,10 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { createInstrumentedIndCallHandlerEntryBB(const MCSymbol *InstrTrampoline, const MCSymbol *IndCallHandler, MCContext *Ctx) override { - // Code sequence used to check whether InstrTampoline was initialized + // Code sequence used to check whether InstrTrampoline was initialized // and call it if so, returns via IndCallHandler - // stp x0, x1, [sp, #-16]! - // mrs x1, nzcv - // adr x0, InstrTrampoline -> adrp + add - // ldr x0, [x0] + // adrp x0, InstrTrampoline + // ldr x0, [x0, #lo12:InstrTrampoline] // subs x0, x0, #0x0 // b.eq IndCallHandler // str x30, [sp, #-16]! @@ -2679,30 +2934,42 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { // ldr x30, [sp], #16 // b IndCallHandler InstructionListType Insts; + + // load handler address + MCInst InstAdrp; + InstAdrp.setOpcode(AArch64::ADRP); + InstAdrp.addOperand(MCOperand::createReg(getIntArgRegister(0))); + InstAdrp.addOperand(MCOperand::createImm(0)); + setOperandToSymbolRef(InstAdrp, /* OpNum */ 1, InstrTrampoline, + /* Addend */ 0, Ctx, ELF::R_AARCH64_ADR_GOT_PAGE); + Insts.emplace_back(InstAdrp); + + MCInst InstLoad; + InstLoad.setOpcode(AArch64::LDRXui); + InstLoad.addOperand(MCOperand::createReg(getIntArgRegister(0))); + InstLoad.addOperand(MCOperand::createReg(getIntArgRegister(0))); + InstLoad.addOperand(MCOperand::createImm(0)); + setOperandToSymbolRef(InstLoad, /* OpNum */ 2, InstrTrampoline, + /* Addend */ 0, Ctx, ELF::R_AARCH64_LD64_GOT_LO12_NC); + Insts.emplace_back(InstLoad); + + InstructionListType CmpJmp = + createCmpJE(getIntArgRegister(0), 0, IndCallHandler, Ctx); + Insts.insert(Insts.end(), CmpJmp.begin(), CmpJmp.end()); + Insts.emplace_back(); - createPushRegisters(Insts.back(), AArch64::X0, AArch64::X1); - Insts.emplace_back(); - getSystemFlag(Insts.back(), getIntArgRegister(1)); - Insts.emplace_back(); - Insts.emplace_back(); - InstructionListType Addr = - materializeAddress(InstrTrampoline, Ctx, AArch64::X0); - std::copy(Addr.begin(), Addr.end(), Insts.end() - Addr.size()); - assert(Addr.size() == 2 && "Invalid Addr size"); - Insts.emplace_back(); - loadReg(Insts.back(), AArch64::X0, AArch64::X0); - InstructionListType cmpJmp = - createCmpJE(AArch64::X0, 0, IndCallHandler, Ctx); - Insts.insert(Insts.end(), cmpJmp.begin(), cmpJmp.end()); - Insts.emplace_back(); - storeReg(Insts.back(), AArch64::LR, AArch64::SP); + storeReg(Insts.back(), AArch64::LR, getStackPointer()); + Insts.emplace_back(); Insts.back().setOpcode(AArch64::BLR); - Insts.back().addOperand(MCOperand::createReg(AArch64::X0)); + Insts.back().addOperand(MCOperand::createReg(getIntArgRegister(0))); + Insts.emplace_back(); - loadReg(Insts.back(), AArch64::LR, AArch64::SP); + loadReg(Insts.back(), AArch64::LR, getStackPointer()); + Insts.emplace_back(); - createDirectCall(Insts.back(), IndCallHandler, Ctx, /*IsTailCall*/ true); + createDirectBranch(Insts.back(), IndCallHandler, Ctx); + return Insts; } diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp index d21bb628dcfcb..957768e5aaa29 100644 --- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp +++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp @@ -821,7 +821,7 @@ class RISCVMCPlusBuilder : public MCPlusBuilder { InstructionListType createInstrumentedIndirectCall(MCInst &&CallInst, MCSymbol *HandlerFuncAddr, - int CallSiteID, + size_t CallSiteID, MCContext *Ctx) override { // Code sequence used to enter indirect call instrumentation helper: // addi sp, sp, -0x10 diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp index 7c24c2ce136fa..51e7d27f18a0b 100644 --- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp @@ -3123,7 +3123,7 @@ class X86MCPlusBuilder : public MCPlusBuilder { InstructionListType createInstrumentedIndirectCall(MCInst &&CallInst, MCSymbol *HandlerFuncAddr, - int CallSiteID, + size_t CallSiteID, MCContext *Ctx) override { // Check if the target address expression used in the original indirect call // uses the stack pointer, which we are going to clobber. diff --git a/bolt/runtime/instr.cpp b/bolt/runtime/instr.cpp index c0b8fc3d807c9..bb5930a6083f3 100644 --- a/bolt/runtime/instr.cpp +++ b/bolt/runtime/instr.cpp @@ -1696,7 +1696,7 @@ extern "C" __attribute((naked)) void __bolt_instr_indirect_call() #if defined(__aarch64__) // clang-format off __asm__ __volatile__(SAVE_ALL - "ldp x0, x1, [sp, #288]\n" + "ldp x0, x1, [sp, #272]\n" "bl instrumentIndirectCall\n" RESTORE_ALL "ret\n" @@ -1733,7 +1733,7 @@ extern "C" __attribute((naked)) void __bolt_instr_indirect_tailcall() #if defined(__aarch64__) // clang-format off __asm__ __volatile__(SAVE_ALL - "ldp x0, x1, [sp, #288]\n" + "ldp x0, x1, [sp, #272]\n" "bl instrumentIndirectCall\n" RESTORE_ALL "ret\n" diff --git a/bolt/runtime/sys_aarch64.h b/bolt/runtime/sys_aarch64.h index b1d04f9d558e0..9cb8e022f58df 100644 --- a/bolt/runtime/sys_aarch64.h +++ b/bolt/runtime/sys_aarch64.h @@ -18,10 +18,12 @@ "stp x24, x25, [sp, #-16]!\n" \ "stp x26, x27, [sp, #-16]!\n" \ "stp x28, x29, [sp, #-16]!\n" \ - "str x30, [sp,#-16]!\n" + "mrs x29, nzcv\n" \ + "stp x29, x30, [sp, #-16]!\n" // Mirrors SAVE_ALL #define RESTORE_ALL \ - "ldr x30, [sp], #16\n" \ + "ldp x29, x30, [sp], #16\n" \ + "msr nzcv, x29\n" \ "ldp x28, x29, [sp], #16\n" \ "ldp x26, x27, [sp], #16\n" \ "ldp x24, x25, [sp], #16\n" \ diff --git a/bolt/test/AArch64/inline-bti-dbg.s b/bolt/test/AArch64/bti-inline-dbg.s similarity index 100% rename from bolt/test/AArch64/inline-bti-dbg.s rename to bolt/test/AArch64/bti-inline-dbg.s diff --git a/bolt/test/AArch64/inline-bti.s b/bolt/test/AArch64/bti-inline.s similarity index 100% rename from bolt/test/AArch64/inline-bti.s rename to bolt/test/AArch64/bti-inline.s diff --git a/bolt/test/AArch64/long-jmp-bti-ignored.s b/bolt/test/AArch64/bti-long-jmp-ignored.s similarity index 100% rename from bolt/test/AArch64/long-jmp-bti-ignored.s rename to bolt/test/AArch64/bti-long-jmp-ignored.s diff --git a/bolt/test/AArch64/long-jmp-bti.s b/bolt/test/AArch64/bti-long-jmp.s similarity index 100% rename from bolt/test/AArch64/long-jmp-bti.s rename to bolt/test/AArch64/bti-long-jmp.s diff --git a/bolt/test/AArch64/no-bti-note.test b/bolt/test/AArch64/bti-note-unused.test similarity index 100% rename from bolt/test/AArch64/no-bti-note.test rename to bolt/test/AArch64/bti-note-unused.test diff --git a/bolt/test/AArch64/bti-patch-entries.s b/bolt/test/AArch64/bti-patch-entries.s new file mode 100644 index 0000000000000..005b811ff4d7c --- /dev/null +++ b/bolt/test/AArch64/bti-patch-entries.s @@ -0,0 +1,38 @@ +# This test checks that BOLT can add BTI to targets at patched entries. + +# REQUIRES: system-linux + +# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \ +# RUN: %s -o %t.o +# RUN: %clang %cflags -pie %t.o -o %t.exe -nostdlib -Wl,-q,-z,force-bti +# RUN: llvm-bolt %t.exe -o %t.bolt --use-old-text=0 --lite=0 --force-patch | FileCheck %s --check-prefix=CHECK-BOLT +# RUN: llvm-objdump -dz %t.bolt | FileCheck %s + +# CHECK-BOLT: binary is using BTI + +# CHECK: : +# CHECK-NEXT: adrp x16, 0x[[#%x,ADRP:]] +# CHECK-NEXT: add x16, x16, #0x[[#%x,ADD:]] +# CHECK-NEXT: br x16 + +# CHECK: [[#ADRP + ADD]] : +# CHECK-NEXT: [[#ADRP + ADD]]: {{.*}} bti c +# CHECK-NEXT: ret + +.text +.balign 4 +.global pathedEntries +.type pathedEntries, %function +pathedEntries: + .rept 32 + nop + .endr + ret +.size pathedEntries, .-pathedEntries + +.global _start +.type _start, %function +_start: + bl pathedEntries + ret +.size _start, .-_start diff --git a/bolt/test/AArch64/compare-and-branch-inversion.S b/bolt/test/AArch64/compare-and-branch-inversion.S new file mode 100644 index 0000000000000..f5903b2b5d25d --- /dev/null +++ b/bolt/test/AArch64/compare-and-branch-inversion.S @@ -0,0 +1,108 @@ +# This test checks that branch inversion works when reordering blocks which +# contain short range conditional branches. Handles edge cases, like when +# the immediate value is the upper or lower allowed value in which case the +# transformation bails. + +# REQUIRES: system-linux, asserts + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: llvm-bolt -v=1 %t -o %t.bolt --data %t.fdata --reorder-blocks=ext-tsp --compact-code-model \ +# RUN: | FileCheck %s --check-prefix=BOLT-INFO +# RUN: llvm-objdump -d %t.bolt | FileCheck %s + +# CHECK: Disassembly of section .text: + + .globl immediate_increment + .type immediate_increment, %function +immediate_increment: +.entry0: +# FDATA: 1 immediate_increment #.entry0# 10 + cbgt x0, #0, .exit0 +.cold0: +# FDATA: 1 immediate_increment #.cold0# 1 + mov x0, #1 + ret +.exit0: +# FDATA: 1 immediate_increment #.exit0# 10 + mov x0, #2 + ret + +# CHECK: : +# CHECK-NEXT: {{.*}} cblt x0, #0x1, 0x[[ADDR0:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: {{.*}} mov x0, #0x2 // =2 +# CHECK-NEXT: {{.*}} ret +# CHECK-NEXT: [[ADDR0]]: {{.*}} mov x0, #0x1 // =1 +# CHECK-NEXT: {{.*}} ret + + .globl immediate_decrement + .type immediate_decrement, %function +immediate_decrement: +.entry1: +# FDATA: 1 immediate_decrement #.entry1# 10 + cblo x0, #1, .exit1 +.cold1: +# FDATA: 1 immediate_decrement #.cold1# 1 + mov x0, #1 + ret +.exit1: +# FDATA: 1 immediate_decrement #.exit1# 10 + mov x0, #2 + ret + +# CHECK: : +# CHECK-NEXT: {{.*}} cbhi x0, #0x0, 0x[[ADDR1:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: {{.*}} mov x0, #0x2 // =2 +# CHECK-NEXT: {{.*}} ret +# CHECK-NEXT: [[ADDR1]]: {{.*}} mov x0, #0x1 // =1 +# CHECK-NEXT: {{.*}} ret + + .globl register_swap + .type register_swap, %function +register_swap: +.entry2: +# FDATA: 1 register_swap #.entry2# 10 + cbge x0, x1, .exit2 +.cold2: +# FDATA: 1 register_swap #.cold2# 1 + mov x0, #1 + ret +.exit2: +# FDATA: 1 register_swap #.exit2# 10 + mov x0, #2 + ret + +# CHECK: : +# CHECK-NEXT: {{.*}} cbgt x1, x0, 0x[[ADDR2:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: {{.*}} mov x0, #0x2 // =2 +# CHECK-NEXT: {{.*}} ret +# CHECK-NEXT: [[ADDR2]]: {{.*}} mov x0, #0x1 // =1 +# CHECK-NEXT: {{.*}} ret + + .globl irreversible + .type irreversible, %function +irreversible: +.entry3: +# FDATA: 1 irreversible #.entry3# 10 + cbgt x0, #63, .exit3 +.cold3: +# FDATA: 1 irreversible #.cold3# 1 + mov x0, #1 + ret +.exit3: +# FDATA: 1 irreversible #.exit3# 10 + mov x0, #2 + ret + +# BOLT-INFO: unable to swap successors in irreversible + +# CHECK: : +# CHECK-NEXT: {{.*}} cbgt x0, #0x3f, 0x[[ADDR3:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: {{.*}} b 0x[[ADDR4:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: [[ADDR3]]: {{.*}} mov x0, #0x2 // =2 +# CHECK-NEXT: {{.*}} ret +# CHECK-NEXT: [[ADDR4]]: {{.*}} mov x0, #0x1 // =1 +# CHECK-NEXT: {{.*}} ret + +## Force relocation mode. +.reloc 0, R_AARCH64_NONE diff --git a/bolt/test/AArch64/compare-and-branch-reorder-blocks.S b/bolt/test/AArch64/compare-and-branch-reorder-blocks.S new file mode 100644 index 0000000000000..464243d788c1f --- /dev/null +++ b/bolt/test/AArch64/compare-and-branch-reorder-blocks.S @@ -0,0 +1,49 @@ +# This test checks that reordering blocks which contain short range +# conditional branches may break if the target goes out of range. + +# REQUIRES: system-linux, asserts + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=0 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata --reorder-blocks=ext-tsp --compact-code-model +# RUN: llvm-objdump -d %t.bolt | FileCheck %s + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=256 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: not llvm-bolt %t -o %t.bolt --data %t.fdata --reorder-blocks=ext-tsp --compact-code-model --keep-nops 2>&1 \ +# RUN: | FileCheck %s --check-prefix=FIXUP_OUT_OF_RANGE + + .globl reorder_blocks + .type reorder_blocks, %function +reorder_blocks: +.entry: +# FDATA: 1 reorder_blocks #.entry# 10 + cbgt x0, #0, .cold_exit +.fall_through: +# FDATA: 1 reorder_blocks #.fall_through# 10 + b .hot_exit +.cold_exit: +# FDATA: 1 reorder_blocks #.cold_exit# 1 + mov x0, #1 + ret +.hot_exit: +# FDATA: 1 reorder_blocks #.hot_exit# 10 + .rept NUM_NOPS + nop + .endr + mov x0, #2 + ret + +## Force relocation mode. +.reloc 0, R_AARCH64_NONE + +# CHECK: Disassembly of section .text: + +# CHECK: : +# CHECK-NEXT: {{.*}} cbgt x0, #0x0, 0x[[ADDR:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: {{.*}} mov x0, #0x2 // =2 +# CHECK-NEXT: {{.*}} ret +# CHECK-NEXT: [[ADDR]]: {{.*}} mov x0, #0x1 // =1 +# CHECK-NEXT: {{.*}} ret + +# FIXUP_OUT_OF_RANGE: error: fixup value out of range diff --git a/bolt/test/AArch64/compare-and-branch-split-functions.S b/bolt/test/AArch64/compare-and-branch-split-functions.S new file mode 100644 index 0000000000000..b9309a1ec69a1 --- /dev/null +++ b/bolt/test/AArch64/compare-and-branch-split-functions.S @@ -0,0 +1,62 @@ +# This test checks that splitting functions which contain short range +# conditional branches works in compact code model without relying on +# relocations. + +# REQUIRES: system-linux, asserts + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=0 -DRESERVE_SPACE=0 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions --compact-code-model +# RUN: llvm-objdump -d %t.bolt | FileCheck %s + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=256 -DRESERVE_SPACE=0 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions --compact-code-model --keep-nops +# RUN: llvm-objdump -d %t.bolt | FileCheck %s + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=0 -DRESERVE_SPACE=1 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: not llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions --compact-code-model 2>&1 \ +# RUN: | FileCheck %s --check-prefix=BEYOND-128MB + + .globl foo + .type foo, %function +foo: +.entry_foo: +# FDATA: 1 foo #.entry_foo# 10 + cbgt x0, #0, .Lcold_foo + mov x0, #1 +.Lcold_foo: + ret + + .globl large_function + .type large_function, %function +large_function: +.entry_large_function: +# FDATA: 1 large_function #.entry_large_function# 10 + .rept NUM_NOPS + nop + .endr + ret + +.if RESERVE_SPACE +.space 0x8000000 +.endif + +## Force relocation mode. +.reloc 0, R_AARCH64_NONE + +# CHECK: Disassembly of section .text: + +# CHECK: : +# CHECK-NEXT: {{.*}} cbgt x0, #0x0, 0x[[ADDR0:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: {{.*}} b 0x[[ADDR1:[0-9a-f]+]] <{{.*}}> +# CHECK-NEXT: [[ADDR0]]: {{.*}} b 0x[[ADDR2:[0-9a-f]+]] <{{.*}}> + +# CHECK: Disassembly of section .text.cold: + +# CHECK: : +# CHECK-NEXT: [[ADDR1]]: {{.*}} mov x0, #0x1 // =1 +# CHECK-NEXT: [[ADDR2]]: {{.*}} ret + +# BEYOND-128MB: BOLT-ERROR: JITLink failed: In graph in-memory object file, section .text: relocation target {{0x[0-9a-f]+}} () is out of range of Branch26PCRel fixup at address {{0x[0-9a-f]+}} diff --git a/bolt/test/AArch64/compare-and-branch-unsupported.S b/bolt/test/AArch64/compare-and-branch-unsupported.S new file mode 100644 index 0000000000000..a09c4fd32506c --- /dev/null +++ b/bolt/test/AArch64/compare-and-branch-unsupported.S @@ -0,0 +1,48 @@ +# This test checks that splitting functions which contain short range +# conditional branches does not work outside compact code model. + +# REQUIRES: system-linux, asserts + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=0 -DRESERVE_SPACE=0 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: not llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions 2>&1 \ +# RUN: | FileCheck %s + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=256 -DRESERVE_SPACE=0 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: not llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions --keep-nops 2>&1 \ +# RUN: | FileCheck %s + +# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DNUM_NOPS=0 -DRESERVE_SPACE=1 +# RUN: link_fdata --no-lbr %s %t %t.fdata +# RUN: not llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions 2>&1 \ +# RUN: | FileCheck %s + + .globl foo + .type foo, %function +foo: +.entry_foo: +# FDATA: 1 foo #.entry_foo# 10 + cbgt x0, #0, .Lcold_foo + mov x0, #1 +.Lcold_foo: + ret + + .globl large_function + .type large_function, %function +large_function: +.entry_large_function: +# FDATA: 1 large_function #.entry_large_function# 10 + .rept NUM_NOPS + nop + .endr + ret + +.if RESERVE_SPACE +.space 0x8000000 +.endif + +## Force relocation mode. +.reloc 0, R_AARCH64_NONE + +# CHECK: BOLT-ERROR: short range branch not supported outside compact code model diff --git a/bolt/test/runtime/AArch64/bti-hugify.c b/bolt/test/runtime/AArch64/bti-hugify.c new file mode 100644 index 0000000000000..9887a9746f113 --- /dev/null +++ b/bolt/test/runtime/AArch64/bti-hugify.c @@ -0,0 +1,36 @@ +// Make sure that during the Hugify pass BOLT adds a BTI instruction to _start. + +#include + +int main(int argc, char **argv) { + printf("Hello world\n"); + return 0; +} + +/* +REQUIRES: system-linux,bolt-runtime + +RUN: %clang %cflags -mbranch-protection=standard -no-pie %s -o %t.nopie.exe \ +RUN: -Wl,-q,-z,force-bti +RUN: %clang %cflags -mbranch-protection=standard -fpic %s -o %t.pie.exe \ +RUN: -Wl,-q,-z,force-bti + +RUN: llvm-bolt %t.nopie.exe --lite=0 -o %t.nopie.bolt --hugify | FileCheck %s \ +RUN: -check-prefix=CHECK-BOLT +RUN: llvm-bolt %t.pie.exe --lite=0 -o %t.pie.bolt --hugify | FileCheck %s \ +RUN: -check-prefix=CHECK-BOLT + +CHECK-BOLT: binary is using BTI + +RUN: llvm-objdump -D %t.nopie.bolt | FileCheck %s -check-prefix=CHECK-OBJDUMP +RUN: llvm-objdump -D %t.pie.bolt | FileCheck %s -check-prefix=CHECK-OBJDUMP + +CHECK-OBJDUMP: <_start>: +CHECK-OBJDUMP-NEXT: bti + +CHECK-OBJDUMP: <__bolt_hugify_start_program>: +CHECK-OBJDUMP-NEXT: adrp x16, 0x[[#%x,ADRP:]] +CHECK-OBJDUMP-NEXT: add x16, x16, #0x[[#%x,ADD:]] +CHECK-OBJDUMP-NEXT: br x16 + +*/ diff --git a/bolt/test/runtime/AArch64/instrumentation-ind-call-bti.c b/bolt/test/runtime/AArch64/bti-instrumentation-ind-call.c similarity index 100% rename from bolt/test/runtime/AArch64/instrumentation-ind-call-bti.c rename to bolt/test/runtime/AArch64/bti-instrumentation-ind-call.c diff --git a/bolt/test/runtime/AArch64/long-jmp-bti-plt.c b/bolt/test/runtime/AArch64/bti-long-jmp-plt.c similarity index 100% rename from bolt/test/runtime/AArch64/long-jmp-bti-plt.c rename to bolt/test/runtime/AArch64/bti-long-jmp-plt.c diff --git a/bolt/test/runtime/AArch64/instrumentation-ind-call.c b/bolt/test/runtime/AArch64/instrumentation-ind-call.c index f9056da333b4e..8d9d1e13060f5 100644 --- a/bolt/test/runtime/AArch64/instrumentation-ind-call.c +++ b/bolt/test/runtime/AArch64/instrumentation-ind-call.c @@ -4,20 +4,96 @@ typedef int (*func_ptr)(int, int); int add(int a, int b) { return a + b; } +int getConst(int a, int b) { return 0xaa55; } + +void foo() { + // clang-format off + __asm__ __volatile("stp x29, x30, [sp, #-16]!\n" + "adrp x0, getConst\n" + "add x0, x0, :lo12:getConst\n" + "blr x0\n" + "ldp x29, x30, [sp], #16\n" + :::); + // clang-format on + return; +} + int main() { func_ptr fun; fun = add; int sum = fun(10, 20); // indirect call to 'add' printf("The sum is: %d\n", sum); + foo(); return 0; } /* REQUIRES: system-linux,bolt-runtime RUN: %clang %cflags %s -o %t.exe -Wl,-q -no-pie -fpie +RUN: llvm-objdump --disassemble-symbols=main %t.exe \ +RUN: | FileCheck %s --check-prefix=CHECKINDIRECTREG + +CHECKINDIRECTREG: mov w0, #0xa +CHECKINDIRECTREG-NEXT: mov w1, #0x14 +CHECKINDIRECTREG-NEXT: blr x8 RUN: llvm-bolt %t.exe --instrument --instrumentation-file=%t.fdata \ -RUN: -o %t.instrumented +RUN: -o %t.instrumented \ +RUN: | FileCheck %s --check-prefix=CHECK-LOG + +CHECK-LOG-NOT: BOLT-INSTRUMENTER: Number of indirect call site descriptors: 0 + +RUN: llvm-objdump --disassemble-symbols=main %t.instrumented \ +RUN: | FileCheck %s --check-prefix=CHECK-MAIN + +RUN: llvm-objdump --disassemble-symbols=foo %t.instrumented \ +RUN: | FileCheck %s --check-prefix=CHECK-FOO + +RUN: llvm-objdump --disassemble-symbols=__bolt_instr_ind_call_handler \ +RUN: %t.instrumented | FileCheck %s --check-prefix=CHECK-INSTR-INDIR-CALL +RUN: llvm-objdump --disassemble-symbols=__bolt_instr_ind_call_handler_func \ +RUN: %t.instrumented | FileCheck %s --check-prefix=CHECK-INSTR-INDIR-CALL-FUNC + +CHECK-MAIN: mov w0, #0xa +CHECK-MAIN-NEXT: mov w1, #0x14 +// store current values +CHECK-MAIN-NEXT: stp x0, x30, [sp +// load callsite id +CHECK-MAIN-NEXT: mov x0, +CHECK-MAIN-NEXT: stp x8, x0, [sp +CHECK-MAIN-NEXT: adrp x8, +CHECK-MAIN-NEXT: add x8, x8 +// call instrumentation library handler function +CHECK-MAIN-NEXT: blr x8 +// restore registers saved before +CHECK-MAIN-NEXT: ldr x8, [sp] +CHECK-MAIN-NEXT: ldp x0, x30, [sp] +// original indirect call instruction +CHECK-MAIN-NEXT: blr x8 + +// instrumented pattern for blr x0 +CHECK-FOO: stp x1, x30, [sp +CHECK-FOO-NEXT: mov x1, +CHECK-FOO-NEXT: stp x0, x1, [sp +CHECK-FOO-NEXT: adrp x0 +CHECK-FOO-NEXT: add x0, x0 +CHECK-FOO-NEXT: blr x0 +CHECK-FOO-NEXT: ldr x0, [sp] +CHECK-FOO-NEXT: ldp x1, x30, [sp] +CHECK-FOO-NEXT: blr x0 + +CHECK-INSTR-INDIR-CALL: __bolt_instr_ind_call_handler>: +CHECK-INSTR-INDIR-CALL-NEXT: ret + +CHECK-INSTR-INDIR-CALL-FUNC: __bolt_instr_ind_call_handler_func>: +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: adrp x0 +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: ldr x0 +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: cmp x0, #0x0 +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: b.eq{{.*}}__bolt_instr_ind_call_handler +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: str x30 +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: blr x0 +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: ldr x30 +CHECK-INSTR-INDIR-CALL-FUNC-NEXT: b{{.*}}__bolt_instr_ind_call_handler # Instrumented program needs to finish returning zero RUN: %t.instrumented | FileCheck %s -check-prefix=CHECK-OUTPUT @@ -25,7 +101,7 @@ RUN: %t.instrumented | FileCheck %s -check-prefix=CHECK-OUTPUT # Test that the instrumented data makes sense RUN: llvm-bolt %t.exe -o %t.bolted --data %t.fdata \ RUN: --reorder-blocks=ext-tsp --reorder-functions=hfsort+ \ -RUN: --print-only=main --print-finalized | FileCheck %s +RUN: --print-only=main,foo --print-finalized | FileCheck %s RUN: %t.bolted | FileCheck %s -check-prefix=CHECK-OUTPUT @@ -33,6 +109,11 @@ CHECK-OUTPUT: The sum is: 30 # Check that our indirect call has 1 hit recorded in the fdata file and that # this was processed correctly by BOLT +CHECK-LABEL: Binary Function "foo" +CHECK: blr x0 # CallProfile: 1 (0 misses) : +CHECK-NEXT: { getConst: 1 (0 misses) } + +CHECK-LABEL: Binary Function "main" CHECK: blr x8 # CallProfile: 1 (0 misses) : CHECK-NEXT: { add: 1 (0 misses) } */ diff --git a/clang-tools-extra/Maintainers.rst b/clang-tools-extra/Maintainers.rst index fa1343a28942d..e3eb6e1ba7967 100644 --- a/clang-tools-extra/Maintainers.rst +++ b/clang-tools-extra/Maintainers.rst @@ -54,9 +54,6 @@ clangd | Nathan Ridge | zeratul976@hotmail.com (email), HighCommander4 (GitHub), HighCommander4 (Discourse), nridge (Discord) -| Chris Bieneman -| chris.bieneman@gmail.com (email), llvm-beanz (GitHub), beanz (Discord), beanz (Discourse) - Inactive Maintainers ==================== @@ -77,3 +74,4 @@ Inactive component maintainers | Peter Chou (peterchou411@gmail.com (email), PeterChou1 (GitHub, Discourse), .peterchou (Discord)) -- clang-doc | Piotr Zegar (me@piotrzegar.pl (email), PiotrZSL (GitHub), PiotrZSL (Discourse), PiotrZSL (Discord)) -- clang-tidy | Kadir Çetinkaya (kadircet@google.com (email), kadircet (GitHub) kadircet (Discourse), kadircet (Discord)) -- clangd +| Chris Bieneman (chris.bieneman@gmail.com (email), llvm-beanz (GitHub), beanz (Discord), beanz (Discourse)) -- clangd diff --git a/clang-tools-extra/clang-doc/assets/head-template.mustache b/clang-tools-extra/clang-doc/assets/head-template.mustache index f0a8057a9c3dc..2ee4823fb77c1 100644 --- a/clang-tools-extra/clang-doc/assets/head-template.mustache +++ b/clang-tools-extra/clang-doc/assets/head-template.mustache @@ -8,7 +8,8 @@ {{/Scripts}} {{! Highlight.js dependency for syntax highlighting }} - + + diff --git a/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp index d46896808bd09..eb9b710b28549 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.cpp @@ -17,6 +17,8 @@ using namespace clang::ast_matchers; namespace clang::tidy::bugprone { + +using utils::lexer::CommentToken; namespace { AST_MATCHER(Decl, isFromStdNamespaceOrSystemHeader) { if (const auto *D = Node.getDeclContext()->getEnclosingNamespaceContext()) @@ -77,55 +79,9 @@ void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) { this); } -static std::vector> -getCommentsInRange(ASTContext *Ctx, CharSourceRange Range) { - std::vector> Comments; - auto &SM = Ctx->getSourceManager(); - const std::pair BeginLoc = - SM.getDecomposedLoc(Range.getBegin()), - EndLoc = - SM.getDecomposedLoc(Range.getEnd()); - - if (BeginLoc.first != EndLoc.first) - return Comments; - - bool Invalid = false; - const StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid); - if (Invalid) - return Comments; - - const char *StrData = Buffer.data() + BeginLoc.second; - - Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(), - Buffer.begin(), StrData, Buffer.end()); - TheLexer.SetCommentRetentionState(true); - - while (true) { - Token Tok; - if (TheLexer.LexFromRawLexer(Tok)) - break; - if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof)) - break; - - if (Tok.is(tok::comment)) { - const std::pair CommentLoc = - SM.getDecomposedLoc(Tok.getLocation()); - assert(CommentLoc.first == BeginLoc.first); - Comments.emplace_back( - Tok.getLocation(), - StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength())); - } else { - // Clear comments found before the different token, e.g. comma. - Comments.clear(); - } - } - - return Comments; -} - -static std::vector> -getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) { - std::vector> Comments; +static std::vector getCommentsBeforeLoc(ASTContext *Ctx, + SourceLocation Loc) { + std::vector Comments; while (Loc.isValid()) { const std::optional Tok = utils::lexer::getPreviousToken( Loc, Ctx->getSourceManager(), Ctx->getLangOpts(), @@ -133,11 +89,12 @@ getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) { if (!Tok || Tok->isNot(tok::comment)) break; Loc = Tok->getLocation(); - Comments.emplace_back( + Comments.emplace_back(CommentToken{ Loc, Lexer::getSourceText(CharSourceRange::getCharRange( Loc, Loc.getLocWithOffset(Tok->getLength())), - Ctx->getSourceManager(), Ctx->getLangOpts())); + Ctx->getSourceManager(), Ctx->getLangOpts()), + }); } return Comments; } @@ -304,9 +261,10 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, MakeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc()); ArgBeginLoc = Args[I]->getEndLoc(); - std::vector> Comments; + std::vector Comments; if (BeforeArgument.isValid()) { - Comments = getCommentsInRange(Ctx, BeforeArgument); + Comments = utils::lexer::getTrailingCommentsInRange( + BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts()); } else { // Fall back to parsing back from the start of the argument. const CharSourceRange ArgsRange = @@ -314,18 +272,18 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin()); } - for (auto Comment : Comments) { + for (const auto &Comment : Comments) { llvm::SmallVector Matches; - if (IdentRE.match(Comment.second, &Matches) && + if (IdentRE.match(Comment.Text, &Matches) && !sameName(Matches[2], II->getName(), StrictMode)) { { const DiagnosticBuilder Diag = - diag(Comment.first, "argument name '%0' in comment does not " - "match parameter name %1") + diag(Comment.Loc, "argument name '%0' in comment does not " + "match parameter name %1") << Matches[2] << II; if (isLikelyTypo(Callee->parameters(), Matches[2], II->getName())) { Diag << FixItHint::CreateReplacement( - Comment.first, (Matches[1] + II->getName() + Matches[3]).str()); + Comment.Loc, (Matches[1] + II->getName() + Matches[3]).str()); } } diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note) << II; diff --git a/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp index 3e1188d5e2463..43a352e389f68 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp @@ -38,9 +38,15 @@ void BadSignalToKillThreadCheck::check(const MatchFinder::MatchResult &Result) { return std::nullopt; const MacroInfo *MI = PP->getMacroInfo(It->first); const Token &T = MI->tokens().back(); - if (!T.isLiteral() || !T.getLiteralData()) + + if (!T.isLiteral()) + return std::nullopt; + + SmallVector Buffer; + bool Invalid = false; + const StringRef ValueStr = PP->getSpelling(T, Buffer, &Invalid); + if (Invalid) return std::nullopt; - const StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); llvm::APInt IntValue; constexpr unsigned AutoSenseRadix = 0; diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp index 93b5b96926865..770d1c8e55fef 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp @@ -95,7 +95,7 @@ void InitVariablesCheck::check(const MatchFinder::MatchResult &Result) { else if (TypePtr->isFloatingType()) { InitializationString = " = NAN"; AddMathInclude = true; - } else if (TypePtr->isAnyPointerType()) { + } else if (TypePtr->isAnyPointerType() || TypePtr->isMemberPointerType()) { if (getLangOpts().CPlusPlus11) InitializationString = " = nullptr"; else diff --git a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp index 9f2c3eb65a3ca..2619331cba0b0 100644 --- a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp @@ -216,11 +216,13 @@ PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context) Inserter(Options.getLocalOrGlobal("IncludeStyle", utils::IncludeSorter::IS_LLVM), areDiagsSelfContained()), - ValuesOnly(Options.get("ValuesOnly", false)) {} + ValuesOnly(Options.get("ValuesOnly", false)), + IgnoreMacros(Options.get("IgnoreMacros", false)) {} void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IncludeStyle", Inserter.getStyle()); Options.store(Opts, "ValuesOnly", ValuesOnly); + Options.store(Opts, "IgnoreMacros", IgnoreMacros); } void PassByValueCheck::registerMatchers(MatchFinder *Finder) { @@ -273,6 +275,9 @@ void PassByValueCheck::check(const MatchFinder::MatchResult &Result) { Result.Nodes.getNodeAs("Initializer"); const SourceManager &SM = *Result.SourceManager; + if (IgnoreMacros && ParamDecl->getBeginLoc().isMacroID()) + return; + // If the parameter is used or anything other than the copy, do not apply // the changes. if (!paramReferredExactlyOnce(Ctor, ParamDecl)) diff --git a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h index eb51f4a4c46ac..aefc81842e633 100644 --- a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h @@ -29,6 +29,7 @@ class PassByValueCheck : public ClangTidyCheck { private: utils::IncludeInserter Inserter; const bool ValuesOnly; + const bool IgnoreMacros; }; } // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp index 831c8565eb74d..f27016a9dc4ea 100644 --- a/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp @@ -7,281 +7,45 @@ //===----------------------------------------------------------------------===// #include "RedundantVoidArgCheck.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Lex/Lexer.h" +#include "../utils/LexerUtils.h" +#include "clang/ASTMatchers/ASTMatchers.h" using namespace clang::ast_matchers; namespace clang::tidy::modernize { -// Determine if the given QualType is a nullary function or pointer to same. -static bool protoTypeHasNoParms(QualType QT) { - if (const auto *PT = QT->getAs()) - QT = PT->getPointeeType(); - if (auto *MPT = QT->getAs()) - QT = MPT->getPointeeType(); - if (const auto *FP = QT->getAs()) - return FP->getNumParams() == 0; - return false; -} - -static const char FunctionId[] = "function"; -static const char TypedefId[] = "typedef"; -static const char FieldId[] = "field"; -static const char VarId[] = "var"; -static const char NamedCastId[] = "named-cast"; -static const char CStyleCastId[] = "c-style-cast"; -static const char ExplicitCastId[] = "explicit-cast"; -static const char LambdaId[] = "lambda"; - void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()), - unless(isInstantiated()), unless(isExternC())) - .bind(FunctionId), - this); - Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId), - this); - auto ParenFunctionType = parenType(innerType(functionType())); - auto PointerToFunctionType = pointee(ParenFunctionType); - auto FunctionOrMemberPointer = - anyOf(hasType(pointerType(PointerToFunctionType)), - hasType(memberPointerType(PointerToFunctionType))); - Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this); - Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this); - auto CastDestinationIsFunction = - hasDestinationType(pointsTo(ParenFunctionType)); - Finder->addMatcher( - cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this); Finder->addMatcher( - cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); - Finder->addMatcher( - cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId), + traverse(TK_IgnoreUnlessSpelledInSource, + functionTypeLoc(unless(hasParent(functionDecl(isExternC())))) + .bind("fn")), this); Finder->addMatcher( - cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); - Finder->addMatcher(lambdaExpr().bind(LambdaId), this); + traverse(TK_IgnoreUnlessSpelledInSource, lambdaExpr().bind("fn")), this); } void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) { - const BoundNodes &Nodes = Result.Nodes; - if (const auto *Function = Nodes.getNodeAs(FunctionId)) - processFunctionDecl(Result, Function); - else if (const auto *TypedefName = - Nodes.getNodeAs(TypedefId)) - processTypedefNameDecl(Result, TypedefName); - else if (const auto *Member = Nodes.getNodeAs(FieldId)) - processFieldDecl(Result, Member); - else if (const auto *Var = Nodes.getNodeAs(VarId)) - processVarDecl(Result, Var); - else if (const auto *NamedCast = - Nodes.getNodeAs(NamedCastId)) - processNamedCastExpr(Result, NamedCast); - else if (const auto *CStyleCast = - Nodes.getNodeAs(CStyleCastId)) - processExplicitCastExpr(Result, CStyleCast); - else if (const auto *ExplicitCast = - Nodes.getNodeAs(ExplicitCastId)) - processExplicitCastExpr(Result, ExplicitCast); - else if (const auto *Lambda = Nodes.getNodeAs(LambdaId)) - processLambdaExpr(Result, Lambda); -} - -void RedundantVoidArgCheck::processFunctionDecl( - const MatchFinder::MatchResult &Result, const FunctionDecl *Function) { - const auto *Method = dyn_cast(Function); - const SourceLocation Start = Method && Method->getParent()->isLambda() - ? Method->getBeginLoc() - : Function->getLocation(); - SourceLocation End = Function->getEndLoc(); - if (Function->isThisDeclarationADefinition()) { - if (const Stmt *Body = Function->getBody()) { - End = Body->getBeginLoc(); - if (End.isMacroID() && - Result.SourceManager->isAtStartOfImmediateMacroExpansion(End)) - End = Result.SourceManager->getExpansionLoc(End); - End = End.getLocWithOffset(-1); - } - removeVoidArgumentTokens(Result, SourceRange(Start, End), - "function definition"); - } else - removeVoidArgumentTokens(Result, SourceRange(Start, End), - "function declaration"); -} - -static bool isMacroIdentifier(const IdentifierTable &Idents, - const Token &ProtoToken) { - if (!ProtoToken.is(tok::TokenKind::raw_identifier)) - return false; - - const IdentifierTable::iterator It = - Idents.find(ProtoToken.getRawIdentifier()); - if (It == Idents.end()) - return false; - - return It->second->hadMacroDefinition(); -} - -void RedundantVoidArgCheck::removeVoidArgumentTokens( - const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range, - StringRef GrammarLocation) { - const CharSourceRange CharRange = - Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range), - *Result.SourceManager, getLangOpts()); - - std::string DeclText = - Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts()) - .str(); - Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(), - DeclText.data(), DeclText.data() + DeclText.size()); - enum class TokenState { - Start, - MacroId, - MacroLeftParen, - MacroArguments, - LeftParen, - Void, - }; - TokenState State = TokenState::Start; - Token VoidToken; - Token ProtoToken; - const IdentifierTable &Idents = Result.Context->Idents; - int MacroLevel = 0; - const std::string Diagnostic = - ("redundant void argument list in " + GrammarLocation).str(); - - while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) { - switch (State) { - case TokenState::Start: - if (ProtoToken.is(tok::TokenKind::l_paren)) - State = TokenState::LeftParen; - else if (isMacroIdentifier(Idents, ProtoToken)) - State = TokenState::MacroId; - break; - case TokenState::MacroId: - if (ProtoToken.is(tok::TokenKind::l_paren)) - State = TokenState::MacroLeftParen; - else - State = TokenState::Start; - break; - case TokenState::MacroLeftParen: - ++MacroLevel; - if (ProtoToken.is(tok::TokenKind::raw_identifier)) { - if (isMacroIdentifier(Idents, ProtoToken)) - State = TokenState::MacroId; - else - State = TokenState::MacroArguments; - } else if (ProtoToken.is(tok::TokenKind::r_paren)) { - --MacroLevel; - if (MacroLevel == 0) - State = TokenState::Start; - else - State = TokenState::MacroId; - } else - State = TokenState::MacroArguments; - break; - case TokenState::MacroArguments: - if (isMacroIdentifier(Idents, ProtoToken)) - State = TokenState::MacroLeftParen; - else if (ProtoToken.is(tok::TokenKind::r_paren)) { - --MacroLevel; - if (MacroLevel == 0) - State = TokenState::Start; - } - break; - case TokenState::LeftParen: - if (ProtoToken.is(tok::TokenKind::raw_identifier)) { - if (isMacroIdentifier(Idents, ProtoToken)) - State = TokenState::MacroId; - else if (ProtoToken.getRawIdentifier() == "void") { - State = TokenState::Void; - VoidToken = ProtoToken; - } - } else if (ProtoToken.is(tok::TokenKind::l_paren)) - State = TokenState::LeftParen; - else - State = TokenState::Start; - break; - case TokenState::Void: - State = TokenState::Start; - if (ProtoToken.is(tok::TokenKind::r_paren)) - removeVoidToken(VoidToken, Diagnostic); - else if (ProtoToken.is(tok::TokenKind::l_paren)) - State = TokenState::LeftParen; - break; - } - } - - if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren)) - removeVoidToken(VoidToken, Diagnostic); -} - -void RedundantVoidArgCheck::removeVoidToken(Token VoidToken, - StringRef Diagnostic) { - const SourceLocation VoidLoc = VoidToken.getLocation(); - diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc); -} - -void RedundantVoidArgCheck::processTypedefNameDecl( - const MatchFinder::MatchResult &Result, - const TypedefNameDecl *TypedefName) { - if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) - removeVoidArgumentTokens(Result, TypedefName->getSourceRange(), - isa(TypedefName) ? "typedef" - : "type alias"); -} - -void RedundantVoidArgCheck::processFieldDecl( - const MatchFinder::MatchResult &Result, const FieldDecl *Member) { - if (protoTypeHasNoParms(Member->getType())) - removeVoidArgumentTokens(Result, Member->getSourceRange(), - "field declaration"); -} - -void RedundantVoidArgCheck::processVarDecl( - const MatchFinder::MatchResult &Result, const VarDecl *Var) { - if (protoTypeHasNoParms(Var->getType())) { - const SourceLocation Begin = Var->getBeginLoc(); - if (Var->hasInit()) { - const SourceLocation InitStart = - Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc()) - .getLocWithOffset(-1); - removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart), - "variable declaration with initializer"); - } else - removeVoidArgumentTokens(Result, Var->getSourceRange(), - "variable declaration"); - } -} - -void RedundantVoidArgCheck::processNamedCastExpr( - const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) { - if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) - removeVoidArgumentTokens( - Result, - NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(), - "named cast"); -} - -void RedundantVoidArgCheck::processExplicitCastExpr( - const MatchFinder::MatchResult &Result, - const ExplicitCastExpr *ExplicitCast) { - if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) - removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(), - "cast expression"); -} - -void RedundantVoidArgCheck::processLambdaExpr( - const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) { - if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 && - Lambda->hasExplicitParameters()) { - const SourceManager *SM = Result.SourceManager; - const TypeLoc TL = - Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc(); - removeVoidArgumentTokens(Result, - {SM->getSpellingLoc(TL.getBeginLoc()), - SM->getSpellingLoc(TL.getEndLoc())}, - "lambda expression"); - } + const FunctionTypeLoc TL = [&] { + if (const auto *TL = Result.Nodes.getNodeAs("fn")) + return *TL; + return Result.Nodes.getNodeAs("fn") + ->getCallOperator() + ->getFunctionTypeLoc(); + }(); + + if (TL.getNumParams() != 0) + return; + + const std::optional Tok = utils::lexer::findNextTokenSkippingComments( + Result.SourceManager->getSpellingLoc(TL.getLParenLoc()), + *Result.SourceManager, getLangOpts()); + + if (!Tok || Tok->isNot(tok::raw_identifier) || + Tok->getRawIdentifier() != "void") + return; + + diag(Tok->getLocation(), "redundant void argument list") + << FixItHint::CreateRemoval(Tok->getLocation()); } } // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h index d6edd9950ddae..77ebdc84be7e9 100644 --- a/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h @@ -10,9 +10,6 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANTVOIDARGCHECK_H #include "../ClangTidyCheck.h" -#include "clang/Lex/Token.h" - -#include namespace clang::tidy::modernize { @@ -38,37 +35,6 @@ class RedundantVoidArgCheck : public ClangTidyCheck { void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - -private: - void processFunctionDecl(const ast_matchers::MatchFinder::MatchResult &Result, - const FunctionDecl *Function); - - void - processTypedefNameDecl(const ast_matchers::MatchFinder::MatchResult &Result, - const TypedefNameDecl *Typedef); - - void processFieldDecl(const ast_matchers::MatchFinder::MatchResult &Result, - const FieldDecl *Member); - - void processVarDecl(const ast_matchers::MatchFinder::MatchResult &Result, - const VarDecl *Var); - - void - processNamedCastExpr(const ast_matchers::MatchFinder::MatchResult &Result, - const CXXNamedCastExpr *NamedCast); - - void - processExplicitCastExpr(const ast_matchers::MatchFinder::MatchResult &Result, - const ExplicitCastExpr *ExplicitCast); - - void processLambdaExpr(const ast_matchers::MatchFinder::MatchResult &Result, - const LambdaExpr *Lambda); - - void - removeVoidArgumentTokens(const ast_matchers::MatchFinder::MatchResult &Result, - SourceRange Range, StringRef GrammarLocation); - - void removeVoidToken(Token VoidToken, StringRef Diagnostic); }; } // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp index 814a4f854319c..8029349891327 100644 --- a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp @@ -100,9 +100,9 @@ void InefficientVectorOperationCheck::addMatcher( .bind(VarDeclStmtName); const auto AppendCallExpr = - cxxMemberCallExpr( - callee(AppendMethodDecl), on(hasType(TargetRecordDecl)), - onImplicitObjectArgument(declRefExpr(to(TargetVarDecl)))) + cxxMemberCallExpr(callee(AppendMethodDecl), on(hasType(TargetRecordDecl)), + onImplicitObjectArgument(ignoringParenImpCasts( + declRefExpr(to(TargetVarDecl))))) .bind(AppendCallName); const auto AppendCall = expr(ignoringImplicit(AppendCallExpr)); const auto LoopVarInit = declStmt(hasSingleDecl( diff --git a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp index 073fdce5018bf..f171cd0f44af4 100644 --- a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp @@ -116,11 +116,15 @@ AST_POLYMORPHIC_MATCHER_P( matchMemberName, AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, CXXDependentScopeMemberExpr), std::string, MemberName) { - if (const auto *E = dyn_cast(&Node)) - return E->getMemberDecl()->getName() == MemberName; + if (const auto *E = dyn_cast(&Node)) { + const IdentifierInfo *II = E->getMemberDecl()->getIdentifier(); + return II && II->getName() == MemberName; + } - if (const auto *E = dyn_cast(&Node)) - return E->getMember().getAsString() == MemberName; + if (const auto *E = dyn_cast(&Node)) { + const IdentifierInfo *II = E->getMember().getAsIdentifierInfo(); + return II && II->getName() == MemberName; + } return false; } diff --git a/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp b/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp index 2ecde56cd7af8..9950eca3fa7bd 100644 --- a/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp @@ -95,8 +95,7 @@ void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) { } } } else if (const auto *CE = dyn_cast(S)) { - for (const auto *Arg : CE->arguments()) - markCanNotBeConst(Arg->IgnoreParenCasts(), true); + markCanNotBeConst(CE, true); } else if (const auto *R = dyn_cast(S)) { markCanNotBeConst(R->getRetValue(), true); } else if (const auto *U = dyn_cast(S)) { @@ -104,8 +103,10 @@ void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) { } } else if (const auto *VD = Result.Nodes.getNodeAs("Mark")) { const QualType T = VD->getType(); - if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) || - T->isArrayType() || T->isRecordType()) + if (T->isDependentType()) + markCanNotBeConst(VD->getInit(), false); + else if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) || + T->isArrayType() || T->isRecordType()) markCanNotBeConst(VD->getInit(), true); else if (T->isLValueReferenceType() && !T->getPointeeType().isConstQualified()) @@ -221,9 +222,17 @@ void NonConstParameterCheck::markCanNotBeConst(const Expr *E, for (const auto *Arg : Constr->arguments()) if (const auto *M = dyn_cast(Arg)) markCanNotBeConst(cast(M->getSubExpr()), CanNotBeConst); + else + markCanNotBeConst(Arg, CanNotBeConst); + } else if (const auto *CE = dyn_cast(E)) { + for (const auto *Arg : CE->arguments()) + markCanNotBeConst(Arg, CanNotBeConst); } else if (const auto *ILE = dyn_cast(E)) { for (unsigned I = 0U; I < ILE->getNumInits(); ++I) - markCanNotBeConst(ILE->getInit(I), true); + markCanNotBeConst(ILE->getInit(I), CanNotBeConst); + } else if (const auto *PLE = dyn_cast(E)) { + for (unsigned I = 0U; I < PLE->getNumExprs(); ++I) + markCanNotBeConst(PLE->getExpr(I), CanNotBeConst); } else if (CanNotBeConst) { // Referencing parameter. if (const auto *D = dyn_cast(E)) { diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index 4469e564e83f9..040f6fb8c843c 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -162,7 +162,8 @@ static std::string replacementExpression(const ASTContext &Context, if (const auto *EC = dyn_cast(E)) E = EC->getSubExpr(); - const bool NeedsStaticCast = needsStaticCast(E); + const bool NeedsStaticCast = + Context.getLangOpts().CPlusPlus && needsStaticCast(E); if (Negated) { if (const auto *UnOp = dyn_cast(E)) { if (UnOp->getOpcode() == UO_LNot) { diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h index 99520d76c6c6f..d43116b102ce3 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h @@ -22,6 +22,9 @@ class SimplifyBooleanExprCheck : public ClangTidyCheck { public: SimplifyBooleanExprCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus || LangOpts.C23; + } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; 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/UppercaseLiteralSuffixCheck.cpp b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp index 6463a82ff68f1..812ade0df42c1 100644 --- a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp @@ -8,10 +8,10 @@ #include "UppercaseLiteralSuffixCheck.h" #include "../utils/ASTUtils.h" +#include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" -#include #include using namespace clang::ast_matchers; @@ -20,40 +20,42 @@ namespace clang::tidy::readability { namespace { -struct IntegerLiteralCheck { - using type = clang::IntegerLiteral; - static constexpr StringRef Name = "integer"; - // What should be skipped before looking for the Suffixes? (Nothing here.) - static constexpr StringRef SkipFirst = ""; - // Suffix can only consist of 'u', 'l', and 'z' chars, can be a bit-precise - // integer (wb), and can be a complex number ('i', 'j'). In MS compatibility - // mode, suffixes like i32 are supported. - static constexpr StringRef Suffixes = "uUlLzZwWiIjJ"; -}; - -struct FloatingLiteralCheck { - using type = clang::FloatingLiteral; - static constexpr StringRef Name = "floating point"; - // C++17 introduced hexadecimal floating-point literals, and 'f' is both a - // valid hexadecimal digit in a hex float literal and a valid floating-point - // literal suffix. - // So we can't just "skip to the chars that can be in the suffix". - // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point - // literals, we first skip everything before the exponent. - static constexpr StringRef SkipFirst = "pP"; - // Suffix can only consist of 'f', 'l', "f16", "bf16", "df", "dd", "dl", - // 'h', 'q' chars, and can be a complex number ('i', 'j'). - static constexpr StringRef Suffixes = "fFlLbBdDhHqQiIjJ"; -}; - struct NewSuffix { SourceRange LiteralLocation; StringRef OldSuffix; std::optional FixIt; }; +struct LiteralParameters { + // What characters should be skipped before looking for the Suffixes? + StringRef SkipFirst; + // What characters can a suffix start with? + StringRef Suffixes; +}; + } // namespace +static constexpr LiteralParameters IntegerParameters = { + "", + // Suffix can only consist of 'u', 'l', and 'z' chars, can be a + // bit-precise integer (wb), and can be a complex number ('i', 'j'). In MS + // compatibility mode, suffixes like i32 are supported. + "uUlLzZwWiIjJ", +}; + +static constexpr LiteralParameters FloatParameters = { + // C++17 introduced hexadecimal floating-point literals, and 'f' is both a + // valid hexadecimal digit in a hex float literal and a valid floating-point + // literal suffix. + // So we can't just "skip to the chars that can be in the suffix". + // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point + // literals, we first skip everything before the exponent. + "pP", + // Suffix can only consist of 'f', 'l', "f16", "bf16", "df", "dd", "dl", + // 'h', 'q' chars, and can be a complex number ('i', 'j'). + "fFlLbBdDhHqQiIjJ", +}; + static std::optional getMacroAwareLocation(SourceLocation Loc, const SourceManager &SM) { // Do nothing if the provided location is invalid. @@ -77,8 +79,7 @@ getMacroAwareSourceRange(SourceRange Loc, const SourceManager &SM) { } static std::optional -getNewSuffix(llvm::StringRef OldSuffix, - const std::vector &NewSuffixes) { +getNewSuffix(StringRef OldSuffix, const std::vector &NewSuffixes) { // If there is no config, just uppercase the entirety of the suffix. if (NewSuffixes.empty()) return OldSuffix.upper(); @@ -94,17 +95,15 @@ getNewSuffix(llvm::StringRef OldSuffix, return std::nullopt; } -template static std::optional shouldReplaceLiteralSuffix(const Expr &Literal, + const LiteralParameters &Parameters, const std::vector &NewSuffixes, const SourceManager &SM, const LangOptions &LO) { NewSuffix ReplacementDsc; - const auto &L = cast(Literal); - // The naive location of the literal. Is always valid. - ReplacementDsc.LiteralLocation = L.getSourceRange(); + ReplacementDsc.LiteralLocation = Literal.getSourceRange(); // Was this literal fully spelled or is it a product of macro expansion? const bool RangeCanBeFixed = @@ -134,11 +133,11 @@ shouldReplaceLiteralSuffix(const Expr &Literal, size_t Skip = 0; // Do we need to ignore something before actually looking for the suffix? - if (!LiteralType::SkipFirst.empty()) { + if (!Parameters.SkipFirst.empty()) { // E.g. we can't look for 'f' suffix in hexadecimal floating-point literals // until after we skip to the exponent (which is mandatory there), // because hex-digit-sequence may contain 'f'. - Skip = LiteralSourceText.find_first_of(LiteralType::SkipFirst); + Skip = LiteralSourceText.find_first_of(Parameters.SkipFirst); // We could be in non-hexadecimal floating-point literal, with no exponent. if (Skip == StringRef::npos) Skip = 0; @@ -147,7 +146,7 @@ shouldReplaceLiteralSuffix(const Expr &Literal, // Find the beginning of the suffix by looking for the first char that is // one of these chars that can be in the suffix, potentially starting looking // in the exponent, if we are skipping hex-digit-sequence. - Skip = LiteralSourceText.find_first_of(LiteralType::Suffixes, /*From=*/Skip); + Skip = LiteralSourceText.find_first_of(Parameters.Suffixes, /*From=*/Skip); // We can't check whether the *Literal has any suffix or not without actually // looking for the suffix. So it is totally possible that there is no suffix. @@ -191,43 +190,33 @@ void UppercaseLiteralSuffixCheck::registerMatchers(MatchFinder *Finder) { // Sadly, we can't check whether the literal has suffix or not. // E.g. i32 suffix still results in 'BuiltinType::Kind::Int'. // And such an info is not stored in the *Literal itself. + Finder->addMatcher( - stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name), - floatLiteral().bind(FloatingLiteralCheck::Name)), - unless(anyOf(hasParent(userDefinedLiteral()), - hasAncestor(substNonTypeTemplateParmExpr())))), + integerLiteral(unless(hasParent(userDefinedLiteral()))).bind("expr"), this); + Finder->addMatcher( + floatLiteral(unless(hasParent(userDefinedLiteral()))).bind("expr"), this); } -template -bool UppercaseLiteralSuffixCheck::checkBoundMatch( +void UppercaseLiteralSuffixCheck::check( const MatchFinder::MatchResult &Result) { - const auto *Literal = - Result.Nodes.getNodeAs(LiteralType::Name); - if (!Literal) - return false; + const auto *const Literal = Result.Nodes.getNodeAs("expr"); + const bool IsInteger = isa(Literal); // We won't *always* want to diagnose. // We might have a suffix that is already uppercase. - if (auto Details = shouldReplaceLiteralSuffix( - *Literal, NewSuffixes, *Result.SourceManager, getLangOpts())) { + if (auto Details = shouldReplaceLiteralSuffix( + *Literal, IsInteger ? IntegerParameters : FloatParameters, + NewSuffixes, *Result.SourceManager, getLangOpts())) { if (Details->LiteralLocation.getBegin().isMacroID() && IgnoreMacros) - return true; + return; auto Complaint = diag(Details->LiteralLocation.getBegin(), - "%0 literal has suffix '%1', which is not uppercase") - << LiteralType::Name << Details->OldSuffix; + "%select{floating point|integer}0 literal has suffix " + "'%1', which is not uppercase") + << IsInteger << Details->OldSuffix; if (Details->FixIt) // Similarly, a fix-it is not always possible. Complaint << *(Details->FixIt); } - - return true; -} - -void UppercaseLiteralSuffixCheck::check( - const MatchFinder::MatchResult &Result) { - if (checkBoundMatch(Result)) - return; // If it *was* IntegerLiteral, don't check for FloatingLiteral. - checkBoundMatch(Result); } } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h index e1eef3d5b58ee..5df24241d1fb7 100644 --- a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h +++ b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h @@ -10,7 +10,6 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H #include "../ClangTidyCheck.h" -#include "../utils/OptionsUtils.h" namespace clang::tidy::readability { @@ -31,9 +30,6 @@ class UppercaseLiteralSuffixCheck : public ClangTidyCheck { } private: - template - bool checkBoundMatch(const ast_matchers::MatchFinder::MatchResult &Result); - const std::vector NewSuffixes; const bool IgnoreMacros; }; diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp index b77d985b76d77..b7de34f7e2e00 100644 --- a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp +++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/SourceManager.h" #include #include +#include namespace clang::tidy::utils::lexer { @@ -99,6 +100,66 @@ bool rangeContainsExpansionsOrDirectives(SourceRange Range, return false; } +std::vector +getTrailingCommentsInRange(CharSourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts) { + std::vector Comments; + if (Range.isInvalid()) + return Comments; + + const CharSourceRange FileRange = + Lexer::makeFileCharRange(Range, SM, LangOpts); + if (FileRange.isInvalid()) + return Comments; + + const std::pair BeginLoc = + SM.getDecomposedLoc(FileRange.getBegin()); + const std::pair EndLoc = + SM.getDecomposedLoc(FileRange.getEnd()); + + if (BeginLoc.first != EndLoc.first) + return Comments; + + bool Invalid = false; + const StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid); + if (Invalid) + return Comments; + + const char *StrData = Buffer.data() + BeginLoc.second; + + Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), LangOpts, + Buffer.begin(), StrData, Buffer.end()); + // Use raw lexing with comment retention so we can see comment tokens without + // preprocessing or macro expansion effects. + TheLexer.SetCommentRetentionState(true); + + while (true) { + Token Tok; + if (TheLexer.LexFromRawLexer(Tok)) + break; + if (Tok.is(tok::eof) || Tok.getLocation() == FileRange.getEnd() || + SM.isBeforeInTranslationUnit(FileRange.getEnd(), Tok.getLocation())) + break; + + if (Tok.is(tok::comment)) { + const std::pair CommentLoc = + SM.getDecomposedLoc(Tok.getLocation()); + assert(CommentLoc.first == BeginLoc.first); + Comments.emplace_back(CommentToken{ + Tok.getLocation(), + StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()), + }); + } else { + // Clear comments found before the different token, e.g. comma. Callers + // use this to retrieve only the contiguous comment block that directly + // precedes a token of interest. + Comments.clear(); + } + } + + return Comments; +} + std::optional getQualifyingToken(tok::TokenKind TK, CharSourceRange Range, const ASTContext &Context, diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.h b/clang-tools-extra/clang-tidy/utils/LexerUtils.h index 61389d86f22f4..38123ae14cff7 100644 --- a/clang-tools-extra/clang-tidy/utils/LexerUtils.h +++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.h @@ -14,6 +14,7 @@ #include "clang/Lex/Lexer.h" #include #include +#include namespace clang { @@ -113,6 +114,18 @@ bool rangeContainsExpansionsOrDirectives(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts); +// Represents a comment token and its source location in the original file. +struct CommentToken { + SourceLocation Loc; + StringRef Text; +}; + +/// Returns comment tokens found in the given range. If a non-comment token is +/// encountered, clears previously collected comments and continues. +std::vector +getTrailingCommentsInRange(CharSourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts); + /// Assuming that ``Range`` spans a CVR-qualified type, returns the /// token in ``Range`` that is responsible for the qualification. ``Range`` /// must be valid with respect to ``SM``. Returns ``std::nullopt`` if no diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index f43b5e71a1dfa..7c390f9c8219d 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -590,10 +590,12 @@ struct CodeCompletionBuilder { if (Snippet->empty()) return ""; - bool MayHaveArgList = Completion.Kind == CompletionItemKind::Function || - Completion.Kind == CompletionItemKind::Method || - Completion.Kind == CompletionItemKind::Constructor || - Completion.Kind == CompletionItemKind::Text /*Macro*/; + bool MayHaveArgList = + Completion.Kind == CompletionItemKind::Function || + Completion.Kind == CompletionItemKind::Method || + Completion.Kind == CompletionItemKind::Constructor || + Completion.Kind == CompletionItemKind::Text /*Macro*/ || + Completion.Kind == CompletionItemKind::Variable /*Lambda*/; // If likely arg list already exists, don't add new parens & placeholders. // Snippet: function(int x, int y) // func^(1,2) -> function(1, 2) @@ -628,7 +630,7 @@ struct CodeCompletionBuilder { return *Snippet; // Replace argument snippets with a simplified pattern. - if (MayHaveArgList) { + if (MayHaveArgList && llvm::StringRef(*Snippet).contains("(")) { // Functions snippets can be of 2 types: // - containing only function arguments, e.g. // foo(${1:int p1}, ${2:int p2}); diff --git a/clang-tools-extra/clangd/ScanningProjectModules.cpp b/clang-tools-extra/clangd/ScanningProjectModules.cpp index 860ce377be532..b1338634e0c29 100644 --- a/clang-tools-extra/clangd/ScanningProjectModules.cpp +++ b/clang-tools-extra/clangd/ScanningProjectModules.cpp @@ -35,9 +35,13 @@ class ModuleDependencyScanner { ModuleDependencyScanner( std::shared_ptr CDB, const ThreadsafeFS &TFS) - : CDB(CDB), TFS(TFS), - Service(dependencies::ScanningMode::CanonicalPreprocessing, - dependencies::ScanningOutputFormat::P1689) {} + : CDB(CDB), Service([&TFS] { + dependencies::DependencyScanningServiceOptions Opts; + Opts.MakeVFS = [&] { return TFS.view(std::nullopt); }; + Opts.Mode = dependencies::ScanningMode::CanonicalPreprocessing; + Opts.Format = dependencies::ScanningOutputFormat::P1689; + return Opts; + }()) {} /// The scanned modules dependency information for a specific source file. struct ModuleDependencyInfo { @@ -76,7 +80,6 @@ class ModuleDependencyScanner { private: std::shared_ptr CDB; - const ThreadsafeFS &TFS; // Whether the scanner has scanned the project globally. bool GlobalScanned = false; @@ -106,9 +109,7 @@ ModuleDependencyScanner::scan(PathRef FilePath, using namespace clang::tooling; - llvm::SmallString<128> FilePathDir(FilePath); - llvm::sys::path::remove_filename(FilePathDir); - DependencyScanningTool ScanningTool(Service, TFS.view(FilePathDir)); + DependencyScanningTool ScanningTool(Service); std::string S; llvm::raw_string_ostream OS(S); diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 267910b571279..31f2d8bd68703 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -2821,6 +2821,18 @@ TEST(CompletionTest, ArgumentListsPolicy) { EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf( named("FOO"), snippetSuffix("($0)")))); } + { + auto Results = completions( + R"cpp( + void function() { + auto Lambda = [](int a, const double &b) {return 1.f;}; + Lam^ + })cpp", + {}, Opts); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(AllOf(named("Lambda"), snippetSuffix("($0)")))); + } { Opts.ArgumentLists = Config::ArgumentListsPolicy::None; auto Results = completions( diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index f9ff6f21009f3..84ceddbd4fc4b 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -823,6 +823,25 @@ TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) { EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash } +TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) { + Annotations Main(R"cpp( + #include "signal.h" + using pthread_t = int; + int pthread_kill(pthread_t thread, int sig); + int func() { + pthread_t thread; + return pthread_kill(thread, 15); + } + )cpp"); + TestTU TU = TestTU::withCode(Main.code()); + TU.HeaderFilename = "signal.h"; + TU.HeaderCode = "#define SIGTERM 15"; + TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread"); + EXPECT_THAT(TU.build().getDiagnostics(), + ifTidyChecks(UnorderedElementsAre( + diagName("bugprone-bad-signal-to-kill-thread")))); +} + TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) { Annotations Main(R"cpp( #if 1 diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 539d2e208a162..2bb4800df47c9 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -94,6 +94,11 @@ Improvements to clang-query Improvements to clang-tidy -------------------------- +- Improved :program:`check_clang_tidy.py` script by adding the `-check-header` + argument to simplify testing of header files. This argument automatically + manages the creation of temporary header files and ensures that diagnostics + and fixes are verified for the specified headers. + New checks ^^^^^^^^^^ @@ -144,6 +149,10 @@ Changes in existing checks ` to also check for C++11 inherited constructors. +- Improved :doc:`bugprone-bad-signal-to-kill-thread + ` check by fixing false + negatives when the ``SIGTERM`` macro is obtained from a precompiled header. + - Improved :doc:`bugprone-exception-escape ` check by adding `TreatFunctionsWithoutSpecificationAsThrowing` option to support reporting @@ -169,6 +178,10 @@ Changes in existing checks the invalidating function in the warning message when a custom invalidation function is used (via the `InvalidationFunctions` option). +- Improved :doc:`cppcoreguidelines-init-variables + ` check by ensuring that + member pointers are correctly flagged as uninitialized. + - Improved :doc:`cppcoreguidelines-pro-type-vararg ` check by no longer warning on builtins with custom type checking (e.g., type-generic builtins @@ -186,6 +199,10 @@ Changes in existing checks - Added support for analyzing function parameters with the `AnalyzeParameters` option. +- Improved :doc:`modernize-pass-by-value + ` check by adding `IgnoreMacros` + option to suppress warnings in macros. + - Improved :doc:`modernize-use-std-format ` check by fixing a crash when an argument is part of a macro expansion. @@ -201,10 +218,19 @@ Changes in existing checks - Improved the ignore list to correctly handle ``typedef`` and ``enum``. +- Improved :doc:`performance-inefficient-vector-operation + ` check by + correctly handling vector-like classes when ``push_back``/``emplace_back`` are + inherited. + - Improved :doc:`performance-move-const-arg ` check by avoiding false positives on trivially copyable types with a non-public copy constructor. +- Improved :doc:`readability-container-size-empty + ` check by fixing a crash + when a member expression has a non-identifier name. + - Improved :doc:`readability-enum-initial-value ` check: the warning message now uses separate note diagnostics for each uninitialized enumerator, making @@ -212,7 +238,12 @@ Changes in existing checks - Improved :doc:`readability-non-const-parameter ` check by avoiding false - positives on parameters used in dependent expressions. + positives on parameters used in dependent expressions (e.g. inside generic + lambdas). + +- Improved :doc:`readability-simplify-boolean-expr + ` check to provide valid + fix suggestions for C23 and later by not using ``static_cast``. - Improved :doc:`readability-suspicious-call-argument ` check by avoiding a diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-goto.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-goto.rst index 1f9dc0a1edb3a..87e14bbe8a850 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-goto.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-goto.rst @@ -9,7 +9,7 @@ with looping constructs. Only forward jumps in nested loops are accepted. This check implements `ES.76 `_ from the C++ Core Guidelines and -`6.3.1 `_ +`6.3.1 `_ from High Integrity C++ Coding Standard. For more information on why to avoid programming diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst index 789235980ad7b..9128e7883b010 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst @@ -8,4 +8,4 @@ hicpp-avoid-c-arrays The hicpp-avoid-c-arrays check is an alias, please see :doc:`modernize-avoid-c-arrays <../modernize/avoid-c-arrays>` for more information. -It partly enforces the `rule 4.1.1 `_. +It partly enforces the `rule 4.1.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst index ccc2a7b34efe5..e483503bdd4cd 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst @@ -8,4 +8,4 @@ hicpp-avoid-goto The `hicpp-avoid-goto` check is an alias, please see :doc:`cppcoreguidelines-avoid-goto <../cppcoreguidelines/avoid-goto>` for more information. -It enforces the `rule 6.3.1 `_. +It enforces the `rule 6.3.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/braces-around-statements.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/braces-around-statements.rst index aef997538d331..a5972d49fac09 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/braces-around-statements.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/braces-around-statements.rst @@ -10,4 +10,4 @@ The `hicpp-braces-around-statements` check is an alias, please see <../readability/braces-around-statements>` for more information. It enforces the `rule 6.1.1 -`_. +`_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/deprecated-headers.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/deprecated-headers.rst index b1446c563863c..efcbbac536e90 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/deprecated-headers.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/deprecated-headers.rst @@ -8,4 +8,4 @@ hicpp-deprecated-headers The `hicpp-deprecated-headers` check is an alias, please see :doc:`modernize-deprecated-headers <../modernize/deprecated-headers>` for more information. -It enforces the `rule 1.3.3 `_. +It enforces the `rule 1.3.3 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/exception-baseclass.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/exception-baseclass.rst index 9bbc7fc59ed71..7a1c1a07c49e3 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/exception-baseclass.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/exception-baseclass.rst @@ -6,7 +6,7 @@ hicpp-exception-baseclass Ensure that every value that in a ``throw`` expression is an instance of ``std::exception``. -This enforces `rule 15.1 `_ +This enforces `rule 15.1 `_ of the High Integrity C++ Coding Standard. .. code-block:: c++ diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/explicit-conversions.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/explicit-conversions.rst index 6072527b28a3a..5bbe5243a1c89 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/explicit-conversions.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/explicit-conversions.rst @@ -9,7 +9,7 @@ This check is an alias for :doc:`google-explicit-constructor <../google/explicit-constructor>`. Used to enforce parts of `rule 5.4.1 -`_. +`_. This check will enforce that constructors and conversion operators are marked ``explicit``. Other forms of casting checks are implemented in other places. The following checks can be used to check for more forms diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/function-size.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/function-size.rst index ad91c2514c97f..2030e61be2386 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/function-size.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/function-size.rst @@ -9,6 +9,6 @@ This check is an alias for :doc:`readability-function-size <../readability/function-size>`. Useful to enforce multiple sections on function complexity. -- `rule 8.2.2 `_ -- `rule 8.3.1 `_ -- `rule 8.3.2 `_ +- `rule 8.2.2 `_ +- `rule 8.3.1 `_ +- `rule 8.3.2 `_ diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/ignored-remove-result.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/ignored-remove-result.rst index 4a89949ce45d7..1c749b169828e 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/ignored-remove-result.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/ignored-remove-result.rst @@ -5,7 +5,7 @@ hicpp-ignored-remove-result Ensure that the result of ``std::remove``, ``std::remove_if`` and ``std::unique`` are not ignored according to -`rule 17.5.1 `_. +`rule 17.5.1 `_. The mutating algorithms ``std::remove``, ``std::remove_if`` and both overloads of ``std::unique`` operate by swapping or moving elements of the range they are diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/invalid-access-moved.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/invalid-access-moved.rst index fd2e04a0fc711..f3aeaafafa461 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/invalid-access-moved.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/invalid-access-moved.rst @@ -8,4 +8,4 @@ hicpp-invalid-access-moved This check is an alias for :doc:`bugprone-use-after-move <../bugprone/use-after-move>`. -Implements parts of the `rule 8.4.1 `_ to check if moved-from objects are accessed. +Implements parts of the `rule 8.4.1 `_ to check if moved-from objects are accessed. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/member-init.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/member-init.rst index 341d8a79931ed..d96f090707ffe 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/member-init.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/member-init.rst @@ -7,5 +7,5 @@ hicpp-member-init This check is an alias for :doc:`cppcoreguidelines-pro-type-member-init <../cppcoreguidelines/pro-type-member-init>`. Implements the check for -`rule 12.4.2 `_ +`rule 12.4.2 `_ to initialize class members in the right order. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/move-const-arg.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/move-const-arg.rst index 7af8401602328..b993a4e4f8d75 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/move-const-arg.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/move-const-arg.rst @@ -8,4 +8,4 @@ hicpp-move-const-arg The `hicpp-move-const-arg` check is an alias, please see :doc:`performance-move-const-arg <../performance/move-const-arg>` for more information. -It enforces the `rule 17.3.1 `_. +It enforces the `rule 17.3.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/multiway-paths-covered.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/multiway-paths-covered.rst index f164f4c0b2f48..13f174778b6de 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/multiway-paths-covered.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/multiway-paths-covered.rst @@ -6,8 +6,8 @@ hicpp-multiway-paths-covered This check discovers situations where code paths are not fully-covered. It furthermore suggests using ``if`` instead of ``switch`` if the code will be more clear. -The `rule 6.1.2 `_ -and `rule 6.1.4 `_ +The `rule 6.1.2 `_ +and `rule 6.1.4 `_ of the High Integrity C++ Coding Standard are enforced. ``if-else if`` chains that miss a final ``else`` branch might lead to @@ -59,7 +59,7 @@ possible code paths. } -The `rule 6.1.4 `_ +The `rule 6.1.4 `_ requires every ``switch`` statement to have at least two ``case`` labels other than a `default` label. Otherwise, the ``switch`` could be better expressed with an ``if`` statement. Degenerated ``switch`` statements without any labels are caught as well. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/named-parameter.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/named-parameter.rst index 4506596946ead..26deac6e1688e 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/named-parameter.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/named-parameter.rst @@ -8,4 +8,4 @@ hicpp-named-parameter This check is an alias for :doc:`readability-named-parameter <../readability/named-parameter>`. -Implements `rule 8.2.1 `_. +Implements `rule 8.2.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/new-delete-operators.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/new-delete-operators.rst index a7891f943e2c3..2adc4efe19cbd 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/new-delete-operators.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/new-delete-operators.rst @@ -6,5 +6,5 @@ hicpp-new-delete-operators ========================== This check is an alias for :doc:`misc-new-delete-overloads <../misc/new-delete-overloads>`. -Implements `rule 12.3.1 `_ to ensure +Implements `rule 12.3.1 `_ to ensure the `new` and `delete` operators have the correct signature. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-array-decay.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-array-decay.rst index 715a1b7638e8b..3cd5ebde65cf9 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-array-decay.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-array-decay.rst @@ -9,4 +9,4 @@ The `hicpp-no-array-decay` check is an alias, please see :doc:`cppcoreguidelines-pro-bounds-array-to-pointer-decay <../cppcoreguidelines/pro-bounds-array-to-pointer-decay>` for more information. -It enforces the `rule 4.1.1 `_. +It enforces the `rule 4.1.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst index cfc6e9b6ec347..55231fbd0a8da 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst @@ -6,5 +6,5 @@ hicpp-no-assembler Checks for assembler statements. Use of inline assembly should be avoided since it restricts the portability of the code. -This enforces `rule 7.5.1 `_ +This enforces `rule 7.5.1 `_ of the High Integrity C++ Coding Standard. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-malloc.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-malloc.rst index d84345e786fc5..006afb204a41a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-malloc.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-malloc.rst @@ -8,4 +8,4 @@ hicpp-no-malloc The `hicpp-no-malloc` check is an alias, please see :doc:`cppcoreguidelines-no-malloc <../cppcoreguidelines/no-malloc>` for more information. -It enforces the `rule 5.3.2 `_. +It enforces the `rule 5.3.2 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/noexcept-move.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/noexcept-move.rst index 1f875f410e9ac..1921cb5f8a61f 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/noexcept-move.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/noexcept-move.rst @@ -7,4 +7,4 @@ hicpp-noexcept-move This check is an alias for :doc:`performance-noexcept-move-constructor <../performance/noexcept-move-constructor>`. -Checks `rule 12.5.4 `_ to mark move assignment and move construction `noexcept`. +Checks `rule 12.5.4 `_ to mark move assignment and move construction `noexcept`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/signed-bitwise.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/signed-bitwise.rst index e290c3c0c2310..0461f0cd61911 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/signed-bitwise.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/signed-bitwise.rst @@ -6,7 +6,7 @@ hicpp-signed-bitwise Finds uses of bitwise operations on signed integer types, which may lead to undefined or implementation defined behavior. -The according rule is defined in the `High Integrity C++ Standard, Section 5.6.1 `_. +The according rule is defined in the `High Integrity C++ Standard, Section 5.6.1 `_. Options ------- diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/special-member-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/special-member-functions.rst index e0be23c8bffab..b065c79ee696b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/special-member-functions.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/special-member-functions.rst @@ -6,4 +6,4 @@ hicpp-special-member-functions ============================== This check is an alias for :doc:`cppcoreguidelines-special-member-functions <../cppcoreguidelines/special-member-functions>`. -Checks that special member functions have the correct signature, according to `rule 12.5.7 `_. +Checks that special member functions have the correct signature, according to `rule 12.5.7 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/static-assert.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/static-assert.rst index 8640e697ce9a2..155dcfb7f507a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/static-assert.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/static-assert.rst @@ -7,4 +7,4 @@ hicpp-static-assert The `hicpp-static-assert` check is an alias, please see :doc:`misc-static-assert <../misc/static-assert>` for more information. -It enforces the `rule 7.1.10 `_. +It enforces the `rule 7.1.10 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/undelegated-constructor.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/undelegated-constructor.rst index f67ca65f5cafa..066d42c91920c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/undelegated-constructor.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/undelegated-constructor.rst @@ -6,7 +6,7 @@ hicpp-undelegated-constructor ============================= This check is an alias for :doc:`bugprone-undelegated-constructor <../bugprone/undelegated-constructor>`. -Partially implements `rule 12.4.5 `_ +Partially implements `rule 12.4.5 `_ to find misplaced constructor calls inside a constructor. .. code-block:: c++ diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/uppercase-literal-suffix.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/uppercase-literal-suffix.rst index 34b9f255a44c8..7661fb82fc689 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/uppercase-literal-suffix.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/uppercase-literal-suffix.rst @@ -9,5 +9,5 @@ The hicpp-uppercase-literal-suffix check is an alias, please see :doc:`readability-uppercase-literal-suffix <../readability/uppercase-literal-suffix>` for more information. -Partially implements `rule 4.2.1 `_ +Partially implements `rule 4.2.1 `_ to ensure that the ``U`` suffix is writeln properly. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-auto.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-auto.rst index c5d0e7c05bea1..9bd044f5bea82 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-auto.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-auto.rst @@ -7,4 +7,4 @@ hicpp-use-auto The `hicpp-use-auto` check is an alias, please see :doc:`modernize-use-auto <../modernize/use-auto>` for more information. -It enforces the `rule 7.1.8 `_. +It enforces the `rule 7.1.8 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-emplace.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-emplace.rst index cdc59e96b8f56..1be05c1186832 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-emplace.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-emplace.rst @@ -7,4 +7,4 @@ hicpp-use-emplace The `hicpp-use-emplace` check is an alias, please see :doc:`modernize-use-emplace <../modernize/use-emplace>` for more information. -It enforces the `rule 17.4.2 `_. +It enforces the `rule 17.4.2 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-default.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-default.rst index f891e820cbf02..e2348e89c4511 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-default.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-default.rst @@ -6,4 +6,4 @@ hicpp-use-equals-default ======================== This check is an alias for :doc:`modernize-use-equals-default <../modernize/use-equals-default>`. -Implements `rule 12.5.1 `_ to explicitly default special member functions. +Implements `rule 12.5.1 `_ to explicitly default special member functions. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-delete.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-delete.rst index f4bba24e34e5a..640553b9d3980 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-delete.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-equals-delete.rst @@ -6,5 +6,5 @@ hicpp-use-equals-delete ======================= This check is an alias for :doc:`modernize-use-equals-delete <../modernize/use-equals-delete>`. -Implements `rule 12.5.1 `_ +Implements `rule 12.5.1 `_ to explicitly default or delete special member functions. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-noexcept.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-noexcept.rst index 3ee4a33f1f45e..9d4998232fa7a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-noexcept.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-noexcept.rst @@ -7,4 +7,4 @@ hicpp-use-noexcept The `hicpp-use-noexcept` check is an alias, please see :doc:`modernize-use-noexcept <../modernize/use-noexcept>` for more information. -It enforces the `rule 1.3.5 `_. +It enforces the `rule 1.3.5 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-nullptr.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-nullptr.rst index 4bcc71898c1c3..75dc6bfbb818e 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-nullptr.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-nullptr.rst @@ -7,4 +7,4 @@ hicpp-use-nullptr The `hicpp-use-nullptr` check is an alias, please see :doc:`modernize-use-nullptr <../modernize/use-nullptr>` for more information. -It enforces the `rule 2.5.3 `_. +It enforces the `rule 2.5.3 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-override.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-override.rst index aa09d0ef49fa5..6caec49fe7a4d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-override.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/use-override.rst @@ -6,5 +6,5 @@ hicpp-use-override ================== This check is an alias for :doc:`modernize-use-override <../modernize/use-override>`. -Implements `rule 10.2.1 `_ to +Implements `rule 10.2.1 `_ to declare a virtual function `override` when overriding. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/vararg.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/vararg.rst index 2d91c4cd6e37b..a9e67b9158fef 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/vararg.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/vararg.rst @@ -8,4 +8,4 @@ hicpp-vararg The `hicpp-vararg` check is an alias, please see :doc:`cppcoreguidelines-pro-type-vararg <../cppcoreguidelines/pro-type-vararg>` for more information. -It enforces the `rule 14.1.1 `_. +It enforces the `rule 14.1.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/pass-by-value.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/pass-by-value.rst index b8d933aab702c..0f5758dc097e6 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/pass-by-value.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/pass-by-value.rst @@ -164,3 +164,8 @@ Options When `true`, the check only warns about copied parameters that are already passed by value. Default is `false`. + +.. option:: IgnoreMacros + + When `true`, the check will not give warnings inside macros. Default is + `false`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/magic-numbers.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/magic-numbers.rst index 55d47d568dcdc..71cb35129cbe4 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/magic-numbers.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/magic-numbers.rst @@ -10,7 +10,7 @@ Many coding guidelines advise replacing the magic values with symbolic constants to improve readability. Here are a few references: * `Rule ES.45: Avoid "magic constants"; use symbolic constants in C++ Core Guidelines `_ - * `Rule 5.1.1 Use symbolic names instead of literal values in code in High Integrity C++ `_ + * `Rule 5.1.1 Use symbolic names instead of literal values in code in High Integrity C++ `_ * Item 17 in "C++ Coding Standards: 101 Rules, Guidelines and Best Practices" by Herb Sutter and Andrei Alexandrescu * Chapter 17 in "Clean Code - A handbook of agile software craftsmanship." diff --git a/clang-tools-extra/test/clang-doc/basic-project.mustache.test b/clang-tools-extra/test/clang-doc/basic-project.mustache.test index 4c882ef2e8384..8950f66068078 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.mustache.test +++ b/clang-tools-extra/test/clang-doc/basic-project.mustache.test @@ -13,7 +13,8 @@ HTML-SHAPE: HTML-SHAPE: Shape HTML-SHAPE: HTML-SHAPE: -HTML-SHAPE: +HTML-SHAPE: +HTML-SHAPE: HTML-SHAPE: HTML-SHAPE: HTML-SHAPE: @@ -123,7 +124,8 @@ HTML-CALC: HTML-CALC: Calculator HTML-CALC: HTML-CALC: -HTML-CALC: +HTML-CALC: +HTML-CALC: HTML-CALC: HTML-CALC: HTML-CALC: @@ -337,7 +339,8 @@ HTML-RECTANGLE: HTML-RECTANGLE: Rectangle HTML-RECTANGLE: HTML-RECTANGLE: -HTML-RECTANGLE: +HTML-RECTANGLE: +HTML-RECTANGLE: HTML-RECTANGLE: HTML-RECTANGLE: HTML-RECTANGLE: @@ -451,7 +454,8 @@ HTML-CIRCLE: HTML-CIRCLE: Circle HTML-CIRCLE: HTML-CIRCLE: -HTML-CIRCLE: +HTML-CIRCLE: +HTML-CIRCLE: HTML-CIRCLE: HTML-CIRCLE: HTML-CIRCLE: diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index 4e42f0b12516b..62377a1ccbf1b 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -49,7 +49,8 @@ import re import subprocess import sys -from typing import List, Tuple +from collections import defaultdict +from typing import Dict, List, Sequence, Tuple def write_file(file_name: str, text: str) -> None: @@ -93,7 +94,8 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.input_file_name = args.input_file_name self.check_name = args.check_name self.temp_file_name = args.temp_file_name - self.original_file_name = self.temp_file_name + ".orig" + self.check_headers = args.check_headers + self.original_file_name = f"{self.temp_file_name}.orig" self.expect_clang_tidy_error = args.expect_clang_tidy_error self.std = args.std self.check_suffix = args.check_suffix @@ -121,6 +123,18 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.clang_extra_args = self.clang_tidy_extra_args[i + 1 :] self.clang_tidy_extra_args = self.clang_tidy_extra_args[:i] + self.check_header_map: Dict[str, str] = {} + self.header_dir = f"{self.temp_file_name}.headers" + if self.check_headers: + self.check_header_map = { + os.path.normcase( + os.path.abspath(os.path.join(self.header_dir, os.path.basename(h))) + ): os.path.abspath(h) + for h in self.check_headers + } + + self.clang_extra_args.insert(0, f"-I{self.header_dir}") + # If the test does not specify a config style, force an empty one; otherwise # auto-detection logic can discover a ".clang-tidy" file that is not related to # the test. @@ -136,7 +150,7 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: "-fblocks", ] + self.clang_extra_args - self.clang_extra_args.append("-std=" + self.std) + self.clang_extra_args.append(f"-std={self.std}") # Tests should not rely on STL being available, and instead provide mock # implementations of relevant APIs. @@ -158,8 +172,8 @@ def get_prefixes(self) -> None: for suffix in self.check_suffix: if suffix and not re.match("^[A-Z0-9\\-]+$", suffix): sys.exit( - 'Only A..Z, 0..9 and "-" are allowed in check suffixes list,' - + ' but "%s" was given' % suffix + 'Only A..Z, 0..9 and "-" are allowed in check suffixes list, ' + f'but "{suffix}" was given' ) file_check_suffix = ("-" + suffix) if suffix else "" @@ -196,15 +210,50 @@ def get_prefixes(self) -> None: ) assert expect_diagnosis or self.expect_no_diagnosis - def prepare_test_inputs(self) -> None: + def _remove_filecheck_content(self, text: str) -> str: # Remove the contents of the CHECK lines to avoid CHECKs matching on - # themselves. We need to keep the comments to preserve line numbers while + # themselves. We need to keep the comments to preserve line numbers while # avoiding empty lines which could potentially trigger formatting-related # checks. - cleaned_test = re.sub("// *CHECK-[A-Z0-9\\-]*:[^\r\n]*", "//", self.input_text) + return re.sub("// *CHECK-[A-Z0-9\\-]*:[^\r\n]*", "//", text) + + def _filter_prefixes(self, prefixes: Sequence[str], check_file: str) -> List[str]: + """ + Filter prefixes to only those present in the check file. + + - Input: + prefixes: A list of potential FileCheck prefixes. + check_file: The file to check for prefix presence. + - Output: + A list of prefixes found in the check file. + + FileCheck fails if a specified prefix is not present. This is common + in header testing scenarios where expectations differ between the + main file and the header (e.g. the main file might verify code + changes while the header only verifies warnings). + """ + if check_file == self.input_file_name: + content = self.input_text + else: + with open(check_file, "r", encoding="utf-8") as f: + content = f.read() + return [p for p in prefixes if p in content] + + def prepare_test_inputs(self) -> None: + cleaned_test = self._remove_filecheck_content(self.input_text) write_file(self.temp_file_name, cleaned_test) write_file(self.original_file_name, cleaned_test) + if self.check_headers: + os.makedirs(self.header_dir, exist_ok=True) + + for temp_header_path, header in self.check_header_map.items(): + with open(header, "r", encoding="utf-8") as f: + cleaned_header = self._remove_filecheck_content(f.read()) + + write_file(temp_header_path, cleaned_header) + write_file(f"{temp_header_path}.orig", cleaned_header) + def run_clang_tidy(self) -> str: args = ( [ @@ -216,11 +265,11 @@ def run_clang_tidy(self) -> str: ( "-fix" if self.export_fixes is None - else "--export-fixes=" + self.export_fixes + else f"--export-fixes={self.export_fixes}" ) ] + [ - "--checks=-*," + self.check_name, + f"--checks=-*,{self.check_name}", ] + self.clang_tidy_extra_args + ["--"] @@ -228,7 +277,7 @@ def run_clang_tidy(self) -> str: ) if self.expect_clang_tidy_error: args.insert(0, "not") - print("Running " + repr(args) + "...") + print(f"Running {repr(args)}...") clang_tidy_output = try_run(args) print("------------------------ clang-tidy output -----------------------") print( @@ -241,6 +290,12 @@ def run_clang_tidy(self) -> str: diff_output = try_run( ["diff", "-u", self.original_file_name, self.temp_file_name], False ) + if self.check_headers: + for temp_header_path in self.check_header_map: + diff_output += try_run( + ["diff", "-u", f"{temp_header_path}.orig", temp_header_path], False + ) + print("------------------------------ Fixes -----------------------------") print(diff_output) print("------------------------------------------------------------------") @@ -250,39 +305,61 @@ def check_no_diagnosis(self, clang_tidy_output: str) -> None: if clang_tidy_output != "": sys.exit("No diagnostics were expected, but found the ones above") - def check_fixes(self) -> None: - if self.has_check_fixes: - try_run( - [ - "FileCheck", - "--input-file=" + self.temp_file_name, - self.input_file_name, - "--check-prefixes=" + ",".join(self.fixes.prefixes), - ( - "--match-full-lines" - if not self.match_partial_fixes - else "--strict-whitespace" # Keeping past behavior. - ), - ] - ) + def check_fixes(self, input_file: str = "", check_file: str = "") -> None: + if not (check_file or self.has_check_fixes): + return - def check_messages(self, clang_tidy_output: str) -> None: - if self.has_check_messages: - messages_file = self.temp_file_name + ".msg" - write_file(messages_file, clang_tidy_output) - try_run( - [ - "FileCheck", - "-input-file=" + messages_file, - self.input_file_name, - "-check-prefixes=" + ",".join(self.messages.prefixes), - "-implicit-check-not={{warning|error}}:", - ] - ) + input_file = input_file or self.temp_file_name + check_file = check_file or self.input_file_name + active_prefixes = self._filter_prefixes(self.fixes.prefixes, check_file) + + if not active_prefixes: + return + + try_run( + [ + "FileCheck", + f"--input-file={input_file}", + check_file, + f"--check-prefixes={','.join(active_prefixes)}", + ( + "--strict-whitespace" # Keeping past behavior. + if self.match_partial_fixes + else "--match-full-lines" + ), + ] + ) + + def check_messages( + self, + clang_tidy_output: str, + messages_file: str = "", + check_file: str = "", + ) -> None: + if not check_file and not self.has_check_messages: + return + + messages_file = messages_file or f"{self.temp_file_name}.msg" + check_file = check_file or self.input_file_name + + active_prefixes = self._filter_prefixes(self.messages.prefixes, check_file) + if not active_prefixes: + return + + write_file(messages_file, clang_tidy_output) + try_run( + [ + "FileCheck", + f"-input-file={messages_file}", + check_file, + f"-check-prefixes={','.join(active_prefixes)}", + "-implicit-check-not={{warning|error}}:", + ] + ) def check_notes(self, clang_tidy_output: str) -> None: if self.has_check_notes: - notes_file = self.temp_file_name + ".notes" + notes_file = f"{self.temp_file_name}.notes" filtered_output = [ line for line in clang_tidy_output.splitlines() @@ -292,25 +369,74 @@ def check_notes(self, clang_tidy_output: str) -> None: try_run( [ "FileCheck", - "-input-file=" + notes_file, + f"-input-file={notes_file}", self.input_file_name, - "-check-prefixes=" + ",".join(self.notes.prefixes), + f"-check-prefixes={','.join(self.notes.prefixes)}", "-implicit-check-not={{note|warning|error}}:", ] ) + def _separate_messages(self, clang_tidy_output: str) -> Tuple[str, Dict[str, str]]: + """ + Separates diagnostics for the main file and headers. + + - Input: The raw diagnostic output from clang-tidy. + - Output: A tuple containing: + 1. The diagnostic output for the main file. + 2. A dictionary mapping header files to their diagnostics. + """ + if not self.check_headers: + return clang_tidy_output, {} + + header_messages = defaultdict(list) + remaining_lines: List[str] = [] + current_file = "" + + for line in clang_tidy_output.splitlines(keepends=True): + if re.match(r"^\d+ warnings? generated\.", line): + continue + + # Matches the beginning of a clang-tidy diagnostic line, + # which starts with "file_path:line:col: ". + if match := re.match(r"^(.+):\d+:\d+: ", line): + abs_path = os.path.normcase(os.path.abspath(match.group(1))) + current_file = abs_path if abs_path in self.check_header_map else "" + + dest_list = ( + header_messages[current_file] if current_file else remaining_lines + ) + dest_list.append(line) + + header_messages_str = {k: "".join(v) for k, v in header_messages.items()} + return "".join(remaining_lines), header_messages_str + def run(self) -> None: self.read_input() if self.export_fixes is None: self.get_prefixes() self.prepare_test_inputs() clang_tidy_output = self.run_clang_tidy() + main_output, header_messages = self._separate_messages(clang_tidy_output) + if self.expect_no_diagnosis: - self.check_no_diagnosis(clang_tidy_output) + self.check_no_diagnosis(main_output) elif self.export_fixes is None: self.check_fixes() - self.check_messages(clang_tidy_output) - self.check_notes(clang_tidy_output) + self.check_messages(main_output) + self.check_notes(main_output) + + for temp_header, original_header in self.check_header_map.items(): + self.check_fixes( + input_file=temp_header, + check_file=original_header, + ) + + if temp_header in header_messages: + self.check_messages( + header_messages[temp_header], + messages_file=f"{temp_header}.msg", + check_file=original_header, + ) CPP_STANDARDS = [ @@ -370,6 +496,13 @@ def parse_arguments() -> Tuple[argparse.Namespace, List[str]]: type=csv, help="comma-separated list of FileCheck suffixes", ) + parser.add_argument( + "-check-header", + action="append", + dest="check_headers", + default=[], + help="Header files to check", + ) parser.add_argument( "-export-fixes", default=None, diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/init-variables.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/init-variables.cpp index 8a8973a032bf2..e4a68fea59b52 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/init-variables.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/init-variables.cpp @@ -168,3 +168,36 @@ namespace gh161978 { // CHECK-FIXES: bool (*fp5)(int, int) = nullptr, (*fp6)(int, int) = nullptr; } } + +void gh180894() { + struct S { + int x; + }; + + int S::* mp; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: variable 'mp' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: int S::* mp = nullptr; + + int S::* mp1 = nullptr, S::* mp2; + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: variable 'mp2' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: int S::* mp1 = nullptr, S::* mp2 = nullptr; + + int S::* mp3, S::* mp4 = &S::x; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: variable 'mp3' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: int S::* mp3 = nullptr, S::* mp4 = &S::x; + + using MemPtr = int S::*; + MemPtr mp5; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: variable 'mp5' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: MemPtr mp5 = nullptr; + + struct S1 { + int x; + int y; + }; + + int S::* mp6, S1::* mp7; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: variable 'mp6' is not initialized [cppcoreguidelines-init-variables] + // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: variable 'mp7' is not initialized [cppcoreguidelines-init-variables] + // CHECK-FIXES: int S::* mp6 = nullptr, S1::* mp7 = nullptr; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h index 703d7e05ca2e9..0d748a81c0067 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h @@ -1,8 +1,9 @@ +// CHECK-MESSAGES-NORMAL: :[[@LINE+1]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] namespace nn1 { namespace nn2 { -// CHECK-FIXES: namespace nn1::nn2 +// CHECK-FIXES-NORMAL: namespace nn1::nn2 { void t(); } // namespace nn2 } // namespace nn1 -// CHECK-FIXES: void t(); -// CHECK-FIXES-NEXT: } // namespace nn1::nn2 +// CHECK-FIXES-NORMAL: void t(); +// CHECK-FIXES-NORMAL-NEXT: } // namespace nn1::nn2 diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h index 62609e25f682a..98512f411fe61 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h @@ -4,5 +4,6 @@ struct S { }; struct Foo { Foo(const S &s); + // CHECK-FIXES: Foo(S s); S s; }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h index c2103cb3fc7a1..e27104e70ac0b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h @@ -6,5 +6,8 @@ class ThreadId { struct A { A(const ThreadId &tid) : threadid(tid) {} + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value] + // CHECK-FIXES: #include + // CHECK-FIXES: A(ThreadId tid) : threadid(std::move(tid)) {} ThreadId threadid; }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp index 35cb5503ba245..4326fa6412021 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp @@ -1,14 +1,7 @@ -// RUN: mkdir -p %t.dir -// RUN: cp %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %t.dir/modernize-concat-nested-namespaces.h -// RUN: %check_clang_tidy -std=c++17 -check-suffix=NORMAL %s modernize-concat-nested-namespaces %t.dir/code -- -header-filter=".*" -- -I %t.dir -// RUN: FileCheck -input-file=%t.dir/modernize-concat-nested-namespaces.h %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h -check-prefix=CHECK-FIXES -// Restore header file and re-run with c++20: -// RUN: cp %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %t.dir/modernize-concat-nested-namespaces.h -// RUN: %check_clang_tidy -std=c++20 -check-suffixes=NORMAL,CPP20 %s modernize-concat-nested-namespaces %t.dir/code -- -header-filter=".*" -- -I %t.dir -// RUN: FileCheck -input-file=%t.dir/modernize-concat-nested-namespaces.h %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h -check-prefix=CHECK-FIXES +// RUN: %check_clang_tidy -std=c++17 -check-suffix=NORMAL -check-header %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %s modernize-concat-nested-namespaces %t +// RUN: %check_clang_tidy -std=c++20 -check-suffixes=NORMAL,CPP20 -check-header %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %s modernize-concat-nested-namespaces %t #include "modernize-concat-nested-namespaces.h" -// CHECK-MESSAGES-NORMAL-DAG: modernize-concat-nested-namespaces.h:1:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] namespace n1 {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp index 461a6378d99c0..e40c21f632d1a 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp @@ -1,10 +1,5 @@ -// RUN: mkdir -p %t.dir -// RUN: cp %S/Inputs/pass-by-value/header.h %t.dir/pass-by-value-header.h -// RUN: clang-tidy %s -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %t.dir | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:" -// RUN: FileCheck -input-file=%t.dir/pass-by-value-header.h %s -check-prefix=CHECK-FIXES -// FIXME: Make the test work in all language modes. +// RUN: %check_clang_tidy -check-header %S/Inputs/pass-by-value/header.h \ +// RUN: %s modernize-pass-by-value %t -- -- -std=c++11 -#include "pass-by-value-header.h" -// CHECK-MESSAGES: :8:5: warning: pass by value and use std::move [modernize-pass-by-value] -// CHECK-FIXES: #include -// CHECK-FIXES: A(ThreadId tid) : threadid(std::move(tid)) {} +#include "header.h" +// FIXME: Make the test work in all language modes. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-ignore-macros.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-ignore-macros.cpp new file mode 100644 index 0000000000000..8b21e97b0fd1f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-ignore-macros.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy -check-suffixes=DEFAULT %s modernize-pass-by-value %t -- -config="{CheckOptions: {modernize-pass-by-value.IgnoreMacros: false}}" +// RUN: %check_clang_tidy -check-suffixes=IGNORE %s modernize-pass-by-value %t -- -config="{CheckOptions: {modernize-pass-by-value.IgnoreMacros: true}}" + +struct A { + A(const A &); + A(A &&); +}; + +#define MACRO_CTOR(TYPE) \ +struct TYPE { \ + TYPE(const A &a) : a(a) {} \ + A a; \ +}; +// CHECK-MESSAGES-DEFAULT: :[[@LINE+3]]:1: warning: pass by value and use std::move [modernize-pass-by-value] +// CHECK-MESSAGES-IGNORE-NOT: warning: pass by value and use std::move + +MACRO_CTOR(B) + +struct C { + C(const A &a) : a(a) {} + A a; +}; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:5: warning: pass by value and use std::move [modernize-pass-by-value] +// CHECK-MESSAGES-IGNORE: :[[@LINE-4]]:5: warning: pass by value and use std::move [modernize-pass-by-value] diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp index b77c74be02f5f..bbe96d39793b0 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp @@ -1,13 +1,9 @@ -// RUN: mkdir -p %t.dir -// RUN: cat %S/Inputs/pass-by-value/header-with-fix.h > %t.dir/pass-by-value-header-with-fix.h -// RUN: sed -e 's#//.*$##' %s > %t.dir/code.cpp -// RUN: clang-tidy %t.dir/code.cpp -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %t.dir | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:" -// RUN: FileCheck -input-file=%t.dir/code.cpp %s -check-prefix=CHECK-FIXES -// RUN: FileCheck -input-file=%t.dir/pass-by-value-header-with-fix.h %s -check-prefix=CHECK-HEADER-FIXES +// RUN: %check_clang_tidy -check-header %S/Inputs/pass-by-value/header-with-fix.h \ +// RUN: %s modernize-pass-by-value %t -- -- -std=c++11 -#include "pass-by-value-header-with-fix.h" -// CHECK-HEADER-FIXES: Foo(S s); +#include "header-with-fix.h" + +// CHECK-MESSAGES: :[[@LINE+1]]:10: warning: pass by value and use std::move [modernize-pass-by-value] Foo::Foo(const S &s) : s(s) {} -// CHECK-MESSAGES: :10:10: warning: pass by value and use std::move [modernize-pass-by-value] // CHECK-FIXES: #include // CHECK-FIXES: Foo::Foo(S s) : s(std::move(s)) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg-delayed.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg-delayed.cpp index 4e05ada1d2992..b5916146ac8d8 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg-delayed.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg-delayed.cpp @@ -1,7 +1,7 @@ // RUN: %check_clang_tidy %s modernize-redundant-void-arg %t -- -- -fdelayed-template-parsing int foo(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: int foo() { return 0; } @@ -9,7 +9,7 @@ int foo(void) { template struct MyFoo { int foo(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: int foo() { return 0; } @@ -21,7 +21,7 @@ template struct MyBar { // This declaration isn't instantiated and won't be parsed 'delayed-template-parsing'. int foo(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: int foo() { return 0; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg.cpp index 631149761e5e8..09dc2bb5c1775 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-void-arg.cpp @@ -18,7 +18,7 @@ extern int i; int j = 1; int foo(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: int foo() { return 0; } @@ -30,24 +30,24 @@ typedef void my_void; // A function taking void and returning a pointer to function taking void // and returning int. int (*returns_fn_void_int(void))(void); -// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} in function declaration -// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: {{.*}} in function declaration +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: int (*returns_fn_void_int())(); typedef int (*returns_fn_void_int_t(void))(void); -// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: {{.*}} in typedef -// CHECK-MESSAGES: :[[@LINE-2]]:44: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:44: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: typedef int (*returns_fn_void_int_t())(); // Should work for type aliases as well as typedef. using returns_fn_void_int_t2 = int (*(void))(void); -// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: {{.*}} in type alias -// CHECK-MESSAGES: :[[@LINE-2]]:46: warning: {{.*}} in type alias +// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:46: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: using returns_fn_void_int_t2 = int (*())(); int (*returns_fn_void_int(void))(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} in function definition -// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: int (*returns_fn_void_int())() { return nullptr; } @@ -55,27 +55,27 @@ int (*returns_fn_void_int(void))(void) { // A function taking void and returning a pointer to a function taking void // and returning a pointer to a function taking void and returning void. void (*(*returns_fn_returns_fn_void_void(void))(void))(void); -// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: {{.*}} in function declaration -// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: {{.*}} in function declaration -// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: {{.*}} in function declaration +// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*(*returns_fn_returns_fn_void_void())())(); typedef void (*(*returns_fn_returns_fn_void_void_t(void))(void))(void); -// CHECK-MESSAGES: :[[@LINE-1]]:52: warning: {{.*}} in typedef -// CHECK-MESSAGES: :[[@LINE-2]]:59: warning: {{.*}} in typedef -// CHECK-MESSAGES: :[[@LINE-3]]:66: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-1]]:52: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:59: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-3]]:66: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: typedef void (*(*returns_fn_returns_fn_void_void_t())())(); void (*(*returns_fn_returns_fn_void_void(void))(void))(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: {{.*}} in function definition -// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: {{.*}} in function definition -// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*(*returns_fn_returns_fn_void_void())())() { return nullptr; } void bar(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void bar() { } @@ -102,13 +102,13 @@ class gronk { double *m_pd; void (*f1)(void); - // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in field declaration + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f1)(); void (*op)(int i); void (gronk::*p1)(void); - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in field declaration + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (gronk::*p1)(); int (gronk::*p_mi); @@ -116,15 +116,15 @@ class gronk { void (gronk::*p2)(int); void (*(*returns_fn_returns_fn_void_void(void))(void))(void); - // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: {{.*}} in function declaration - // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: {{.*}} in function declaration - // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: {{.*}} in function declaration + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*(*returns_fn_returns_fn_void_void())())(); void (*(*(gronk::*returns_fn_returns_fn_void_void_mem)(void))(void))(void); - // CHECK-MESSAGES: :[[@LINE-1]]:58: warning: {{.*}} in field declaration - // CHECK-MESSAGES: :[[@LINE-2]]:65: warning: {{.*}} in field declaration - // CHECK-MESSAGES: :[[@LINE-3]]:72: warning: {{.*}} in field declaration + // CHECK-MESSAGES: :[[@LINE-1]]:58: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-2]]:65: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-3]]:72: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*(*(gronk::*returns_fn_returns_fn_void_void_mem)())())(); }; @@ -137,35 +137,35 @@ double d; double *pd; void (*f1)(void); -// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f1)(); void (*f2)(void) = nullptr; -// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2)() = nullptr; void (*f2b)(void)(nullptr); -// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2b)()(nullptr); void (*f2c)(void){nullptr}; -// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2c)(){nullptr}; void (*f2d)(void) = NULL; -// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2d)() = NULL; void (*f2e)(void)(NULL); -// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2e)()(NULL); void (*f2f)(void){NULL}; -// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2f)(){NULL}; void (*f3)(void) = bar; -// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f3)() = bar; void (*o1)(int i); @@ -184,7 +184,7 @@ void (*fb)() = nullptr; void (*fc)() = bar; typedef void (function_ptr)(void); -// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: typedef void (function_ptr)(); // intentionally not LLVM style to check preservation of whitespace @@ -192,7 +192,7 @@ typedef void (function_ptr2) ( void ); -// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^typedef void \(function_ptr2\)$}} // CHECK-FIXES-NEXT: {{^ \($}} // CHECK-FIXES-NEXT: {{^ $}} @@ -219,9 +219,9 @@ void void ) ; -// CHECK-MESSAGES: :[[@LINE-11]]:1: warning: {{.*}} in typedef -// CHECK-MESSAGES: :[[@LINE-8]]:1: warning: {{.*}} in typedef -// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-11]]:1: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-8]]:1: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^typedef$}} // CHECK-FIXES-NEXT: {{^void$}} // CHECK-FIXES-NEXT: {{^\($}} @@ -244,15 +244,15 @@ void // clang-format on void (gronk::*p1)(void); -// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} in variable declaration +// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (gronk::*p1)(); void (gronk::*p2)(void) = &gronk::foo; -// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} in variable declaration with initializer +// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (gronk::*p2)() = &gronk::foo; typedef void (gronk::*member_function_ptr)(void); -// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: typedef void (gronk::*member_function_ptr)(); // intentionally not LLVM style to check preservation of whitespace @@ -260,7 +260,7 @@ typedef void (gronk::*member_function_ptr2) ( void ); -// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: {{.*}} in typedef +// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^typedef void \(gronk::\*member_function_ptr2\)$}} // CHECK-FIXES-NEXT: {{^ \($}} // CHECK-FIXES-NEXT: {{^ $}} @@ -268,11 +268,11 @@ typedef void (gronk::*member_function_ptr2) void gronk::foo() { void (*f1)(void) = &::bar; - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f1)() = &::bar; void (*f2)(void); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f2)(); // intentionally not LLVM style to check preservation of whitespace @@ -280,7 +280,7 @@ void gronk::foo() { ( void ); - // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: {{.*}} in variable declaration + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^ }}void (*f3){{$}} // CHECK-FIXES-NEXT: {{^ \($}} // CHECK-FIXES-NEXT: {{^ $}} @@ -288,14 +288,14 @@ void gronk::foo() { } void gronk::bar(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void gronk::bar() { void (gronk::*p3)(void) = &gronk::foo; - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in variable declaration with initializer + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (gronk::*p3)() = &gronk::foo; void (gronk::*p4)(void); - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in variable declaration + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (gronk::*p4)(); // intentionally not LLVM style to check preservation of whitespace @@ -303,11 +303,11 @@ void gronk::bar(void) { ( void ); - // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: {{.*}} in variable declaration + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^ }}void (gronk::*p5){{$}} // CHECK-FIXES-NEXT: {{^ \($}} - // CHECK-FIXES-NExT: {{^ $}} - // CHECK-FIXES-NExT: {{^ \);$}} + // CHECK-FIXES-NEXT: {{^ $}} + // CHECK-FIXES-NEXT: {{^ \);$}} } // intentionally not LLVM style to check preservation of whitespace @@ -315,7 +315,7 @@ void gronk::bar2 ( void ) -// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^void gronk::bar2$}} // CHECK-FIXES-NEXT: {{^ \($}} // CHECK-FIXES-NEXT: {{^ $}} @@ -324,14 +324,14 @@ void gronk::bar2 } gronk::gronk(void) -// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: gronk::gronk() : f1(nullptr), p1(nullptr) { } gronk::~gronk(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: gronk::~gronk() { } @@ -341,21 +341,21 @@ class nutter { }; nutter::nutter(void) { -// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: nutter::nutter() { void (*f3)(void) = static_cast(0); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer - // CHECK-MESSAGES: :[[@LINE-2]]:43: warning: {{.*}} in named cast + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-2]]:43: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f3)() = static_cast(0); void (*f4)(void) = (void (*)(void)) 0; - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer - // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: {{.*}} in cast expression + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f4)() = (void (*)()) 0; void (*f5)(void) = reinterpret_cast(0); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer - // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: {{.*}} in named cast + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void (*f5)() = reinterpret_cast(0); // intentionally not LLVM style to check preservation of whitespace @@ -363,8 +363,8 @@ nutter::nutter(void) { ( void )>(0); - // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer - // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in named cast + // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^ }}void (*f6)() = static_cast(0); - // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer - // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in named cast + // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: {{^ }}void (*f8)() = reinterpret_cast void (*)(void) { return f1; }; - // CHECK-MESSAGES: [[@LINE-1]]:27: warning: {{.*}} in lambda expression - // CHECK-MESSAGES: [[@LINE-2]]:45: warning: {{.*}} in lambda expression + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: redundant void argument list [modernize-redundant-void-arg] + // CHECK-MESSAGES: [[@LINE-2]]:45: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: auto void_returner = []() -> void (*)() { return f1; }; } #define M(x) x M(void inmacro(void) {}) -// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: M(void inmacro() {}) #define F(A, B) \ @@ -436,6 +436,11 @@ M(void inmacro(void) {}) F_##A##_##B(void); \ }; \ F_##A##_##B::F_##A##_##B(void) +// CHECK-MESSAGES: :[[@LINE-3]]:17: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-FIXES: F_##A##_##B(); {{\\}} + +// CHECK-MESSAGES: :[[@LINE-4]]:28: warning: redundant void argument list [modernize-redundant-void-arg] +// CHECK-FIXES: F_##A##_##B::F_##A##_##B() F(Foo, Bar) { @@ -443,7 +448,7 @@ F(Foo, Bar) { struct DefinitionWithNoBody { DefinitionWithNoBody(void) = delete; - // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in function definition + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: DefinitionWithNoBody() = delete; }; @@ -451,55 +456,55 @@ struct DefinitionWithNoBody { #define BODY {} #define LAMBDA1 [](void){} -// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: #define LAMBDA1 [](){} #define LAMBDA2 [](void)BODY -// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: #define LAMBDA2 []()BODY #define LAMBDA3(captures, args, body) captures args body #define WRAP(...) __VA_ARGS__ #define LAMBDA4 (void)LAMBDA3([],(void),BODY) -// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: #define LAMBDA4 (void)LAMBDA3([],(),BODY) #define LAMBDA5 []() -> void (*)(void) {return BODY;} -// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: #define LAMBDA5 []() -> void (*)() {return BODY;} void lambda_expression_with_macro_test(){ (void)LAMBDA1; (void)LAMBDA2; (void)LAMBDA3([], (void), BODY); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: (void)LAMBDA3([], (), BODY); LAMBDA4; LAMBDA5; WRAP((void)WRAP(WRAP(LAMBDA3(WRAP([]), WRAP((void)), WRAP(BODY))))); - // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: WRAP((void)WRAP(WRAP(LAMBDA3(WRAP([]), WRAP(()), WRAP(BODY))))); (void)WRAP([](void) {}); - // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: (void)WRAP([]() {}); [](void) BODY; - // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: []() BODY; } namespace qqq { void foo() BODY void bar(void) BODY; -// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list in function definition +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void bar() BODY; } struct S_1 { void g_1(void) const { - // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void g_1() const { int a; (void)a; @@ -514,7 +519,7 @@ struct S_1 { template struct S_2 { void g_1(void) const { - // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void g_1() const { int a; (void)a; @@ -530,7 +535,7 @@ template struct S_3 { template void g_1(void) const { - // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void g_1() const { int a; (void)a; @@ -544,7 +549,7 @@ struct S_3 { template void g_3(void) { - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: void g_3() { int a; (void)a; @@ -560,33 +565,33 @@ void f_testTemplate() { #define return_t(T) T extern return_t(void) func(void); -// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: redundant void argument list in function declaration +// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: extern return_t(void) func(); return_t(void) func(void) { - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant void argument list in function definition + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: return_t(void) func() { int a; (void)a; } extern return_t(void) func(return_t(void) (*fp)(void)); -// CHECK-MESSAGES: :[[@LINE-1]]:49: warning: redundant void argument list in variable declaration +// CHECK-MESSAGES: :[[@LINE-1]]:49: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: extern return_t(void) func(return_t(void) (*fp)()); return_t(void) func(return_t(void) (*fp)(void)) { - // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: redundant void argument list in variable declaration + // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: return_t(void) func(return_t(void) (*fp)()) { int a; (void)a; } extern return_t(return_t(void)) func2(return_t(return_t(void)) (*fp)(void)); -// CHECK-MESSAGES: :[[@LINE-1]]:70: warning: redundant void argument list in variable declaration +// CHECK-MESSAGES: :[[@LINE-1]]:70: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: extern return_t(return_t(void)) func2(return_t(return_t(void)) (*fp)()); return_t(return_t(void)) func2(return_t(return_t(void)) (*fp)(void)) { - // CHECK-MESSAGES: :[[@LINE-1]]:63: warning: redundant void argument list in variable declaration + // CHECK-MESSAGES: :[[@LINE-1]]:63: warning: redundant void argument list [modernize-redundant-void-arg] // CHECK-FIXES: return_t(return_t(void)) func2(return_t(return_t(void)) (*fp)()) { int a; (void)a; diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h b/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h index d6f6e65ace79d..32b3d3374bc59 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h @@ -7,9 +7,11 @@ struct ABC { int f1(int n, ABC v1, ABC v2); // line 9 +// CHECK-FIXES: int f1(int n, const ABC& v1, const ABC& v2); // line 9 int f1(int n, ABC v1); // line 11 void f2( int n, ABC v2); // line 15 +// CHECK-FIXES: void f2( int n, const ABC& v2); // line 15 diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation-vectorlike-classes.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation-vectorlike-classes.cpp new file mode 100644 index 0000000000000..41d2d3733e07a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation-vectorlike-classes.cpp @@ -0,0 +1,59 @@ +// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- \ +// RUN: -config='{CheckOptions: \ +// RUN: {performance-inefficient-vector-operation.VectorLikeClasses: \ +// RUN: "VectorLikeInheritedPushBack;VectorLikeDirectPushBack;VectorLikeInheritedEmplaceBack"}}' + +class VectorLikePushBackBase { +public: + void push_back(int) {} +}; + +class VectorLikeInheritedPushBack : public VectorLikePushBackBase { +public: + void reserve(int); +}; + +class VectorLikeDirectPushBack { +public: + void push_back(int) {} + void reserve(int) {} +}; + +class VectorLikeEmplaceBackBase { +public: + void emplace_back(int) {} +}; + +class VectorLikeInheritedEmplaceBack : public VectorLikeEmplaceBackBase { +public: + void reserve(int); +}; + +void testVectorLikeClasses() { + { + VectorLikeInheritedPushBack inheritedPushBackVector; + // CHECK-FIXES: inheritedPushBackVector.reserve(100); + for (int I = 0; I < 100; ++I) { + inheritedPushBackVector.push_back(I); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop + } + } + + { + VectorLikeDirectPushBack directPushBackVector; + // CHECK-FIXES: directPushBackVector.reserve(100); + for (int I = 0; I < 100; ++I) { + directPushBackVector.push_back(I); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop + } + } + + { + VectorLikeInheritedEmplaceBack inheritedEmplaceBackVector; + // CHECK-FIXES: inheritedEmplaceBackVector.reserve(100); + for (int I = 0; I < 100; ++I) { + inheritedEmplaceBackVector.emplace_back(I); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'emplace_back' is called inside a loop; consider pre-allocating the container capacity before the loop + } + } +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp index a7fc1eace67c5..4e53a385af629 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp @@ -1,8 +1,5 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: cp %S/Inputs/unnecessary-value-param/header.h %t/header.h -// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t/temp -- -- -I %t -// RUN: diff %t/header.h %S/Inputs/unnecessary-value-param/header-fixed.h +// RUN: %check_clang_tidy -check-header %S/Inputs/unnecessary-value-param/header.h \ +// RUN: %s performance-unnecessary-value-param %t #include "header.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp index 7844275c3a960..aacb74306edc7 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp @@ -985,3 +985,18 @@ class ReportInContainerNonEmptyMethodCompare { } } }; + +namespace GH181552 { +struct DestructorContainer { + unsigned long size() const; + bool empty() const; + ~DestructorContainer(); +}; + +struct DestructorUser { + DestructorContainer Indexes; + ~DestructorUser() { + Indexes.~DestructorContainer(); + } +}; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp index c452f69fad07d..13c54dac16e05 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp @@ -1,5 +1,5 @@ -// RUN: %check_clang_tidy %s readability-duplicate-include %t -- \ -// RUN: -header-filter='' \ +// RUN: %check_clang_tidy -check-header %S/Inputs/duplicate-include/duplicate-include.h \ +// RUN: %s readability-duplicate-include %t -- \ // RUN: -- -isystem %S/Inputs/duplicate-include/system -I %S/Inputs/duplicate-include int a; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.cpp index 0079fa9e96434..c07312f989cea 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.cpp @@ -360,6 +360,15 @@ class B final { }; void gh176623() { + // CHECK-MESSAGES-NOT: warning: auto const V1 = [](char* p) { auto X = A(p); }; + // CHECK-MESSAGES-NOT: warning: auto const V2 = [](char* p) { auto Y = B(p); }; } + +void testGenericLambdaIssue177354() { + // CHECK-MESSAGES-NOT: warning: pointer parameter 'p' can be pointer to const + auto genericLambda = [](int *p) { + T x(*p); + }; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-c23.c b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-c23.c new file mode 100644 index 0000000000000..5a3254b0de478 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-c23.c @@ -0,0 +1,792 @@ +// RUN: %check_clang_tidy -std=c23-or-later %s readability-simplify-boolean-expr %t + +bool a1 = false; + +void if_with_bool_literal_condition() { + int i = 0; + if (false) { + i = 1; + } else { + i = 2; + } + i = 3; + // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: redundant boolean literal in if statement condition + // CHECK-FIXES: {{^ int i = 0;$}} + // CHECK-FIXES-NEXT: {{^ {$}} + // CHECK-FIXES-NEXT: {{^ i = 2;$}} + // CHECK-FIXES-NEXT: {{^ }$}} + // CHECK-FIXES-NEXT: {{^ i = 3;$}} + + i = 4; + if (true) { + i = 5; + } else { + i = 6; + } + i = 7; + // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: redundant boolean literal in if statement condition + // CHECK-FIXES: {{^ i = 4;$}} + // CHECK-FIXES-NEXT: {{^ {$}} + // CHECK-FIXES-NEXT: {{^ i = 5;$}} + // CHECK-FIXES-NEXT: {{^ }$}} + // CHECK-FIXES-NEXT: {{^ i = 7;$}} + + i = 8; + if (false) { + i = 9; + } + i = 11; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: redundant boolean literal in if statement condition + // CHECK-FIXES: {{^ i = 8;$}} + // CHECK-FIXES-NEXT: {{^ $}} + // CHECK-FIXES-NEXT: {{^ i = 11;$}} +} + +void if_with_negated_bool_condition() { + int i = 10; + if (!true) { + i = 11; + } else { + i = 12; + } + i = 13; + // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: redundant boolean literal in if statement condition + // CHECK-FIXES: {{^ int i = 10;$}} + // CHECK-FIXES-NEXT: {{^ {$}} + // CHECK-FIXES-NEXT: {{^ i = 12;$}} + // CHECK-FIXES-NEXT: {{^ }$}} + // CHECK-FIXES-NEXT: {{^ i = 13;$}} + + i = 14; + if (!false) { + i = 15; + } else { + i = 16; + } + i = 17; + // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: redundant boolean literal in if statement condition + // CHECK-FIXES: {{^ i = 14;$}} + // CHECK-FIXES-NEXT: {{^ {$}} + // CHECK-FIXES-NEXT: {{^ i = 15;$}} + // CHECK-FIXES-NEXT: {{^ }$}} + // CHECK-FIXES-NEXT: {{^ i = 17;$}} + + i = 18; + if (!true) { + i = 19; + } + i = 20; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: redundant boolean literal in if statement condition + // CHECK-FIXES: {{^ i = 18;$}} + // CHECK-FIXES-NEXT: {{^ $}} + // CHECK-FIXES-NEXT: {{^ i = 20;$}} +} + +void operator_equals() { + int i = 0; + bool b1 = (i > 2); + if (b1 == true) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(b1\) {$}} + i = 5; + } else { + i = 6; + } + bool b2 = (i > 4); + if (b2 == false) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(!b2\) {$}} + i = 7; + } else { + i = 9; + } + bool b3 = (i > 6); + if (true == b3) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(b3\) {$}} + i = 10; + } else { + i = 11; + } + bool b4 = (i > 8); + if (false == b4) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(!b4\) {$}} + i = 12; + } else { + i = 13; + } +} + +void operator_or() { + int i = 0; + bool b5 = (i > 10); + if (b5 || false) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(b5\) {$}} + i = 14; + } else { + i = 15; + } + bool b6 = (i > 10); + if (b6 || true) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(true\) {$}} + i = 16; + } else { + i = 17; + } + bool b7 = (i > 10); + if (false || b7) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(b7\) {$}} + i = 18; + } else { + i = 19; + } + bool b8 = (i > 10); + if (true || b8) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(true\) {$}} + i = 20; + } else { + i = 21; + } +} + +void operator_and() { + int i = 0; + bool b9 = (i > 20); + if (b9 && false) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(false\) {$}} + i = 22; + } else { + i = 23; + } + bool ba = (i > 20); + if (ba && true) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(ba\) {$}} + i = 24; + } else { + i = 25; + } + bool bb = (i > 20); + if (false && bb) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(false\) {$}} + i = 26; + } else { + i = 27; + } + bool bc = (i > 20); + if (true && bc) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(bc\) {$}} + i = 28; + } else { + i = 29; + } +} + +void ternary_operator() { + int i = 0; + bool bd = (i > 20) ? true : false; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: redundant boolean literal in ternary expression result + // CHECK-FIXES: {{^ bool bd = i > 20;$}} + + bool be = (i > 20) ? false : true; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: redundant boolean literal in ternary expression result + // CHECK-FIXES: {{^ bool be = i <= 20;$}} + + bool bf = ((i > 20)) ? false : true; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant boolean literal in ternary expression result + // CHECK-FIXES: {{^ bool bf = i <= 20;$}} +} + +void operator_not_equal() { + int i = 0; + bool bf = (i > 20); + if (false != bf) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(bf\) {$}} + i = 30; + } else { + i = 31; + } + bool bg = (i > 20); + if (true != bg) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(!bg\) {$}} + i = 32; + } else { + i = 33; + } + bool bh = (i > 20); + if (bh != false) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(bh\) {$}} + i = 34; + } else { + i = 35; + } + bool bi = (i > 20); + if (bi != true) { + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(!bi\) {$}} + i = 36; + } else { + i = 37; + } +} + +void nested_booleans() { + if (false || (true || false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(false \|\| \(true\)\) {$}} + } + if (true && (true || false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(true && \(true\)\) {$}} + } + if (false || (true && false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(false \|\| \(false\)\) {$}} + } + if (true && (true && false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant boolean literal supplied to boolean operator + // CHECK-FIXES: {{^ if \(true && \(false\)\) {$}} + } +} + +#define HAS_XYZ_FEATURE true +#define M1(what) M2(true, what) +#define M2(condition, what) if (condition) what + +void macros() { + int i = 0; + bool b = (i == 1); + i = 2; + if (b && HAS_XYZ_FEATURE) { + // leave this alone; if you want it simplified, then you should + // inline the macro first. + i = 3; + } + if (HAS_XYZ_FEATURE) { + i = 5; + } + i = 4; + M1(i = 7); +} + +#undef HAS_XYZ_FEATURE + +bool conditional_return_statements(int i) { + if (i == 0) return true; else return false; +} +// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^ return i == 0;$}} + +bool conditional_return_statements_no_fix_1(int i) { + if (i == 0) return true; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant boolean literal in conditional return statement + // CHECK-MESSAGES: :[[@LINE-2]]:7: note: conditions that can be simplified + // comment + return false; + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: return statement that can be simplified +} + +bool conditional_return_statements_no_fix_2(int i) { + if (i == 0) return true; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant boolean literal in conditional return statement + // CHECK-MESSAGES: :[[@LINE-2]]:7: note: conditions that can be simplified + // comment + else return false; +} + +bool conditional_return_statements_then_expr(int i, int j) { + if (i == j) return (i == 0); else return false; +} + +bool conditional_return_statements_else_expr(int i, int j) { + if (i == j) return true; else return (i == 0); +} + +bool negated_conditional_return_statements(int i) { + if (i == 0) return false; else return true; +} +// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^ return i != 0;$}} + +bool negative_condition_conditional_return_statement(int i) { + if (!(i == 0)) return false; else return true; +} +// CHECK-MESSAGES: :[[@LINE-2]]:25: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^ return i == 0;$}} + +bool conditional_compound_return_statements(int i) { + if (i == 1) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^ return i == 1;$}} + +bool negated_conditional_compound_return_statements(int i) { + if (i == 1) { + return false; + } else { + return true; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^ return i != 1;$}} + +bool conditional_return_statements_side_effects_then(int i) { + if (i == 2) { + macros(); + return true; + } else + return false; +} + +bool negated_conditional_return_statements_side_effects_then(int i) { + if (i == 2) { + macros(); + return false; + } else + return true; +} + +bool conditional_return_statements_side_effects_else(int i) { + if (i == 2) + return true; + else { + macros(); + return false; + } +} + +bool negated_conditional_return_statements_side_effects_else(int i) { + if (i == 2) + return false; + else { + macros(); + return true; + } +} + +void simple_conditional_assignment_statements(int i) { + bool b; + if (i > 10) + b = true; + else + b = false; + bool bb = false; + // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: redundant boolean literal in conditional assignment + // CHECK-FIXES: bool b; + // CHECK-FIXES: {{^ b = i > 10;$}} + // CHECK-FIXES: bool bb = false; + + bool c; + if (i > 20) + c = false; + else + c = true; + bool c2 = false; + // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: redundant boolean literal in conditional assignment + // CHECK-FIXES: bool c; + // CHECK-FIXES: {{^ c = i <= 20;$}} + // CHECK-FIXES: bool c2 = false; + + // Unchanged: different variables. + bool b2; + if (i > 12) + b = true; + else + b2 = false; + + // Unchanged: no else statement. + bool b3; + if (i > 15) + b3 = true; + + // Unchanged: not boolean assignment. + int j; + if (i > 17) + j = 10; + else + j = 20; + + // Unchanged: different variables assigned. + int k = 0; + bool b4 = false; + if (i > 10) + b4 = true; + else + k = 10; +} + +void complex_conditional_assignment_statements(int i) { + bool d; + if (i > 30) { + d = true; + } else { + d = false; + } + d = false; + // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: redundant boolean literal in conditional assignment + // CHECK-FIXES: bool d; + // CHECK-FIXES: {{^ d = i > 30;$}} + // CHECK-FIXES: d = false; + + bool e; + if (i > 40) { + e = false; + } else { + e = true; + } + e = false; + // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: redundant boolean literal in conditional assignment + // CHECK-FIXES: bool e; + // CHECK-FIXES: {{^ e = i <= 40;$}} + // CHECK-FIXES: e = false; + + // Unchanged: no else statement. + bool b3; + if (i > 15) { + b3 = true; + } + + // Unchanged: not a boolean assignment. + int j; + if (i > 17) { + j = 10; + } else { + j = 20; + } + + // Unchanged: multiple statements. + bool f; + if (j > 10) { + j = 10; + f = true; + } else { + j = 20; + f = false; + } + + // Unchanged: multiple statements. + bool g; + if (j > 10) + g = true; + else { + j = 20; + g = false; + } + + // Unchanged: multiple statements. + bool h; + if (j > 10) { + j = 10; + h = true; + } else + h = false; +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +bool chained_conditional_compound_return(int i) { + if (i < 0) { + return true; + } else if (i < 10) { + return false; + } else if (i > 20) { + return true; + } else { + return false; + } +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +bool chained_conditional_return(int i) { + if (i < 0) + return true; + else if (i < 10) + return false; + else if (i > 20) + return true; + else + return false; +} + +// Unchanged: chained assignments, but ChainedConditionalAssignment not set. +void chained_conditional_compound_assignment(int i) { + bool b; + if (i < 0) { + b = true; + } else if (i < 10) { + b = false; + } else if (i > 20) { + b = true; + } else { + b = false; + } +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +void chained_conditional_assignment(int i) { + bool b; + if (i < 0) + b = true; + else if (i < 10) + b = false; + else if (i > 20) + b = true; + else + b = false; +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +bool chained_simple_if_return_negated(int i) { + if (i < 5) + return false; + if (i > 10) + return false; + return true; +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +bool complex_chained_if_return_return(int i) { + if (i < 5) { + return true; + } + if (i > 10) { + return true; + } + return false; +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +bool complex_chained_if_return_return_negated(int i) { + if (i < 5) { + return false; + } + if (i > 10) { + return false; + } + return true; +} + +// Unchanged: chained return statements, but ChainedConditionalReturn not set. +bool chained_simple_if_return(int i) { + if (i < 5) + return true; + if (i > 10) + return true; + return false; +} + +bool simple_if_return_return(int i) { + if (i > 10) + return true; + return false; +} +// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}}bool simple_if_return_return(int i) {{{$}} +// CHECK-FIXES: {{^ return i > 10;$}} +// CHECK-FIXES: {{^}$}} + +bool simple_if_return_return_negated(int i) { + if (i > 10) + return false; + return true; +} +// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}}bool simple_if_return_return_negated(int i) {{{$}} +// CHECK-FIXES: {{^ return i <= 10;$}} +// CHECK-FIXES: {{^}$}} + +bool complex_if_return_return(int i) { + if (i > 10) { + return true; + } + return false; +} +// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}}bool complex_if_return_return(int i) {{{$}} +// CHECK-FIXES: {{^ return i > 10;$}} +// CHECK-FIXES: {{^}$}} + +bool complex_if_return_return_negated(int i) { + if (i > 10) { + return false; + } + return true; +} +// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}}bool complex_if_return_return_negated(int i) {{{$}} +// CHECK-FIXES: {{^ return i <= 10;$}} +// CHECK-FIXES: {{^}$}} + +bool if_implicit_bool_expr(int i) { + if (i & 1) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return i & 1;$}} + +bool negated_if_implicit_bool_expr(int i) { + if (i - 1) { + return false; + } else { + return true; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return !\(i - 1\);$}} + +bool implicit_int(int i) { + if (i) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}} return i;{{$}} + +bool explicit_bool(bool b) { + if (b) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}} return b;{{$}} + +bool negated_explicit_bool(bool b) { + if (!b) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return !b;$}} + +bool bitwise_complement_conversion(int i) { + if (~i) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return ~i;$}} + +bool logical_or(bool a, bool b) { + if (a || b) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return a \|\| b;$}} + +bool logical_and(bool a, bool b) { + if (a && b) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return a && b;$}} + +void ternary_integer_condition(int i) { + bool b = i ? true : false; +} +// CHECK-MESSAGES: :[[@LINE-2]]:16: warning: redundant boolean literal in ternary expression result +// CHECK-FIXES: {{^ bool b = i;$}} + +bool non_null_pointer_condition(int *p1) { + if (p1) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return p1;$}} + +bool null_pointer_condition(int *p2) { + if (!p2) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return !p2;$}} + +bool negated_non_null_pointer_condition(int *p3) { + if (p3) { + return false; + } else { + return true; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return !p3;$}} + +bool negated_null_pointer_condition(int *p4) { + if (!p4) { + return false; + } else { + return true; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return p4;$}} + +bool comments_in_the_middle(bool b) { + if (b) { + return true; + } else { + // something wicked this way comes + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-6]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}} if (b) { +// CHECK-FIXES: // something wicked this way comes{{$}} + +bool preprocessor_in_the_middle(bool b) { + if (b) { + return true; + } else { +#define SOMETHING_WICKED false + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-6]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^}} if (b) { +// CHECK-FIXES: {{^}}#define SOMETHING_WICKED false + +bool integer_not_zero(int i) { + if (i) { + return false; + } else { + return true; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: redundant boolean literal in conditional return +// CHECK-FIXES: {{^ return !i;$}} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp index fc1976b0e6b22..07c741c4c7afc 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp @@ -132,3 +132,8 @@ void macros() { static_assert(is_same::value, ""); static_assert(m0 == 1.0F, ""); } + +long double operator""_f(long double); +void user_defined_literals() { + 1.0_f; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp index e4dd968f49c12..fd5e4a529d99c 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp @@ -185,7 +185,7 @@ void macros() { // Check that user-defined literals do not cause any diags. -unsigned long long int operator"" _ull(unsigned long long int); +unsigned long long int operator""_ull(unsigned long long int); void user_defined_literals() { 1_ull; } diff --git a/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt index 64bf47e61736c..167f5d3def06b 100644 --- a/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt +++ b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt @@ -24,6 +24,7 @@ add_extra_unittest(ClangTidyTests DeclRefExprUtilsTest.cpp IncludeCleanerTest.cpp IncludeInserterTest.cpp + LexerUtilsTest.cpp GlobListTest.cpp GoogleModuleTest.cpp LLVMModuleTest.cpp diff --git a/clang-tools-extra/unittests/clang-tidy/LexerUtilsTest.cpp b/clang-tools-extra/unittests/clang-tidy/LexerUtilsTest.cpp new file mode 100644 index 0000000000000..438a78b4694ee --- /dev/null +++ b/clang-tools-extra/unittests/clang-tidy/LexerUtilsTest.cpp @@ -0,0 +1,144 @@ +//===--- LexerUtilsTest.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 "../clang-tidy/utils/LexerUtils.h" + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Serialization/PCHContainerOperations.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Error.h" +#include "llvm/Testing/Annotations/Annotations.h" +#include "gtest/gtest.h" + +namespace clang::tidy::test { + +using clang::tooling::FileContentMappings; + +static std::unique_ptr +buildAST(StringRef Code, const FileContentMappings &Mappings = {}) { + std::vector Args = {"-std=c++20"}; + return clang::tooling::buildASTFromCodeWithArgs( + Code, Args, "input.cc", "clang-tool", + std::make_shared(), + clang::tooling::getClangStripDependencyFileAdjuster(), Mappings); +} + +static CharSourceRange rangeFromAnnotations(const llvm::Annotations &A, + const SourceManager &SM, FileID FID, + llvm::StringRef Name = "") { + const auto R = A.range(Name); + const SourceLocation Begin = + SM.getLocForStartOfFile(FID).getLocWithOffset(R.Begin); + const SourceLocation End = + SM.getLocForStartOfFile(FID).getLocWithOffset(R.End); + return CharSourceRange::getCharRange(Begin, End); +} + +namespace { + +TEST(LexerUtilsTest, GetTrailingCommentsInRangeAdjacentComments) { + llvm::Annotations Code(R"cpp( +void f() { + $range[[/*first*/ /*second*/]] + int x = 0; +} +)cpp"); + std::unique_ptr AST = buildAST(Code.code()); + ASSERT_TRUE(AST); + const ASTContext &Context = AST->getASTContext(); + const SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + + const CharSourceRange Range = + rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range"); + const std::vector Comments = + utils::lexer::getTrailingCommentsInRange(Range, SM, LangOpts); + ASSERT_EQ(2u, Comments.size()); + EXPECT_EQ("/*first*/", Comments[0].Text); + EXPECT_EQ("/*second*/", Comments[1].Text); + const StringRef CodeText = Code.code(); + const size_t FirstOffset = CodeText.find("/*first*/"); + ASSERT_NE(StringRef::npos, FirstOffset); + const size_t SecondOffset = CodeText.find("/*second*/"); + ASSERT_NE(StringRef::npos, SecondOffset); + EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc)); + EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc)); +} + +TEST(LexerUtilsTest, GetTrailingCommentsInRangeClearsOnToken) { + llvm::Annotations Code(R"cpp( +void f() { + int x = ($range[[/*first*/ 0, /*second*/]] 1); +} +)cpp"); + std::unique_ptr AST = buildAST(Code.code()); + ASSERT_TRUE(AST); + const ASTContext &Context = AST->getASTContext(); + const SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + + const CharSourceRange Range = + rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range"); + const std::vector Comments = + utils::lexer::getTrailingCommentsInRange(Range, SM, LangOpts); + ASSERT_EQ(1u, Comments.size()); + EXPECT_EQ("/*second*/", Comments.front().Text); + const StringRef CodeText = Code.code(); + const size_t SecondOffset = CodeText.find("/*second*/"); + ASSERT_NE(StringRef::npos, SecondOffset); + EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments.front().Loc)); +} + +TEST(LexerUtilsTest, GetTrailingCommentsInRangeLineComments) { + llvm::Annotations Code(R"cpp( +void f() { + $range[[// first + // second + ]] + int x = 0; +} +)cpp"); + std::unique_ptr AST = buildAST(Code.code()); + ASSERT_TRUE(AST); + const ASTContext &Context = AST->getASTContext(); + const SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + + const CharSourceRange Range = + rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range"); + const std::vector Comments = + utils::lexer::getTrailingCommentsInRange(Range, SM, LangOpts); + ASSERT_EQ(2u, Comments.size()); + EXPECT_EQ("// first", Comments[0].Text); + EXPECT_EQ("// second", Comments[1].Text); + const StringRef CodeText = Code.code(); + const size_t FirstOffset = CodeText.find("// first"); + ASSERT_NE(StringRef::npos, FirstOffset); + const size_t SecondOffset = CodeText.find("// second"); + ASSERT_NE(StringRef::npos, SecondOffset); + EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc)); + EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc)); +} + +TEST(LexerUtilsTest, GetTrailingCommentsInRangeInvalidRange) { + std::unique_ptr AST = buildAST("int value = 0;"); + ASSERT_TRUE(AST); + const ASTContext &Context = AST->getASTContext(); + const SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + + const std::vector Comments = + utils::lexer::getTrailingCommentsInRange(CharSourceRange(), SM, LangOpts); + EXPECT_TRUE(Comments.empty()); +} + +} // namespace + +} // namespace clang::tidy::test diff --git a/clang/Maintainers.md b/clang/Maintainers.md index 08c5046fd32c5..a05b510ebaede 100644 --- a/clang/Maintainers.md +++ b/clang/Maintainers.md @@ -59,9 +59,6 @@ henrich.lau@gmail.com (email), henrich.lauko (Discord), [xlauko](https://github. Yitzhak Mandelbaum \ yitzhakm@google.com (email), ymandel (Phabricator), [ymand](https://github.com/ymand) (GitHub) -Stanislav Gatev \ -sgatev@google.com (email), sgatev (Phabricator), [sgatev](https://github.com/sgatev) (GitHub) - ### Sema Sirraide \ @@ -354,4 +351,5 @@ Manuel Klimek (klimek@google.com (email), klimek (Phabricator), [r4nt](https://g Dmitri Gribenko (gribozavr@gmail.com (email), gribozavr (Phabricator), [gribozavr](https://github.com/gribozavr) (GitHub)) \-- Analysis & CFG \ Tom Honermann (tom@honermann.net (email), tahonermann (Phabricator), [tahonermann](https://github.com/tahonermann) (GitHub)) \-- Text Encodings \ John McCall (rjmccall@apple.com (email), rjmccall (Phabricator), [rjmccall](https://github.com/rjmccall) (GitHub)) \-- Clang LLVM IR generation, Objective-C/C++ conformance, Itanium ABI \ -John Ericson (git@johnericson.me (email), Ericson2314 (Phabricator), [Ericson2314](https://github.com/Ericson2314) (GitHub)) \-- CMake Integration +John Ericson (git@johnericson.me (email), Ericson2314 (Phabricator), [Ericson2314](https://github.com/Ericson2314) (GitHub)) \-- CMake Integration \ +Stanislav Gatev (sgatev@google.com (email), sgatev (Phabricator), [sgatev](https://github.com/sgatev) (GitHub)) -- Analysis & CFG diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index f4d7f4fe68966..1896a0a9c1c34 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -93,6 +93,7 @@ Generic, Iterator, Literal, + NoReturn, Optional, Sequence, Type as TType, @@ -212,12 +213,12 @@ def __init__(self, enumeration, message): if enumeration < 1 or enumeration > 3: raise Exception( "Encountered undefined TranslationUnit save error " - "constant: %d. Please file a bug to have this " - "value supported." % enumeration + "constant: {}. Please file a bug to have this " + "value supported.".format(enumeration) ) self.save_error = enumeration - Exception.__init__(self, "Error %d: %s" % (enumeration, message)) + Exception.__init__(self, "Error {}: {}".format(enumeration, message)) ### Structures and Utility Classes ### @@ -246,7 +247,9 @@ def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: property_name = self.wrapped.__name__ class_name = instance_type.__name__ raise TypeError( - f"'{property_name}' is not a static attribute of '{class_name}'" + "'{}' is not a static attribute of '{}'".format( + property_name, class_name + ) ) value = self.wrapped(instance) @@ -357,10 +360,8 @@ def __repr__(self) -> str: filename = self.file.name else: filename = None - return "" % ( - filename, - self.line, - self.column, + return "".format( + repr(filename), repr(self.line), repr(self.column) ) @@ -545,10 +546,8 @@ def format(self, options=None): return _CXString.from_result(conf.lib.clang_formatDiagnostic(self, options)) def __repr__(self): - return "" % ( - self.severity, - self.location, - self.spelling, + return "".format( + repr(self.severity), repr(self.location), repr(self.spelling) ) def __str__(self): @@ -570,7 +569,7 @@ def __init__(self, range, value): self.value = value def __repr__(self): - return "" % (self.range, self.value) + return "".format(repr(self.range), repr(self.value)) class TokenGroup: @@ -644,10 +643,7 @@ def from_id(cls, id): return cls(id) def __repr__(self): - return "%s.%s" % ( - self.__class__.__name__, - self.name, - ) + return "{}.{}".format(self.__class__.__name__, self.name) class TokenKind(BaseEnumeration): @@ -2729,8 +2725,9 @@ def __getitem__(self, key: int) -> Type: if key >= len(self): raise IndexError( - "Index greater than container length: " - "%d > %d" % (key, len(self)) + "Index greater than container length: {} > {}".format( + key, len(self) + ) ) result = Type.from_result( @@ -3091,14 +3088,14 @@ class SpellingCacheAlias: "will be removed in a future release." ) - def __getattr__(self, _): + def __getattr__(self, _: Any) -> NoReturn: raise AttributeError(self.deprecation_message) - def __getitem__(self, value: int): + def __getitem__(self, value: int) -> str: warnings.warn(self.deprecation_message, DeprecationWarning) return CompletionChunk.SPELLING_CACHE[CompletionChunkKind.from_id(value)] - def __contains__(self, value: int): + def __contains__(self, value: int) -> bool: warnings.warn(self.deprecation_message, DeprecationWarning) return CompletionChunkKind.from_id(value) in CompletionChunk.SPELLING_CACHE @@ -3134,7 +3131,7 @@ def __init__(self, completionString: CObjP, key: int): self.key = key def __repr__(self) -> str: - return "{'" + self.spelling + "', " + str(self.kind) + "}" + return "{{'{}', {}}}".format(self.spelling, self.kind) @CachedProperty def spelling(self) -> str: @@ -3294,14 +3291,11 @@ def briefComment(self) -> str: return _CXString.from_result(conf.lib.clang_getCompletionBriefComment(self.obj)) def __repr__(self) -> str: - return ( - " | ".join([str(a) for a in self]) - + " || Priority: " - + str(self.priority) - + " || Availability: " - + str(self.availability) - + " || Brief comment: " - + str(self.briefComment) + return "{chunks} || Priority: {priority} || Availability: {availability} || Brief comment: {comment}".format( + chunks=" | ".join(str(a) for a in self), + priority=self.priority, + availability=self.availability, + comment=self.briefComment, ) @@ -3722,7 +3716,7 @@ def reparse(self, unsaved_files=None, options=0): ) ) if result != 0: - msg = "Error reparsing translation unit. Error code: " + str(result) + msg = "Error reparsing translation unit. Error code: {}".format(result) raise TranslationUnitLoadError(msg) def save(self, filename): @@ -3840,7 +3834,7 @@ def __str__(self): return self.name def __repr__(self): - return "" % (self.name) + return "".format(self.name) def __eq__(self, other) -> bool: return isinstance(other, File) and bool( @@ -3900,13 +3894,12 @@ def __init__(self, enumeration, message): if enumeration > 1: raise Exception( - "Encountered undefined CompilationDatabase error " - "constant: %d. Please file a bug to have this " - "value supported." % enumeration + "Encountered undefined CompilationDatabase error constant: {}." + "Please file a bug to have this value supported.".format(enumeration) ) self.cdb_error = enumeration - Exception.__init__(self, "Error %d: %s" % (enumeration, message)) + Exception.__init__(self, "Error {}: {}".format(enumeration, message)) class CompileCommand: diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 378a0c591d747..437f559fc0395 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3686,6 +3686,14 @@ the configuration (without a prefix: ``Auto``). initializer1(), initializer2() + * ``BCIS_AfterComma`` (in configuration: ``AfterComma``) + Break constructor initializers only after the commas. + + .. code-block:: c++ + + Constructor() : initializer1(), + initializer2() + .. _BreakFunctionDefinitionParameters: diff --git a/clang/docs/ClangIRCleanupAndEHDesign.md b/clang/docs/ClangIRCleanupAndEHDesign.md index 324cf51aa526b..84c9dbc4e897d 100644 --- a/clang/docs/ClangIRCleanupAndEHDesign.md +++ b/clang/docs/ClangIRCleanupAndEHDesign.md @@ -350,11 +350,22 @@ the destructor. Therefore, this cleanup handler is marked as eh_only. Try-catch blocks will be represented, as they are in the ClangIR incubator project, using a `cir.try` operation. +The `cir.catch_param` operation is used to represent the capturing of the +exception object in an ABI-independent way. When the catch handler includes +a source variable representing the exception object, the result of the +`cir.catch_param` operation will be stored to an alloca object for the +source variable. If the handler is a catch-all, the `cir.catch_param` operation +will return a pointer to void, but this cannot be captured by a source variable. + +The first operation in a catch handler region must be a `cir.catch_param` +operation. + ``` cir.try { cir.call exception @function() : () -> () cir.yield } catch [type #cir.global_view<@_ZTIPf> : !cir.ptr] { + %1 = cir.catch_param : !cir.ptr ... cir.yield } unwind { @@ -385,11 +396,15 @@ void someFunc() { ``` cir.func @someFunc(){ + %0 = cir.alloca !cir.ptr, !cir.ptr>, ["e"] cir.scope { cir.try { cir.call exception @_Z1fv() : () -> () cir.yield } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr] { + %1 = cir.catch_param : !cir.ptr> + %2 = cir.load align(8) %1 : !cir.ptr>, !cir.ptr + cir.store align(8) %2, %0 : !cir.ptr, !cir.ptr> cir.yield } unwind { cir.resume @@ -429,13 +444,18 @@ void someFunc() { ``` cir.func @someFunc(){ + %0 = cir.alloca !cir.ptr, !cir.ptr>, ["e"] cir.scope { cir.try { cir.call exception @_Z1fv() : () -> () cir.yield } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr] { + %1 = cir.catch_param : !cir.ptr> + %2 = cir.load align(8) %1 : !cir.ptr>, !cir.ptr + cir.store align(8) %2, %0 : !cir.ptr, !cir.ptr> cir.yield } catch all { + %3 = cir.catch_param : !cir.ptr cir.yield } } @@ -484,6 +504,7 @@ cir.func @someFunc(){ cir.yield } } catch all { + %1 = cir.catch_param : !cir.ptr cir.yield } } @@ -516,12 +537,17 @@ void someFunc() { ``` cir.func @someFunc(){ %0 = cir.alloca !rec_SomeClass, !cir.ptr, ["c", init] + %1 = cir.alloca !cir.ptr, !cir.ptr>, ["e"] cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr) -> () cir.cleanup.scope { cir.scope { cir.try { cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr) -> () + cir.yield } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr] { + %2 = cir.catch_param : !cir.ptr> + %3 = cir.load align(8) %2 : !cir.ptr>, !cir.ptr + cir.store align(8) %3, %1 : !cir.ptr, !cir.ptr> cir.yield } unwind { cir.resume @@ -731,7 +757,11 @@ of the catch handling block must be a `cir.begin_catch` operation. The `cir.begin_catch` operation returns two values: a new token that uniquely identify this catch handler, and a pointer to the exception object. All paths through the catch handler must converge on a single -`cir.end_catch` operation, which marks the end of the handler. +`cir.end_catch` operation, which marks the end of the handler. The +`cir.begin_catch` replaces the `cir.catch_param` in the structured +form, and the exception object extracted from its return value should +be stored to the same alloca location as the return value of +`cir.catch_param` was in the structured representation. `cir.end_catch %catch_token` @@ -769,6 +799,7 @@ cir.func @someFunc(){ cir.yield } } catch all { + %1 = cir.catch_param : !cir.ptr cir.yield } } diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 982f866e228b6..86cee7d1b6f9b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -103,6 +103,8 @@ What's New in Clang |release|? C++ Language Changes -------------------- +- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672) + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -138,11 +140,19 @@ Non-comprehensive list of changes in this release Usable in constant expressions. Implicit conversion is supported for class/struct types with conversion operators. +- A new generic bit-reverse builtin function ``__builtin_bitreverseg`` that + extends bit-reversal support to all standard integers type, including + ``_BitInt`` + New Compiler Flags ------------------ - New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added to enable or disable Microsoft's anonymous struct/union extension without enabling other ``-fms-extensions`` features (#GH177607). +- New option ``--precompile-reduced-bmi`` allows build system to generate a + reduced BMI only for a C++20 importable module unit. Previously the users + can only generate the reduced BMI as a by-product, e.g, an object files or + a full BMI. Deprecated Compiler Flags ------------------------- @@ -162,6 +172,9 @@ Attribute Changes in Clang will still generate a stack protector if other local variables or command line flags require it. +- Added a new attribute, ``[[clang::no_outline]]`` to suppress outlining from + annotated functions. This uses the LLVM `nooutline` attribute. + Improvements to Clang's diagnostics ----------------------------------- - Added ``-Wlifetime-safety`` to enable lifetime safety analysis, @@ -231,6 +244,9 @@ Improvements to Clang's diagnostics - Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354) +- The ``-Wloop-analysis`` warning has been extended to catch more cases of + variable modification inside lambda expressions (#GH132038). + Improvements to Clang's time-trace ---------------------------------- @@ -247,12 +263,13 @@ Bug Fixes in This Version - Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210) - Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088) - - Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182) - Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098) - Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943) - +- Fixed an assertion failure caused by nested macro expansion during header-name lexing (``__has_embed(__has_include)``). (#GH178635) - Clang now outputs relative paths of embeds for dependency output. (#GH161950) +- Fixed an assertion failure when evaluating ``_Countof`` on invalid ``void``-typed operands. (#GH180893) +- Fixed a ``-Winvalid-noreturn`` false positive for unreachable ``try`` blocks following an unconditional ``throw``. (#GH174822) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -266,6 +283,9 @@ Bug Fixes to C++ Support - Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402) - Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639) - Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741) +- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable + when used inside decltype in the return type. (#GH180460) +- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -282,6 +302,9 @@ Miscellaneous Clang Crashes Fixed - Fixed a crash when using loop hint with a value dependent argument inside a generic lambda. (#GH172289) - Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433) +- Fixed a crash when initializing a ``constexpr`` pointer with a floating-point literal in C23. (#GH180313) +- Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319) +- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with different lifetimes (#GH178524) OpenACC Specific Changes ------------------------ @@ -364,8 +387,10 @@ AST Matchers clang-format ------------ -- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the +- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the '-'/'+' and the return type in Objective-C method declarations +- Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking + constructor initializers after commas, keeping the colon on the same line. libclang -------- @@ -373,6 +398,9 @@ libclang Code Completion --------------- +- Fixed a crash in code completion when using a C-Style cast with a parenthesized + operand in Objective-C++ mode. (#GH180125) + Static Analyzer --------------- diff --git a/clang/docs/StandardCPlusPlusModules.rst b/clang/docs/StandardCPlusPlusModules.rst index c714a41d37aff..ec9c4a27a786b 100644 --- a/clang/docs/StandardCPlusPlusModules.rst +++ b/clang/docs/StandardCPlusPlusModules.rst @@ -208,12 +208,16 @@ is ``-std=c++20`` or newer. How to produce a BMI ~~~~~~~~~~~~~~~~~~~~ -To generate a BMI for an importable module unit, use either the ``--precompile`` -or ``-fmodule-output`` command line options. +To generate a BMI for an importable module unit, use either the ``--precompile``, +``--precompile-reduced-bmi``, or ``-fmodule-output`` command line options. The ``--precompile`` option generates the BMI as the output of the compilation with the output path specified using the ``-o`` option. +The ``--precompile-reduced-bmi`` option generates a Reduced BMI (See the +following section for the definition of Reduced BMI) as the output of +the compilation with the output path specified using the ``-o`` option. + The ``-fmodule-output`` option generates the BMI as a by-product of the compilation. If ``-fmodule-output=`` is specified, the BMI will be emitted to the specified location. If ``-fmodule-output`` and ``-c`` are specified, the @@ -604,8 +608,14 @@ unnecessary dependencies for the BMI. To mitigate the problem, Clang has a compiler option to reduce the information contained in the BMI. These two formats are known as Full BMI and Reduced BMI, respectively. -Users can use the ``-fmodules-reduced-bmi`` option to produce a -Reduced BMI. +Users can use the ``-fmodules-reduced-bmi`` or ``--precompile-reduced-bmi`` +option to produce a Reduced BMI. + +The ``--precompile-reduced-bmi`` option will produce the reduced BMI +to the location specified by ``-o``. + +Note that ``--precompile`` will always generate the full BMI. So that build +system which may generate the BMI only should take care of this. For the one-phase compilation model (CMake implements this model), with ``-fmodules-reduced-bmi``, the generated BMI will be a Reduced @@ -1140,6 +1150,189 @@ This can potentially be improved by introducing a module partition implementation unit. An internal module partition unit is an importable module unit which is internal to the module itself. +The ABI of your library +^^^^^^^^^^^^^^^^^^^^^^^ + +You can skip this section your library doesn't ship ABI. + +With the ABI breaking style, for every ABI you shipped in your library, +you should provide a corresponding ABI within the modules version. + +For example, if this your library before provding modules, + +.. code-block:: c++ + + // header.h + #pragma once + + #include + + namespace example { + class C { + public: + std::size_t inline_get() { return 42; } + std::size_t get(); + }; + } + + // src.cpp + #include "header.h" + + std::size_t example::C::get() { + return 43 + inline_get(); + } + +Then you will ship ABI may be like: + +.. code-block:: text + + $nm -ACD libexample.so + libexample.so:0000000000001130 W example::C::inline_get() + libexample.so:0000000000001110 T example::C::get() + +Then with ABI breaking style, your code may look like: + +.. code-block:: c++ + + // example.cppm + export module example; + import std; + // and other third-party modules, if any + #define IN_MODULE_WRAPPER + #include "header.h" // omit changing of header.h for brevity + + // src.module.cpp + module example; + #define IN_MODULE_IMPL + #include "src.cpp" // omit changing of src.cpp for brevity + +And your ABI should look like: + +.. code-block:: text + + $llvm-nm -ACD libexample.so + libexample.so: 0000000000001060 T initializer for module example + libexample.so: 0000000000001150 W example::C::inline_get() + libexample.so: 0000000000001130 T example::C::get() + libexample.so: 0000000000001180 T example::C@example::inline_get() + libexample.so: 0000000000001160 T example::C@example::get() + +Here ``example::C@example::inline_get()`` and ``example::C@example::get()`` is +the corresponding version for ``example::C::inline_get()`` and ``example::C::get()`` +in modules version. + +Which part of the ABI will be broken? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While your library keeps ABI compatible by providing both ABI versions. +The users's ABI may be breaking if they used the ABI of modules' version. + +This is similar with +`the famous ABI break in GCC 5's libstdc++ for C++11 `_. + +Although your library remains compatible with both ABIs, for your library's users, +choosing the module-based ABI will also break their ABI. For example, if your user's +code contains: + +.. code-block:: c++ + + #include "header.h" + + namespace user { + void user_def(example::C& c) { + + } + } + +then, (if your user will ship ABI too), their shipped ABI may look like: + +.. code-block:: c++ + + $ llvm-nm -ACD libuser.so + libuser.so: 0000000000001100 T user::user_def(example::C&) + +But when your user switches to your ABI-breaking style module: + +.. code-block:: c++ + + import example; + + namespace user { + void user_def(example::C& c) { + + } + } + +The corresponding ABI may look like: + +.. code-block:: text + + $ llvm-nm -ACD libuser.so + libuser.so: 0000000000001100 T user::user_def(example::C@example&) + +Here we can find the ABI break from ``user::user_def(example::C&)`` to +``user::user_def(example::C@example&)``. + +Less duplicated generated code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Another benefit of ABI breaking style is, it is more likely to modules native +style. So that compiler can generate the previously implicitly inline entities +(e.g., virtual tables, variables and functions) +to the module unit instead of generating them in every translation unit used. + +e.g, for the above example, in header version, the definition of +``example::C::inline_get()`` will be generated in every translation unit used. +But in modules version, ``example::C::inline_get()`` will only be generated once +in the module unit. + +This is helpful for the whole build process to generate less duplicated code, +which will results in smaller binary size and faster compilation speed. + +Note that for compatibility, the explicitly inline entities will still be inline +in modules. So that if you have explicitly inline entities and you're using +ABI breaking style, it is suggestted to use a macro to control inliness of the entity. + +e.g., + +.. code-block:: c++ + + // your_header.h + #include + + inline void your_interface() {} + +within ABI breaking style, we suggest, + +.. code-block:: c++ + + // your_header.h + #ifndef IN_MODULE_WRAPPER + #include + #endif + + #ifdef IN_MODULE_WRAPPER + #define MY_EXPORT export + #else + #define MY_EXPORT + #endif + + #ifdef IN_MODULE_WRAPPER + #define MY_INLINE + #else + #define MY_INLINE inline + #endif + + MY_EXPORT MY_INLINE void your_interface() {} + + // your_module_interface.cppm + export module your_library; + import std; + #define IN_MODULE_WRAPPER + #include "your_header.h" + +So that ``your_interface()`` will not be inline in modules version. + Providing a header to skip parsing redundant headers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/clang/docs/ThreadSanitizer.rst b/clang/docs/ThreadSanitizer.rst index 86dc2600626b9..ecbfbb6f170fa 100644 --- a/clang/docs/ThreadSanitizer.rst +++ b/clang/docs/ThreadSanitizer.rst @@ -207,6 +207,126 @@ and can be run with ``make check-tsan`` command. We are actively working on enhancing the tool --- stay tuned. Any help, especially in the form of minimized standalone tests is more than welcome. +Adaptive Delay +-------------- + +Overview +~~~~~~~~ + +Adaptive Delay is an optional ThreadSanitizer feature that injects delays at +synchronization points to explore novel thread interleavings and increase the +likelihood of exposing data races. By perturbing thread scheduling, adaptive +delay creates more opportunities for concurrent accesses to shared data, +improving race detection. + +Adaptive delay is particularly useful for: + +* Detecting races in rarely-executed thread interleavings or code paths +* Testing parallel data structures and algorithms + +When enabled, adaptive delay maintains a configurable time budget to balance +race exposure against performance overhead. The delays can be + + * random amount of spin cycles + * a single yield to the OS scheduler + * random usleep + +The strategy prioritizes high-value synchronization points: + +* Relaxed atomic operations receive cheap delays (spin cycles) with low sampling +* Synchronizing atomic operations (acquire/release/seq_cst) receive moderate + delays with higher sampling +* Mutex and thread lifecycle operations receive the longest delays with highest + sampling + +The delays focus on synchronization points with clear happens-before relationships, +as those are most likely to expose data races. + +Enabling Adaptive Delay +~~~~~~~~~~~~~~~~~~~~~~~ + +Adaptive delay is disabled by default. Enable it by setting the +``enable_adaptive_delay`` flag: + +.. code-block:: console + + $ TSAN_OPTIONS=enable_adaptive_delay=1 ./myapp + +Configuration Options +~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: Adaptive Delay Options + :name: adaptive-delay-options-table + :header-rows: 1 + :widths: 35 10 15 40 + + * - Flag + - Type + - Default + - Description + * - ``enable_adaptive_delay`` + - bool + - false + - Enable adaptive delay injection to expose data races. + * - ``adaptive_delay_aggressiveness`` + - int + - 25 + - Controls delay injection intensity for race detection. Higher values inject + more delays to expose races. Value must be greater than 0. Suggested values: + 10 (minimal), 50 (moderate), 200 (aggressive). This is a tuning parameter; + actual overhead varies by workload and platform. + * - ``adaptive_delay_relaxed_sample_rate`` + - int + - 10000 + - Sample 1 in N relaxed atomic operations for delay injection. Relaxed atomics + have minimal synchronization, so sampling helps avoid excessive overhead. + * - ``adaptive_delay_sync_atomic_sample_rate`` + - int + - 100 + - Sample 1 in N acquire/release/seq_cst atomic operations for delay injection. + These synchronizing atomics are more likely to expose races, so are sampled + more often. + * - ``adaptive_delay_mutex_sample_rate`` + - int + - 10 + - Sample 1 in N mutex/condition variable operations for delay injection. Mutex + ops are high-value synchronization points and are sampled frequently. + * - ``adaptive_delay_max_atomic`` + - string + - ``"sleep_us=50"`` + - Maximum delay for atomic operations. Format: ``"spin=N"`` (N spin cycles, + 1 <= N <= 10,000), ``"yield"`` (one yield to the OS), or ``"sleep_us=N"`` + (up to N microseconds). The delay is randomly chosen up to the specified + maximum N. + * - ``adaptive_delay_max_sync`` + - string + - ``"sleep_us=500"`` + - Maximum delay for synchronization operations (mutex and thread lifecycle + operations). Format: same as ``adaptive_delay_max_atomic``. Typically set + longer than atomic delays since these operations involve waking blocked threads + and may be more likely to expose races. + +Examples +~~~~~~~~ + +Enable adaptive delay with moderate aggressiveness: + +.. code-block:: console + + $ TSAN_OPTIONS=enable_adaptive_delay=1:adaptive_delay_aggressiveness=50 ./myapp + +Enable aggressive delay injection: + +.. code-block:: console + + $ TSAN_OPTIONS=enable_adaptive_delay=1:adaptive_delay_aggressiveness=200 ./myapp + +Increase sampling frequency for mutex operations: + +.. code-block:: console + + $ TSAN_OPTIONS=enable_adaptive_delay=1:adaptive_delay_mutex_sample_rate=5 ./myapp + More Information ---------------- ``_ diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 499b78895392b..7ff55bc9d77a7 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -167,6 +167,20 @@ numerical value. int x = (*p_function)('x', 'y'); // NO warning yet at functon pointer calls } + void volatile_pointee() { + *(volatile int *)0x404 = 1; // no warning: constant non-null "volatile" pointee, you must know what you are doing + } + + void deref_volatile_nullptr() { + *(volatile int *)0 = 1; // core.NullDereference still warns about this + } + +If your project is low-level (e.g., firmware), or deals with hardware interop with a lot of genuine constant addresses, then consider disabling this checker. +The checker automatically suppresses issues if the type of the pointee of the address is ``volatile``. +You probably already need this to be ``volatile`` for legitimate access, so the checker suppresses such issues to avoid false-positives. +Note that null pointers will still be reported by :ref:`core.NullDereference ` +regardless if the pointee is ``volatile`` or not. + If the analyzer option ``suppress-dereferences-from-any-address-space`` is set to true (the default value), then this checker never reports dereference of pointers with a specified address space. If the option is set to false, then diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 6148f86091110..7761a5c24c606 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -28,6 +28,7 @@ #include "clang/Analysis/Analyses/LifetimeSafety/MovedLoans.h" #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h" #include "clang/Analysis/AnalysisDeclContext.h" +#include #include namespace clang::lifetimes { @@ -39,6 +40,12 @@ enum class Confidence : uint8_t { Definite // Reported as a definite error (-Wlifetime-safety-permissive) }; +struct LifetimeSafetyOpts { + /// Maximum number of CFG blocks to analyze. Functions with larger CFGs will + /// be skipped. + size_t MaxCFGBlocks; +}; + /// Enum to track functions visible across or within TU. enum class SuggestionScope { CrossTU, // For suggestions on declarations visible across Translation Units. @@ -130,7 +137,8 @@ struct LifetimeFactory { class LifetimeSafetyAnalysis { public: LifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetySemaHelper *SemaHelper); + LifetimeSafetySemaHelper *SemaHelper, + const LifetimeSafetyOpts &LSOpts); void run(); @@ -144,6 +152,7 @@ class LifetimeSafetyAnalysis { private: AnalysisDeclContext &AC; LifetimeSafetySemaHelper *SemaHelper; + const LifetimeSafetyOpts LSOpts; LifetimeFactory Factory; std::unique_ptr FactMgr; std::unique_ptr LiveOrigins; diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h index 5ca26df1e9252..2cd8990708b8d 100644 --- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h +++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h @@ -63,6 +63,7 @@ class BuildNamespace { bool operator<(const BuildNamespace &Other) const; friend class SerializationFormat; + friend class TestFixture; }; /// Represents a hierarchical sequence of build namespaces. @@ -75,8 +76,6 @@ class BuildNamespace { /// For example, an entity might be qualified by a compilation unit namespace /// followed by a shared library namespace. class NestedBuildNamespace { - friend class SerializationFormat; - std::vector Namespaces; public: @@ -114,8 +113,8 @@ class NestedBuildNamespace { bool operator!=(const NestedBuildNamespace &Other) const; bool operator<(const NestedBuildNamespace &Other) const; - friend class JSONWriter; - friend class LinkUnitResolution; + friend class SerializationFormat; + friend class TestFixture; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h index 6fa059445d853..00a6f3447bbce 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h @@ -29,6 +29,8 @@ class EntityIdTable; /// \see EntityIdTable class EntityId { friend class EntityIdTable; + friend class SerializationFormat; + friend class TestFixture; size_t Index; diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h index cf4bb83efddd0..6c5f27907adb4 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h @@ -21,6 +21,9 @@ namespace clang::ssaf { /// The table maps each unique EntityName to exactly one EntityId. /// Entities are never removed. class EntityIdTable { + friend class SerializationFormat; + friend class TestFixture; + std::map Entities; public: diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h new file mode 100644 index 0000000000000..155ec69ea0b3a --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h @@ -0,0 +1,40 @@ +//===- EntityLinkage.h ------------------------------------------*- 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_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H + +namespace clang::ssaf { + +/// Represents the linkage properties of an entity in the program model. +/// +/// EntityLinkage captures whether an entity has no linkage, internal linkage, +/// or external linkage, which determines its visibility and accessibility +/// across translation units. +class EntityLinkage { + friend class SerializationFormat; + friend class TestFixture; + +public: + enum class LinkageType { + None, ///< local variables, function parameters + Internal, ///< static functions/variables, anonymous namespace + External ///< globally visible across translation units + }; + + explicit EntityLinkage(LinkageType L) : Linkage(L) {} + + LinkageType getLinkage() const { return Linkage; } + +private: + LinkageType Linkage; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h index 23890ab7bea43..72dd9ac803052 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h @@ -47,8 +47,8 @@ class EntityName { /// \param Namespace The namespace steps to append to this entity's namespace. EntityName makeQualified(NestedBuildNamespace Namespace) const; - friend class LinkUnitResolution; friend class SerializationFormat; + friend class TestFixture; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def index 59064659996b4..f4e952c04ed2f 100644 --- a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def +++ b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def @@ -19,12 +19,16 @@ FIELD(BuildNamespace, Kind) FIELD(BuildNamespace, Name) +FIELD(EntityId, Index) +FIELD(EntityIdTable, Entities) +FIELD(EntityLinkage, Linkage) FIELD(EntityName, Namespace) FIELD(EntityName, Suffix) FIELD(EntityName, USR) FIELD(NestedBuildNamespace, Namespaces) FIELD(TUSummary, Data) FIELD(TUSummary, IdTable) +FIELD(TUSummary, LinkageTable) FIELD(TUSummary, TUNamespace) #undef FIELD diff --git a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h index 785fe0eb10372..36a8cf5da78b1 100644 --- a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h +++ b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h @@ -31,6 +31,7 @@ class SummaryName { private: std::string Name; + friend class TestFixture; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h new file mode 100644 index 0000000000000..052aa2641dbce --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h @@ -0,0 +1,135 @@ +//===- JSONFormat.h ---------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// JSON serialization format implementation. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_ANALYSIS_SCALABLE_SERIALIZATION_JSONFORMAT_H +#define CLANG_ANALYSIS_SCALABLE_SERIALIZATION_JSONFORMAT_H + +#include "clang/Analysis/Scalable/Serialization/SerializationFormat.h" +#include "clang/Support/Compiler.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Registry.h" + +namespace clang::ssaf { + +class EntityIdTable; +class EntitySummary; +class SummaryName; + +class JSONFormat final : public SerializationFormat { + using Array = llvm::json::Array; + using Object = llvm::json::Object; + +public: + // Helper class to provide limited access to EntityId conversion methods. + // Only exposes EntityId serialization/deserialization to format handlers. + class EntityIdConverter { + public: + EntityId fromJSON(uint64_t EntityIdIndex) const { + return Format.entityIdFromJSON(EntityIdIndex); + } + + uint64_t toJSON(EntityId EI) const { return Format.entityIdToJSON(EI); } + + private: + friend class JSONFormat; + EntityIdConverter(const JSONFormat &Format) : Format(Format) {} + const JSONFormat &Format; + }; + + llvm::Expected readTUSummary(llvm::StringRef Path) override; + + llvm::Error writeTUSummary(const TUSummary &Summary, + llvm::StringRef Path) override; + + using SerializerFn = llvm::function_ref; + using DeserializerFn = + llvm::function_ref>( + const Object &, EntityIdTable &, const EntityIdConverter &)>; + + using FormatInfo = FormatInfoEntry; + +private: + static std::map initFormatInfos(); + const std::map FormatInfos = initFormatInfos(); + + EntityId entityIdFromJSON(const uint64_t EntityIdIndex) const; + uint64_t entityIdToJSON(EntityId EI) const; + + llvm::Expected + buildNamespaceKindFromJSON(llvm::StringRef BuildNamespaceKindStr) const; + + llvm::Expected + buildNamespaceFromJSON(const Object &BuildNamespaceObject) const; + Object buildNamespaceToJSON(const BuildNamespace &BN) const; + + llvm::Expected + nestedBuildNamespaceFromJSON(const Array &NestedBuildNamespaceArray) const; + Array nestedBuildNamespaceToJSON(const NestedBuildNamespace &NBN) const; + + llvm::Expected + entityNameFromJSON(const Object &EntityNameObject) const; + Object entityNameToJSON(const EntityName &EN) const; + + llvm::Expected> + entityIdTableEntryFromJSON(const Object &EntityIdTableEntryObject) const; + llvm::Expected + entityIdTableFromJSON(const Array &EntityIdTableArray) const; + Object entityIdTableEntryToJSON(const EntityName &EN, EntityId EI) const; + Array entityIdTableToJSON(const EntityIdTable &IdTable) const; + + llvm::Expected> + entitySummaryFromJSON(const SummaryName &SN, + const Object &EntitySummaryObject, + EntityIdTable &IdTable) const; + llvm::Expected entitySummaryToJSON(const SummaryName &SN, + const EntitySummary &ES) const; + + llvm::Expected>> + entityDataMapEntryFromJSON(const Object &EntityDataMapEntryObject, + const SummaryName &SN, + EntityIdTable &IdTable) const; + llvm::Expected>> + entityDataMapFromJSON(const SummaryName &SN, const Array &EntityDataArray, + EntityIdTable &IdTable) const; + llvm::Expected + entityDataMapToJSON(const SummaryName &SN, + const std::map> + &EntityDataMap) const; + + llvm::Expected>>> + summaryDataMapEntryFromJSON(const Object &SummaryDataObject, + EntityIdTable &IdTable) const; + llvm::Expected summaryDataMapEntryToJSON( + const SummaryName &SN, + const std::map> &SD) const; + + llvm::Expected< + std::map>>> + summaryDataMapFromJSON(const Array &SummaryDataArray, + EntityIdTable &IdTable) const; + llvm::Expected summaryDataMapToJSON( + const std::map>> + &SummaryDataMap) const; +}; + +} // namespace clang::ssaf + +namespace llvm { +extern template class CLANG_TEMPLATE_ABI + Registry; +} // namespace llvm + +#endif // CLANG_ANALYSIS_SCALABLE_SERIALIZATION_JSONFORMAT_H diff --git a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h index 866fe8bfdcee0..5686a088c72d4 100644 --- a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h +++ b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h @@ -18,8 +18,7 @@ #include "clang/Analysis/Scalable/Model/SummaryName.h" #include "clang/Analysis/Scalable/TUSummary/TUSummary.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/ExtensibleRTTI.h" -#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/Error.h" namespace clang::ssaf { @@ -27,31 +26,29 @@ class EntityId; class EntityIdTable; class EntityName; class EntitySummary; +class SummaryName; +class TUSummary; /// Abstract base class for serialization formats. -class SerializationFormat - : public llvm::RTTIExtends { +class SerializationFormat { public: - explicit SerializationFormat( - llvm::IntrusiveRefCntPtr FS); virtual ~SerializationFormat() = default; - virtual TUSummary readTUSummary(llvm::StringRef Path) = 0; + virtual llvm::Expected readTUSummary(llvm::StringRef Path) = 0; - virtual void writeTUSummary(const TUSummary &Summary, - llvm::StringRef OutputDir) = 0; - - static char ID; // For RTTIExtends. + virtual llvm::Error writeTUSummary(const TUSummary &Summary, + llvm::StringRef Path) = 0; protected: // Helpers providing access to implementation details of basic data structures // for efficient serialization/deserialization. + + EntityId makeEntityId(const size_t Index) const { return EntityId(Index); } + #define FIELD(CLASS, FIELD_NAME) \ static const auto &get##FIELD_NAME(const CLASS &X) { return X.FIELD_NAME; } \ static auto &get##FIELD_NAME(CLASS &X) { return X.FIELD_NAME; } #include "clang/Analysis/Scalable/Model/PrivateFieldNames.def" - - llvm::IntrusiveRefCntPtr FS; }; template struct FormatInfoEntry { @@ -59,6 +56,7 @@ template struct FormatInfoEntry { DeserializerFn Deserialize) : ForSummary(ForSummary), Serialize(Serialize), Deserialize(Deserialize) { } + virtual ~FormatInfoEntry() = default; SummaryName ForSummary; SerializerFn Serialize; diff --git a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormatRegistry.h b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormatRegistry.h index 40281d549b402..d7e77b9b18f77 100644 --- a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormatRegistry.h +++ b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormatRegistry.h @@ -51,21 +51,16 @@ bool isFormatRegistered(llvm::StringRef FormatName); /// This might return null if the construction of the desired /// SerializationFormat failed. /// It's a fatal error if there is no format registered with the name. -std::unique_ptr -makeFormat(llvm::IntrusiveRefCntPtr FS, - llvm::StringRef FormatName); +std::unique_ptr makeFormat(llvm::StringRef FormatName); // Registry for adding new SerializationFormat implementations. -using SerializationFormatRegistry = - llvm::Registry>; +using SerializationFormatRegistry = llvm::Registry; } // namespace clang::ssaf namespace llvm { extern template class CLANG_TEMPLATE_ABI - Registry>; + Registry; } // namespace llvm #endif // CLANG_ANALYSIS_SCALABLE_SERIALIZATION_SERIALIZATION_FORMAT_REGISTRY_H diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h index b5b2fe128d0c4..dddd69cb7d372 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h @@ -10,19 +10,21 @@ #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_ENTITYSUMMARY_H #include "clang/Analysis/Scalable/Model/SummaryName.h" -#include "llvm/Support/ExtensibleRTTI.h" +#include namespace clang::ssaf { /// Base class for analysis-specific summary data. -class EntitySummary : public llvm::RTTIExtends { +class EntitySummary { public: virtual ~EntitySummary() = default; virtual SummaryName getSummaryName() const = 0; - - static char ID; // For RTTIExtends. }; +template +using DerivesFromEntitySummary = + std::enable_if_t>; + } // namespace clang::ssaf #endif // LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_ENTITYSUMMARY_H diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h index 4af1c70e1a488..40cb7d582cf6e 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h @@ -12,6 +12,7 @@ #include "clang/Analysis/Scalable/Model/BuildNamespace.h" #include "clang/Analysis/Scalable/Model/EntityId.h" #include "clang/Analysis/Scalable/Model/EntityIdTable.h" +#include "clang/Analysis/Scalable/Model/EntityLinkage.h" #include "clang/Analysis/Scalable/Model/SummaryName.h" #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" #include @@ -23,8 +24,11 @@ namespace clang::ssaf { class TUSummary { /// Identifies the translation unit. BuildNamespace TUNamespace; + EntityIdTable IdTable; + std::map LinkageTable; + std::map>> Data; @@ -32,6 +36,8 @@ class TUSummary { TUSummary(BuildNamespace TUNamespace) : TUNamespace(std::move(TUNamespace)) {} friend class SerializationFormat; + friend class TestFixture; + friend class TUSummaryBuilder; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h index fa679c145faa5..64d8adc0791c1 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h @@ -9,12 +9,68 @@ #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" +#include +#include + namespace clang::ssaf { +class EntityName; +class TUSummary; + class TUSummaryBuilder { - // Empty for now. +public: + explicit TUSummaryBuilder(TUSummary &Summary) : Summary(Summary) {} + + /// Add an entity to the summary and return its EntityId. + /// If the entity already exists, returns the existing ID (idempotent). + EntityId addEntity(const EntityName &E); + + /// Associate the \p Data \c EntitySummary with the \p Entity. + /// This consumes the \p Data only if \p Entity wasn't associated yet with the + /// same kind of \c EntitySummary. + /// \returns a pointer to the \c EntitySummary and whether it inserted or not. + template * = nullptr> + std::pair + addSummary(EntityId Entity, std::unique_ptr &&Data); + +private: + TUSummary &Summary; + + std::pair + addSummaryImpl(EntityId Entity, std::unique_ptr &&Data); }; +// Why is this a template? +// +// We use template here to avoid an implicit conversion from +// `std::unique_ptr` to `std::unique_ptr` +// because constructing that implicit temporary would unconditionally "consume" +// the Data. This would make it impossible to recover from the call-site the +// Data you pass in even if no insertion happens. +template *> +std::pair +TUSummaryBuilder::addSummary(EntityId Entity, + std::unique_ptr &&Data) { + // Prepare a unique_ptr of the base type to avoid implicit conversions at the + // call-site. + std::unique_ptr TypeErasedData = std::move(Data); + + // This only moves (consumes) TypeErasedData if insertion happened. + // Otherwise it doesn't touch the `TypeErasedData`. + auto [It, Inserted] = addSummaryImpl(Entity, std::move(TypeErasedData)); + + // Move it back on failue to keep the `Data` unconsumed. + if (!Inserted) { + Data = std::unique_ptr( + static_cast(TypeErasedData.release())); + } + return {It, Inserted}; +} + } // namespace clang::ssaf #endif // LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ea3f9df6d8342..2621d178d99e8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2371,6 +2371,13 @@ def NoInline : DeclOrStmtAttr { let SimpleHandler = 1; } +def NoOutline : DeclOrStmtAttr { + let Spellings = [Clang<"no_outline">]; + let Subjects = SubjectList<[Function, ObjCMethod, Block], ErrorDiag>; + let Documentation = [NoOutlineDocs]; + let SimpleHandler = true; +} + def NoMips16 : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"nomips16">]; let Subjects = SubjectList<[Function], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index cad45501df6d2..f91fe2c7b0259 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -862,6 +862,18 @@ with ``__noinline__`` defined as a macro as ``__attribute__((noinline))``. }]; } +def NoOutlineDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This function attribute suppresses outlining from the annotated function. + +Outlining is the process where common parts of separate functions are extracted +into a separate function (or assembly snippet), and calls to that function or +snippet are inserted in the original functions. In this way, it can be seen as +the opposite of inlining. It can help to reduce code size. + }]; +} + def MustTailDocs : Documentation { let Category = DocCatStmt; let Content = [{ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index ed4ec10375e48..78dd26aa2c455 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -767,6 +767,12 @@ def Bitreverse : BitInt8_16_32_64BuiltinsTemplate, Builtin { let Prototype = "T(T)"; } +def Bitreverseg : Builtin { + let Spellings = ["__builtin_bitreverseg"]; + let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking]; + let Prototype = "int(...)"; +} + def RotateLeft : BitInt8_16_32_64BuiltinsTemplate, Builtin { let Spellings = ["__builtin_rotateleft"]; let Attributes = [NoThrow, Const, Constexpr]; @@ -5204,6 +5210,12 @@ def HLSLWavePrefixSum : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLWavePrefixProduct : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_wave_prefix_product"]; + let Attributes = [NoThrow, Const]; + let Prototype = "void(...)"; +} + def HLSLClamp : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_elementwise_clamp"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.td b/clang/include/clang/Basic/BuiltinsAMDGPU.td index c1ca7d4fd8e77..78443ac291f31 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.td +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.td @@ -896,15 +896,15 @@ def __builtin_amdgcn_wmma_scale16_f32_32x16x128_f4 : AMDGPUBuiltin<"_ExtVector<1 def __builtin_amdgcn_swmmac_f32_16x16x64_bf16 : AMDGPUBuiltin<"_ExtVector<8, float>(_Constant bool, _ExtVector<16, __bf16>, _Constant bool, _ExtVector<32, __bf16>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; def __builtin_amdgcn_swmmac_bf16_16x16x64_bf16 : AMDGPUBuiltin<"_ExtVector<8, __bf16>(_Constant bool, _ExtVector<16, __bf16>, _Constant bool, _ExtVector<32, __bf16>, _ExtVector<8, __bf16>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; def __builtin_amdgcn_swmmac_bf16f32_16x16x64_bf16 : AMDGPUBuiltin<"_ExtVector<8, float>(_Constant bool, _ExtVector<16, __bf16>, _Constant bool, _ExtVector<32, __bf16>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f32_16x16x128_fp8_fp8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f32_16x16x128_fp8_bf8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f32_16x16x128_bf8_fp8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f32_16x16x128_bf8_bf8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f16_16x16x128_fp8_fp8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f16_16x16x128_fp8_bf8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f16_16x16x128_bf8_fp8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_f16_16x16x128_bf8_bf8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; -def __builtin_amdgcn_swmmac_i32_16x16x128_iu8 : AMDGPUBuiltin<"_ExtVector<8, int>(_Constant bool, _ExtVector<8, int>, _Constant bool, _ExtVector<16, int>, _ExtVector<8, int>, int, _Constant bool, _Constant bool, ...)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f32_16x16x128_fp8_fp8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f32_16x16x128_fp8_bf8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f32_16x16x128_bf8_fp8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f32_16x16x128_bf8_bf8 : AMDGPUBuiltin<"_ExtVector<8, float>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, float>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f16_16x16x128_fp8_fp8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f16_16x16x128_fp8_bf8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f16_16x16x128_bf8_fp8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_f16_16x16x128_bf8_bf8 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_ExtVector<8, int>, _ExtVector<16, int>, _ExtVector<8, __fp16>, _ExtVector<2, int>, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; +def __builtin_amdgcn_swmmac_i32_16x16x128_iu8 : AMDGPUBuiltin<"_ExtVector<8, int>(_Constant bool, _ExtVector<8, int>, _Constant bool, _ExtVector<16, int>, _ExtVector<8, int>, _ExtVector<2, int>, _Constant bool, _Constant bool, ...)", [Const], "gfx1250-insts,wavefrontsize32">; def __builtin_amdgcn_swmmac_f32_16x16x64_f16 : AMDGPUBuiltin<"_ExtVector<8, float>(_Constant bool, _ExtVector<16, __fp16>, _Constant bool, _ExtVector<32, __fp16>, _ExtVector<8, float>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; def __builtin_amdgcn_swmmac_f16_16x16x64_f16 : AMDGPUBuiltin<"_ExtVector<8, __fp16>(_Constant bool, _ExtVector<16, __fp16>, _Constant bool, _ExtVector<32, __fp16>, _ExtVector<8, __fp16>, int, _Constant bool, _Constant bool)", [Const], "gfx1250-insts,wavefrontsize32">; diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index c6e931d0c9517..674c4d03c8b1b 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -310,6 +310,11 @@ class DiagnosticsEngine : public RefCountedBase { // Suppress all diagnostics. bool SuppressAllDiagnostics = false; + // Force system warnings to be shown, regardless of the current + // diagnostic state. This is used for temporary overrides and is not + // stored as location-specific state in modules. + bool ForceSystemWarnings = false; + // Elide common types of templates. bool ElideType = true; @@ -730,6 +735,9 @@ class DiagnosticsEngine : public RefCountedBase { void setSuppressAllDiagnostics(bool Val) { SuppressAllDiagnostics = Val; } bool getSuppressAllDiagnostics() const { return SuppressAllDiagnostics; } + void setForceSystemWarnings(bool Val) { ForceSystemWarnings = Val; } + bool getForceSystemWarnings() const { return ForceSystemWarnings; } + /// Set type eliding, to skip outputting same types occurring in /// template types. void setElideType(bool Val) { ElideType = Val; } diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index f36c02851a6a1..5446c0d89597b 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -404,10 +404,8 @@ def note_constexpr_infer_alloc_token_type_inference_failed : Note< "could not infer allocation type for __builtin_infer_alloc_token">; def note_constexpr_infer_alloc_token_no_metadata : Note< "could not get token metadata for inferred type">; -def note_constexpr_infer_alloc_token_stateful_mode : Note< - "stateful alloc token mode not supported in constexpr">; -def err_experimental_clang_interp_failed : Error< - "the experimental clang interpreter failed to evaluate an expression">; +def note_constexpr_infer_alloc_token_stateful_mode + : Note<"stateful alloc token mode not supported in constexpr">; def warn_attribute_needs_aggregate : Warning< "%0 attribute is ignored in non-aggregate type %1">, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 887d1b5f2bbfd..85a023435ba23 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7964,24 +7964,31 @@ def warn_arith_conv_mixed_unicode_types "different Unicode character types %1 and %2">, InGroup; -def err_typecheck_assign_const : Error< - "%select{" - "cannot assign to return value because function %1 returns a const value|" - "cannot assign to variable %1 with const-qualified type %2|" - "cannot assign to %select{non-|}1static data member %2 " - "with const-qualified type %3|" - "cannot assign to non-static data member within const member function %1|" - "cannot assign to %select{variable %2|non-static data member %2|lvalue}1 " - "with %select{|nested }3const-qualified data member %4|" - "read-only variable is not assignable}0">; - -def note_typecheck_assign_const : Note< - "%select{" - "function %1 which returns const-qualified type %2 declared here|" - "variable %1 declared const here|" - "%select{non-|}1static data member %2 declared const here|" - "member function %q1 is declared const here|" - "%select{|nested }1data member %2 declared const here}0">; +def err_typecheck_assign_const + : Error<"%select{" + "cannot assign to return value because function %1 returns a const " + "value|" + "cannot assign to variable %1 with const-qualified type %2|" + "cannot assign to %select{non-|}1static data member %2 " + "with const-qualified type %3|" + "cannot assign to %select{variable %2|non-static data member " + "%2|lvalue}1 " + "with %select{|nested }3const-qualified data member %4|" + "read-only variable is not assignable}0">; + +def note_typecheck_assign_const + : Note<"%select{" + "function %1 which returns const-qualified type %2 declared here|" + "variable %1 declared const here|" + "%select{non-|}1static data member %2 declared const here|" + "%select{|nested }1data member %2 declared const here}0">; + +def err_typecheck_assign_const_method + : Error<"cannot assign to non-static data member within const member " + "function %0">; + +def note_typecheck_assign_const_method + : Note<"member function %q0 is declared const here">; def warn_unsigned_always_true_comparison : Warning< "result of comparison of %select{%3|unsigned expression}0 %2 " diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 45e2777def4fa..08f102839b89e 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -504,6 +504,8 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C") LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, "Lifetime safety analysis for C++") +LANGOPT(LifetimeSafetyMaxCFGBlocks, 32, 0, NotCompatible, "Skip LifetimeSafety analysis for functions with CFG block count exceeding this threshold. Specify 0 for no limit") + LANGOPT(EnableLifetimeSafetyInference, 1, 0, NotCompatible, "Lifetime safety inference analysis for C++") // TODO: Remove flag and default to end-of-TU analysis for lifetime safety after performance validation. diff --git a/clang/include/clang/Basic/SourceLocation.h b/clang/include/clang/Basic/SourceLocation.h index bd0038d5ae1ae..b73b43d953662 100644 --- a/clang/include/clang/Basic/SourceLocation.h +++ b/clang/include/clang/Basic/SourceLocation.h @@ -86,6 +86,10 @@ using FileIDAndOffset = std::pair; /// In addition, one bit of SourceLocation is used for quick access to the /// information whether the location is in a file or a macro expansion. /// +/// SourceLocation operates on a byte level, i.e. offsets describe +/// byte distances, but in most cases, they are used on a token level, +/// where a SourceLocation points to the first byte of a lexer token. +/// /// It is important that this type remains small. It is currently 32 bits wide. class SourceLocation { friend class ASTReader; @@ -212,6 +216,11 @@ inline bool operator>=(const SourceLocation &LHS, const SourceLocation &RHS) { } /// A trivial tuple used to represent a source range. +/// +/// When referring to tokens, a SourceRange is an inclusive range [begin, end] +/// that contains its endpoints, its begin SourceLocation points to the first +/// byte of the first token and its end SourceLocation points to the first byte +/// of the last token. class SourceRange { SourceLocation B; SourceLocation E; @@ -248,13 +257,22 @@ class SourceRange { void dump(const SourceManager &SM) const; }; -/// Represents a character-granular source range. +/// Represents a byte-granular source range. /// -/// The underlying SourceRange can either specify the starting/ending character +/// The underlying SourceRange can either specify the starting/ending byte /// of the range, or it can specify the start of the range and the start of the /// last token of the range (a "token range"). In the token range case, the /// size of the last token must be measured to determine the actual end of the /// range. +/// +/// CharSourceRange is interpreted differently depending on whether it is a +/// TokenRange or a CharRange. +/// For a TokenRange, the range contains the endpoint, i.e. the token containing +/// the end SourceLocation. +/// For a CharRange, the range doesn't contain the endpoint, i.e. it ends at the +/// byte before the end SourceLocation. This allows representing a point +/// CharRange [begin, begin) that points at the empty range right in front of +/// the begin SourceLocation. class CharSourceRange { SourceRange Range; bool IsTokenRange = false; @@ -280,8 +298,8 @@ class CharSourceRange { } /// Return true if the end of this range specifies the start of - /// the last token. Return false if the end of this range specifies the last - /// character in the range. + /// the last token. Return false if the end of this range specifies the first + /// byte after the range. bool isTokenRange() const { return IsTokenRange; } bool isCharRange() const { return !IsTokenRange; } diff --git a/clang/include/clang/Basic/riscv_vector.td b/clang/include/clang/Basic/riscv_vector.td index c899dc70fc0b7..0985ea8a4fdec 100644 --- a/clang/include/clang/Basic/riscv_vector.td +++ b/clang/include/clang/Basic/riscv_vector.td @@ -2043,6 +2043,22 @@ let UnMaskedPolicyScheme = HasPassthruOperand in { defm vclmul : RVVInt64BinBuiltinSet; defm vclmulh : RVVInt64BinBuiltinSet; } + + // zvabd + let RequiredFeatures = ["zvabd"] in { + defm vabs : RVVOutBuiltinSet<"vabs", "csil", [["v", "Uv", "Uvv"]]>; + defm vabd : RVVOutBuiltinSet<"vabd", "cs", [["vv", "Uv", "Uvvv"]]>; + defm vabdu : RVVOutBuiltinSet<"vabdu", "cs", [["vv", "Uv", "UvUvUv"]]>; + } +} + +let UnMaskedPolicyScheme = HasPolicyOperand, HasMaskedOffOperand = false in { + let RequiredFeatures = ["zvabd"] in { + defm vwabda : RVVOutOp1Op2BuiltinSet<"vwabda", "cs", + [["vv", "Uw", "UwUwvv"]]>; + defm vwabdau : RVVOutOp1Op2BuiltinSet<"vwabdau", "cs", + [["vv", "Uw", "UwUwUvUv"]]>; + } } let UnMaskedPolicyScheme = HasPolicyOperand, HasMasked = false in { diff --git a/clang/include/clang/CIR/ABIArgInfo.h b/clang/include/clang/CIR/ABIArgInfo.h index b8d10445f9586..cd679961eb49f 100644 --- a/clang/include/clang/CIR/ABIArgInfo.h +++ b/clang/include/clang/CIR/ABIArgInfo.h @@ -65,6 +65,22 @@ class ABIArgInfo { Kind getKind() const { return theKind; } bool isDirect() const { return theKind == Direct; } bool isIgnore() const { return theKind == Ignore; } + bool isIndirect() const { + assert(!cir::MissingFeatures::abiArgInfo()); + return false; + } + bool isExtend() const { + assert(!cir::MissingFeatures::abiArgInfo()); + return false; + } + bool isNoExt() const { + assert(!cir::MissingFeatures::abiArgInfo()); + return false; + } + bool isIndirectAliased() const { + assert(!cir::MissingFeatures::abiArgInfo()); + return false; + } bool canHaveCoerceToType() const { assert(!cir::MissingFeatures::abiArgInfo()); diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index eb80b6099a4f3..dd9dc7f0fe0f5 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -16,6 +16,7 @@ #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" #include "llvm/ADT/STLForwardCompat.h" +#include "llvm/IR/FPEnv.h" #include "llvm/Support/ErrorHandling.h" #include "mlir/IR/Builders.h" @@ -400,34 +401,47 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, mlir::Type returnType, mlir::ValueRange operands, - llvm::ArrayRef attrs = {}) { + llvm::ArrayRef attrs = {}, + llvm::ArrayRef resAttrs = {}) { auto op = cir::CallOp::create(*this, loc, callee, returnType, operands); op->setAttrs(attrs); + + assert(!cir::MissingFeatures::functionArgumentAttrs()); + // TODO(cir): At one point we'll have to do a similar thing to this for the + // argument attributes, except for those, there are 1 Dictionary per + // argument. Since we only have 1 result however, we can just use a single + // dictionary here, wrapped in an ArrayAttr of 1. + auto resultDictAttr = mlir::DictionaryAttr::get(getContext(), resAttrs); + op.setResAttrsAttr(mlir::ArrayAttr::get(getContext(), resultDictAttr)); return op; } cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee, mlir::ValueRange operands, - llvm::ArrayRef attrs = {}) { + llvm::ArrayRef attrs = {}, + llvm::ArrayRef resAttrs = {}) { return createCallOp(loc, mlir::SymbolRefAttr::get(callee), callee.getFunctionType().getReturnType(), operands, - attrs); + attrs, resAttrs); } cir::CallOp createIndirectCallOp(mlir::Location loc, mlir::Value indirectTarget, cir::FuncType funcType, mlir::ValueRange operands, - llvm::ArrayRef attrs = {}) { + llvm::ArrayRef attrs = {}, + llvm::ArrayRef resAttrs = {}) { llvm::SmallVector resOperands{indirectTarget}; resOperands.append(operands.begin(), operands.end()); return createCallOp(loc, mlir::SymbolRefAttr(), funcType.getReturnType(), - resOperands, attrs); + resOperands, attrs, resAttrs); } cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, mlir::ValueRange operands = mlir::ValueRange(), - llvm::ArrayRef attrs = {}) { - return createCallOp(loc, callee, cir::VoidType(), operands, attrs); + llvm::ArrayRef attrs = {}, + llvm::ArrayRef resAttrs = {}) { + return createCallOp(loc, callee, cir::VoidType(), operands, attrs, + resAttrs); } //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h index 858d4d6350bed..eb87dc083b0f5 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h @@ -19,6 +19,7 @@ #include "clang/CIR/Dialect/IR/CIROpsEnums.h" +#include "clang/CIR/Interfaces/ASTAttrInterfaces.h" #include "clang/CIR/Interfaces/CIRTypeInterfaces.h" //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index d7938bc350925..15c2c6e034af9 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -19,6 +19,8 @@ include "clang/CIR/Dialect/IR/CIRAttrConstraints.td" include "clang/CIR/Dialect/IR/CIRDialect.td" include "clang/CIR/Dialect/IR/CIREnumAttr.td" +include "clang/CIR/Interfaces/ASTAttrInterfaces.td" + //===----------------------------------------------------------------------===// // CIR Attrs //===----------------------------------------------------------------------===// @@ -1286,5 +1288,64 @@ def CIR_SideEffect : CIR_I32EnumAttr< }]; } +//===----------------------------------------------------------------------===// +// StaticLocalGuardAttr +//===----------------------------------------------------------------------===// + +def CIR_StaticLocalGuardAttr : CIR_Attr<"StaticLocalGuard", + "static_local_guard"> { + let summary = "Guard variable name for static local variables"; + let description = [{ + Contains the mangled guard variable name for static local variable + initialization. + + Example: + ```mlir + cir.global internal static_local_guard<"_ZGVZ3foovE1x"> @_ZZ3foovE1x = ... + ``` + }]; + let parameters = (ins "mlir::StringAttr":$name); + let assemblyFormat = "`<` $name `>`"; +} + +//===----------------------------------------------------------------------===// +// AST Wrappers +//===----------------------------------------------------------------------===// + +class CIR_AST traits = []> + : CIR_Attr { + string clang_name = !strconcat("const clang::", name, " *"); + + let summary = !strconcat("Wraps a '", clang_name, "' AST node."); + let description = [{ + Operations optionally refer to this node, they could be available depending + on the CIR lowering stage. Whether it's attached to the appropriate + CIR operation is delegated to the operation verifier. + + Note: the AST pointer can be null when CIR is parsed from text, since + there is no serialization support for AST nodes yet. + }]; + let parameters = (ins clang_name:$ast); + + // Printing and parsing available in CIRDialect.cpp + let hasCustomAssemblyFormat = 1; + + let extraClassDefinition = [{ + ::mlir::Attribute $cppClass::parse(::mlir::AsmParser &parser, + ::mlir::Type type) { + // We cannot really parse anything AST related at this point + // since we have no serialization/JSON story. + return $cppClass::get(parser.getContext(), nullptr); + } + + void $cppClass::print(::mlir::AsmPrinter &printer) const { + // Nothing to print besides the mnemonics. + } + }]; +} + +def CIR_ASTVarDeclAttr : CIR_AST<"VarDecl", "var.decl", [ + ASTVarDeclInterface +]>; #endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h index 5c6ce7abeae61..17b339417ad5a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h @@ -25,6 +25,8 @@ class CIRDataLayout { // being upstreamed. Additional methods and members will be added as needed. bool bigEndian = false; + unsigned programAddrSpace = 0; + public: mlir::DataLayout layout; @@ -36,6 +38,8 @@ class CIRDataLayout { bool isBigEndian() const { return bigEndian; } + unsigned getProgramAddressSpace() const { return programAddrSpace; } + /// Internal helper method that returns requested alignment for type. llvm::Align getAlignment(mlir::Type ty, bool useABIAlign) const; @@ -56,6 +60,20 @@ class CIRDataLayout { baseSize.isScalable()}; } + /// Returns the maximum number of bits that may be overwritten by + /// storing the specified type; always a multiple of 8. + /// + /// If Ty is a scalable vector type, the scalable property will be set and + /// the runtime size will be a positive integer multiple of the base size. + /// + /// For example, returns 40 for i36 and 80 for x86_fp80. + llvm::TypeSize getTypeStoreSizeInBits(mlir::Type ty) const { + llvm::TypeSize baseSize = getTypeSizeInBits(ty); + uint64_t alignedSizeInBits = + llvm::alignToPowerOf2(baseSize.getKnownMinValue(), 8); + return {alignedSizeInBits, baseSize.isScalable()}; + } + /// Returns the offset in bytes between successive objects of the /// specified type, including alignment padding. /// @@ -94,6 +112,14 @@ class CIRDataLayout { return cir::IntType::get(ty.getContext(), getPointerTypeSizeInBits(ty), false); } + + /// Returns true if no extra padding bits are needed when storing the + /// specified type. + /// + /// For example, returns false for i19 that has a 24-bit store size. + bool typeSizeEqualsStoreSize(mlir::Type ty) const { + return getTypeSizeInBits(ty) == getTypeStoreSizeInBits(ty); + } }; } // namespace cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index a576119b97dae..cf312af194e85 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -540,6 +540,9 @@ def CIR_AllocaOp : CIR_Op<"alloca", [ when handling VLAs or the `alloca` builtin and is omitted when declaring regular local variables. + The `cleanup_dest_slot` attribute indicates that this was a temporary + alloca generated by the compiler to handle cleanup exit dispatching. + The result type is a pointer to the input's type. Example: @@ -560,6 +563,7 @@ def CIR_AllocaOp : CIR_Op<"alloca", [ StrAttr:$name, UnitAttr:$init, UnitAttr:$constant, + UnitAttr:$cleanup_dest_slot, ConfinedAttr]>:$alignment, OptionalAttr:$annotations ); @@ -598,6 +602,7 @@ def CIR_AllocaOp : CIR_Op<"alloca", [ `[` $name (`,` `init` $init^)? (`,` `const` $constant^)? + (`,` `cleanup_dest_slot` $cleanup_dest_slot^)? `]` ($annotations^)? attr-dict }]; @@ -665,6 +670,51 @@ def CIR_LoadOp : CIR_Op<"load", [ // FIXME: add verifier. } +//===----------------------------------------------------------------------===// +// MaskLoadOp +//===----------------------------------------------------------------------===// + +def CIR_VecMaskedLoadOp : CIR_Op<"vec.masked_load", [ + TypesMatchWith<"type of 'result' must match pointee type of 'addr'", + "addr", "result", "mlir::cast($_self).getPointee()"> +]> { + let summary = "Masked vector load from memory"; + let description = [{ + `cir.masked_load` conditionally loads elements from memory based on a mask. + Elements for which the mask is false are taken from `pass_thru`. + + This operation corresponds to LLVM's masked load op (`llvm.intr.maskedload`) + and lower directly to it. + + `alignment` can be provided to override the default alignment derived from + the pointee/element type data layout. + + Example: + + ```mlir + %v = cir.masked_load align(16) %ptr, %mask, %passthru + : !cir.ptr, <4xi1>, <4xi32> -> <4xi32> + ``` + }]; + + let arguments = (ins + Arg:$addr, + CIR_VectorType:$mask, + CIR_VectorType:$pass_thru, + OptionalAttr>:$alignment + ); + + let results = (outs CIR_AnyType:$result); + + let assemblyFormat = [{ + (`align` `(` $alignment^ `)`)? + $addr `,` $mask `,` $pass_thru + `:` qualified(type($addr)) `,` type($mask) `,` type($pass_thru) + `->` type($result) + attr-dict + }]; +} + //===----------------------------------------------------------------------===// // StoreOp //===----------------------------------------------------------------------===// @@ -2335,6 +2385,11 @@ def CIR_GlobalOp : CIR_Op<"global", [ The `linkage` tracks C/C++ linkage types, currently very similar to LLVM's. Symbol visibility in `sym_visibility` is defined in terms of MLIR's visibility and verified to be in accordance to `linkage`. + + The `static_local_guard` attribute indicates that this global represents a + function-local static variable that requires guarded initialization + (e.g., C++ static local variables with non-constant initializers). + It contains the mangled name of the guard variable. }]; // Note that both sym_name and sym_visibility are tied to Symbol trait. @@ -2353,7 +2408,9 @@ def CIR_GlobalOp : CIR_Op<"global", [ UnitAttr:$comdat, UnitAttr:$constant, UnitAttr:$dso_local, - OptionalAttr:$alignment); + OptionalAttr:$static_local_guard, + OptionalAttr:$alignment, + OptionalAttr:$ast); let regions = (region MaxSizedRegion<1>:$ctorRegion, MaxSizedRegion<1>:$dtorRegion); @@ -2366,6 +2423,7 @@ def CIR_GlobalOp : CIR_Op<"global", [ (`comdat` $comdat^)? ($tls_model^)? (`dso_local` $dso_local^)? + (`static_local_guard` `` $static_local_guard^)? $sym_name custom($sym_type, $initial_value, $ctorRegion, $dtorRegion) @@ -2433,19 +2491,27 @@ def CIR_GetGlobalOp : CIR_Op<"get_global", [ Addresses of thread local globals can only be retrieved if this operation is marked `thread_local`, which indicates the address isn't constant. + The `static_local` attribute indicates that this global is a function-local + static variable that requires guarded initialization (e.g., C++ static + local variables with non-constant initializers). + Example: ```mlir %x = cir.get_global @gv : !cir.ptr ... %y = cir.get_global thread_local @tls_gv : !cir.ptr + ... + %w = cir.get_global static_local @func_static : !cir.ptr ``` }]; - let arguments = (ins FlatSymbolRefAttr:$name, UnitAttr:$tls); + let arguments = (ins FlatSymbolRefAttr:$name, UnitAttr:$tls, + UnitAttr:$static_local); let results = (outs Res:$addr); let assemblyFormat = [{ (`thread_local` $tls^)? + (`static_local` $static_local^)? $name `:` qualified(type($addr)) attr-dict }]; } @@ -3267,6 +3333,7 @@ def CIR_LLVMIntrinsicCallOp : CIR_Op<"call_llvm_intrinsic"> { class CIR_CallOpBase extra_traits = []> : CIR_Op, DeclareOpInterfaceMethods, DeclareOpInterfaceMethods ])> { @@ -3286,15 +3353,6 @@ class CIR_CallOpBase extra_traits = []> mlir::cast(callee)); } - mlir::ArrayAttr getArgAttrsAttr() { return {}; } - ::mlir::ArrayAttr getResAttrsAttr() { return {}; } - - void setResAttrsAttr(::mlir::ArrayAttr attrs) {} - void setArgAttrsAttr(::mlir::ArrayAttr attrs) {} - - ::mlir::Attribute removeArgAttrsAttr() { return {}; } - ::mlir::Attribute removeResAttrsAttr() { return {}; } - bool isIndirect() { return !getCallee(); } mlir::Value getIndirectCall(); @@ -3320,7 +3378,10 @@ class CIR_CallOpBase extra_traits = []> dag commonArgs = (ins OptionalAttr:$callee, Variadic:$args, UnitAttr:$nothrow, - DefaultValuedAttr:$side_effect); + DefaultValuedAttr:$side_effect, + OptionalAttr:$arg_attrs, + OptionalAttr:$res_attrs + ); } def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { @@ -6167,6 +6228,7 @@ def CIR_TryOp : CIR_Op<"try",[ cir.call exception @function() : () -> () cir.yield } catch [type #cir.global_view<@_ZTIPf> : !cir.ptr] { + %1 = cir.catch_param : !cir.ptr ... cir.yield } unwind { @@ -6217,6 +6279,7 @@ def CIR_TryOp : CIR_Op<"try",[ }]> ]; + let hasVerifier = 1; let hasLLVMLowering = false; } @@ -6227,7 +6290,7 @@ def CIR_TryOp : CIR_Op<"try",[ def CIR_CatchParamOp : CIR_Op<"catch_param", [HasParent<"cir::TryOp">]> { let summary = "Represents the catch clause formal parameter"; let description = [{ - The `cir.catch_param` is used to retrieves the exception object inside + The `cir.catch_param` is used to retrieve the exception object inside the handler regions of `cir.try`. This operation is used only before the CFG flatterning pass. @@ -6310,6 +6373,57 @@ def CIR_EhTypeIdOp : CIR_Op<"eh.typeid", }]; } +//===----------------------------------------------------------------------===// +// Exception related: EhSetjmpOp +//===----------------------------------------------------------------------===// + +def CIR_EhSetjmpOp : CIR_Op<"eh.setjmp"> { + let summary = "CIR setjmp operation"; + let description = [{ + Saves call-site information (e.g., stack pointer, instruction pointer, + signal mask, and other registers) in memory at `env` for use by longjmp(). + In this case, setjmp() returns 0. Following a successful longjmp(), + execution proceeds from cir.eh.setjmp with the operation yielding a + non-zero value. + + Examples: + ```mlir + %0 = cir.eh.setjmp %arg0 : (!cir.ptr) -> !s32i + ``` + }]; + let arguments = (ins CIR_PointerType:$env); + + let results = (outs CIR_SInt32:$res); + + let assemblyFormat = [{ + $env `:` functional-type($env, results) attr-dict + }]; +} + +//===----------------------------------------------------------------------===// +// Exception related: EhLongjmpOp +//===----------------------------------------------------------------------===// + +def CIR_EhLongjmpOp : CIR_Op<"eh.longjmp"> { + let summary = "CIR longjmp operation"; + let description = [{ + Restore the environment (e.g., stack pointer, instruction pointer, + signal mask, and other registers) at the time of setjmp() call, by using + the information saved in `env` by setjmp(). + + Examples: + ```mlir + cir.eh.longjmp %arg0 : !cir.ptr + ``` + }]; + + let arguments = (ins CIR_PointerType:$env); + + let assemblyFormat = [{ + $env `:` qualified(type($env)) attr-dict + }]; +} + //===----------------------------------------------------------------------===// // Flattened EH Operations: EhInitiateOp //===----------------------------------------------------------------------===// @@ -6616,12 +6730,14 @@ def CIR_AtomicFetchOp : CIR_Op<"atomic.fetch", [ CIR_AnyIntOrFloatType:$val, CIR_AtomicFetchKind:$binop, Arg:$mem_order, + Arg:$sync_scope, UnitAttr:$is_volatile, UnitAttr:$fetch_first ); let assemblyFormat = [{ $binop $mem_order + `syncscope` `(` $sync_scope `)` (`fetch_first` $fetch_first^)? $ptr `,` $val (`volatile` $is_volatile^)? @@ -6704,6 +6820,9 @@ def CIR_AtomicCmpXchgOp : CIR_Op<"atomic.cmpxchg", [ when the exchange takes place. The `fail_order` attribute gives the memory order of this atomic operation when the exchange does not take place. + The `sync_scope` attribute specifies the synchronization scope for this + atomic operation. + The `weak` attribute is a boolean flag that indicates whether this is a "weak" compare-and-exchange operation. A weak compare-and-exchange operation allows "spurious failures", meaning that be treated as if the comparison @@ -6722,7 +6841,7 @@ def CIR_AtomicCmpXchgOp : CIR_Op<"atomic.cmpxchg", [ ```mlir %old, %success = cir.atomic.cmpxchg weak success(seq_cst) failure(acquire) - %ptr, %expected, %desired + syncscope(system) %ptr, %expected, %desired : (!cir.ptr, !u64i, !u64i) -> (!u64i, !cir.bool) ``` }]; @@ -6732,6 +6851,7 @@ def CIR_AtomicCmpXchgOp : CIR_Op<"atomic.cmpxchg", [ CIR_AnyType:$desired, Arg:$succ_order, Arg:$fail_order, + CIR_SyncScopeKind:$sync_scope, OptionalAttr:$alignment, UnitAttr:$weak, UnitAttr:$is_volatile); @@ -6739,6 +6859,7 @@ def CIR_AtomicCmpXchgOp : CIR_Op<"atomic.cmpxchg", [ let assemblyFormat = [{ (`weak` $weak^)? `success` `(` $succ_order `)` `failure` `(` $fail_order `)` + `syncscope` `(` $sync_scope `)` $ptr `,` $expected `,` $desired (`align` `(` $alignment^ `)`)? (`volatile` $is_volatile^)? diff --git a/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.h b/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.h new file mode 100644 index 0000000000000..4bfa8df7ad55a --- /dev/null +++ b/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.h @@ -0,0 +1,20 @@ +//===- ASTAttrInterfaces.h - CIR AST Interfaces -----------------*- 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 CLANG_CIR_INTERFACES_ASTATTRINTERFACES_H +#define CLANG_CIR_INTERFACES_ASTATTRINTERFACES_H + +#include "mlir/IR/Attributes.h" + +#include "clang/AST/Attr.h" +#include "clang/AST/DeclTemplate.h" + +/// Include the generated interface declarations. +#include "clang/CIR/Interfaces/ASTAttrInterfaces.h.inc" + +#endif // CLANG_CIR_INTERFACES_ASTATTRINTERFACES_H diff --git a/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td b/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td new file mode 100644 index 0000000000000..53909063abbab --- /dev/null +++ b/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td @@ -0,0 +1,39 @@ +//===- ASTAttrInterfaces.td - CIR AST Interface Definitions -----*- 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 MLIR_CIR_INTERFACES_ASTATTRINTERFACES_TD +#define MLIR_CIR_INTERFACES_ASTATTRINTERFACES_TD + +include "mlir/IR/OpBase.td" + +let cppNamespace = "::cir" in { + def ASTVarDeclInterface : AttrInterface<"ASTVarDeclInterface"> { + let methods = [ + InterfaceMethod<"", "bool", "isLocalVarDecl", (ins), [{}], + /*defaultImplementation=*/[{ + return $_attr.getAst()->isLocalVarDecl(); + }]>, + InterfaceMethod<"", "clang::VarDecl::TLSKind", "getTLSKind", + (ins), [{}], + /*defaultImplementation=*/[{ + return $_attr.getAst()->getTLSKind(); + }]>, + InterfaceMethod<"", "bool", "isInline", (ins), [{}], + /*defaultImplementation=*/[{ + return $_attr.getAst()->isInline(); + }]>, + InterfaceMethod<"", "clang::TemplateSpecializationKind", + "getTemplateSpecializationKind", (ins), [{}], + /*defaultImplementation=*/[{ + return $_attr.getAst()->getTemplateSpecializationKind(); + }]> + ]; + } +} // namespace cir + +#endif // MLIR_CIR_INTERFACES_ASTATTRINTERFACES_TD diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td index 4702b4ef08acc..c73eadaff6f3e 100644 --- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -21,7 +21,8 @@ let cppNamespace = "::cir" in { // The CIRCallOpInterface must be used instead of CallOpInterface when looking // at arguments and other bits of CallOp. This creates a level of abstraction // that's useful for handling indirect calls and other details. - def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", [CallOpInterface]> { + def CIRCallOpInterface + : OpInterface<"CIRCallOpInterface", [CallOpInterface]> { // Currently we don't have any methods defined in CIRCallOpInterface. We'll // add more methods as the upstreaming proceeds. let methods = [ diff --git a/clang/include/clang/CIR/Interfaces/CMakeLists.txt b/clang/include/clang/CIR/Interfaces/CMakeLists.txt index bc8d94ff9dc56..b7f8bd2f64cdb 100644 --- a/clang/include/clang/CIR/Interfaces/CMakeLists.txt +++ b/clang/include/clang/CIR/Interfaces/CMakeLists.txt @@ -3,6 +3,14 @@ # directory which is not the case for CIR (and also FIR, both have similar # workarounds). +function(add_clang_mlir_attr_interface interface) + set(LLVM_TARGET_DEFINITIONS ${interface}.td) + mlir_tablegen(${interface}.h.inc -gen-attr-interface-decls) + mlir_tablegen(${interface}.cpp.inc -gen-attr-interface-defs) + add_public_tablegen_target(MLIRCIR${interface}IncGen) + add_dependencies(mlir-generic-headers MLIRCIR${interface}IncGen) +endfunction() + function(add_clang_mlir_op_interface interface) set(LLVM_TARGET_DEFINITIONS ${interface}.td) mlir_tablegen(${interface}.h.inc -gen-op-interface-decls) @@ -19,6 +27,7 @@ function(add_clang_mlir_type_interface interface) add_dependencies(mlir-generic-headers MLIR${interface}IncGen) endfunction() +add_clang_mlir_attr_interface(ASTAttrInterfaces) add_clang_mlir_op_interface(CIROpInterfaces) add_clang_mlir_op_interface(CIRLoopOpInterface) add_clang_mlir_type_interface(CIRTypeInterfaces) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 5cb0991326a3c..59d0482d37f15 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -279,13 +279,21 @@ struct MissingFeatures { static bool emitNullabilityCheck() { return false; } static bool emitTypeCheck() { return false; } static bool emitTypeMetadataCodeForVCall() { return false; } + + // Fast math. + static bool fastMathGuard() { return false; } + // Should be implemented with a moduleOp level attribute and directly + // mapped to LLVM - those can be set directly for every relevant LLVM IR + // dialect operation (log10, ...). static bool fastMathFlags() { return false; } + static bool fastMathFuncAttributes() { return false; } static bool fpConstraints() { return false; } static bool generateDebugInfo() { return false; } static bool getRuntimeFunctionDecl() { return false; } static bool globalViewIndices() { return false; } static bool globalViewIntLowering() { return false; } + static bool guardAbortOnException() { return false; } static bool handleBuiltinICEArguments() { return false; } static bool hip() { return false; } static bool incrementProfileCounter() { return false; } @@ -380,6 +388,7 @@ struct MissingFeatures { // Future CIR attributes static bool optInfoAttr() { return false; } + static bool functionArgumentAttrs() { return false; } // Maybe only needed for Windows exception handling static bool currentFuncletPad() { return false; } diff --git a/clang/include/clang/DependencyScanning/DependencyScanningService.h b/clang/include/clang/DependencyScanning/DependencyScanningService.h index 371b862996706..20ba86bd097b0 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningService.h @@ -12,7 +12,6 @@ #include "clang/DependencyScanning/DependencyScanningFilesystem.h" #include "clang/DependencyScanning/InProcessModuleCache.h" #include "llvm/ADT/BitmaskEnum.h" -#include "llvm/Support/Chrono.h" namespace clang { namespace dependencies { @@ -77,26 +76,39 @@ enum class ScanningOptimizations { #undef DSS_LAST_BITMASK_ENUM +/// The configuration knobs for the dependency scanning service. +struct DependencyScanningServiceOptions { + DependencyScanningServiceOptions(); + + /// The function invoked to create each worker's VFS. This function and the + /// VFS itself must be thread-safe whenever using multiple workers + /// concurrently or whenever \c AsyncScanModules is true. + std::function()> + MakeVFS; // = [] { return llvm::vfs::createPhysicalFileSystem(); } + /// Whether to use optimized dependency directive scan or full preprocessing. + ScanningMode Mode = ScanningMode::DependencyDirectivesScan; + /// What output format are we expected to produce. + ScanningOutputFormat Format = ScanningOutputFormat::Full; + /// How to optimize resulting explicit module command lines. + ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default; + /// Whether the resulting command lines should load explicit PCMs eagerly. + bool EagerLoadModules = false; + /// Whether to trace VFS accesses during the scan. + bool TraceVFS = false; + /// Whether to scan modules asynchronously. + bool AsyncScanModules = false; + /// The build session timestamp for validate-once-per-build-session logic. + std::time_t BuildSessionTimestamp; // = std::chrono::system_clock::now(); +}; + /// The dependency scanning service contains shared configuration and state that /// is used by the individual dependency scanning workers. class DependencyScanningService { public: - DependencyScanningService( - ScanningMode Mode, ScanningOutputFormat Format, - ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default, - bool EagerLoadModules = false, bool TraceVFS = false, - std::time_t BuildSessionTimestamp = - llvm::sys::toTimeT(std::chrono::system_clock::now())); - - ScanningMode getMode() const { return Mode; } - - ScanningOutputFormat getFormat() const { return Format; } + explicit DependencyScanningService(DependencyScanningServiceOptions Opts) + : Opts(std::move(Opts)) {} - ScanningOptimizations getOptimizeArgs() const { return OptimizeArgs; } - - bool shouldEagerLoadModules() const { return EagerLoadModules; } - - bool shouldTraceVFS() const { return TraceVFS; } + const DependencyScanningServiceOptions &getOpts() const { return Opts; } DependencyScanningFilesystemSharedCache &getSharedCache() { return SharedCache; @@ -104,23 +116,13 @@ class DependencyScanningService { ModuleCacheEntries &getModuleCacheEntries() { return ModCacheEntries; } - std::time_t getBuildSessionTimestamp() const { return BuildSessionTimestamp; } - private: - const ScanningMode Mode; - const ScanningOutputFormat Format; - /// Whether to optimize the modules' command-line arguments. - const ScanningOptimizations OptimizeArgs; - /// Whether to set up command-lines to load PCM files eagerly. - const bool EagerLoadModules; - /// Whether to trace VFS accesses. - const bool TraceVFS; + /// The options customizing dependency scanning behavior. + DependencyScanningServiceOptions Opts; /// The global file system cache. DependencyScanningFilesystemSharedCache SharedCache; /// The global module cache entries. ModuleCacheEntries ModCacheEntries; - /// The build session timestamp. - std::time_t BuildSessionTimestamp; }; } // end namespace dependencies diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index 2a48f342335de..e1e0c14c7b52c 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -87,9 +87,7 @@ class DependencyScanningWorker { /// Construct a dependency scanning worker. /// /// @param Service The parent service. Must outlive the worker. - /// @param BaseFS The filesystem for the worker to use. - DependencyScanningWorker(DependencyScanningService &Service, - IntrusiveRefCntPtr BaseFS); + DependencyScanningWorker(DependencyScanningService &Service); ~DependencyScanningWorker(); diff --git a/clang/include/clang/Driver/SyclInstallationDetector.h b/clang/include/clang/Driver/SyclInstallationDetector.h index 6925ec24bcd29..f92228817f045 100644 --- a/clang/include/clang/Driver/SyclInstallationDetector.h +++ b/clang/include/clang/Driver/SyclInstallationDetector.h @@ -21,6 +21,14 @@ class SYCLInstallationDetector { void addSYCLIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const; + + // Return the filesystem path to the SYCL runtime library (libsycl.so), that + // was detected. + StringRef getSYCLRTLibPath() const { return SYCLRTLibPath; } + +private: + const Driver &D; + SmallString<0> SYCLRTLibPath; }; } // namespace driver diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 57ae14304b969..ca0bd47d10613 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2477,7 +2477,13 @@ struct FormatStyle { /// initializer1(), /// initializer2() /// \endcode - BCIS_AfterColon + BCIS_AfterColon, + /// Break constructor initializers only after the commas. + /// \code + /// Constructor() : initializer1(), + /// initializer2() + /// \endcode + BCIS_AfterComma }; /// The break constructor initializers style to use. diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 423f2ffe2f852..0459a863bc08d 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -627,7 +627,7 @@ class Lexer : public PreprocessorLexer { /// LexTokenInternal - Internal interface to lex a preprocessing token. Called /// by Lex. /// - bool LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine); + bool LexTokenInternal(Token &Result); bool CheckUnicodeWhitespace(Token &Result, uint32_t C, const char *CurPtr); @@ -762,12 +762,9 @@ class Lexer : public PreprocessorLexer { bool LexCharConstant (Token &Result, const char *CurPtr, tok::TokenKind Kind); bool LexEndOfFile (Token &Result, const char *CurPtr); - bool SkipWhitespace (Token &Result, const char *CurPtr, - bool &TokAtPhysicalStartOfLine); - bool SkipLineComment (Token &Result, const char *CurPtr, - bool &TokAtPhysicalStartOfLine); - bool SkipBlockComment (Token &Result, const char *CurPtr, - bool &TokAtPhysicalStartOfLine); + bool SkipWhitespace(Token &Result, const char *CurPtr); + bool SkipLineComment(Token &Result, const char *CurPtr); + bool SkipBlockComment(Token &Result, const char *CurPtr); bool SaveLineComment (Token &Result, const char *CurPtr); bool IsStartOfConflictMarker(const char *CurPtr); diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 5fb83eafc6b2a..b6e42a6151ac3 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -137,30 +137,6 @@ struct CXXStandardLibraryVersionInfo { std::uint64_t Version; }; -/// Record the previous 'export' keyword info. -/// -/// Since P1857R3, the standard introduced several rules to determine whether -/// the 'module', 'export module', 'import', 'export import' is a valid -/// directive introducer. This class is used to record the previous 'export' -/// keyword token, and then handle 'export module' and 'export import'. -class ExportContextualKeywordInfo { - Token ExportTok; - bool AtPhysicalStartOfLine = false; - -public: - ExportContextualKeywordInfo() = default; - ExportContextualKeywordInfo(const Token &Tok, bool AtPhysicalStartOfLine) - : ExportTok(Tok), AtPhysicalStartOfLine(AtPhysicalStartOfLine) {} - - bool isValid() const { return ExportTok.is(tok::kw_export); } - bool isAtPhysicalStartOfLine() const { return AtPhysicalStartOfLine; } - Token getExportTok() const { return ExportTok; } - void reset() { - ExportTok.startToken(); - AtPhysicalStartOfLine = false; - } -}; - class ModuleNameLoc final : llvm::TrailingObjects { friend TrailingObjects; @@ -415,7 +391,7 @@ class Preprocessor { bool ImportingCXXNamedModules = false; /// Whether the last token we lexed was an 'export' keyword. - ExportContextualKeywordInfo LastTokenWasExportKeyword; + Token LastExportKeyword; /// First pp-token source location in current translation unit. SourceLocation FirstPPTokenLoc; @@ -1869,8 +1845,7 @@ class Preprocessor { /// This consumes the import/module directive, modifies the /// lexer/preprocessor state, and advances the lexer(s) so that the next token /// read is the correct one. - bool HandleModuleContextualKeyword(Token &Result, - bool TokAtPhysicalStartOfLine); + bool HandleModuleContextualKeyword(Token &Result); /// Get the start location of the first pp-token in main file. SourceLocation getMainFileFirstPPTokenLoc() const { diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index d09e951908129..9904b271c200e 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -92,6 +92,8 @@ class Token { HasSeenNoTrivialPPDirective = 0x1000, // Whether we've seen any 'no-trivial' pp-directives before // current position. + PhysicalStartOfLine = + 0x2000, // This token is at the start of a physical line. }; tok::TokenKind getKind() const { return Kind; } @@ -283,6 +285,10 @@ class Token { /// bool isAtStartOfLine() const { return getFlag(StartOfLine); } + /// isAtPhysicalStartOfLine - Return true if this token is at the start of a + /// physical line. + bool isAtPhysicalStartOfLine() const { return getFlag(PhysicalStartOfLine); } + /// Return true if this token has whitespace before it. /// bool hasLeadingSpace() const { return getFlag(LeadingSpace); } diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 40251a65f8f70..24b31fb3fefcc 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -849,7 +849,7 @@ def O_flag : Flag<["-"], "O">, Visibility<[ClangOption, CC1Option, FC1Option]>, def Ofast : Joined<["-"], "Ofast">, Group, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>, HelpTextForVariants<[FlangOption, FC1Option], - "Deprecated; use '-O3 -ffast-math -fstack-arrays' for the same behavior," + "Deprecated; use '-O3 -ffast-math -fstack-arrays -fno-protect-parens' for the same behavior," " or '-O3 -fstack-arrays' to enable only conforming optimizations">, HelpText<"Deprecated; use '-O3 -ffast-math' for the same behavior," " or '-O3' to enable only conforming optimizations">; @@ -1967,6 +1967,14 @@ defm lifetime_safety : BoolFOption< NegFlag, BothFlags<[], [CC1Option], " lifetime safety for C++">>; +def lifetime_safety_max_cfg_blocks + : Joined<["-"], "lifetime-safety-max-cfg-blocks=">, + Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Skip LifetimeSafety analysis for functions with CFG block " + "count exceeding this threshold. Specify 0 for no limit.">, + MarshallingInfoInt>; + defm lifetime_safety_inference : BoolFOption<"lifetime-safety-inference", LangOpts<"EnableLifetimeSafetyInference">, DefaultFalse, @@ -3645,10 +3653,6 @@ defm implicit_modules : BoolFOption<"implicit-modules", NegFlag, PosFlag, BothFlags< [NoXarchOption], [ClangOption, CLOption]>>; -def fno_modules_check_relocated : Joined<["-"], "fno-modules-check-relocated">, - Group, Visibility<[ClangOption, CC1Option]>, - HelpText<"Skip checks for relocated modules when loading PCM files">, - MarshallingInfoNegativeFlag>; def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-system-headers">, Group, Visibility<[ClangOption, CC1Option]>, MarshallingInfoFlag>; @@ -6649,6 +6653,9 @@ def _param_EQ : Joined<["--"], "param=">, Alias<_param>; def _precompile : Flag<["--"], "precompile">, Flags<[NoXarchOption]>, Visibility<[ClangOption, CLOption]>, Group, HelpText<"Only precompile the input">; +def _precompile_reduced_bmi : Flag<["--"], "precompile-reduced-bmi">, Flags<[NoXarchOption]>, + Visibility<[ClangOption, CLOption]>, + Group, HelpText<"Only precompile the input as a reduced BMI">; def _prefix_EQ : Joined<["--"], "prefix=">, Alias; def _prefix : Separate<["--"], "prefix">, Alias; def _preprocess : Flag<["--"], "preprocess">, Alias; @@ -8988,6 +8995,11 @@ def disable_pragma_debug_crash : Flag<["-"], "disable-pragma-debug-crash">, def source_date_epoch : Separate<["-"], "source-date-epoch">, MetaVarName<"