88 changes: 88 additions & 0 deletions .github/workflows/release-documentation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Release Documentation

permissions:
contents: read

on:
workflow_dispatch:
inputs:
release-version:
description: 'Release Version'
required: true
type: string
upload:
description: 'Upload documentation'
required: false
type: boolean

workflow_call:
inputs:
release-version:
description: 'Release Version'
required: true
type: string
upload:
description: 'Upload documentation'
required: false
type: boolean

jobs:
release-documentation:
name: Build and Upload Release Documentation
runs-on: ubuntu-latest
env:
upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }}
steps:
- name: Checkout LLVM
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

- name: Setup Python env
uses: actions/setup-python@v4
with:
cache: 'pip'
cache-dependency-path: './llvm/docs/requirements.txt'

- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
graphviz \
python3-github \
ninja-build \
texlive-font-utils
pip3 install --user -r ./llvm/docs/requirements.txt
- name: Build Documentation
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-doxygen
- name: Create Release Notes Artifact
uses: actions/upload-artifact@v3
with:
name: release-notes
path: docs-build/html-export/

- name: Clone www-releases
if: env.upload
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: ${{ github.repository_owner }}/www-releases
ref: main
fetch-depth: 0
path: www-releases

- name: Upload Release Notes
if: env.upload
env:
WWW_RELEASES_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }}
run: |
mkdir -p ../www-releases/${{ inputs.release-version }}
mv ./docs-build/html-export/* ../www-releases/${{ inputs.release-version }}
cd ../www-releases
git add ${{ inputs.release-version }}
git config user.email "llvmbot@llvm.org"
git config user.name "llvmbot"
git commit -a -m "Add ${{ inputs.release-version }} documentation"
git push "https://$WWW_RELEASES_TOKEN@github.com/${{ github.repository_owner }}/www-releases" main:main
67 changes: 67 additions & 0 deletions .github/workflows/release-doxygen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Release Doxygen

permissions:
contents: read

on:
workflow_dispatch:
inputs:
release-version:
description: 'Release Version'
required: true
type: string
upload:
description: 'Upload documentation'
required: false
type: boolean

workflow_call:
inputs:
release-version:
description: 'Release Version'
required: true
type: string
upload:
description: 'Upload documentation'
required: false
type: boolean

jobs:
release-doxygen:
name: Build and Upload Release Doxygen
runs-on: ubuntu-latest
permissions:
contents: write
env:
upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }}
steps:
- name: Checkout LLVM
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

- name: Setup Python env
uses: actions/setup-python@v4
with:
cache: 'pip'
cache-dependency-path: './llvm/docs/requirements.txt'

- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
doxygen \
graphviz \
python3-github \
ninja-build \
texlive-font-utils
pip3 install --user -r ./llvm/docs/requirements.txt
- name: Build Doxygen
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-sphinx
- name: Upload Doxygen
if: env.upload
run: |
./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" upload --files ./*doxygen*.tar.xz
74 changes: 74 additions & 0 deletions .github/workflows/release-lit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Release Lit

permissions:
contents: read

on:
workflow_dispatch:
inputs:
release-version:
description: 'Release Version'
required: true
type: string

workflow_call:
inputs:
release-version:
description: 'Release Version'
required: true
type: string

jobs:
release-lit:
name: Release Lit
runs-on: ubuntu-latest
steps:
- name: Checkout LLVM
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: "llvmorg-${{ inputs.release-version }}"

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3-setuptools python3-psutil python3-github
- name: Check Permissions
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} check-permissions
- name: Setup Cpp
uses: aminya/setup-cpp@v1
with:
compiler: llvm-16.0.6
cmake: true
ninja: true

- name: Test lit
run: |
mkdir build && cd build
export FILECHECK_OPTS='-dump-input-filter=all -vv -color'
cmake ../llvm -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja -v -j $(nproc) check-lit
- name: Package lit
run: |
cd llvm/utils/lit
# Remove 'dev' suffix from lit version.
sed -i 's/ + "dev"//g' lit/__init__.py
python3 setup.py sdist
- name: Upload lit to test.pypi.org
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.LLVM_LIT_TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: llvm/utils/lit/dist/

- name: Upload lit to pypi.org
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.LLVM_LIT_PYPI_API_TOKEN }}
packages-dir: llvm/utils/lit/dist/
142 changes: 50 additions & 92 deletions .github/workflows/release-tasks.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Release Task

permissions:
contents: read
contents: write

on:
push:
Expand All @@ -10,112 +10,70 @@ on:
- 'llvmorg-*'

jobs:
release-tasks:
permissions:
contents: write # To upload assets to release.
validate-tag:
name: Validate Tag
runs-on: ubuntu-latest
if: github.repository == 'llvm/llvm-project'
outputs:
release-version: ${{ steps.validate-tag.outputs.release-version }}
steps:
- name: Validate Tag
id: validate-tag
run: |
test "${{ github.actor }}" = "tstellar" || test "${{ github.actor }}" = "tru"
echo "${{ github.ref_name }}" | grep -e '^llvmorg-[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc[0-9]\+\)\?$'
release_version=$(echo "${{ github.ref_name }}" | sed 's/llvmorg-//g')
echo "release-version=$release_version" >> "$GITHUB_OUTPUT"
- name: Checkout LLVM
uses: actions/checkout@v4

- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
doxygen \
graphviz \
python3-github \
ninja-build \
texlive-font-utils
pip3 install --user -r ./llvm/docs/requirements.txt
- name: Create Release
run: |
./llvm/utils/release/./github-upload-release.py --token ${{ github.token }} --release ${{ steps.validate-tag.outputs.release-version }} create
- name: Build Documentation
run: |
./llvm/utils/release/build-docs.sh -release ${{ steps.validate-tag.outputs.release-version }}
./llvm/utils/release/github-upload-release.py --token ${{ github.token }} --release ${{ steps.validate-tag.outputs.release-version }} upload --files ./*doxygen*.tar.xz
- name: Create Release Notes Artifact
uses: actions/upload-artifact@v3
with:
name: release-notes
path: docs-build/html-export/

- name: Clone www-releases
if: ${{ !contains(steps.validate-tag.outputs.release-version, 'rc') }}
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/www-releases
ref: main
fetch-depth: 0
path: www-releases

- name: Upload Release Notes
if: ${{ !contains(steps.validate-tag.outputs.release-version, 'rc') }}
run: |
mkdir -p ../www-releases/${{ steps.validate-tag.outputs.release-version }}
mv ./docs-build/html-export/* ../www-releases/${{ steps.validate-tag.outputs.release-version }}
cd ../www-releases
git add ${{ steps.validate-tag.outputs.release-version }}
git config user.email "llvmbot@llvm.org"
git config user.name "llvmbot"
git commit -a -m "Add ${{ steps.validate-tag.outputs.release-version }} documentation"
git push https://${{ secrets.WWW_RELEASES_TOKEN }}@github.com/${{ github.repository_owner }}/www-releases main:main
release-lit:
release-create:
name: Create a New Release
runs-on: ubuntu-latest
if: github.repository == 'llvm/llvm-project'
needs: validate-tag
steps:
- name: Checkout LLVM
uses: actions/checkout@v4

- name: Setup Cpp
uses: aminya/setup-cpp@v1
with:
compiler: llvm-16.0.6
cmake: true
ninja: true

- name: Install dependencies
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3-setuptools python3-psutil
sudo apt-get install python3-github
- name: Test lit
run: |
mkdir build && cd build
export FILECHECK_OPTS='-dump-input-filter=all -vv -color'
cmake ../llvm -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja -v -j $(nproc) check-lit
- name: Checkout LLVM
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

- name: Package lit
- name: Create Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
cd llvm/utils/lit
# Remove 'dev' suffix from lit version.
sed -i 's/ + "dev"//g' lit/__init__.py
python3 setup.py sdist
- name: Upload lit to test.pypi.org
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.LLVM_LIT_TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: llvm/utils/lit/dist/
./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --release ${{ needs.validate-tag.outputs.release-version }} --user ${{ github.actor }} create
release-documentation:
name: Build and Upload Release Documentation
needs:
- validate-tag
uses: ./.github/workflows/release-documentation.yml
with:
release-version: ${{ needs.validate-tag.outputs.release-version }}
upload: true

release-doxygen:
name: Build and Upload Release Doxygen
needs:
- validate-tag
- release-create
uses: ./.github/workflows/release-doxygen.yml
with:
release-version: ${{ needs.validate-tag.outputs.release-version }}
upload: true

- name: Upload lit to pypi.org
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.LLVM_LIT_PYPI_API_TOKEN }}
packages-dir: llvm/utils/lit/dist/
release-lit:
name: Release Lit
needs: validate-tag
uses: ./.github/workflows/release-lit.yml
with:
release-version: ${{ needs.validate-tag.outputs.release-version }}

release-binaries:
name: Build Release Binaries
needs:
- validate-tag
- release-create
uses: ./.github/workflows/release-binaries.yml
with:
release-version: ${{ needs.validate-tag.outputs.release-version }}
upload: true
10 changes: 2 additions & 8 deletions .github/workflows/set-release-binary-outputs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,8 @@ if [ -z "$GITHUB_OUTPUT" ]; then
echo "Writing output variables to $GITHUB_OUTPUT"
fi

github_user=$1
tag=$2
upload=$3

if [[ "$github_user" != "tstellar" && "$github_user" != "tru" ]]; then
echo "ERROR: User not allowed: $github_user"
exit 1
fi
tag=$1
upload=$2

if echo $tag | grep -e '^[0-9a-f]\+$'; then
# This is a plain commit.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# The LLVM Compiler Infrastructure

[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8273/badge)](https://www.bestpractices.dev/projects/8273)
[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml?query=event%3Aschedule)

Welcome to the LLVM project!

Expand Down
97 changes: 97 additions & 0 deletions bolt/docs/BAT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# BOLT Address Translation (BAT)
# Purpose
A regular profile collection for BOLT involves collecting samples from
unoptimized binary. BOLT Address Translation allows collecting profile
from BOLT-optimized binary and using it for optimizing the input (pre-BOLT)
binary.

# Overview
BOLT Address Translation is an extra section (`.note.bolt_bat`) inserted by BOLT
into the output binary containing translation tables and split functions linkage
information. This information enables mapping the profile back from optimized
binary onto the original binary.

# Usage
`--enable-bat` flag controls the generation of BAT section. Sampled profile
needs to be passed along with the optimized binary containing BAT section to
`perf2bolt` which reads BAT section and produces fdata profile for the original
binary. Note that YAML profile generation is not supported since BAT doesn't
contain the metadata for input functions.

# Internals
## Section contents
The section is organized as follows:
- Hot functions table
- Address translation tables
- Cold functions table

## Construction and parsing
BAT section is created from `BoltAddressTranslation` class which captures
address translation information provided by BOLT linker. It is then encoded as a
note section in the output binary.

During profile conversion when BAT-enabled binary is passed to perf2bolt,
`BoltAddressTranslation` class is populated from BAT section. The class is then
queried by `DataAggregator` during sample processing to reconstruct addresses/
offsets in the input binary.

## Encoding format
The encoding is specified in
[BoltAddressTranslation.h](/bolt/include/bolt/Profile/BoltAddressTranslation.h)
and [BoltAddressTranslation.cpp](/bolt/lib/Profile/BoltAddressTranslation.cpp).

### Layout
The general layout is as follows:
```
Hot functions table header
|------------------|
| Function entry |
| |--------------| |
| | OutOff InOff | |
| |--------------| |
~~~~~~~~~~~~~~~~~~~~
Cold functions table header
|------------------|
| Function entry |
| |--------------| |
| | OutOff InOff | |
| |--------------| |
~~~~~~~~~~~~~~~~~~~~
```

### Functions table
Hot and cold functions tables share the encoding except difference marked below.
Header:
| Entry | Encoding | Description |
| ------ | ----- | ----------- |
| `NumFuncs` | ULEB128 | Number of functions in the functions table |

The header is followed by Functions table with `NumFuncs` entries.
Output binary addresses are delta encoded, meaning that only the difference with
the last previous output address is stored. Addresses implicitly start at zero.
Output addresses are continuous through function start addresses and function
internal offsets, and between hot and cold fragments, to better spread deltas
and save space.

Hot indices are delta encoded, implicitly starting at zero.
| Entry | Encoding | Description |
| ------ | ------| ----------- |
| `Address` | Continuous, Delta, ULEB128 | Function address in the output binary |
| `HotIndex` | Delta, ULEB128 | Cold functions only: index of corresponding hot function in hot functions table |
| `NumEntries` | ULEB128 | Number of address translation entries for a function |

Function header is followed by `NumEntries` pairs of offsets for current
function.

### Address translation table
Delta encoding means that only the difference with the previous corresponding
entry is encoded. Input offsets implicitly start at zero.
| Entry | Encoding | Description |
| ------ | ------| ----------- |
| `OutputOffset` | Continuous, Delta, ULEB128 | Function offset in output binary |
| `InputOffset` | Delta, SLEB128 | Function offset in input binary with `BRANCHENTRY` LSB bit |

`BRANCHENTRY` bit denotes whether a given offset pair is a control flow source
(branch or call instruction). If not set, it signifies a control flow target
(basic block offset).
14 changes: 13 additions & 1 deletion bolt/include/bolt/Profile/BoltAddressTranslation.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataExtractor.h"
#include <cstdint>
#include <map>
#include <optional>
Expand Down Expand Up @@ -118,14 +119,25 @@ class BoltAddressTranslation {
void writeEntriesForBB(MapTy &Map, const BinaryBasicBlock &BB,
uint64_t FuncAddress);

/// Write the serialized address translation table for a function.
template <bool Cold>
void writeMaps(std::map<uint64_t, MapTy> &Maps, uint64_t &PrevAddress,
raw_ostream &OS);

/// Read the serialized address translation table for a function.
/// Return a parse error if failed.
template <bool Cold>
void parseMaps(std::vector<uint64_t> &HotFuncs, uint64_t &PrevAddress,
DataExtractor &DE, uint64_t &Offset, Error &Err);

std::map<uint64_t, MapTy> Maps;

/// Links outlined cold bocks to their original function
std::map<uint64_t, uint64_t> ColdPartSource;

/// Identifies the address of a control-flow changing instructions in a
/// translation map entry
const static uint32_t BRANCHENTRY = 0x80000000;
const static uint32_t BRANCHENTRY = 0x1;
};
} // namespace bolt

Expand Down
33 changes: 15 additions & 18 deletions bolt/lib/Passes/CacheMetrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,20 @@
#include "bolt/Passes/CacheMetrics.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
#include "llvm/Support/CommandLine.h"
#include <unordered_map>

using namespace llvm;
using namespace bolt;

namespace opts {

extern cl::OptionCategory BoltOptCategory;

extern cl::opt<unsigned> ITLBPageSize;
extern cl::opt<unsigned> ITLBEntries;

} // namespace opts

namespace {

/// The following constants are used to estimate the number of i-TLB cache
/// misses for a given code layout. Empirically the values result in high
/// correlations between the estimations and the perf measurements.
/// The constants do not affect the code layout algorithms.
constexpr unsigned ITLBPageSize = 4096;
constexpr unsigned ITLBEntries = 16;

/// Initialize and return a position map for binary basic blocks
void extractBasicBlockInfo(
const std::vector<BinaryFunction *> &BinaryFunctions,
Expand Down Expand Up @@ -133,9 +130,6 @@ double expectedCacheHitRatio(
const std::vector<BinaryFunction *> &BinaryFunctions,
const std::unordered_map<BinaryBasicBlock *, uint64_t> &BBAddr,
const std::unordered_map<BinaryBasicBlock *, uint64_t> &BBSize) {

const double PageSize = opts::ITLBPageSize;
const uint64_t CacheEntries = opts::ITLBEntries;
std::unordered_map<const BinaryFunction *, Predecessors> Calls =
extractFunctionCalls(BinaryFunctions);
// Compute 'hotness' of the functions
Expand All @@ -155,7 +149,8 @@ double expectedCacheHitRatio(
for (BinaryFunction *BF : BinaryFunctions) {
if (BF->getLayout().block_empty())
continue;
double Page = BBAddr.at(BF->getLayout().block_front()) / PageSize;
const uint64_t Page =
BBAddr.at(BF->getLayout().block_front()) / ITLBPageSize;
PageSamples[Page] += FunctionSamples.at(BF);
}

Expand All @@ -166,15 +161,17 @@ double expectedCacheHitRatio(
if (BF->getLayout().block_empty() || FunctionSamples.at(BF) == 0.0)
continue;
double Samples = FunctionSamples.at(BF);
double Page = BBAddr.at(BF->getLayout().block_front()) / PageSize;
const uint64_t Page =
BBAddr.at(BF->getLayout().block_front()) / ITLBPageSize;
// The probability that the page is not present in the cache
double MissProb = pow(1.0 - PageSamples[Page] / TotalSamples, CacheEntries);
const double MissProb =
pow(1.0 - PageSamples[Page] / TotalSamples, ITLBEntries);

// Processing all callers of the function
for (std::pair<BinaryFunction *, uint64_t> Pair : Calls[BF]) {
BinaryFunction *SrcFunction = Pair.first;
double SrcPage =
BBAddr.at(SrcFunction->getLayout().block_front()) / PageSize;
const uint64_t SrcPage =
BBAddr.at(SrcFunction->getLayout().block_front()) / ITLBPageSize;
// Is this a 'long' or a 'short' call?
if (Page != SrcPage) {
// This is a miss
Expand Down
147 changes: 83 additions & 64 deletions bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Core/BinaryFunction.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/LEB128.h"

#define DEBUG_TYPE "bolt-bat"

Expand Down Expand Up @@ -44,7 +45,7 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
// and this deleted block will both share the same output address (the same
// key), and we need to map back. We choose here to privilege the successor by
// allowing it to overwrite the previously inserted key in the map.
Map[BBOutputOffset] = BBInputOffset;
Map[BBOutputOffset] = BBInputOffset << 1;

const auto &IOAddressMap =
BB.getFunction()->getBinaryContext().getIOAddressMap();
Expand All @@ -61,8 +62,8 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,

LLVM_DEBUG(dbgs() << " Key: " << Twine::utohexstr(OutputOffset) << " Val: "
<< Twine::utohexstr(InputOffset) << " (branch)\n");
Map.insert(
std::pair<uint32_t, uint32_t>(OutputOffset, InputOffset | BRANCHENTRY));
Map.insert(std::pair<uint32_t, uint32_t>(OutputOffset,
(InputOffset << 1) | BRANCHENTRY));
}
}

Expand Down Expand Up @@ -101,36 +102,53 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
}
}

const uint32_t NumFuncs = Maps.size();
OS.write(reinterpret_cast<const char *>(&NumFuncs), 4);
LLVM_DEBUG(dbgs() << "Writing " << NumFuncs << " functions for BAT.\n");
// Output addresses are delta-encoded
uint64_t PrevAddress = 0;
writeMaps</*Cold=*/false>(Maps, PrevAddress, OS);
writeMaps</*Cold=*/true>(Maps, PrevAddress, OS);

outs() << "BOLT-INFO: Wrote " << Maps.size() << " BAT maps\n";
}

template <bool Cold>
void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
uint64_t &PrevAddress, raw_ostream &OS) {
const uint32_t NumFuncs =
llvm::count_if(llvm::make_first_range(Maps), [&](const uint64_t Address) {
return Cold == ColdPartSource.count(Address);
});
encodeULEB128(NumFuncs, OS);
LLVM_DEBUG(dbgs() << "Writing " << NumFuncs << (Cold ? " cold" : "")
<< " functions for BAT.\n");
size_t PrevIndex = 0;
for (auto &MapEntry : Maps) {
const uint64_t Address = MapEntry.first;
// Only process cold fragments in cold mode, and vice versa.
if (Cold != ColdPartSource.count(Address))
continue;
MapTy &Map = MapEntry.second;
const uint32_t NumEntries = Map.size();
LLVM_DEBUG(dbgs() << "Writing " << NumEntries << " entries for 0x"
<< Twine::utohexstr(Address) << ".\n");
OS.write(reinterpret_cast<const char *>(&Address), 8);
OS.write(reinterpret_cast<const char *>(&NumEntries), 4);
encodeULEB128(Address - PrevAddress, OS);
PrevAddress = Address;
if (Cold) {
size_t HotIndex =
std::distance(ColdPartSource.begin(), ColdPartSource.find(Address));
encodeULEB128(HotIndex - PrevIndex, OS);
PrevIndex = HotIndex;
}
encodeULEB128(NumEntries, OS);
uint64_t InOffset = 0;
// Output and Input addresses and delta-encoded
for (std::pair<const uint32_t, uint32_t> &KeyVal : Map) {
OS.write(reinterpret_cast<const char *>(&KeyVal.first), 4);
OS.write(reinterpret_cast<const char *>(&KeyVal.second), 4);
const uint64_t OutputAddress = KeyVal.first + Address;
encodeULEB128(OutputAddress - PrevAddress, OS);
PrevAddress = OutputAddress;
encodeSLEB128(KeyVal.second - InOffset, OS);
InOffset = KeyVal.second;
}
}
const uint32_t NumColdEntries = ColdPartSource.size();
LLVM_DEBUG(dbgs() << "Writing " << NumColdEntries
<< " cold part mappings.\n");
OS.write(reinterpret_cast<const char *>(&NumColdEntries), 4);
for (std::pair<const uint64_t, uint64_t> &ColdEntry : ColdPartSource) {
OS.write(reinterpret_cast<const char *>(&ColdEntry.first), 8);
OS.write(reinterpret_cast<const char *>(&ColdEntry.second), 8);
LLVM_DEBUG(dbgs() << " " << Twine::utohexstr(ColdEntry.first) << " -> "
<< Twine::utohexstr(ColdEntry.second) << "\n");
}

outs() << "BOLT-INFO: Wrote " << Maps.size() << " BAT maps\n";
outs() << "BOLT-INFO: Wrote " << NumColdEntries
<< " BAT cold-to-hot entries\n";
}

std::error_code BoltAddressTranslation::parse(StringRef Buf) {
Expand All @@ -152,53 +170,54 @@ std::error_code BoltAddressTranslation::parse(StringRef Buf) {
if (Name.substr(0, 4) != "BOLT")
return make_error_code(llvm::errc::io_error);

if (Buf.size() - Offset < 4)
return make_error_code(llvm::errc::io_error);
Error Err(Error::success());
std::vector<uint64_t> HotFuncs;
uint64_t PrevAddress = 0;
parseMaps</*Cold=*/false>(HotFuncs, PrevAddress, DE, Offset, Err);
parseMaps</*Cold=*/true>(HotFuncs, PrevAddress, DE, Offset, Err);
outs() << "BOLT-INFO: Parsed " << Maps.size() << " BAT entries\n";
return errorToErrorCode(std::move(Err));
}

const uint32_t NumFunctions = DE.getU32(&Offset);
LLVM_DEBUG(dbgs() << "Parsing " << NumFunctions << " functions\n");
template <bool Cold>
void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
uint64_t &PrevAddress, DataExtractor &DE,
uint64_t &Offset, Error &Err) {
const uint32_t NumFunctions = DE.getULEB128(&Offset, &Err);
LLVM_DEBUG(dbgs() << "Parsing " << NumFunctions << (Cold ? " cold" : "")
<< " functions\n");
size_t HotIndex = 0;
for (uint32_t I = 0; I < NumFunctions; ++I) {
if (Buf.size() - Offset < 12)
return make_error_code(llvm::errc::io_error);

const uint64_t Address = DE.getU64(&Offset);
const uint32_t NumEntries = DE.getU32(&Offset);
const uint64_t Address = PrevAddress + DE.getULEB128(&Offset, &Err);
PrevAddress = Address;
if (Cold) {
HotIndex += DE.getULEB128(&Offset, &Err);
ColdPartSource.emplace(Address, HotFuncs[HotIndex]);
} else {
HotFuncs.push_back(Address);
}
const uint32_t NumEntries = DE.getULEB128(&Offset, &Err);
MapTy Map;

LLVM_DEBUG(dbgs() << "Parsing " << NumEntries << " entries for 0x"
<< Twine::utohexstr(Address) << "\n");
if (Buf.size() - Offset < 8 * NumEntries)
return make_error_code(llvm::errc::io_error);
uint64_t InputOffset = 0;
for (uint32_t J = 0; J < NumEntries; ++J) {
const uint32_t OutputAddr = DE.getU32(&Offset);
const uint32_t InputAddr = DE.getU32(&Offset);
Map.insert(std::pair<uint32_t, uint32_t>(OutputAddr, InputAddr));
LLVM_DEBUG(dbgs() << Twine::utohexstr(OutputAddr) << " -> "
<< Twine::utohexstr(InputAddr) << "\n");
const uint64_t OutputDelta = DE.getULEB128(&Offset, &Err);
const uint64_t OutputAddress = PrevAddress + OutputDelta;
const uint64_t OutputOffset = OutputAddress - Address;
PrevAddress = OutputAddress;
const int64_t InputDelta = DE.getSLEB128(&Offset, &Err);
InputOffset += InputDelta;
Map.insert(std::pair<uint32_t, uint32_t>(OutputOffset, InputOffset));
LLVM_DEBUG(
dbgs() << formatv("{0:x} -> {1:x} ({2}/{3}b -> {4}/{5}b), {6:x}\n",
OutputOffset, InputOffset, OutputDelta,
encodeULEB128(OutputDelta, nulls()), InputDelta,
encodeSLEB128(InputDelta, nulls()), OutputAddress));
}
Maps.insert(std::pair<uint64_t, MapTy>(Address, Map));
}

if (Buf.size() - Offset < 4)
return make_error_code(llvm::errc::io_error);

const uint32_t NumColdEntries = DE.getU32(&Offset);
LLVM_DEBUG(dbgs() << "Parsing " << NumColdEntries << " cold part mappings\n");
for (uint32_t I = 0; I < NumColdEntries; ++I) {
if (Buf.size() - Offset < 16)
return make_error_code(llvm::errc::io_error);
const uint32_t ColdAddress = DE.getU64(&Offset);
const uint32_t HotAddress = DE.getU64(&Offset);
ColdPartSource.insert(
std::pair<uint64_t, uint64_t>(ColdAddress, HotAddress));
LLVM_DEBUG(dbgs() << Twine::utohexstr(ColdAddress) << " -> "
<< Twine::utohexstr(HotAddress) << "\n");
}
outs() << "BOLT-INFO: Parsed " << Maps.size() << " BAT entries\n";
outs() << "BOLT-INFO: Parsed " << NumColdEntries
<< " BAT cold-to-hot entries\n";

return std::error_code();
}

void BoltAddressTranslation::dump(raw_ostream &OS) {
Expand All @@ -209,7 +228,7 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
OS << "BB mappings:\n";
for (const auto &Entry : MapEntry.second) {
const bool IsBranch = Entry.second & BRANCHENTRY;
const uint32_t Val = Entry.second & ~BRANCHENTRY;
const uint32_t Val = Entry.second >> 1; // dropping BRANCHENTRY bit
OS << "0x" << Twine::utohexstr(Entry.first) << " -> "
<< "0x" << Twine::utohexstr(Val);
if (IsBranch)
Expand Down Expand Up @@ -244,7 +263,7 @@ uint64_t BoltAddressTranslation::translate(uint64_t FuncAddress,

--KeyVal;

const uint32_t Val = KeyVal->second & ~BRANCHENTRY;
const uint32_t Val = KeyVal->second >> 1; // dropping BRANCHENTRY bit
// Branch source addresses are translated to the first instruction of the
// source BB to avoid accounting for modifications BOLT may have made in the
// BB regarding deletion/addition of instructions.
Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
Object
Support
DWARFLinker
DWARFLinkerClassic
AsmPrinter
TargetParser
)
Expand Down
11 changes: 7 additions & 4 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DWARFLinker/DWARFStreamer.h"
#include "llvm/DWARFLinker/Classic/DWARFStreamer.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
Expand Down Expand Up @@ -178,6 +178,9 @@ translateInputToOutputLocationList(const BinaryFunction &BF,
return MergedLL;
}

using namespace dwarf_linker;
using namespace dwarf_linker::classic;

namespace llvm {
namespace bolt {
/// Emits debug information into .debug_info or .debug_types section.
Expand Down Expand Up @@ -278,10 +281,10 @@ class DIEStreamer : public DwarfStreamer {

public:
DIEStreamer(DIEBuilder *DIEBldr, DWARFRewriter &Rewriter,
DWARFLinker::OutputFileType OutFileType,
DWARFLinkerBase::OutputFileType OutFileType,
raw_pwrite_stream &OutFile,
std::function<StringRef(StringRef Input)> Translator,
DWARFLinker::messageHandler Warning)
DWARFLinkerBase::MessageHandlerTy Warning)
: DwarfStreamer(OutFileType, OutFile, Translator, Warning),
DIEBldr(DIEBldr), Rewriter(Rewriter){};

Expand Down Expand Up @@ -457,7 +460,7 @@ createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
DWARFRewriter &Rewriter) {

std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
&DIEBldr, Rewriter, llvm::DWARFLinker::OutputFileType::Object, OutFile,
&DIEBldr, Rewriter, DWARFLinkerBase::OutputFileType::Object, OutFile,
[](StringRef Input) -> StringRef { return Input; },
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {});
Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName);
Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4112,6 +4112,7 @@ void RewriteInstance::encodeBATSection() {
copyByteArray(BoltInfo), BoltInfo.size(),
/*Alignment=*/1,
/*IsReadOnly=*/true, ELF::SHT_NOTE);
outs() << "BOLT-INFO: BAT section size (bytes): " << BoltInfo.size() << '\n';
}

template <typename ELFShdrTy>
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/RISCV/relax.s
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// CHECK: Binary Function "_start" after building cfg {
// CHECK: jal ra, near_f
// CHECK-NEXT: auipc ra, far_f@plt
// CHECK-NEXT: auipc ra, far_f
// CHECK-NEXT: jalr ra, 0xc(ra)
// CHECK-NEXT: j near_f

Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/bolt-address-translation.test
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#
# CHECK: BOLT: 3 out of 7 functions were overwritten.
# CHECK: BOLT-INFO: Wrote 6 BAT maps
# CHECK: BOLT-INFO: Wrote 3 BAT cold-to-hot entries
# CHECK: BOLT-INFO: BAT section size (bytes): 404
#
# usqrt mappings (hot part). We match against any key (left side containing
# the bolted binary offsets) because BOLT may change where it puts instructions
Expand Down
3 changes: 1 addition & 2 deletions bolt/test/X86/merge-fdata-bat-mode.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ RUN: merge-fdata %S/Inputs/bat_profile_1.fdata \
RUN: %S/Inputs/bat_profile_2.fdata \
RUN: | FileCheck %s --check-prefix=CHECK-FDATA

CHECK-FDATA: boltedcollection
CHECK-FDATA: 1 main 451 1 SolveCubic 0 0 302


6 changes: 6 additions & 0 deletions bolt/test/X86/merge-fdata-nobat-mode.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Check that merge-fdata tool doesn't spuriously print boltedcollection

RUN: merge-fdata %S/Inputs/blarge.fdata %S/Inputs/blarge.fdata \
RUN: | FileCheck %s --check-prefix=CHECK-FDATA

CHECK-FDATA-NOT: boltedcollection
2 changes: 1 addition & 1 deletion bolt/tools/bat-dump/bat-dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static std::string GetExecutablePath(const char *Argv0) {
if (llvm::ErrorOr<std::string> P =
llvm::sys::findProgramByName(ExecutablePath))
ExecutablePath = *P;
return std::string(ExecutablePath.str());
return std::string(ExecutablePath);
}

void dumpBATFor(llvm::object::ELFObjectFileBase *InputFile) {
Expand Down
2 changes: 1 addition & 1 deletion bolt/tools/driver/llvm-bolt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ static std::string GetExecutablePath(const char *Argv0) {
if (llvm::ErrorOr<std::string> P =
llvm::sys::findProgramByName(ExecutablePath))
ExecutablePath = *P;
return std::string(ExecutablePath.str());
return std::string(ExecutablePath);
}

int main(int argc, char **argv) {
Expand Down
2 changes: 1 addition & 1 deletion bolt/tools/heatmap/heatmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static std::string GetExecutablePath(const char *Argv0) {
if (llvm::ErrorOr<std::string> P =
llvm::sys::findProgramByName(ExecutablePath))
ExecutablePath = *P;
return std::string(ExecutablePath.str());
return std::string(ExecutablePath);
}

int main(int argc, char **argv) {
Expand Down
2 changes: 1 addition & 1 deletion bolt/tools/merge-fdata/merge-fdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ void mergeLegacyProfiles(const SmallVectorImpl<std::string> &Filenames) {
MergedProfile.insert_or_assign(Key, Count);
}

if (BoltedCollection)
if (BoltedCollection.value_or(false))
output() << "boltedcollection\n";
for (const auto &[Key, Value] : MergedProfile)
output() << Key << " " << Value << "\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,27 @@ using FileToChangesMap =
/// Directories starting with '.' are ignored during traversal.
///
/// \param[in] Directory Directory to begin search for serialized
/// TranslationUnitReplacements.
/// TranslationUnitReplacements or TranslationUnitDiagnostics.
/// \param[out] TUs Collection of all found and deserialized
/// TranslationUnitReplacements or TranslationUnitDiagnostics.
/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
/// found in \c Directory.
/// \param[out] TUFiles Collection of all TranslationUnitReplacement or
/// TranslationUnitDiagnostics files found in \c Directory.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns An error_code indicating success or failure in navigating the
/// directory structure.
template <typename TranslationUnits>
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TranslationUnits &TUs,
TUReplacementFiles &TUFiles,
clang::DiagnosticsEngine &Diagnostics) = delete;

template <>
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUReplacements &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);

template <>
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ static void eatDiagnostics(const SMDiagnostic &, void *) {}
namespace clang {
namespace replace {

std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUReplacements &TUs,
namespace detail {
template <typename TranslationUnits>
static std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TranslationUnits &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;
Expand Down Expand Up @@ -68,7 +70,7 @@ std::error_code collectReplacementsFromDirectory(
}

yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
tooling::TranslationUnitReplacements TU;
typename TranslationUnits::value_type TU;
YIn >> TU;
if (YIn.error()) {
// File doesn't appear to be a header change description. Ignore it.
Expand All @@ -81,49 +83,22 @@ std::error_code collectReplacementsFromDirectory(

return ErrorCode;
}
} // namespace detail

template <>
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
const llvm::StringRef Directory, TUReplacements &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;

std::error_code ErrorCode;

for (recursive_directory_iterator I(Directory, ErrorCode), E;
I != E && !ErrorCode; I.increment(ErrorCode)) {
if (filename(I->path())[0] == '.') {
// Indicate not to descend into directories beginning with '.'
I.no_push();
continue;
}

if (extension(I->path()) != ".yaml")
continue;

TUFiles.push_back(I->path());

ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
MemoryBuffer::getFile(I->path());
if (std::error_code BufferError = Out.getError()) {
errs() << "Error reading " << I->path() << ": " << BufferError.message()
<< "\n";
continue;
}

yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
tooling::TranslationUnitDiagnostics TU;
YIn >> TU;
if (YIn.error()) {
// File doesn't appear to be a header change description. Ignore it.
continue;
}

// Only keep files that properly parse.
TUs.push_back(TU);
}
return detail::collectReplacementsFromDirectory(Directory, TUs, TUFiles,
Diagnostics);
}

return ErrorCode;
template <>
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
return detail::collectReplacementsFromDirectory(Directory, TUs, TUFiles,
Diagnostics);
}

/// Extract replacements from collected TranslationUnitReplacements and
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ writeFileDefinition(const Location &L,
Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
auto LocFileNode = std::make_unique<TagNode>(
HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
LocFileNode->Attributes.emplace_back("href", std::string(FileURL.str()));
LocFileNode->Attributes.emplace_back("href", std::string(FileURL));
Node->Children.emplace_back(std::move(LocFileNode));
return Node;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
;;; clang-include-fixer.el --- Emacs integration of the clang include fixer -*- lexical-binding: t; -*-

;; Version: 0.1.0
;; Keywords: tools, c
;; Package-Requires: ((cl-lib "0.5") (json "1.2") (let-alist "1.0.4"))

Expand Down
9 changes: 3 additions & 6 deletions clang-tools-extra/clang-query/QueryParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ namespace query {
// is found before End, return StringRef(). Begin is adjusted to exclude the
// lexed region.
StringRef QueryParser::lexWord() {
Line = Line.drop_while([](char c) {
// Don't trim newlines.
return StringRef(" \t\v\f\r").contains(c);
});
// Don't trim newlines.
Line = Line.ltrim(" \t\v\f\r");

if (Line.empty())
// Even though the Line is empty, it contains a pointer and
Expand Down Expand Up @@ -152,8 +150,7 @@ QueryRef QueryParser::parseSetTraversalKind(TraversalKind QuerySession::*Var) {

QueryRef QueryParser::endQuery(QueryRef Q) {
StringRef Extra = Line;
StringRef ExtraTrimmed = Extra.drop_while(
[](char c) { return StringRef(" \t\v\f\r").contains(c); });
StringRef ExtraTrimmed = Extra.ltrim(" \t\v\f\r");

if ((!ExtraTrimmed.empty() && ExtraTrimmed[0] == '\n') ||
(ExtraTrimmed.size() >= 2 && ExtraTrimmed[0] == '\r' &&
Expand Down
6 changes: 1 addition & 5 deletions clang-tools-extra/clang-tidy/GlobList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ namespace clang::tidy {
// from the GlobList.
static bool consumeNegativeIndicator(StringRef &GlobList) {
GlobList = GlobList.trim();
if (GlobList.starts_with("-")) {
GlobList = GlobList.substr(1);
return true;
}
return false;
return GlobList.consume_front("-");
}

// Converts first glob from the comma-separated list of globs to Regex and
Expand Down
22 changes: 14 additions & 8 deletions clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ AST_MATCHER(FunctionDecl, isExplicitThrow) {
Node.getExceptionSpecSourceRange().isValid();
}

AST_MATCHER(FunctionDecl, hasAtLeastOneParameter) {
return Node.getNumParams() > 0;
}

} // namespace

ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
Expand Down Expand Up @@ -58,14 +62,16 @@ void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {

void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
functionDecl(isDefinition(),
anyOf(isNoThrow(),
allOf(anyOf(cxxDestructorDecl(),
cxxConstructorDecl(isMoveConstructor()),
cxxMethodDecl(isMoveAssignmentOperator()),
isMain(), hasName("swap")),
unless(isExplicitThrow())),
isEnabled(FunctionsThatShouldNotThrow)))
functionDecl(
isDefinition(),
anyOf(isNoThrow(),
allOf(anyOf(cxxDestructorDecl(),
cxxConstructorDecl(isMoveConstructor()),
cxxMethodDecl(isMoveAssignmentOperator()), isMain(),
allOf(hasAnyName("swap", "iter_swap", "iter_move"),
hasAtLeastOneParameter())),
unless(isExplicitThrow())),
isEnabled(FunctionsThatShouldNotThrow)))
.bind("thrower"),
this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "ImplicitWideningOfMultiplicationResultCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include <optional>

using namespace clang::ast_matchers;
Expand Down Expand Up @@ -99,15 +100,16 @@ void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
"make conversion explicit to silence this warning",
DiagnosticIDs::Note)
<< E->getSourceRange();

const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
E->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
if (ShouldUseCXXStaticCast)
Diag << FixItHint::CreateInsertion(
E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
<< FixItHint::CreateInsertion(E->getEndLoc(), ")");
<< FixItHint::CreateInsertion(EndLoc, ")");
else
Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
"(" + Ty.getAsString() + ")(")
<< FixItHint::CreateInsertion(E->getEndLoc(), ")");
<< FixItHint::CreateInsertion(EndLoc, ")");
Diag << includeStddefHeader(E->getBeginLoc());
}

Expand Down Expand Up @@ -137,7 +139,11 @@ void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
"static_cast<" +
WideExprTy.getAsString() + ">(")
<< FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
<< FixItHint::CreateInsertion(
Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,
*Result->SourceManager,
getLangOpts()),
")");
else
Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
"(" + WideExprTy.getAsString() + ")");
Expand Down Expand Up @@ -206,16 +212,17 @@ void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
"make conversion explicit to silence this warning",
DiagnosticIDs::Note)
<< IndexExpr->getSourceRange();

const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
IndexExpr->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
if (ShouldUseCXXStaticCast)
Diag << FixItHint::CreateInsertion(
IndexExpr->getBeginLoc(),
(Twine("static_cast<") + TyAsString + ">(").str())
<< FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
<< FixItHint::CreateInsertion(EndLoc, ")");
else
Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
(Twine("(") + TyAsString + ")(").str())
<< FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
<< FixItHint::CreateInsertion(EndLoc, ")");
Diag << includeStddefHeader(IndexExpr->getBeginLoc());
}

Expand All @@ -229,7 +236,11 @@ void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
Diag << FixItHint::CreateInsertion(
LHS->getBeginLoc(),
(Twine("static_cast<") + TyAsString + ">(").str())
<< FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
<< FixItHint::CreateInsertion(
Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,
*Result->SourceManager,
getLangOpts()),
")");
else
Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
(Twine("(") + TyAsString + ")").str());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ bool MultipleInheritanceCheck::isCurrentClassInterface(

// Interfaces should have exclusively pure methods.
return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
return M->isUserProvided() && !M->isPure() && !M->isStatic();
return M->isUserProvided() && !M->isPureVirtual() && !M->isStatic();
});
}

Expand Down Expand Up @@ -103,8 +103,8 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
if (!isInterface(Base)) NumConcrete++;
}
// Check virtual bases to see if there is more than one concrete

// Check virtual bases to see if there is more than one concrete
// non-virtual base.
for (const auto &V : D->vbases()) {
const auto *Ty = V.getType()->getAs<RecordType>();
Expand Down
10 changes: 9 additions & 1 deletion clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "StaticAssertCheck.h"
#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
Expand Down Expand Up @@ -45,13 +46,20 @@ void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {
IsAlwaysFalse);
auto NonConstexprFunctionCall =
callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
auto NonConstexprVariableReference =
declRefExpr(to(varDecl(unless(isConstexpr()))),
unless(hasAncestor(expr(matchers::hasUnevaluatedContext()))),
unless(hasAncestor(typeLoc())));

auto NonConstexprCode =
expr(anyOf(NonConstexprFunctionCall, NonConstexprVariableReference));
auto AssertCondition =
expr(
anyOf(expr(ignoringParenCasts(anyOf(
AssertExprRoot, unaryOperator(hasUnaryOperand(
ignoringParenCasts(AssertExprRoot)))))),
anything()),
unless(findAll(NonConstexprFunctionCall)))
unless(NonConstexprCode), unless(hasDescendant(NonConstexprCode)))
.bind("condition");
auto Condition =
anyOf(ignoringParenImpCasts(callExpr(
Expand Down
14 changes: 10 additions & 4 deletions clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
/*SkipTrailingWhitespaceAndNewLine=*/true));
for (const auto *UsingShadow : Using->shadows()) {
const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
if (shouldCheckDecl(TargetDecl))
if (shouldCheckDecl(TargetDecl)) {
Context.UsingTargetDecls.insert(TargetDecl);
UsingTargetDeclsCache.insert(TargetDecl);
}
}
if (!Context.UsingTargetDecls.empty())
Contexts.push_back(Context);
Expand Down Expand Up @@ -201,13 +203,16 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) {
if (!D)
return;
const Decl *CanonicalDecl = D->getCanonicalDecl();
if (!UsingTargetDeclsCache.contains(CanonicalDecl))
return;
// FIXME: Currently, we don't handle the using-decls being used in different
// scopes (such as different namespaces, different functions). Instead of
// giving an incorrect message, we mark all of them as used.
//
// FIXME: Use a more efficient way to find a matching context.
for (auto &Context : Contexts) {
if (Context.UsingTargetDecls.contains(D->getCanonicalDecl()))
if (Context.IsUsed)
continue;
if (Context.UsingTargetDecls.contains(CanonicalDecl))
Context.IsUsed = true;
}
}
Expand All @@ -224,6 +229,7 @@ void UnusedUsingDeclsCheck::onEndOfTranslationUnit() {
}
}
Contexts.clear();
UsingTargetDeclsCache.clear();
}

} // namespace clang::tidy::misc
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class UnusedUsingDeclsCheck : public ClangTidyCheck {
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void onEndOfTranslationUnit() override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}

private:
void removeFromFoundDecls(const Decl *D);
Expand All @@ -46,6 +49,7 @@ class UnusedUsingDeclsCheck : public ClangTidyCheck {
};

std::vector<UsingDeclContext> Contexts;
llvm::SmallPtrSet<const Decl *, 32> UsingTargetDeclsCache;

StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ namespace clang::tidy::modernize {

namespace {
AST_MATCHER(FunctionDecl, hasAnyDefinition) {
if (Node.hasBody() || Node.isPure() || Node.isDefaulted() || Node.isDeleted())
if (Node.hasBody() || Node.isPureVirtual() || Node.isDefaulted() ||
Node.isDeleted())
return true;

if (const FunctionDecl *Definition = Node.getDefinition())
if (Definition->hasBody() || Definition->isPure() ||
if (Definition->hasBody() || Definition->isPureVirtual() ||
Definition->isDefaulted() || Definition->isDeleted())
return true;

Expand Down
13 changes: 10 additions & 3 deletions clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
return true;
}

static bool isReferenced(const VarDecl &LoopVar, const Stmt &Stmt,
ASTContext &Context) {
const auto IsLoopVar = varDecl(equalsNode(&LoopVar));
return !match(stmt(hasDescendant(declRefExpr(to(valueDecl(anyOf(
IsLoopVar, bindingDecl(forDecomposition(IsLoopVar)))))))),
Stmt, Context)
.empty();
}

bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
ASTContext &Context) {
Expand All @@ -113,9 +122,7 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
// compiler warning which can't be suppressed.
// Since this case is very rare, it is safe to ignore it.
if (!ExprMutationAnalyzer(*ForRange.getBody(), Context).isMutated(&LoopVar) &&
!utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
Context)
.empty()) {
isReferenced(LoopVar, *ForRange.getBody(), Context)) {
auto Diag = diag(
LoopVar.getLocation(),
"loop variable is copied but only used as const reference; consider "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ void NoexceptSwapCheck::registerMatchers(MatchFinder *Finder) {
.bind("type"))))),
hasParameter(1, hasType(qualType(hasCanonicalType(
qualType(equalsBoundNode("type")))))));
Finder->addMatcher(functionDecl(unless(isDeleted()), hasName("swap"),
Finder->addMatcher(functionDecl(unless(isDeleted()),
hasAnyName("swap", "iter_swap"),
anyOf(MethodMatcher, FunctionMatcher))
.bind(BindFuncDeclName),
this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===--- AvoidNestedConditionalOperatorCheck.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 "AvoidNestedConditionalOperatorCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/DiagnosticIDs.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

void AvoidNestedConditionalOperatorCheck::registerMatchers(
MatchFinder *Finder) {
Finder->addMatcher(
conditionalOperator(
anyOf(
hasCondition(ignoringParenCasts(
conditionalOperator().bind("nested-conditional-operator"))),
hasTrueExpression(ignoringParenCasts(
conditionalOperator().bind("nested-conditional-operator"))),
hasFalseExpression(ignoringParenCasts(
conditionalOperator().bind("nested-conditional-operator")))))
.bind("conditional-operator"),
this);
}

void AvoidNestedConditionalOperatorCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *CO =
Result.Nodes.getNodeAs<ConditionalOperator>("conditional-operator");
const auto *NCO = Result.Nodes.getNodeAs<ConditionalOperator>(
"nested-conditional-operator");
assert(CO);
assert(NCO);

if (CO->getBeginLoc().isMacroID() || NCO->getBeginLoc().isMacroID())
return;

diag(NCO->getBeginLoc(),
"conditional operator is used as sub-expression of parent conditional "
"operator, refrain from using nested conditional operators");
diag(CO->getBeginLoc(), "parent conditional operator here",
DiagnosticIDs::Note);
}

} // namespace clang::tidy::readability
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===--- AvoidNestedConditionalOperatorCheck.h - clang-tidy -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_NESTED_CONDITIONAL_OPERATOR_CHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_NESTED_CONDITIONAL_OPERATOR_CHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// Identifies instances of nested conditional operators in the code.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/avoid-nested-conditional-operator.html
class AvoidNestedConditionalOperatorCheck : public ClangTidyCheck {
public:
AvoidNestedConditionalOperatorCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_NESTED_CONDITIONAL_OPERATOR_CHECK_H
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS

add_clang_library(clangTidyReadabilityModule
AvoidConstParamsInDecls.cpp
AvoidNestedConditionalOperatorCheck.cpp
AvoidReturnWithVoidValueCheck.cpp
AvoidUnconditionalPreprocessorIfCheck.cpp
BracesAroundStatementsCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,10 +291,14 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
OpCode == BinaryOperatorKind::BO_NE))
return;

// Always true, no warnings for that.
if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
return;
// Always true/false, no warnings for that.
if (Value == 0) {
if ((OpCode == BinaryOperatorKind::BO_GT && !ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_LT && ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_LE && !ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_GE && ContainerIsLHS))
return;
}

// Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
if (Value == 1) {
Expand All @@ -306,12 +310,32 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
return;
}

// Do not warn for size < 1, 1 > size, size <= 0, 0 >= size for non signed
// types
if ((OpCode == BinaryOperatorKind::BO_GT && Value == 1 &&
!ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_LT && Value == 1 && ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_GE && Value == 0 &&
!ContainerIsLHS) ||
(OpCode == BinaryOperatorKind::BO_LE && Value == 0 && ContainerIsLHS)) {
const Expr *Container = ContainerIsLHS
? BinaryOp->getLHS()->IgnoreImpCasts()
: BinaryOp->getRHS()->IgnoreImpCasts();
if (Container->getType()
.getCanonicalType()
.getNonReferenceType()
->isSignedIntegerType())
return;
}

if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
Negation = true;

if ((OpCode == BinaryOperatorKind::BO_GT ||
OpCode == BinaryOperatorKind::BO_GE) &&
ContainerIsLHS)
Negation = true;

if ((OpCode == BinaryOperatorKind::BO_LT ||
OpCode == BinaryOperatorKind::BO_LE) &&
!ContainerIsLHS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "ImplicitBoolConversionCheck.h"
#include "../utils/FixItHintUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
Expand Down Expand Up @@ -61,31 +62,6 @@ bool isUnaryLogicalNotOperator(const Stmt *Statement) {
return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
}

bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
switch (OperatorKind) {
case OO_New:
case OO_Delete: // Fall-through on purpose.
case OO_Array_New:
case OO_Array_Delete:
case OO_ArrowStar:
case OO_Arrow:
case OO_Call:
case OO_Subscript:
return false;

default:
return true;
}
}

bool areParensNeededForStatement(const Stmt *Statement) {
if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
}

return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
}

void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
const ImplicitCastExpr *Cast, const Stmt *Parent,
ASTContext &Context) {
Expand All @@ -105,9 +81,10 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag,

const Expr *SubExpr = Cast->getSubExpr();

bool NeedInnerParens = areParensNeededForStatement(SubExpr);
bool NeedInnerParens =
SubExpr != nullptr && utils::fixit::areParensNeededForStatement(*SubExpr);
bool NeedOuterParens =
Parent != nullptr && areParensNeededForStatement(Parent);
Parent != nullptr && utils::fixit::areParensNeededForStatement(*Parent);

std::string StartLocInsertion;

Expand Down Expand Up @@ -274,8 +251,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
allOf(anyOf(hasCastKind(CK_NullToPointer),
hasCastKind(CK_NullToMemberPointer)),
hasSourceExpression(cxxBoolLiteral()))),
hasSourceExpression(expr(hasType(booleanType()))),
unless(ExceptionCases));
hasSourceExpression(expr(hasType(booleanType()))));
auto BoolXor =
binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool),
hasRHS(ImplicitCastFromBool));
Expand Down Expand Up @@ -315,7 +291,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
traverse(
TK_AsIs,
implicitCastExpr(
ImplicitCastFromBool,
ImplicitCastFromBool, unless(ExceptionCases),
// Exclude comparisons of bools, as they are always cast to
// integers in such context:
// bool_expr_a == bool_expr_b
Expand Down Expand Up @@ -365,7 +341,7 @@ void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
return;
}

auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> bool")
auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> 'bool'")
<< Cast->getSubExpr()->getType();

StringRef EquivalentLiteral =
Expand All @@ -382,7 +358,7 @@ void ImplicitBoolConversionCheck::handleCastFromBool(
ASTContext &Context) {
QualType DestType =
NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
auto Diag = diag(Cast->getBeginLoc(), "implicit conversion bool -> %0")
auto Diag = diag(Cast->getBeginLoc(), "implicit conversion 'bool' -> %0")
<< DestType;

if (const auto *BoolLiteral =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "AvoidConstParamsInDecls.h"
#include "AvoidNestedConditionalOperatorCheck.h"
#include "AvoidReturnWithVoidValueCheck.h"
#include "AvoidUnconditionalPreprocessorIfCheck.h"
#include "BracesAroundStatementsCheck.h"
Expand Down Expand Up @@ -64,6 +65,8 @@ class ReadabilityModule : public ClangTidyModule {
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<AvoidConstParamsInDecls>(
"readability-avoid-const-params-in-decls");
CheckFactories.registerCheck<AvoidNestedConditionalOperatorCheck>(
"readability-avoid-nested-conditional-operator");
CheckFactories.registerCheck<AvoidReturnWithVoidValueCheck>(
"readability-avoid-return-with-void-value");
CheckFactories.registerCheck<AvoidUnconditionalPreprocessorIfCheck>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "RedundantMemberInitCheck.h"
#include "../utils/LexerUtils.h"
#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
Expand All @@ -18,52 +19,80 @@ using namespace clang::tidy::matchers;

namespace clang::tidy::readability {

static SourceRange
getFullInitRangeInclWhitespaces(SourceRange Range, const SourceManager &SM,
const LangOptions &LangOpts) {
const Token PrevToken =
utils::lexer::getPreviousToken(Range.getBegin(), SM, LangOpts, false);
if (PrevToken.is(tok::unknown))
return Range;

if (PrevToken.isNot(tok::equal))
return {PrevToken.getEndLoc(), Range.getEnd()};

return getFullInitRangeInclWhitespaces(
{PrevToken.getLocation(), Range.getEnd()}, SM, LangOpts);
}

void RedundantMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreBaseInCopyConstructors",
IgnoreBaseInCopyConstructors);
}

void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
auto ConstructorMatcher =
cxxConstructExpr(argumentCountIs(0),
hasDeclaration(cxxConstructorDecl(ofClass(cxxRecordDecl(
unless(isTriviallyDefaultConstructible()))))))
.bind("construct");

Finder->addMatcher(
cxxConstructorDecl(
unless(isDelegatingConstructor()), ofClass(unless(isUnion())),
forEachConstructorInitializer(
cxxCtorInitializer(
withInitializer(
cxxConstructExpr(
hasDeclaration(
cxxConstructorDecl(ofClass(cxxRecordDecl(
unless(isTriviallyDefaultConstructible()))))))
.bind("construct")),
unless(forField(hasType(isConstQualified()))),
unless(forField(hasParent(recordDecl(isUnion())))))
cxxCtorInitializer(withInitializer(ConstructorMatcher),
unless(forField(fieldDecl(
anyOf(hasType(isConstQualified()),
hasParent(recordDecl(isUnion())))))))
.bind("init")))
.bind("constructor"),
this);

Finder->addMatcher(fieldDecl(hasInClassInitializer(ConstructorMatcher),
unless(hasParent(recordDecl(isUnion()))))
.bind("field"),
this);
}

void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");

if (const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field")) {
const Expr *Init = Field->getInClassInitializer();
diag(Construct->getExprLoc(), "initializer for member %0 is redundant")
<< Field
<< FixItHint::CreateRemoval(getFullInitRangeInclWhitespaces(
Init->getSourceRange(), *Result.SourceManager, getLangOpts()));
return;
}

const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
const auto *ConstructorDecl =
Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor");

if (IgnoreBaseInCopyConstructors && ConstructorDecl->isCopyConstructor() &&
Init->isBaseInitializer())
return;

if (Construct->getNumArgs() == 0 ||
Construct->getArg(0)->isDefaultArgument()) {
if (Init->isAnyMemberInitializer()) {
diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
<< Init->getAnyMember()
<< FixItHint::CreateRemoval(Init->getSourceRange());
} else {
diag(Init->getSourceLocation(),
"initializer for base class %0 is redundant")
<< Construct->getType()
<< FixItHint::CreateRemoval(Init->getSourceRange());
}
if (Init->isAnyMemberInitializer()) {
diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
<< Init->getAnyMember()
<< FixItHint::CreateRemoval(Init->getSourceRange());
} else {
diag(Init->getSourceLocation(),
"initializer for base class %0 is redundant")
<< Construct->getType()
<< FixItHint::CreateRemoval(Init->getSourceRange());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,13 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
}

bool dataTraverseStmtPre(Stmt *S) {
if (S && !shouldIgnore(S))
if (!S) {
return true;
}
if (Check->IgnoreMacros && S->getBeginLoc().isMacroID()) {
return false;
}
if (!shouldIgnore(S))
StmtStack.push_back(S);
return true;
}
Expand Down Expand Up @@ -583,6 +589,7 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreMacros(Options.get("IgnoreMacros", false)),
ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
ChainedConditionalAssignment(
Options.get("ChainedConditionalAssignment", false)),
Expand Down Expand Up @@ -671,6 +678,7 @@ void SimplifyBooleanExprCheck::reportBinOp(const ASTContext &Context,
}

void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreMacros", IgnoreMacros);
Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
Options.store(Opts, "ChainedConditionalAssignment",
ChainedConditionalAssignment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class SimplifyBooleanExprCheck : public ClangTidyCheck {
StringRef Description, SourceRange ReplacementRange,
StringRef Replacement);

const bool IgnoreMacros;
const bool ChainedConditionalReturn;
const bool ChainedConditionalAssignment;
const bool SimplifyDeMorgan;
Expand Down
34 changes: 34 additions & 0 deletions clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,40 @@ std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
return std::nullopt;
}

bool areParensNeededForStatement(const Stmt &Node) {
if (isa<ParenExpr>(&Node))
return false;

if (isa<clang::BinaryOperator>(&Node) || isa<UnaryOperator>(&Node))
return true;

if (isa<clang::ConditionalOperator>(&Node) ||
isa<BinaryConditionalOperator>(&Node))
return true;

if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&Node)) {
switch (Op->getOperator()) {
case OO_PlusPlus:
[[fallthrough]];
case OO_MinusMinus:
return Op->getNumArgs() != 2;
case OO_Call:
[[fallthrough]];
case OO_Subscript:
[[fallthrough]];
case OO_Arrow:
return false;
default:
return true;
};
}

if (isa<CStyleCastExpr>(&Node))
return true;

return false;
}

// Return true if expr needs to be put in parens when it is an argument of a
// prefix unary operator, e.g. when it is a binary or ternary operator
// syntactically.
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ addQualifierToVarDecl(const VarDecl &Var, const ASTContext &Context,

// \brief Format a pointer to an expression
std::string formatDereference(const Expr &ExprNode, const ASTContext &Context);

// \brief Checks whatever a expression require extra () to be always used in
// safe way in any other expression.
bool areParensNeededForStatement(const Stmt &Node);
} // namespace clang::tidy::utils::fixit

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/utils/HeaderGuard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ void HeaderGuardCheck::registerPPCallbacks(const SourceManager &SM,

std::string HeaderGuardCheck::sanitizeHeaderGuard(StringRef Guard) {
// Only reserved identifiers are allowed to start with an '_'.
return Guard.drop_while([](char C) { return C == '_'; }).str();
return Guard.ltrim('_').str();
}

bool HeaderGuardCheck::shouldSuggestEndifComment(StringRef FileName) {
Expand Down
12 changes: 6 additions & 6 deletions clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ const TemplateTypeParmType *getFunctionPackType(const FunctionDecl *Callee) {
// Returns the template parameter pack type that this parameter was expanded
// from (if in the Args... or Args&... or Args&&... form), if this is the case,
// nullptr otherwise.
const TemplateTypeParmType *getUnderylingPackType(const ParmVarDecl *Param) {
const TemplateTypeParmType *getUnderlyingPackType(const ParmVarDecl *Param) {
const auto *PlainType = Param->getType().getTypePtr();
if (auto *RT = dyn_cast<ReferenceType>(PlainType))
PlainType = RT->getPointeeTypeAsWritten().getTypePtr();
Expand Down Expand Up @@ -793,8 +793,8 @@ class ForwardingCallVisitor
: public RecursiveASTVisitor<ForwardingCallVisitor> {
public:
ForwardingCallVisitor(ArrayRef<const ParmVarDecl *> Parameters)
: Parameters{Parameters}, PackType{getUnderylingPackType(
Parameters.front())} {}
: Parameters{Parameters},
PackType{getUnderlyingPackType(Parameters.front())} {}

bool VisitCallExpr(CallExpr *E) {
auto *Callee = getCalleeDeclOrUniqueOverload(E);
Expand Down Expand Up @@ -859,7 +859,7 @@ class ForwardingCallVisitor
if (const auto *TTPT = getFunctionPackType(Callee)) {
// In this case: Separate the parameters into head, pack and tail
auto IsExpandedPack = [&](const ParmVarDecl *P) {
return getUnderylingPackType(P) == TTPT;
return getUnderlyingPackType(P) == TTPT;
};
ForwardingInfo FI;
FI.Head = MatchingParams.take_until(IsExpandedPack);
Expand Down Expand Up @@ -964,7 +964,7 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
if (const auto *TTPT = getFunctionPackType(D)) {
// Split the parameters into head, pack and tail
auto IsExpandedPack = [TTPT](const ParmVarDecl *P) {
return getUnderylingPackType(P) == TTPT;
return getUnderlyingPackType(P) == TTPT;
};
ArrayRef<const ParmVarDecl *> Head = Parameters.take_until(IsExpandedPack);
ArrayRef<const ParmVarDecl *> Pack =
Expand Down Expand Up @@ -1016,7 +1016,7 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
}

bool isExpandedFromParameterPack(const ParmVarDecl *D) {
return getUnderylingPackType(D) != nullptr;
return getUnderlyingPackType(D) != nullptr;
}

} // namespace clangd
Expand Down
23 changes: 13 additions & 10 deletions clang-tools-extra/clangd/CompileCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,26 +313,29 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,

tooling::addTargetAndModeForProgramName(Cmd, Cmd.front());

// Check whether the flag exists, either as -flag or -flag=*
auto Has = [&](llvm::StringRef Flag) {
for (llvm::StringRef Arg : Cmd) {
if (Arg.consume_front(Flag) && (Arg.empty() || Arg[0] == '='))
return true;
}
return false;
// Check whether the flag exists in the command.
auto HasExact = [&](llvm::StringRef Flag) {
return llvm::any_of(Cmd, [&](llvm::StringRef Arg) { return Arg == Flag; });
};

// Check whether the flag appears in the command as a prefix.
auto HasPrefix = [&](llvm::StringRef Flag) {
return llvm::any_of(
Cmd, [&](llvm::StringRef Arg) { return Arg.starts_with(Flag); });
};

llvm::erase_if(Cmd, [](llvm::StringRef Elem) {
return Elem.starts_with("--save-temps") || Elem.starts_with("-save-temps");
});

std::vector<std::string> ToAppend;
if (ResourceDir && !Has("-resource-dir"))
if (ResourceDir && !HasExact("-resource-dir") && !HasPrefix("-resource-dir="))
ToAppend.push_back(("-resource-dir=" + *ResourceDir));

// Don't set `-isysroot` if it is already set or if `--sysroot` is set.
// `--sysroot` is a superset of the `-isysroot` argument.
if (Sysroot && !Has("-isysroot") && !Has("--sysroot")) {
if (Sysroot && !HasPrefix("-isysroot") && !HasExact("--sysroot") &&
!HasPrefix("--sysroot=")) {
ToAppend.push_back("-isysroot");
ToAppend.push_back(*Sysroot);
}
Expand All @@ -343,7 +346,7 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,
}

if (!Cmd.empty()) {
bool FollowSymlink = !Has("-no-canonical-prefixes");
bool FollowSymlink = !HasExact("-no-canonical-prefixes");
Cmd.front() =
(FollowSymlink ? ResolvedDrivers : ResolvedDriversNoFollow)
.get(Cmd.front(), [&, this] {
Expand Down
13 changes: 10 additions & 3 deletions clang-tools-extra/clangd/FindTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,16 @@ struct TargetFinder {
Outer.add(TST->getAliasedType(), Flags | Rel::Underlying);
// Don't *traverse* the alias, which would result in traversing the
// template of the underlying type.
Outer.report(
TST->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(),
Flags | Rel::Alias | Rel::TemplatePattern);

TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();
// Builtin templates e.g. __make_integer_seq, __type_pack_element
// are such that they don't have alias *decls*. Even then, we still
// traverse their desugared *types* so that instantiated decls are
// collected.
if (llvm::isa<BuiltinTemplateDecl>(TD))
return;
Outer.report(TD->getTemplatedDecl(),
Flags | Rel::Alias | Rel::TemplatePattern);
}
// specializations of template template parameters aren't instantiated
// into decls, so they must refer to the parameter itself.
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/Selection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,9 @@ const DeclContext &SelectionTree::Node::getDeclContext() const {
return *DC;
return *Current->getLexicalDeclContext();
}
if (const auto *LE = CurrentNode->ASTNode.get<LambdaExpr>())
if (CurrentNode != this)
return *LE->getCallOperator();
}
llvm_unreachable("A tree must always be rooted at TranslationUnitDecl.");
}
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ bool isStatic(const Decl *D) {

bool isAbstract(const Decl *D) {
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
return CMD->isPure();
return CMD->isPureVirtual();
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
return CRD->hasDefinition() && CRD->isAbstract();
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
// Special case: virtual void ^method() = 0: jump to all overrides.
// FIXME: extend it to ^virtual, unfortunately, virtual location is not
// saved in the AST.
if (CMD->isPure()) {
if (CMD->isPureVirtual()) {
if (TouchedIdentifier && SM.getSpellingLoc(CMD->getLocation()) ==
TouchedIdentifier->location()) {
VirtualMethods.insert(getSymbolID(CMD));
Expand Down
81 changes: 78 additions & 3 deletions clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,8 @@ TEST(ArgStripperTest, OrderDependent) {
}

TEST(PrintArgvTest, All) {
std::vector<llvm::StringRef> Args = {
"one", "two", "thr ee", "f\"o\"ur", "fi\\ve", "$"
};
std::vector<llvm::StringRef> Args = {"one", "two", "thr ee",
"f\"o\"ur", "fi\\ve", "$"};
const char *Expected = R"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
EXPECT_EQ(Expected, printArgv(Args));
}
Expand Down Expand Up @@ -450,6 +449,82 @@ TEST(CommandMangler, PathsAsPositional) {
Mangler(Cmd, "a.cc");
EXPECT_THAT(Cmd.CommandLine, Contains("foo"));
}

TEST(CommandMangler, RespectsOriginalResourceDir) {
auto Mangler = CommandMangler::forTests();
Mangler.ResourceDir = testPath("fake/resources");

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-resource-dir", testPath("true/resources"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-resource-dir " + testPath("true/resources")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/resources"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-resource-dir=" + testPath("true/resources"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-resource-dir=" + testPath("true/resources")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/resources"))));
}
}

TEST(CommandMangler, RespectsOriginalSysroot) {
auto Mangler = CommandMangler::forTests();
Mangler.Sysroot = testPath("fake/sysroot");

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-isysroot", testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-isysroot " + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-isysroot" + testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-isysroot" + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "--sysroot", testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("--sysroot " + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "--sysroot=" + testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("--sysroot=" + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}
}
} // namespace
} // namespace clangd
} // namespace clang
11 changes: 11 additions & 0 deletions clang-tools-extra/clangd/unittests/DumpASTTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ TEST(DumpASTTests, Arcana) {
EXPECT_THAT(Node.children.front().arcana, testing::StartsWith("QualType "));
}

TEST(DumpASTTests, UnbalancedBraces) {
// Test that we don't crash while trying to compute a source range for the
// node whose ending brace is missing, and also that the source range is
// not empty.
Annotations Case("/*error-ok*/ $func[[int main() {]]");
ParsedAST AST = TestTU::withCode(Case.code()).build();
auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "main")),
AST.getTokens(), AST.getASTContext());
ASSERT_EQ(Node.range, Case.range("func"));
}

} // namespace
} // namespace clangd
} // namespace clang
27 changes: 27 additions & 0 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,33 @@ TEST_F(TargetDeclTest, TypeAliasTemplate) {
Rel::Alias | Rel::TemplatePattern});
}

TEST_F(TargetDeclTest, BuiltinTemplates) {
Code = R"cpp(
template <class T, T... Index> struct integer_sequence {};
[[__make_integer_seq]]<integer_sequence, int, 3> X;
)cpp";
EXPECT_DECLS(
"TemplateSpecializationTypeLoc",
{"struct integer_sequence", Rel::TemplatePattern | Rel::Underlying},
{"template<> struct integer_sequence<int, <0, 1, 2>>",
Rel::TemplateInstantiation | Rel::Underlying});

// Dependent context.
Code = R"cpp(
template <class T, T... Index> struct integer_sequence;

template <class T, int N>
using make_integer_sequence = [[__make_integer_seq]]<integer_sequence, T, N>;
)cpp";
EXPECT_DECLS("TemplateSpecializationTypeLoc");

Code = R"cpp(
template <int N, class... Pack>
using type_pack_element = [[__type_pack_element]]<N, Pack...>;
)cpp";
EXPECT_DECLS("TemplateSpecializationTypeLoc");
}

TEST_F(TargetDeclTest, MemberOfTemplate) {
Code = R"cpp(
template <typename T> struct Foo {
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/clangd/unittests/SelectionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,19 @@ TEST(SelectionTest, DeclContextIsLexical) {
}
}

TEST(SelectionTest, DeclContextLambda) {
llvm::Annotations Test(R"cpp(
void foo();
auto lambda = [] {
return $1^foo();
};
)cpp");
auto AST = TestTU::withCode(Test.code()).build();
auto ST = SelectionTree::createRight(AST.getASTContext(), AST.getTokens(),
Test.point("1"), Test.point("1"));
EXPECT_TRUE(ST.commonAncestor()->getDeclContext().isFunctionOrMethod());
}

} // namespace
} // namespace clangd
} // namespace clang
23 changes: 23 additions & 0 deletions clang-tools-extra/clangd/unittests/tweaks/AddUsingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,29 @@ namespace foo { void fun(); }
void foo::fun() {
ff();
})cpp"},
// Inside a lambda.
{
R"cpp(
namespace NS {
void unrelated();
void foo();
}

auto L = [] {
using NS::unrelated;
NS::f^oo();
};)cpp",
R"cpp(
namespace NS {
void unrelated();
void foo();
}

auto L = [] {
using NS::foo;using NS::unrelated;
foo();
};)cpp",
},
// If all other using are fully qualified, add ::
{R"cpp(
#include "test.hpp"
Expand Down
69 changes: 52 additions & 17 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Improvements to clang-tidy

- Improved `--dump-config` to print check options in alphabetical order.

- Improved :program:`clang-tidy-diff.py` script.
- Improved :program:`clang-tidy-diff.py` script.
* Return exit code `1` if any :program:`clang-tidy` subprocess exits with
a non-zero code or if exporting fixes fails.

Expand Down Expand Up @@ -224,18 +224,23 @@ New checks
Recommends the smallest possible underlying type for an ``enum`` or ``enum``
class based on the range of its enumerators.

- New :doc:`readability-reference-to-constructed-temporary
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.
- New :doc:`readability-avoid-nested-conditional-operator
<clang-tidy/checks/readability/avoid-nested-conditional-operator>` check.

Detects C++ code where a reference variable is used to extend the lifetime
of a temporary object that has just been constructed.
Identifies instances of nested conditional operators in the code.

- New :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check.

Finds return statements with ``void`` values used within functions with
``void`` result types.

- New :doc:`readability-reference-to-constructed-temporary
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.

Detects C++ code where a reference variable is used to extend the lifetime
of a temporary object that has just been constructed.

New check aliases
^^^^^^^^^^^^^^^^^

Expand All @@ -260,6 +265,14 @@ Changes in existing checks
casting during type conversions at variable initialization, now with improved
compatibility for C++17 and later versions.

- Improved :doc:`bugprone-exception-escape
<clang-tidy/checks/bugprone/exception-escape>` check by extending the default
check function names to include ``iter_swap`` and ``iter_move``.

- Improved :doc:`bugprone-implicit-widening-of-multiplication-result
<clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result>` check
to correctly emit fixes.

- Improved :doc:`bugprone-lambda-function-name
<clang-tidy/checks/bugprone/lambda-function-name>` check by adding option
`IgnoreMacros` to ignore warnings in macros.
Expand Down Expand Up @@ -368,7 +381,8 @@ Changes in existing checks
<clang-tidy/checks/misc/const-correctness>` check to avoid false positive when
using pointer to member function. Additionally, the check no longer emits
a diagnostic when a variable that is not type-dependent is an operand of a
type-dependent binary operator.
type-dependent binary operator. Improved performance of the check through
optimizations.

- Improved :doc:`misc-include-cleaner
<clang-tidy/checks/misc/include-cleaner>` check by adding option
Expand All @@ -380,9 +394,13 @@ Changes in existing checks
<clang-tidy/checks/misc/redundant-expression>` check to ignore
false-positives in unevaluated context (e.g., ``decltype``).

- Improved :doc:`misc-static-assert
<clang-tidy/checks/misc/static-assert>` check to ignore false-positives when
referring to non-``constexpr`` variables in non-unevaluated context.

- Improved :doc:`misc-unused-using-decls
<clang-tidy/checks/misc/unused-using-decls>` check to avoid false positive when
using in elaborated type.
using in elaborated type and only check C++ files.

- Improved :doc:`modernize-avoid-bind
<clang-tidy/checks/modernize/avoid-bind>` check to
Expand Down Expand Up @@ -422,38 +440,45 @@ Changes in existing checks

- Improved :doc:`modernize-use-using
<clang-tidy/checks/modernize/use-using>` check to fix function pointer and
forward declared ``typedef`` correctly. Added option `IgnoreExternC` to ignore ``typedef``
declaration in ``extern "C"`` scope.
forward declared ``typedef`` correctly. Added option `IgnoreExternC` to ignore
``typedef`` declaration in ``extern "C"`` scope.

- Improved :doc:`performance-faster-string-find
<clang-tidy/checks/performance/faster-string-find>` check to properly escape
single quotes.

- Improved :doc:`performance-for-range-copy
<clang-tidy/checks/performance/for-range-copy>` check to handle cases where
the loop variable is a structured binding.

- Improved :doc:`performance-noexcept-move-constructor
<clang-tidy/checks/performance/noexcept-move-constructor>` to better handle
conditional noexcept expressions, eliminating false-positives.
conditional ``noexcept`` expressions, eliminating false-positives.

- Improved :doc:`performance-noexcept-swap
<clang-tidy/checks/performance/noexcept-swap>` check to enforce a stricter
match with the swap function signature and better handling of condition
noexcept expressions, eliminating false-positives.
``noexcept`` expressions, eliminating false-positives. ``iter_swap`` function
name is checked by default.

- Improved :doc:`readability-braces-around-statements
<clang-tidy/checks/readability/braces-around-statements>` check to
ignore false-positive for ``if constexpr`` in lambda expression.

- Improved :doc:`readability-avoid-const-params-in-decls
<clang-tidy/checks/readability/avoid-const-params-in-decls>` diagnositics to
highlight the const location
<clang-tidy/checks/readability/avoid-const-params-in-decls>` diagnostics to
highlight the ``const`` location

- Improved :doc:`readability-container-contains
<clang-tidy/checks/readability/container-contains>` to correctly handle
interger literals with suffixes in fix-its.
integer literals with suffixes in fix-its.

- Improved :doc:`readability-container-size-empty
<clang-tidy/checks/readability/container-size-empty>` check to
detect comparison between string and empty string literals and support
``length()`` method as an alternative to ``size()``.
``length()`` method as an alternative to ``size()``. Resolved false positives
tied to negative values from size-like methods, and one triggered by size
checks below zero.

- Improved :doc:`readability-function-size
<clang-tidy/checks/readability/function-size>` check configuration to use
Expand All @@ -471,13 +496,14 @@ Changes in existing checks
``camel_Snake_Case`` now detect more invalid identifier names. Fields in
anonymous records (i.e. anonymous structs and unions) now can be checked with
the naming rules associated with their enclosing scopes rather than the naming
rules of public struct/union members.
rules of public ``struct``/``union`` members.

- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check to take
do-while loops into account for the `AllowIntegerConditions` and
`AllowPointerConditions` options. It also now provides more consistent
suggestions when parentheses are added to the return value.
suggestions when parentheses are added to the return value or expressions.
It also ignores false-positives for comparison containing bool bitfield.

- Improved :doc:`readability-misleading-indentation
<clang-tidy/checks/readability/misleading-indentation>` check to ignore
Expand All @@ -487,6 +513,15 @@ Changes in existing checks
<clang-tidy/checks/readability/non-const-parameter>` check to ignore
false-positives in initializer list of record.

- Improved :doc:`readability-redundant-member-init
<clang-tidy/checks/readability/redundant-member-init>` check to now also
detect redundant in-class initializers.

- Improved :doc:`readability-simplify-boolean-expr
<clang-tidy/checks/readability/simplify-boolean-expr>` check by adding the
new option `IgnoreMacros` that allows to ignore boolean expressions originating
from expanded macros.

- Improved :doc:`readability-simplify-subscript-expr
<clang-tidy/checks/readability/simplify-subscript-expr>` check by extending
the default value of the `Types` option to include ``std::span``.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ should not. The functions which should not throw exceptions are the following:
* Move assignment operators
* The ``main()`` functions
* ``swap()`` functions
* ``iter_swap()`` functions
* ``iter_move()`` functions
* Functions marked with ``throw()`` or ``noexcept``
* Other functions given as option

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ Clang-Tidy Checks
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`,
:doc:`readability-avoid-unconditional-preprocessor-if <readability/avoid-unconditional-preprocessor-if>`,
:doc:`readability-braces-around-statements <readability/braces-around-statements>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This check implements detection of local variables which could be declared as
coding guidelines, such as:
`ES.25 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es25-declare-an-object-const-or-constexpr-unless-you-want-to-modify-its-value-later-on>`_
from the C++ Core Guidelines and `AUTOSAR C++14 Rule A7-1-1 (6.7.1 Specifiers)
<https://www.autosar.org/fileadmin/user_upload/standards/adaptive/17-03/AUTOSAR_RS_CPP14Guidelines.pdf>`_.
<https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf>`_.

Please note that this check's analysis is type-based only. Variables that are not modified
but used to create a non-const handle that might escape the scope are not diagnosed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ types and definitions with good return type but wrong ``return`` statements.
type (e.g. ``int``).
* Private and deleted operators are ignored.
* The operator must always return ``*this``.

This check implements `AUTOSAR C++14 Rule A13-2-1
<https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf>`_.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
performance-noexcept-swap
=========================

The check flags user-defined swap functions not marked with ``noexcept`` or
The check flags user-defined swap and iter_swap functions not marked with ``noexcept`` or
marked with ``noexcept(expr)`` where ``expr`` evaluates to ``false``
(but is not a ``false`` literal itself).

When a swap function is marked as ``noexcept``, it assures the compiler that
When a swap or iter_swap function is marked as ``noexcept``, it assures the compiler that
no exceptions will be thrown during the swapping of two objects, which allows
the compiler to perform certain optimizations such as omitting exception
handling code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.. title:: clang-tidy - readability-avoid-nested-conditional-operator

readability-avoid-nested-conditional-operator
=============================================

Identifies instances of nested conditional operators in the code.

Nested conditional operators, also known as ternary operators, can contribute
to reduced code readability and comprehension. So they should be split as
several statements and stored the intermediate results in temporary variable.

Examples:

.. code-block:: c++

int NestInConditional = (condition1 ? true1 : false1) ? true2 : false2;
int NestInTrue = condition1 ? (condition2 ? true1 : false1) : false2;
int NestInFalse = condition1 ? true1 : condition2 ? true2 : false1;

This check implements part of `AUTOSAR C++14 Rule A5-16-1
<https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf>`_.
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ Example

.. code-block:: c++

// Explicitly initializing the member s is unnecessary.
// Explicitly initializing the member s and v is unnecessary.
class Foo {
public:
Foo() : s() {}

private:
std::string s;
std::vector<int> v {};
};

Options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ Examples:
Options
-------

.. option:: IgnoreMacros

If `true`, ignore boolean expressions originating from expanded macros.
Default is `false`.

.. option:: ChainedConditionalReturn

If `true`, conditional boolean return statements at the end of an
Expand All @@ -99,8 +104,8 @@ Options

.. option:: SimplifyDeMorganRelaxed

If `true`, :option:`SimplifyDeMorgan` will also transform negated
conjunctions and disjunctions where there is no negation on either operand.
If `true`, :option:`SimplifyDeMorgan` will also transform negated
conjunctions and disjunctions where there is no negation on either operand.
This option has no effect if :option:`SimplifyDeMorgan` is `false`.
Default is `false`.

Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/modularize/ModularizeUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ std::error_code ModularizeUtilities::loadSingleHeaderListsAndDependencies(
// Get canonical form.
HeaderFileName = getCanonicalPath(HeaderFileName);
// Save the resulting header file path and dependencies.
HeaderFileNames.push_back(std::string(HeaderFileName.str()));
HeaderFileNames.push_back(std::string(HeaderFileName));
Dependencies[HeaderFileName.str()] = Dependents;
}
return std::error_code();
Expand Down Expand Up @@ -248,7 +248,7 @@ std::error_code ModularizeUtilities::loadProblemHeaderList(
// Get canonical form.
HeaderFileName = getCanonicalPath(HeaderFileName);
// Save the resulting header file path.
ProblemFileNames.push_back(std::string(HeaderFileName.str()));
ProblemFileNames.push_back(std::string(HeaderFileName));
}
return std::error_code();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,16 @@ void swap(int&, int&) {
throw 1;
}

void iter_swap(int&, int&) {
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'iter_swap' which should not throw exceptions
throw 1;
}

void iter_move(int&) {
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'iter_move' which should not throw exceptions
throw 1;
}

namespace std {
class bad_alloc {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ char *t0(char *base, int a, int b) {
// CHECK-NOTES-CXX: static_cast<ptrdiff_t>( )
// CHECK-NOTES-ALL: :[[@LINE-5]]:16: note: perform multiplication in a wider type
// CHECK-NOTES-C: (ptrdiff_t)
// CHECK-NOTES-CXX: static_cast<ptrdiff_t>()
// CHECK-NOTES-CXX: static_cast<ptrdiff_t>( )
}
void *t1(char *base, int a, int b) {
return &((a * b)[base]);
Expand All @@ -35,7 +35,7 @@ char *t2(char *base, unsigned int a, int b) {
// CHECK-NOTES-CXX: static_cast<size_t>( )
// CHECK-NOTES-ALL: :[[@LINE-5]]:16: note: perform multiplication in a wider type
// CHECK-NOTES-C: (size_t)
// CHECK-NOTES-CXX: static_cast<size_t>()
// CHECK-NOTES-CXX: static_cast<size_t>( )
}

char *t3(char *base, int a, unsigned int b) {
Expand Down
Loading