335 changes: 335 additions & 0 deletions .ci/metrics/requirements.lock.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .ci/metrics/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pygithub==2.5.0
6 changes: 6 additions & 0 deletions .github/new-issues-labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@

'infra:commit-access-request':
- '/Request Commit Access/'

'false-positive':
- '\bfalse[- ]positive\b'

'false-negative':
- '\bfalse[- ]negative\b'
78 changes: 78 additions & 0 deletions .github/workflows/build-metrics-container.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Build Metrics Container

permissions:
contents: read

on:
push:
branches:
- main
paths:
- .github/workflows/build-metrics-container.yml
- '.ci/metrics/**'
pull_request:
branches:
- main
paths:
- .github/workflows/build-metrics-container.yml
- '.ci/metrics/**'

jobs:
build-metrics-container:
if: github.repository_owner == 'llvm'
runs-on: ubuntu-latest
outputs:
container-name: ${{ steps.vars.outputs.container-name }}
container-name-tag: ${{ steps.vars.outputs.container-name-tag }}
container-filename: ${{ steps.vars.outputs.container-filename }}
steps:
- name: Checkout LLVM
uses: actions/checkout@v4
with:
sparse-checkout: .ci/metrics/
- name: Write Variables
id: vars
run: |
tag=`date +%s`
container_name="ghcr.io/$GITHUB_REPOSITORY_OWNER/metrics"
echo "container-name=$container_name" >> $GITHUB_OUTPUT
echo "container-name-tag=$container_name:$tag" >> $GITHUB_OUTPUT
echo "container-filename=$(echo $container_name:$tag | sed -e 's/\//-/g' -e 's/:/-/g').tar" >> $GITHUB_OUTPUT
- name: Build Container
working-directory: ./.ci/metrics
run: |
podman build -t ${{ steps.vars.outputs.container-name-tag }} -f Dockerfile .
# Save the container so we have it in case the push fails. This also
# allows us to separate the push step into a different job so we can
# maintain minimal permissions while building the container.
- name: Save Container Image
run: |
podman save ${{ steps.vars.outputs.container-name-tag }} > ${{ steps.vars.outputs.container-filename }}
- name: Upload Container Image
uses: actions/upload-artifact@v4
with:
name: container
path: ${{ steps.vars.outputs.container-filename }}
retention-days: 14

push-metrics-container:
if: github.event_name == 'push'
needs:
- build-metrics-container
permissions:
packages: write
runs-on: ubuntu-24.04
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Download Container
uses: actions/download-artifact@v4
with:
name: container
- name: Push Container
run: |
podman load -i ${{ needs.build-metrics-container.outputs.container-filename }}
podman tag ${{ needs.build-metrics-container.outputs.container-name-tag }} ${{ needs.build-metrics-container.outputs.container-name }}:latest
podman login -u ${{ github.actor }} -p $GITHUB_TOKEN ghcr.io
podman push ${{ needs.build-metrics-container.outputs.container-name-tag }}
podman push ${{ needs.build-metrics-container.outputs.container-name }}:latest
29 changes: 29 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,58 +112,87 @@ jobs:
sudo apt-get update
# swig and graphviz are lldb specific dependencies
sudo apt-get install -y cmake ninja-build swig graphviz
- name: Setup output folder
run: mkdir built-docs
- name: Build LLVM docs
if: steps.docs-changed-subprojects.outputs.llvm_any_changed == 'true'
run: |
cmake -B llvm-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C llvm-build docs-llvm-html docs-llvm-man
mkdir built-docs/llvm
cp -r llvm-build/docs/* built-docs/llvm/
- name: Build Clang docs
if: steps.docs-changed-subprojects.outputs.clang_any_changed == 'true'
run: |
cmake -B clang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C clang-build docs-clang-html docs-clang-man
mkdir built-docs/clang
cp -r clang-build/docs/* built-docs/clang/
- name: Build clang-tools-extra docs
if: steps.docs-changed-subprojects.outputs.clang-tools-extra_any_changed == 'true'
run: |
cmake -B clang-tools-extra-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C clang-tools-extra-build docs-clang-tools-html docs-clang-tools-man
mkdir built-docs/clang-tools-extra
cp -r clang-tools-extra-build/docs/* built-docs/clang-tools-extra/
- name: Build LLDB docs
if: steps.docs-changed-subprojects.outputs.lldb_any_changed == 'true'
run: |
cmake -B lldb-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lldb" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C lldb-build docs-lldb-html docs-lldb-man
mkdir built-docs/lldb
cp -r lldb-build/docs/* built-docs/lldb/
- name: Build libunwind docs
if: steps.docs-changed-subprojects.outputs.libunwind_any_changed == 'true'
run: |
cmake -B libunwind-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes
TZ=UTC ninja -C libunwind-build docs-libunwind-html
mkdir built-docs/libunwind
cp -r libunwind-build/docs/* built-docs/libunwind
- name: Build libcxx docs
if: steps.docs-changed-subprojects.outputs.libcxx_any_changed == 'true'
run: |
cmake -B libcxx-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libcxxabi;libcxx;libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes
TZ=UTC ninja -C libcxx-build docs-libcxx-html
mkdir built-docs/libcxx
cp -r libcxx-build/docs/* built-docs/libcxx/
- name: Build libc docs
if: steps.docs-changed-subprojects.outputs.libc_any_changed == 'true'
run: |
cmake -B libc-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libc" -DLLVM_ENABLE_SPHINX=ON ./runtimes
TZ=UTC ninja -C libc-build docs-libc-html
mkdir built-docs/libc
cp -r libc-build/docs/* built-docs/libc/
- name: Build LLD docs
if: steps.docs-changed-subprojects.outputs.lld_any_changed == 'true'
run: |
cmake -B lld-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="lld" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C lld-build docs-lld-html
mkdir built-docs/lld
cp -r lld-build/docs/* built-docs/lld/
- name: Build OpenMP docs
if: steps.docs-changed-subprojects.outputs.openmp_any_changed == 'true'
run: |
cmake -B openmp-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;openmp" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C openmp-build docs-openmp-html
mkdir built-docs/openmp
cp -r openmp-build/docs/* built-docs/openmp/
- name: Build Polly docs
if: steps.docs-changed-subprojects.outputs.polly_any_changed == 'true'
run: |
cmake -B polly-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="polly" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C polly-build docs-polly-html docs-polly-man
mkdir built-docs/polly
cp -r polly-build/docs/* built-docs/polly/
- name: Build Flang docs
if: steps.docs-changed-subprojects.outputs.flang_any_changed == 'true'
run: |
cmake -B flang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;mlir;flang" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C flang-build docs-flang-html
mkdir built-docs/flang
cp -r flang-build/docs/* built-docs/flang/
- name: Upload docs
uses: actions/upload-artifact@v4
with:
name: docs-output
path: built-docs/
File renamed without changes.
13 changes: 13 additions & 0 deletions bolt/include/bolt/Core/BinaryFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2407,6 +2407,19 @@ inline raw_ostream &operator<<(raw_ostream &OS,
return OS;
}

/// Compare function by index if it is valid, fall back to the original address
/// otherwise.
inline bool compareBinaryFunctionByIndex(const BinaryFunction *A,
const BinaryFunction *B) {
if (A->hasValidIndex() && B->hasValidIndex())
return A->getIndex() < B->getIndex();
if (A->hasValidIndex() && !B->hasValidIndex())
return true;
if (!A->hasValidIndex() && B->hasValidIndex())
return false;
return A->getAddress() < B->getAddress();
}

} // namespace bolt

// GraphTraits specializations for function basic block graphs (CFGs)
Expand Down
8 changes: 8 additions & 0 deletions bolt/include/bolt/Profile/DataAggregator.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ class DataAggregator : public DataReader {
std::string BuildIDBinaryName;

/// Memory map info for a single file as recorded in perf.data
/// When a binary has multiple text segments, the Size is computed as the
/// difference of the last address of these segments from the BaseAddress.
/// The base addresses of all text segments must be the same.
struct MMapInfo {
uint64_t BaseAddress{0}; /// Base address of the mapped binary.
uint64_t MMapAddress{0}; /// Address of the executable segment.
Expand Down Expand Up @@ -493,6 +496,11 @@ class DataAggregator : public DataReader {
/// and return a file name matching a given \p FileBuildID.
std::optional<StringRef> getFileNameForBuildID(StringRef FileBuildID);

/// Get a constant reference to the parsed binary mmap entries.
const std::unordered_map<uint64_t, MMapInfo> &getBinaryMMapInfo() {
return BinaryMMapInfo;
}

friend class YAMLProfileWriter;
};
} // namespace bolt
Expand Down
8 changes: 1 addition & 7 deletions bolt/lib/Core/BinaryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1607,13 +1607,7 @@ std::vector<BinaryFunction *> BinaryContext::getSortedFunctions() {
SortedFunctions.begin(),
[](BinaryFunction &BF) { return &BF; });

llvm::stable_sort(SortedFunctions,
[](const BinaryFunction *A, const BinaryFunction *B) {
if (A->hasValidIndex() && B->hasValidIndex()) {
return A->getIndex() < B->getIndex();
}
return A->hasValidIndex();
});
llvm::stable_sort(SortedFunctions, compareBinaryFunctionByIndex);
return SortedFunctions;
}

Expand Down
11 changes: 1 addition & 10 deletions bolt/lib/Core/BinaryFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,16 +385,7 @@ bool BinaryFunction::isForwardCall(const MCSymbol *CalleeSymbol) const {
if (CalleeBF) {
if (CalleeBF->isInjected())
return true;

if (hasValidIndex() && CalleeBF->hasValidIndex()) {
return getIndex() < CalleeBF->getIndex();
} else if (hasValidIndex() && !CalleeBF->hasValidIndex()) {
return true;
} else if (!hasValidIndex() && CalleeBF->hasValidIndex()) {
return false;
} else {
return getAddress() < CalleeBF->getAddress();
}
return compareBinaryFunctionByIndex(this, CalleeBF);
} else {
// Absolute symbol.
ErrorOr<uint64_t> CalleeAddressOrError = BC.getSymbolValue(*CalleeSymbol);
Expand Down
11 changes: 1 addition & 10 deletions bolt/lib/Passes/ReorderFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,16 +473,7 @@ Error ReorderFunctions::runOnFunctions(BinaryContext &BC) {
[](BinaryFunction &BF) { return &BF; });

// Sort functions by index.
llvm::stable_sort(SortedFunctions,
[](const BinaryFunction *A, const BinaryFunction *B) {
if (A->hasValidIndex() && B->hasValidIndex())
return A->getIndex() < B->getIndex();
if (A->hasValidIndex() && !B->hasValidIndex())
return true;
if (!A->hasValidIndex() && B->hasValidIndex())
return false;
return A->getAddress() < B->getAddress();
});
llvm::stable_sort(SortedFunctions, compareBinaryFunctionByIndex);

for (const BinaryFunction *Func : SortedFunctions) {
if (!Func->hasValidIndex())
Expand Down
43 changes: 29 additions & 14 deletions bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ cl::opt<bool> ReadPreAggregated(
"pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
cl::cat(AggregatorCategory));

cl::opt<std::string>
ReadPerfEvents("perf-script-events",
cl::desc("skip perf event collection by supplying a "
"perf-script output in a textual format"),
cl::ReallyHidden, cl::init(""), cl::cat(AggregatorCategory));

static cl::opt<bool>
TimeAggregator("time-aggr",
cl::desc("time BOLT aggregator"),
Expand Down Expand Up @@ -167,8 +173,9 @@ void DataAggregator::findPerfExecutable() {
void DataAggregator::start() {
outs() << "PERF2BOLT: Starting data aggregation job for " << Filename << "\n";

// Don't launch perf for pre-aggregated files
if (opts::ReadPreAggregated)
// Don't launch perf for pre-aggregated files or when perf input is specified
// by the user.
if (opts::ReadPreAggregated || !opts::ReadPerfEvents.empty())
return;

findPerfExecutable();
Expand Down Expand Up @@ -464,6 +471,13 @@ void DataAggregator::filterBinaryMMapInfo() {

int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process,
PerfProcessErrorCallbackTy Callback) {
if (!opts::ReadPerfEvents.empty()) {
outs() << "PERF2BOLT: using pre-processed perf events for '" << Name
<< "' (perf-script-events)\n";
ParsingBuf = opts::ReadPerfEvents;
return 0;
}

std::string Error;
outs() << "PERF2BOLT: waiting for perf " << Name
<< " collection to finish...\n";
Expand Down Expand Up @@ -2056,15 +2070,6 @@ std::error_code DataAggregator::parseMMapEvents() {
if (FileMMapInfo.first == "(deleted)")
continue;

// Consider only the first mapping of the file for any given PID
auto Range = GlobalMMapInfo.equal_range(FileMMapInfo.first);
bool PIDExists = llvm::any_of(make_range(Range), [&](const auto &MI) {
return MI.second.PID == FileMMapInfo.second.PID;
});

if (PIDExists)
continue;

GlobalMMapInfo.insert(FileMMapInfo);
}

Expand Down Expand Up @@ -2116,12 +2121,22 @@ std::error_code DataAggregator::parseMMapEvents() {
<< " using file offset 0x" << Twine::utohexstr(MMapInfo.Offset)
<< ". Ignoring profile data for this mapping\n";
continue;
} else {
MMapInfo.BaseAddress = *BaseAddress;
}
MMapInfo.BaseAddress = *BaseAddress;
}

BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo));
// Try to add MMapInfo to the map and update its size. Large binaries may
// span to multiple text segments, so the mapping is inserted only on the
// first occurrence.
if (!BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo)).second)
assert(MMapInfo.BaseAddress == BinaryMMapInfo[MMapInfo.PID].BaseAddress &&
"Base address on multiple segment mappings should match");

// Update mapping size.
const uint64_t EndAddress = MMapInfo.MMapAddress + MMapInfo.Size;
const uint64_t Size = EndAddress - BinaryMMapInfo[MMapInfo.PID].BaseAddress;
if (Size > BinaryMMapInfo[MMapInfo.PID].Size)
BinaryMMapInfo[MMapInfo.PID].Size = Size;
}

if (BinaryMMapInfo.empty()) {
Expand Down
20 changes: 20 additions & 0 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2927,6 +2927,23 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: ignoring relocation from data to data\n");
}

static BinaryFunction *getInitFunctionIfStaticBinary(BinaryContext &BC) {
// Workaround for https://github.com/llvm/llvm-project/issues/100096
// ("[BOLT] GOT array pointer incorrectly rewritten"). In aarch64
// static glibc binaries, the .init section's _init function pointer can
// alias with a data pointer for the end of an array. GOT rewriting
// currently can't detect this and updates the data pointer to the
// moved _init, causing a runtime crash. Skipping _init on the other
// hand should be harmless.
if (!BC.IsStaticExecutable)
return nullptr;
const BinaryData *BD = BC.getBinaryDataByName("_init");
if (!BD || BD->getSectionName() != ".init")
return nullptr;
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: skip _init in for GOT workaround.\n");
return BC.getBinaryFunctionAtAddress(BD->getAddress());
}

void RewriteInstance::selectFunctionsToProcess() {
// Extend the list of functions to process or skip from a file.
auto populateFunctionNames = [](cl::opt<std::string> &FunctionNamesFile,
Expand Down Expand Up @@ -3047,6 +3064,9 @@ void RewriteInstance::selectFunctionsToProcess() {
return true;
};

if (BinaryFunction *Init = getInitFunctionIfStaticBinary(*BC))
Init->setIgnored();

for (auto &BFI : BC->getBinaryFunctions()) {
BinaryFunction &Function = BFI.second;

Expand Down
43 changes: 43 additions & 0 deletions bolt/test/AArch64/check-init-not-moved.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Regression test for https://github.com/llvm/llvm-project/issues/100096
# static glibc binaries crash on startup because _init is moved and
# shares its address with an array end pointer. The GOT rewriting can't
# tell the two pointers apart and incorrectly updates the _array_end
# address. Test checks that _init is not moved.

# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static -Wl,--section-start=.data=0x1000 -Wl,--section-start=.init=0x1004
# RUN: llvm-bolt %t.exe -o %t.bolt
# RUN: llvm-nm %t.exe | FileCheck --check-prefix=CHECK-ORIGINAL %s
# RUN: llvm-nm %t.bolt | FileCheck --check-prefix=CHECK-BOLTED %s

.section .data
.globl _array_end
_array_start:
.word 0x0

_array_end:
.section .init,"ax",@progbits
.globl _init

# Check that bolt doesn't move _init.
#
# CHECK-ORIGINAL: 0000000000001004 T _init
# CHECK-BOLTED: 0000000000001004 T _init
_init:
ret

.section .text,"ax",@progbits
.globl _start

# Check that bolt is moving some other functions.
#
# CHECK-ORIGINAL: 0000000000001008 T _start
# CHECK-BOLTED-NOT: 0000000000001008 T _start
_start:
bl _init
adrp x0, #:got:_array_end
ldr x0, [x0, #:gotpage_lo15:_array_end]
adrp x0, #:got:_init
ldr x0, [x0, #:gotpage_lo15:_init]
ret

2 changes: 1 addition & 1 deletion bolt/test/AArch64/data-at-0-offset.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang %cflags -O2 -fPIE -Wl,-q -pie %s -o %t.exe
// RUN: %clang %cflags -O2 -fPIE -std=gnu99 -Wl,-q -pie %s -o %t.exe
// RUN: llvm-bolt %t.exe -o %t.bolt 2>&1 | FileCheck %s
// CHECK-NOT: BOLT-WARNING: unable to disassemble instruction at offset

Expand Down
2 changes: 1 addition & 1 deletion bolt/test/AArch64/double_jump.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// A contrived example to test the double jump removal peephole.

// RUN: %clang %cflags -O0 %s -o %t.exe
// RUN: %clangxx %cxxflags -O0 %s -o %t.exe
// RUN: llvm-bolt %t.exe -o %t.bolt --peepholes=double-jumps | \
// RUN: FileCheck %s -check-prefix=CHECKBOLT
// RUN: llvm-objdump --no-print-imm-hex -d %t.bolt | FileCheck %s
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/R_ABS.pic.lld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* with libc available.
* REQUIRES: system-linux
*
* RUN: %clang %cflags -fPIC -shared %s -o %t.so -Wl,-q -fuse-ld=lld
* RUN: %clangxx %cxxflags -fPIC -shared %s -o %t.so -Wl,-q -fuse-ld=lld
* RUN: llvm-bolt %t.so -o %t.so.bolt --relocs
*/

Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/double-jump.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## correctly on Windows e.g. subshell execution
REQUIRES: shell

RUN: %clang %cflags %p/Inputs/double_jump.cpp -o %t.exe
RUN: %clangxx %cxxflags %p/Inputs/double_jump.cpp -o %t.exe
RUN: (llvm-bolt %t.exe --peepholes=double-jumps \
RUN: --eliminate-unreachable -o %t 2>&1 \
RUN: && llvm-objdump -d %t --print-imm-hex --no-show-raw-insn) | FileCheck %s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
; RUN: -split-dwarf-file=main.dwo -o main.o
; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-inlined-subroutine-gc-sections-range-helper.s \
; RUN: -split-dwarf-file=helper.dwo -o helper.o
; RUN: %clang --target=x86_64-pc-linux -fuse-ld=lld -Wl,-gc-sections -Wl,-q -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe
; RUN: %clang -fuse-ld=lld -Wl,-gc-sections -Wl,-q -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections
; RUN: llvm-dwarfdump --debug-addr main.exe > log.txt
; RUN: llvm-dwarfdump --debug-rnglists --verbose --show-form main.dwo >> log.txt
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/jmp-optimization.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## correctly on Windows e.g. unsupported parameter expansion
REQUIRES: shell

RUN: %clang %cflags -O2 %S/Inputs/jmp_opt{,2,3}.cpp -o %t
RUN: %clangxx %cxxflags -O2 %S/Inputs/jmp_opt{,2,3}.cpp -o %t
RUN: llvm-bolt -inline-small-functions %t -o %t.bolt
RUN: llvm-objdump -d %t.bolt --print-imm-hex | FileCheck %s

Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/match-functions-with-call-graph.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# REQUIRES: system-linux
# RUN: split-file %s %t
# RUN: %clang %cflags %t/main.cpp -o %t.exe -Wl,-q -nostdlib
# RUN: %clangxx %cxxflags %t/main.cpp -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \
# RUN: --dyno-stats --print-cfg --infer-stale-profile=1 --match-with-call-graph 2>&1 | FileCheck %s

Expand Down
2 changes: 1 addition & 1 deletion bolt/test/pie.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## on Linux systems where the host triple matches the target.
REQUIRES: system-linux

RUN: %clang %cflags -fPIC -pie %p/Inputs/jump_table_icp.cpp -o %t
RUN: %clangxx %cxxflags -fPIC -pie %p/Inputs/jump_table_icp.cpp -o %t
RUN: llvm-bolt %t -o %t.null 2>&1 | FileCheck %s

CHECK: BOLT-INFO: shared object or position-independent executable detected
2 changes: 1 addition & 1 deletion bolt/test/runtime/X86/instrumentation-indirect.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ int main(int argc, char **argv) {
/*
REQUIRES: system-linux,bolt-runtime,lit-max-individual-test-time
RUN: %clang %cflags %s -o %t.exe -Wl,-q -pie -fpie
RUN: %clang %cflags -D_GNU_SOURCE %s -o %t.exe -Wl,-q -pie -fpie
RUN: llvm-bolt %t.exe --instrument --instrumentation-file=%t.fdata \
RUN: --instrumentation-wait-forks=1 --conservative-instrumentation \
Expand Down
4 changes: 2 additions & 2 deletions bolt/test/runtime/bolt-reserved.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* new sections.
*/

// RUN: %clang %s -o %t.exe -Wl,-q
// RUN: %clangxx %s -o %t.exe -Wl,-q
// RUN: llvm-bolt %t.exe -o %t.bolt.exe 2>&1 | FileCheck %s
// RUN: %t.bolt.exe

Expand All @@ -16,7 +16,7 @@
* not enough for allocating new sections.
*/

// RUN: %clang %s -o %t.tiny.exe -Wl,--no-eh-frame-hdr -Wl,-q -DTINY
// RUN: %clangxx %s -o %t.tiny.exe -Wl,--no-eh-frame-hdr -Wl,-q -DTINY
// RUN: not llvm-bolt %t.tiny.exe -o %t.tiny.bolt.exe 2>&1 | \
// RUN: FileCheck %s --check-prefix=CHECK-TINY

Expand Down
3 changes: 3 additions & 0 deletions bolt/unittests/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS
add_bolt_unittest(CoreTests
BinaryContext.cpp
MCPlusBuilder.cpp
MemoryMaps.cpp
DynoStats.cpp

DISABLE_LLVM_LINK_LLVM_DYLIB
Expand All @@ -17,6 +18,8 @@ target_link_libraries(CoreTests
PRIVATE
LLVMBOLTCore
LLVMBOLTRewrite
LLVMBOLTProfile
LLVMTestingSupport
)

foreach (tgt ${BOLT_TARGETS_TO_BUILD})
Expand Down
5 changes: 3 additions & 2 deletions bolt/unittests/Core/MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
::testing::Values(Triple::aarch64));

TEST_P(MCPlusBuilderTester, AliasX0) {
uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0, AArch64::W0_W1,
uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI,
AArch64::X0, AArch64::W0_W1,
AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7};
size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count);
}

TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0};
uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI, AArch64::X0};
size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count, true);
}
Expand Down
142 changes: 142 additions & 0 deletions bolt/unittests/Core/MemoryMaps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//===- bolt/unittest/Core/MemoryMaps.cpp ----------------------------------===//
//
// 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 "bolt/Core/BinaryContext.h"
#include "bolt/Profile/DataAggregator.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace bolt;

namespace opts {
extern cl::opt<std::string> ReadPerfEvents;
} // namespace opts

namespace {

/// Perform checks on memory map events normally captured in perf. Tests use
/// the 'opts::ReadPerfEvents' flag to emulate these events, passing a custom
/// 'perf script' output to DataAggregator.
struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
void SetUp() override {
initalizeLLVM();
prepareElf();
initializeBOLT();
}

protected:
void initalizeLLVM() {
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllDisassemblers();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
}

void prepareElf() {
memcpy(ElfBuf, "\177ELF", 4);
ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
ObjFile = cantFail(ObjectFile::createObjectFile(Source));
}

void initializeBOLT() {
Relocation::Arch = ObjFile->makeTriple().getArch();
BC = cantFail(BinaryContext::createBinaryContext(
ObjFile->makeTriple(), ObjFile->getFileName(), nullptr, true,
DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
ASSERT_FALSE(!BC);
}

char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
std::unique_ptr<ObjectFile> ObjFile;
std::unique_ptr<BinaryContext> BC;
};
} // namespace

#ifdef X86_AVAILABLE

INSTANTIATE_TEST_SUITE_P(X86, MemoryMapsTester,
::testing::Values(Triple::x86_64));

#endif

#ifdef AARCH64_AVAILABLE

INSTANTIATE_TEST_SUITE_P(AArch64, MemoryMapsTester,
::testing::Values(Triple::aarch64));

#endif

/// Check that the correct mmap size is computed when we have multiple text
/// segment mappings.
TEST_P(MemoryMapsTester, ParseMultipleSegments) {
const int Pid = 1234;
StringRef Filename = "BINARY";
opts::ReadPerfEvents = formatv(
"name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
"[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
"name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
"[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
Pid, Filename);

BC->SegmentMapInfo[0x11da000] =
SegmentInfo{0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true};
BC->SegmentMapInfo[0x31d0000] =
SegmentInfo{0x31d0000, 0x51ac82c, 0x31d0000, 0x3000000, 0x200000, true};

DataAggregator DA("");
BC->setFilename(Filename);
Error Err = DA.preprocessProfile(*BC);

// Ignore errors from perf2bolt when parsing memory events later on.
ASSERT_THAT_ERROR(std::move(Err), Succeeded());

auto &BinaryMMapInfo = DA.getBinaryMMapInfo();
auto El = BinaryMMapInfo.find(Pid);
// Check that memory mapping is present and has the expected size.
ASSERT_NE(El, BinaryMMapInfo.end());
ASSERT_EQ(El->second.Size, static_cast<uint64_t>(0xb1d0000));
}

/// Check that DataAggregator aborts when pre-processing an input binary
/// with multiple text segments that have different base addresses.
TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
const int Pid = 1234;
StringRef Filename = "BINARY";
opts::ReadPerfEvents = formatv(
"name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
"[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
"name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
"[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
Pid, Filename);

BC->SegmentMapInfo[0x11da000] =
SegmentInfo{0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true};
// Using '0x31d0fff' FileOffset which triggers a different base address
// for this second text segment.
BC->SegmentMapInfo[0x31d0000] =
SegmentInfo{0x31d0000, 0x51ac82c, 0x31d0fff, 0x3000000, 0x200000, true};

DataAggregator DA("");
BC->setFilename(Filename);
ASSERT_DEBUG_DEATH(
{ Error Err = DA.preprocessProfile(*BC); },
"Base address on multiple segment mappings should match");
}
4 changes: 2 additions & 2 deletions bolt/utils/bughunter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ if [[ $FAIL -eq "0" ]]; then
fi
else
echo "Did it pass? Type the return code [0 = pass, 1 = fail]"
read -n1 PASS
read -n1 FAIL
fi
if [[ $FAIL -eq "0" ]] ; then
echo " Warning: optimized binary passes."
Expand Down Expand Up @@ -205,7 +205,7 @@ while [[ "$CONTINUE" -ne "0" ]] ; do
echo " OPTIMIZED_BINARY failure=$FAIL"
else
echo "Did it pass? Type the return code [0 = pass, 1 = fail]"
read -n1 PASS
read -n1 FAIL
fi
else
FAIL=1
Expand Down
30 changes: 0 additions & 30 deletions clang-tools-extra/CODE_OWNERS.TXT

This file was deleted.

75 changes: 75 additions & 0 deletions clang-tools-extra/Maintainers.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
=============================
Clang Tools Extra Maintainers
=============================

This file is a list of the maintainers
(https://llvm.org/docs/DeveloperPolicy.html#maintainers) for clang-tools-extra.


Active Maintainers
==================
The following people are the active maintainers for the project. Please reach
out to them for code reviews, questions about their area of expertise, or other
assistance.

Lead Maintainer
---------------
| Aaron Ballman
| aaron@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC)


clang-tidy
----------
| Congcong Cai
| congcongcai0907@163.com (email), HerrCai0907 (GitHub), HerrCai0907 (Discourse)

| Julian Schmidt
| git.julian.schmidt@gmail.com (email), 5chmidti (GitHub), 5chmidti (Discourse), 5chmidti (Discord)

| Piotr Zegar
| me@piotrzegar.pl (email), PiotrZSL (GitHub), PiotrZSL (Discourse), PiotrZSL (Discord)


clang-query
-----------
| Aaron Ballman
| aaron@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC)


clang-doc
---------
| Paul Kirth
| paulkirth@google.com (email), ilovepi (GitHub), ilovepi (Discourse)

| Peter Chou
| peterchou411@gmail.com (email), PeterChou1 (GitHub), PeterChou1 (Discourse), .peterchou (Discord)


clangd
------
| Nathan Ridge
| zeratul976@hotmail.com (email), HighCommander4 (GitHub), HighCommander4 (Discourse), nridge (Discord)

| Chris Bieneman
| chris.bieneman@gmail.com (email), llvm-beanz (GitHub), beanz (Discord), beanz (Discourse)

| Kadir Çetinkaya
| kadircet@google.com (email), kadircet (GitHub) kadircet (Discourse), kadircet (Discord)


Inactive Maintainers
====================
The following people have graciously spent time performing maintainership
responsibilities but are no longer active in that role. Thank you for all your
help with the success of the project!

Emeritus Lead Maintainers
-------------------------
| Manuel Klimek (klimek@google.com (email), r4nt (GitHub))


Inactive component maintainers
------------------------------
| Nathan James (n.james93@hotmail.co.uk) -- clang-tidy
| Julie Hockett (juliehockett@google.com) -- clang-doc
| Sam McCall (sammccall@google.com (email), sam-mccall (GitHub, Discourse, Discord)) -- clangd
29 changes: 26 additions & 3 deletions clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@ findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,
return Results;
}

/// Returns the full source range for the field declaration up to (not
/// including) the trailing semicolumn, including potential macro invocations,
/// e.g. `int a GUARDED_BY(mu);`.
static SourceRange getFullFieldSourceRange(const FieldDecl &Field,
const ASTContext &Context) {
SourceRange Range = Field.getSourceRange();
SourceLocation End = Range.getEnd();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
while (true) {
std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);

if (!CurrentToken || CurrentToken->is(tok::semi))
break;

if (CurrentToken->is(tok::eof))
return Range; // Something is wrong, return the original range.
End = CurrentToken->getLastLoc();
}
return SourceRange(Range.getBegin(), End);
}

/// Reorders fields in the definition of a struct/class.
///
/// At the moment reordering of fields with
Expand Down Expand Up @@ -145,9 +167,10 @@ static bool reorderFieldsInDefinition(
const auto FieldIndex = Field->getFieldIndex();
if (FieldIndex == NewFieldsOrder[FieldIndex])
continue;
addReplacement(Field->getSourceRange(),
Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
Context, Replacements);
addReplacement(
getFullFieldSourceRange(*Field, Context),
getFullFieldSourceRange(*Fields[NewFieldsOrder[FieldIndex]], Context),
Context, Replacements);
}
return true;
}
Expand Down
221 changes: 221 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,212 @@ void BranchCloneCheck::registerMatchers(MatchFinder *Finder) {
this);
Finder->addMatcher(switchStmt().bind("switch"), this);
Finder->addMatcher(conditionalOperator().bind("condOp"), this);
Finder->addMatcher(
ifStmt((hasThen(hasDescendant(ifStmt())))).bind("ifWithDescendantIf"),
this);
}

/// Determines whether two statement trees are identical regarding
/// operators and symbols.
///
/// Exceptions: expressions containing macros or functions with possible side
/// effects are never considered identical.
/// Limitations: (t + u) and (u + t) are not considered identical.
/// t*(u + t) and t*u + t*t are not considered identical.
///
static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
const Stmt *Stmt2, bool IgnoreSideEffects) {

if (!Stmt1 || !Stmt2)
return !Stmt1 && !Stmt2;

// If Stmt1 & Stmt2 are of different class then they are not
// identical statements.
if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
return false;

const auto *Expr1 = dyn_cast<Expr>(Stmt1);
const auto *Expr2 = dyn_cast<Expr>(Stmt2);

if (Expr1 && Expr2) {
// If Stmt1 has side effects then don't warn even if expressions
// are identical.
if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx) &&
Expr2->HasSideEffects(Ctx))
return false;
// If either expression comes from a macro then don't warn even if
// the expressions are identical.
if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
return false;

// If all children of two expressions are identical, return true.
Expr::const_child_iterator I1 = Expr1->child_begin();
Expr::const_child_iterator I2 = Expr2->child_begin();
while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
return false;
++I1;
++I2;
}
// If there are different number of children in the statements, return
// false.
if (I1 != Expr1->child_end())
return false;
if (I2 != Expr2->child_end())
return false;
}

switch (Stmt1->getStmtClass()) {
default:
return false;
case Stmt::CallExprClass:
case Stmt::ArraySubscriptExprClass:
case Stmt::ArraySectionExprClass:
case Stmt::OMPArrayShapingExprClass:
case Stmt::OMPIteratorExprClass:
case Stmt::ImplicitCastExprClass:
case Stmt::ParenExprClass:
case Stmt::BreakStmtClass:
case Stmt::ContinueStmtClass:
case Stmt::NullStmtClass:
return true;
case Stmt::CStyleCastExprClass: {
const auto *CastExpr1 = cast<CStyleCastExpr>(Stmt1);
const auto *CastExpr2 = cast<CStyleCastExpr>(Stmt2);

return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
}
case Stmt::ReturnStmtClass: {
const auto *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
const auto *ReturnStmt2 = cast<ReturnStmt>(Stmt2);

return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
ReturnStmt2->getRetValue(), IgnoreSideEffects);
}
case Stmt::ForStmtClass: {
const auto *ForStmt1 = cast<ForStmt>(Stmt1);
const auto *ForStmt2 = cast<ForStmt>(Stmt2);

if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::DoStmtClass: {
const auto *DStmt1 = cast<DoStmt>(Stmt1);
const auto *DStmt2 = cast<DoStmt>(Stmt2);

if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::WhileStmtClass: {
const auto *WStmt1 = cast<WhileStmt>(Stmt1);
const auto *WStmt2 = cast<WhileStmt>(Stmt2);

if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::IfStmtClass: {
const auto *IStmt1 = cast<IfStmt>(Stmt1);
const auto *IStmt2 = cast<IfStmt>(Stmt2);

if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::CompoundStmtClass: {
const auto *CompStmt1 = cast<CompoundStmt>(Stmt1);
const auto *CompStmt2 = cast<CompoundStmt>(Stmt2);

if (CompStmt1->size() != CompStmt2->size())
return false;

if (!llvm::all_of(llvm::zip(CompStmt1->body(), CompStmt2->body()),
[&Ctx, IgnoreSideEffects](
std::tuple<const Stmt *, const Stmt *> stmtPair) {
const Stmt *stmt0 = std::get<0>(stmtPair);
const Stmt *stmt1 = std::get<1>(stmtPair);
return isIdenticalStmt(Ctx, stmt0, stmt1,
IgnoreSideEffects);
})) {
return false;
}

return true;
}
case Stmt::CompoundAssignOperatorClass:
case Stmt::BinaryOperatorClass: {
const auto *BinOp1 = cast<BinaryOperator>(Stmt1);
const auto *BinOp2 = cast<BinaryOperator>(Stmt2);
return BinOp1->getOpcode() == BinOp2->getOpcode();
}
case Stmt::CharacterLiteralClass: {
const auto *CharLit1 = cast<CharacterLiteral>(Stmt1);
const auto *CharLit2 = cast<CharacterLiteral>(Stmt2);
return CharLit1->getValue() == CharLit2->getValue();
}
case Stmt::DeclRefExprClass: {
const auto *DeclRef1 = cast<DeclRefExpr>(Stmt1);
const auto *DeclRef2 = cast<DeclRefExpr>(Stmt2);
return DeclRef1->getDecl() == DeclRef2->getDecl();
}
case Stmt::IntegerLiteralClass: {
const auto *IntLit1 = cast<IntegerLiteral>(Stmt1);
const auto *IntLit2 = cast<IntegerLiteral>(Stmt2);

llvm::APInt I1 = IntLit1->getValue();
llvm::APInt I2 = IntLit2->getValue();
if (I1.getBitWidth() != I2.getBitWidth())
return false;
return I1 == I2;
}
case Stmt::FloatingLiteralClass: {
const auto *FloatLit1 = cast<FloatingLiteral>(Stmt1);
const auto *FloatLit2 = cast<FloatingLiteral>(Stmt2);
return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
}
case Stmt::StringLiteralClass: {
const auto *StringLit1 = cast<StringLiteral>(Stmt1);
const auto *StringLit2 = cast<StringLiteral>(Stmt2);
return StringLit1->getBytes() == StringLit2->getBytes();
}
case Stmt::MemberExprClass: {
const auto *MemberStmt1 = cast<MemberExpr>(Stmt1);
const auto *MemberStmt2 = cast<MemberExpr>(Stmt2);
return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
}
case Stmt::UnaryOperatorClass: {
const auto *UnaryOp1 = cast<UnaryOperator>(Stmt1);
const auto *UnaryOp2 = cast<UnaryOperator>(Stmt2);
return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
}
}
}

void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
Expand Down Expand Up @@ -269,6 +475,21 @@ void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
return;
}

if (const auto *IS = Result.Nodes.getNodeAs<IfStmt>("ifWithDescendantIf")) {
const Stmt *Then = IS->getThen();
auto CS = dyn_cast<CompoundStmt>(Then);
if (CS && (!CS->body_empty())) {
const auto *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());
if (InnerIf && isIdenticalStmt(Context, IS->getCond(), InnerIf->getCond(),
/*IgnoreSideEffects=*/false)) {
diag(IS->getBeginLoc(), "if with identical inner if statement");
diag(InnerIf->getBeginLoc(), "inner if starts here",
DiagnosticIDs::Note);
}
}
return;
}

llvm_unreachable("No if statement and no switch statement.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
to(parmVarDecl(hasType(hasCanonicalType(
qualType(lValueReferenceType(pointee(
qualType(isConstQualified()))))
.bind("type"))))
.bind("type"))),
hasDeclContext(functionDecl().bind("owner")))
.bind("param")))
.bind("dref"));
const auto Func =
functionDecl(hasReturnTypeLoc(loc(
functionDecl(equalsBoundNode("owner"),
hasReturnTypeLoc(loc(
qualType(hasCanonicalType(equalsBoundNode("type"))))))
.bind("func");

Expand Down
70 changes: 54 additions & 16 deletions clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -855,9 +855,6 @@ static bool areExprsMacroAndNonMacro(const Expr *&LhsExpr,
} // namespace

void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
const auto AnyLiteralExpr = ignoringParenImpCasts(
anyOf(cxxBoolLiteral(), characterLiteral(), integerLiteral()));

const auto BannedIntegerLiteral =
integerLiteral(expandedByMacro(KnownBannedMacroNames));
const auto IsInUnevaluatedContext = expr(anyOf(
Expand All @@ -866,19 +863,16 @@ void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
// Binary with equivalent operands, like (X != 2 && X != 2).
Finder->addMatcher(
traverse(TK_AsIs,
binaryOperator(
anyOf(isComparisonOperator(),
hasAnyOperatorName("-", "/", "%", "|", "&", "^", "&&",
"||", "=")),
operandsAreEquivalent(),
// Filter noisy false positives.
unless(isInTemplateInstantiation()),
unless(binaryOperatorIsInMacro()),
unless(hasType(realFloatingPointType())),
unless(hasEitherOperand(hasType(realFloatingPointType()))),
unless(hasLHS(AnyLiteralExpr)),
unless(hasDescendant(BannedIntegerLiteral)),
unless(IsInUnevaluatedContext))
binaryOperator(anyOf(isComparisonOperator(),
hasAnyOperatorName("-", "/", "%", "|", "&",
"^", "&&", "||", "=")),
operandsAreEquivalent(),
// Filter noisy false positives.
unless(isInTemplateInstantiation()),
unless(binaryOperatorIsInMacro()),
unless(hasAncestor(arraySubscriptExpr())),
unless(hasDescendant(BannedIntegerLiteral)),
unless(IsInUnevaluatedContext))
.bind("binary")),
this);

Expand Down Expand Up @@ -1238,6 +1232,50 @@ void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary")) {
// If the expression's constants are macros, check whether they are
// intentional.

//
// Special case for floating-point representation.
//
// If expressions on both sides of comparison operator are of type float,
// then for some comparison operators no warning shall be
// reported even if the expressions are identical from a symbolic point of
// view. Comparison between expressions, declared variables and literals
// are treated differently.
//
// != and == between float literals that have the same value should NOT
// warn. < > between float literals that have the same value SHOULD warn.
//
// != and == between the same float declaration should NOT warn.
// < > between the same float declaration SHOULD warn.
//
// != and == between eq. expressions that evaluates into float
// should NOT warn.
// < > between eq. expressions that evaluates into float
// should NOT warn.
//
const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts();
const Expr *RHS = BinOp->getRHS()->IgnoreParenImpCasts();
const BinaryOperator::Opcode Op = BinOp->getOpcode();
const bool OpEqualEQorNE = ((Op == BO_EQ) || (Op == BO_NE));

const auto *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
const auto *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
const auto *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
const auto *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);

if (DeclRef1 && DeclRef2 &&
DeclRef1->getType()->hasFloatingRepresentation() &&
DeclRef2->getType()->hasFloatingRepresentation() &&
(DeclRef1->getDecl() == DeclRef2->getDecl()) && OpEqualEQorNE) {
return;
}

if (FloatLit1 && FloatLit2 &&
FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue()) &&
OpEqualEQorNE) {
return;
}

if (areSidesBinaryConstExpressions(BinOp, Result.Context)) {
const Expr *LhsConst = nullptr, *RhsConst = nullptr;
BinaryOperatorKind MainOpcode{}, SideOpcode{};
Expand Down
30 changes: 27 additions & 3 deletions clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"

using namespace clang::ast_matchers;

Expand Down Expand Up @@ -78,6 +80,22 @@ AST_POLYMORPHIC_MATCHER(isExternStorageClass,
return Node.getStorageClass() == SC_Extern;
}

AST_MATCHER(FunctionDecl, isAllocationOrDeallocationOverloadedFunction) {
// [basic.stc.dynamic.allocation]
// An allocation function that is not a class member function shall belong to
// the global scope and not have a name with internal linkage.
// [basic.stc.dynamic.deallocation]
// A deallocation function that is not a class member function shall belong to
// the global scope and not have a name with internal linkage.
static const llvm::DenseSet<OverloadedOperatorKind> OverloadedOperators{
OverloadedOperatorKind::OO_New,
OverloadedOperatorKind::OO_Array_New,
OverloadedOperatorKind::OO_Delete,
OverloadedOperatorKind::OO_Array_Delete,
};
return OverloadedOperators.contains(Node.getOverloadedOperator());
}

} // namespace

UseInternalLinkageCheck::UseInternalLinkageCheck(StringRef Name,
Expand All @@ -100,10 +118,16 @@ void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) {
isExternStorageClass(), isExternC(),
// 3. template
isExplicitTemplateSpecialization(),
// 4. friend
hasAncestor(friendDecl()))));
hasAncestor(decl(anyOf(
// 4. friend
friendDecl(),
// 5. module export decl
exportDecl()))))));
Finder->addMatcher(
functionDecl(Common, hasBody(), unless(cxxMethodDecl()), unless(isMain()))
functionDecl(Common, hasBody(),
unless(anyOf(cxxMethodDecl(),
isAllocationOrDeallocationOverloadedFunction(),
isMain())))
.bind("fn"),
this);
Finder->addMatcher(varDecl(Common, hasGlobalStorage()).bind("var"), this);
Expand Down
76 changes: 55 additions & 21 deletions clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@
using namespace clang::ast_matchers;

namespace clang::tidy::modernize {

static bool isNegativeComparison(const Expr *ComparisonExpr) {
if (const auto *Op = llvm::dyn_cast<BinaryOperator>(ComparisonExpr))
return Op->getOpcode() == BO_NE;

if (const auto *Op = llvm::dyn_cast<CXXOperatorCallExpr>(ComparisonExpr))
return Op->getOperator() == OO_ExclaimEqual;

if (const auto *Op =
llvm::dyn_cast<CXXRewrittenBinaryOperator>(ComparisonExpr))
return Op->getOperator() == BO_NE;

return false;
}

struct NotLengthExprForStringNode {
NotLengthExprForStringNode(std::string ID, DynTypedNode Node,
ASTContext *Context)
Expand Down Expand Up @@ -171,10 +186,26 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
hasRHS(lengthExprForStringNode("needle")))))
.bind("expr"),
this);

// Case 6: X.substr(0, LEN(Y)) [!=]= Y -> starts_with.
Finder->addMatcher(
binaryOperation(
hasAnyOperatorName("==", "!="),
hasOperands(
expr().bind("needle"),
cxxMemberCallExpr(
argumentCountIs(2), hasArgument(0, ZeroLiteral),
hasArgument(1, lengthExprForStringNode("needle")),
callee(cxxMethodDecl(hasName("substr"),
ofClass(OnClassWithStartsWithFunction))
.bind("find_fun")))
.bind("find_expr")))
.bind("expr"),
this);
}

void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
const auto *ComparisonExpr = Result.Nodes.getNodeAs<Expr>("expr");
const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>("find_expr");
const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("find_fun");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("needle");
Expand All @@ -183,39 +214,42 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *EndsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("ends_with_fun");
assert(bool(StartsWithFunction) != bool(EndsWithFunction));

const CXXMethodDecl *ReplacementFunction =
StartsWithFunction ? StartsWithFunction : EndsWithFunction;

if (ComparisonExpr->getBeginLoc().isMacroID())
if (ComparisonExpr->getBeginLoc().isMacroID() ||
FindExpr->getBeginLoc().isMacroID())
return;

const bool Neg = ComparisonExpr->getOpcode() == BO_NE;
// Make sure FindExpr->getArg(0) can be used to make a range in the FitItHint.
if (FindExpr->getNumArgs() == 0)
return;

auto Diagnostic =
diag(FindExpr->getExprLoc(), "use %0 instead of %1() %select{==|!=}2 0")
<< ReplacementFunction->getName() << FindFun->getName() << Neg;
// Retrieve the source text of the search expression.
const auto SearchExprText = Lexer::getSourceText(
CharSourceRange::getTokenRange(SearchExpr->getSourceRange()),
*Result.SourceManager, Result.Context->getLangOpts());

// Remove possible arguments after search expression and ' [!=]= .+' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
*Result.SourceManager, getLangOpts()),
ComparisonExpr->getEndLoc()),
")");
auto Diagnostic = diag(FindExpr->getExprLoc(), "use %0 instead of %1")
<< ReplacementFunction->getName() << FindFun->getName();

// Remove possible '.+ [!=]= ' prefix.
// Remove everything before the function call.
Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));

// Replace method name by '(starts|ends)_with'.
// Remove possible arguments before search expression.
// Rename the function to `starts_with` or `ends_with`.
Diagnostic << FixItHint::CreateReplacement(FindExpr->getExprLoc(),
ReplacementFunction->getName());

// Replace arguments and everything after the function call.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(FindExpr->getExprLoc(),
SearchExpr->getBeginLoc()),
(ReplacementFunction->getName() + "(").str());
CharSourceRange::getTokenRange(FindExpr->getArg(0)->getBeginLoc(),
ComparisonExpr->getEndLoc()),
(SearchExprText + ")").str());

// Add possible negation '!'.
if (Neg)
// Add negation if necessary.
if (isNegativeComparison(ComparisonExpr))
Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(), "!");
}

Expand Down
13 changes: 2 additions & 11 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,18 +467,9 @@ class DefineOutline : public Tweak {
}
}

// For function templates, the same limitations as for class templates
// apply.
if (const TemplateParameterList *Params =
MD->getDescribedTemplateParams()) {
// FIXME: Is this really needed? It inhibits application on
// e.g. std::enable_if.
for (NamedDecl *P : *Params) {
if (!P->getIdentifier())
return false;
}
// Function templates must be defined in the same file.
if (MD->getDescribedTemplate())
SameFile = true;
}

// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
Expand Down
18 changes: 6 additions & 12 deletions clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,6 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
template <> void fo^o<int>() {}
)cpp");

// Not available on member function templates with unnamed template
// parameters.
EXPECT_UNAVAILABLE(R"cpp(
struct Foo { template <typename> void ba^r() {} };
)cpp");

// Not available on methods of unnamed classes.
EXPECT_UNAVAILABLE(R"cpp(
struct Foo {
Expand Down Expand Up @@ -410,14 +404,14 @@ inline typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>
{
R"cpp(
struct Foo {
template <typename T, bool B = true>
template <typename T, typename, bool B = true>
T ^bar() { return {}; }
};)cpp",
R"cpp(
struct Foo {
template <typename T, bool B = true>
template <typename T, typename, bool B = true>
T bar() ;
};template <typename T, bool B>
};template <typename T, typename, bool B>
inline T Foo::bar() { return {}; }
)cpp",
""},
Expand All @@ -426,13 +420,13 @@ inline T Foo::bar() { return {}; }
{
R"cpp(
template <typename T> struct Foo {
template <typename U> T ^bar(const T& t, const U& u) { return {}; }
template <typename U, bool> T ^bar(const T& t, const U& u) { return {}; }
};)cpp",
R"cpp(
template <typename T> struct Foo {
template <typename U> T bar(const T& t, const U& u) ;
template <typename U, bool> T bar(const T& t, const U& u) ;
};template <typename T>
template <typename U>
template <typename U, bool>
inline T Foo<T>::bar(const T& t, const U& u) { return {}; }
)cpp",
""},
Expand Down
23 changes: 19 additions & 4 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Hover
Code completion
^^^^^^^^^^^^^^^

- Added completion for C++20 keywords.

Code actions
^^^^^^^^^^^^

Expand Down Expand Up @@ -154,6 +156,10 @@ Changes in existing checks
<clang-tidy/checks/altera/id-dependent-backward-branch>` check by fixing
crashes from invalid code.

- Improved :doc:`bugprone-branch-clone
<clang-tidy/checks/bugprone/branch-clone>` check to improve detection of
branch clones by now detecting duplicate inner and outer if statements.

- Improved :doc:`bugprone-casting-through-void
<clang-tidy/checks/bugprone/casting-through-void>` check to suggest replacing
the offending code with ``reinterpret_cast``, to more clearly express intent.
Expand All @@ -177,7 +183,8 @@ Changes in existing checks
- Improved :doc:`bugprone-return-const-ref-from-parameter
<clang-tidy/checks/bugprone/return-const-ref-from-parameter>` check to
diagnose potential dangling references when returning a ``const &`` parameter
by using the conditional operator ``cond ? var1 : var2``.
by using the conditional operator ``cond ? var1 : var2`` and no longer giving
false positives for functions which contain lambda.

- Improved :doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` check to find suspicious
Expand Down Expand Up @@ -228,14 +235,21 @@ Changes in existing checks
<clang-tidy/checks/misc/definitions-in-headers>` check by rewording the
diagnostic note that suggests adding ``inline``.

- Improved :doc:`misc-redundant-expression
<clang-tidy/checks/misc/redundant-expression>` check by extending the
checker to detect floating point and integer literals in redundant
expressions.

- Improved :doc:`misc-unconventional-assign-operator
<clang-tidy/checks/misc/unconventional-assign-operator>` check to avoid
false positive for C++23 deducing this.

- Improved :doc:`misc-use-internal-linkage
<clang-tidy/checks/misc/use-internal-linkage>` check to insert ``static``
keyword before type qualifiers such as ``const`` and ``volatile`` and fix
false positives for function declaration without body.
false positives for function declaration without body and fix false positives
for C++20 export declarations and fix false positives for global scoped
overloaded ``operator new`` and ``operator delete``.

- Improved :doc:`modernize-avoid-c-arrays
<clang-tidy/checks/modernize/avoid-c-arrays>` check to suggest using
Expand All @@ -260,8 +274,9 @@ Changes in existing checks
``NULL``/``__null`` (but not ``0``) when used with a templated type.

- Improved :doc:`modernize-use-starts-ends-with
<clang-tidy/checks/modernize/use-starts-ends-with>` check to handle two cases
that can be replaced with ``ends_with``
<clang-tidy/checks/modernize/use-starts-ends-with>` check to handle two new
cases from ``rfind`` and ``compare`` to ``ends_with``, and one new case from
``substr`` to ``starts_with``, and a small adjustment to the diagnostic message.

- Improved :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check to support replacing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,28 @@ If this is the intended behavior, then there is no reason to use a conditional
statement; otherwise the issue can be solved by fixing the branch that is
handled incorrectly.

The check also detects repeated branches in longer ``if/else if/else`` chains
The check detects repeated branches in longer ``if/else if/else`` chains
where it would be even harder to notice the problem.

The check also detects repeated inner and outer ``if`` statements that may
be a result of a copy-paste error. This check cannot currently detect
identical inner and outer ``if`` statements if code is between the ``if``
conditions. An example is as follows.

.. code-block:: c++

void test_warn_inner_if_1(int x) {
if (x == 1) { // warns, if with identical inner if
if (x == 1) // inner if is here
;
if (x == 1) { // does not warn, cannot detect
int y = x;
if (x == 1)
;
}
}


In ``switch`` statements the check only reports repeated branches when they are
consecutive, because it is relatively common that the ``case:`` labels have
some natural ordering and rearranging them would decrease the readability of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,22 @@ Examples:
(p->x == p->x) // always true
(p->x < p->x) // always false
(speed - speed + 1 == 12) // speed - speed is always zero
int b = a | 4 | a // identical expr on both sides
((x=1) | (x=1)) // expression is identical

Floats are handled except in the case that NaNs are checked like so:

.. code-block:: c++

int TestFloat(float F) {
if (F == F) // Identical float values used
return 1;
return 0;
}

int TestFloat(float F) {
// Testing NaN.
if (F != F && F == F) // does not warn
return 1;
return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ Example:
void fn3(); // without function body in all declaration, maybe external linkage
void fn3();

// export declarations
export void fn4() {}
export namespace t { void fn5() {} }
export int v2;

Options
-------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,16 @@ Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
and suggests replacing with the simpler method when it is available. Notably,
this will work with ``std::string`` and ``std::string_view``.

.. code-block:: c++
Covered scenarios:

std::string s = "...";
if (s.find("prefix") == 0) { /* do something */ }
if (s.rfind("prefix", 0) == 0) { /* do something */ }
if (s.compare(0, strlen("prefix"), "prefix") == 0) { /* do something */ }
if (s.compare(s.size() - strlen("suffix"), strlen("suffix"), "suffix") == 0) {
/* do something */
}
if (s.rfind("suffix") == (s.length() - 6)) {
/* do something */
}
becomes

.. code-block:: c++

std::string s = "...";
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
==================================================== =====================
Expression Replacement
---------------------------------------------------- ---------------------
``u.find(v) == 0`` ``u.starts_with(v)``
``u.rfind(v, 0) != 0`` ``!u.starts_with(v)``
``u.compare(0, v.size(), v) == 0`` ``u.starts_with(v)``
``u.substr(0, v.size()) == v`` ``u.starts_with(v)``
``v != u.substr(0, v.size())`` ``!u.starts_with(v)``
``u.compare(u.size() - v.size(), v.size(), v) == 0`` ``u.ends_with(v)``
``u.rfind(v) == u.size() - v.size()`` ``u.ends_with(v)``
==================================================== =====================
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: clang-reorder-fields -record-name Foo -fields-order y,x %s -- | FileCheck %s

#define GUARDED_BY(x) __attribute__((guarded_by(x)))

class Foo {
int x GUARDED_BY(x); // CHECK: {{^ int y;}}
int y; // CHECK-NEXT: {{^ int x GUARDED_BY\(x\);}}
};

12 changes: 8 additions & 4 deletions clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct basic_string {
_Type& insert(size_type pos, const C* s);
_Type& insert(size_type pos, const C* s, size_type n);

_Type substr(size_type pos = 0, size_type count = npos) const;

constexpr bool starts_with(std::basic_string_view<C, T> sv) const noexcept;
constexpr bool starts_with(C ch) const noexcept;
constexpr bool starts_with(const C* s) const;
Expand Down Expand Up @@ -134,10 +136,6 @@ bool operator==(const std::string&, const std::string&);
bool operator==(const std::string&, const char*);
bool operator==(const char*, const std::string&);

bool operator!=(const std::string&, const std::string&);
bool operator!=(const std::string&, const char*);
bool operator!=(const char*, const std::string&);

bool operator==(const std::wstring&, const std::wstring&);
bool operator==(const std::wstring&, const wchar_t*);
bool operator==(const wchar_t*, const std::wstring&);
Expand All @@ -146,9 +144,15 @@ bool operator==(const std::string_view&, const std::string_view&);
bool operator==(const std::string_view&, const char*);
bool operator==(const char*, const std::string_view&);

#if __cplusplus < 202002L
bool operator!=(const std::string&, const std::string&);
bool operator!=(const std::string&, const char*);
bool operator!=(const char*, const std::string&);

bool operator!=(const std::string_view&, const std::string_view&);
bool operator!=(const std::string_view&, const char*);
bool operator!=(const char*, const std::string_view&);
#endif

size_t strlen(const char* str);
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ struct C {
// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: returning a constant reference parameter
};

const auto Lf1 = [](const T& t) -> const T& { return t; };
// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter

} // namespace invalid

namespace false_negative_because_dependent_and_not_instantiated {
Expand Down Expand Up @@ -151,6 +154,14 @@ void instantiate(const int &param, const float &paramf, int &mut_param, float &m
itf6(mut_paramf);
}

template<class T>
void f(const T& t) {
const auto get = [&t] -> const T& { return t; };
return T{};
}

const auto Lf1 = [](T& t) -> const T& { return t; };

} // namespace valid

namespace overload {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,13 @@ void func_with_body() {}
void func_without_body();
void func_without_body();
}

// gh117489 start
namespace std {
using size_t = decltype(sizeof(int));
}
void * operator new(std::size_t) { return nullptr; }
void * operator new[](std::size_t) { return nullptr; }
void operator delete(void*) noexcept {}
void operator delete[](void*) noexcept {}
// gh117489 end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %check_clang_tidy -std=c++20 %s misc-use-internal-linkage %t -- -- -I%S/Inputs/use-internal-linkage

module;

export module test;

export void single_export_fn() {}
export int single_export_var;

export {
void group_export_fn1() {}
void group_export_fn2() {}
int group_export_var1;
int group_export_var2;
}

export namespace aa {
void namespace_export_fn() {}
int namespace_export_var;
} // namespace aa
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
string_like sl, string_like_camel slc, prefer_underscore_version puv,
prefer_underscore_version_flip puvf) {
s.find("a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find() == 0
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find [modernize-use-starts-ends-with]
// CHECK-FIXES: s.starts_with("a");

(((((s)).find("a")))) == ((0));
Expand Down Expand Up @@ -68,7 +68,7 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-FIXES: !s.starts_with("a");

s.rfind("a", 0) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of rfind() == 0
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of rfind [modernize-use-starts-ends-with]
// CHECK-FIXES: s.starts_with("a");

s.rfind(s, 0) == 0;
Expand All @@ -91,16 +91,6 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: !s.starts_with("a");

#define STR(x) std::string(x)
0 == STR(s).find("a");
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: STR(s).starts_with("a");

#define STRING s
if (0 == STRING.find("ala")) { /* do something */}
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: if (STRING.starts_with("ala"))

#define FIND find
s.FIND("a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
Expand Down Expand Up @@ -149,11 +139,11 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-FIXES: puvf.starts_with("a");

s.compare(0, 1, "a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare() == 0
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare [modernize-use-starts-ends-with]
// CHECK-FIXES: s.starts_with("a");

s.compare(0, 1, "a") != 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare() != 0
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare [modernize-use-starts-ends-with]
// CHECK-FIXES: !s.starts_with("a");

s.compare(0, strlen("a"), "a") == 0;
Expand Down Expand Up @@ -265,4 +255,78 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,

s.compare(0, 1, "ab") == 0;
s.rfind(suffix, 1) == s.size() - suffix.size();

#define STR(x) std::string(x)
0 == STR(s).find("a");

#define STRING s
if (0 == STRING.find("ala")) { /* do something */}
}

void test_substr() {
std::string str("hello world");
std::string prefix = "hello";

// Basic pattern
str.substr(0, 5) == "hello";
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: str.starts_with("hello");

// With string literal on left side
"hello" == str.substr(0, 5);
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: str.starts_with("hello");

// Inequality comparison
str.substr(0, 5) != "world";
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: !str.starts_with("world");

// Ensure non-zero start position is not transformed
str.substr(1, 5) == "hello";
str.substr(0, 4) == "hello"; // Length mismatch

size_t len = 5;
str.substr(0, len) == "hello"; // Non-constant length

// String literal with size calculation
str.substr(0, strlen("hello")) == "hello";
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: str.starts_with("hello");

str.substr(0, prefix.size()) == prefix;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: str.starts_with(prefix);

str.substr(0, prefix.length()) == prefix;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: str.starts_with(prefix);

// Tests to verify macro behavior
#define MSG "hello"
str.substr(0, strlen(MSG)) == MSG;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]
// CHECK-FIXES: str.starts_with(MSG);

#define STARTS_WITH(X, Y) (X).substr(0, (Y).size()) == (Y)
STARTS_WITH(str, prefix);

#define SUBSTR(X, A, B) (X).substr((A), (B))
SUBSTR(str, 0, 6) == "prefix";

#define STR() str
SUBSTR(STR(), 0, 6) == "prefix";
"prefix" == SUBSTR(STR(), 0, 6);

str.substr(0, strlen("hello123")) == "hello";
}

void test_operator_rewriting(std::string str, std::string prefix) {
str.substr(0, prefix.size()) == prefix;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr
// CHECK-FIXES: str.starts_with(prefix);

str.substr(0, prefix.size()) != prefix;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr
// CHECK-FIXES: !str.starts_with(prefix);
}
2 changes: 1 addition & 1 deletion clang/docs/ClangFormat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
Clang-format options:
--Werror - If set, changes formatting warnings to errors
--Wno-error=<value> - If set don't error out on the specified warning type.
--Wno-error=<value> - If set, don't error out on the specified warning type.
=unknown - If set, unknown format options are only warned about.
This can be used to enable formatting, even if the
configuration contains unknown (newer) options.
Expand Down
41 changes: 20 additions & 21 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3759,9 +3759,9 @@ the configuration (without a prefix: ``Auto``).
lists.

Important differences:
- No spaces inside the braced list.
- No line break before the closing brace.
- Indentation with the continuation indent, not with the block indent.
* No spaces inside the braced list.
* No line break before the closing brace.
* Indentation with the continuation indent, not with the block indent.

Fundamentally, C++11 braced lists are formatted exactly like function
calls would be formatted in their place. If the braced list follows a name
Expand Down Expand Up @@ -4104,10 +4104,10 @@ the configuration (without a prefix: ``Auto``).
When guessing whether a #include is the "main" include (to assign
category 0, see above), use this regex of allowed suffixes to the header
stem. A partial match is done, so that:
- "" means "arbitrary suffix"
- "$" means "no suffix"
* ``""`` means "arbitrary suffix"
* ``"$"`` means "no suffix"

For example, if configured to "(_test)?$", then a header a.h would be seen
For example, if configured to ``"(_test)?$"``, then a header a.h would be seen
as the "main" include in both a.cc and a_test.cc.

.. _IncludeIsMainSourceRegex:
Expand Down Expand Up @@ -5313,21 +5313,21 @@ the configuration (without a prefix: ``Auto``).

**QualifierOrder** (``List of Strings``) :versionbadge:`clang-format 14` :ref:`¶ <QualifierOrder>`
The order in which the qualifiers appear.
Order is an array that can contain any of the following:
The order is an array that can contain any of the following:

* const
* inline
* static
* friend
* constexpr
* volatile
* restrict
* type
* ``const``
* ``inline``
* ``static``
* ``friend``
* ``constexpr``
* ``volatile``
* ``restrict``
* ``type``


.. note::

It **must** contain ``type``.
It must contain ``type``.

Items to the left of ``type`` will be placed to the left of the type and
aligned in the order supplied. Items to the right of ``type`` will be
Expand Down Expand Up @@ -6645,12 +6645,11 @@ the configuration (without a prefix: ``Auto``).
.. _StatementMacros:

**StatementMacros** (``List of Strings``) :versionbadge:`clang-format 8` :ref:`¶ <StatementMacros>`
A vector of macros that should be interpreted as complete
statements.
A vector of macros that should be interpreted as complete statements.

Typical macros are expressions, and require a semi-colon to be
added; sometimes this is not the case, and this allows to make
clang-format aware of such cases.
Typical macros are expressions and require a semicolon to be added.
Sometimes this is not the case, and this allows to make clang-format aware
of such cases.

For example: Q_UNUSED

Expand Down
3 changes: 3 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,9 @@ elementwise to the input.

Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±infinity

The integer elementwise intrinsics, including ``__builtin_elementwise_popcount``,
can be called in a ``constexpr`` context.

============================================== ====================================================================== =========================================
Name Operation Supported element types
============================================== ====================================================================== =========================================
Expand Down
11 changes: 11 additions & 0 deletions clang/docs/LibASTMatchersReference.html
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,17 @@ <h2 id="decl-matchers">Node Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('exportDecl0')"><a name="exportDecl0Anchor">exportDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ExportDecl.html">ExportDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="exportDecl0"><pre>Matches any export declaration.

Example matches following declarations.
export void foo();
export { void foo(); }
export namespace { void foo(); }
export int v;
</pre></td></tr>


<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('fieldDecl0')"><a name="fieldDecl0Anchor">fieldDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FieldDecl.html">FieldDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="fieldDecl0"><pre>Matches field declarations.

Expand Down
2 changes: 1 addition & 1 deletion clang/docs/OpenMPSupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ implementation.
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| memory management | changes to omp_alloctrait_key enum | :none:`unclaimed` | |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| memory model | seq_cst clause on flush construct | :none:`unclaimed` | |
| memory model | seq_cst clause on flush construct | :good:`done` | https://github.com/llvm/llvm-project/pull/114072 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | 'omp_all_memory' keyword and use in 'depend' clause | :good:`done` | D125828, D126321 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
Expand Down
72 changes: 70 additions & 2 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,35 @@ C++ Specific Potentially Breaking Changes

Previously, this code was erroneously accepted.

- Clang will now consider the implicitly deleted destructor of a union or
a non-union class without virtual base class to be ``constexpr`` in C++20
mode (Clang 19 only did so in C++23 mode but the standard specification for
this changed in C++20). (#GH85550)

.. code-block:: c++

struct NonLiteral {
NonLiteral() {}
~NonLiteral() {}
};

template <class T>
struct Opt {
union {
char c;
T data;
};
bool engaged = false;

constexpr Opt() {}
constexpr ~Opt() {
if (engaged)
data.~T();
}
};

// Previously only accepted in C++23 and later, now also accepted in C++20.
consteval void foo() { Opt<NonLiteral>{}; }

ABI Changes in This Version
---------------------------
Expand Down Expand Up @@ -227,6 +256,8 @@ C++2c Feature Support
- Added the ``__builtin_is_within_lifetime`` builtin, which supports
`P2641R4 Checking if a union alternative is active <https://wg21.link/p2641r4>`_

- Implemented `P3176R1 The Oxford variadic comma <https://wg21.link/P3176R1>`_

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Removed the restriction to literal types in constexpr functions in C++23 mode.
Expand Down Expand Up @@ -332,6 +363,8 @@ C23 Feature Support

- Clang now supports `N3029 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3029.htm>`_ Improved Normal Enumerations.
- Clang now officially supports `N3030 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3030.htm>`_ Enhancements to Enumerations. Clang already supported it as an extension, so there were no changes to compiler behavior.
- Fixed the value of ``BOOL_WIDTH`` in ``<limits.h>`` to return ``1``
explicitly, as mandated by the standard. Fixes #GH117348

Non-comprehensive list of changes in this release
-------------------------------------------------
Expand Down Expand Up @@ -370,6 +403,7 @@ Non-comprehensive list of changes in this release
- ``__builtin_reduce_mul`` function can now be used in constant expressions.
- ``__builtin_reduce_and`` function can now be used in constant expressions.
- ``__builtin_reduce_or`` and ``__builtin_reduce_xor`` functions can now be used in constant expressions.
- ``__builtin_elementwise_popcount`` function can now be used in constant expressions.

New Compiler Flags
------------------
Expand Down Expand Up @@ -404,10 +438,10 @@ Modified Compiler Flags
to utilize these vector libraries. The behavior for all other vector function
libraries remains unchanged.

- The ``-Wnontrivial-memaccess`` warning has been updated to also warn about
- The ``-Wnontrivial-memcall`` warning has been added to warn about
passing non-trivially-copyable destrination parameter to ``memcpy``,
``memset`` and similar functions for which it is a documented undefined
behavior.
behavior. It is implied by ``-Wnontrivial-memaccess``

Removed Compiler Flags
-------------------------
Expand Down Expand Up @@ -441,6 +475,11 @@ Attribute Changes in Clang
- The ``hybrid_patchable`` attribute is now supported on ARM64EC targets. It can be used to specify
that a function requires an additional x86-64 thunk, which may be patched at runtime.

- The attribute ``[[clang::no_specializations]]`` has been added to warn
users that a specific template shouldn't be specialized. This is useful for
e.g. standard library type traits, where adding a specialization results in
undefined behaviour.

- ``[[clang::lifetimebound]]`` is now explicitly disallowed on explicit object member functions
where they were previously silently ignored.

Expand Down Expand Up @@ -471,6 +510,8 @@ Attribute Changes in Clang
- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be
used to specify when a reference to a function parameter is captured by another capturing entity ``X``.

- The ``target_version`` attribute is now only supported for AArch64 and RISC-V architectures.

Improvements to Clang's diagnostics
-----------------------------------

Expand Down Expand Up @@ -583,6 +624,10 @@ Improvements to Clang's diagnostics
- For an rvalue reference bound to a temporary struct with an integer member, Clang will detect constant integer overflow
in the initializer for the integer member (#GH46755).

- Fixed a false negative ``-Wunused-private-field`` diagnostic when a defaulted comparison operator is defined out of class (#GH116961).

- Clang now diagnoses dangling references for C++20's parenthesized aggregate initialization (#101957).

Improvements to Clang's time-trace
----------------------------------

Expand Down Expand Up @@ -714,6 +759,10 @@ Bug Fixes to C++ Support
assumption if they also occur inside of a dependent lambda. (#GH114787)
- Clang now uses valid deduced type locations when diagnosing functions with trailing return type
missing placeholder return type. (#GH78694)
- Fixed a bug where bounds of partially expanded pack indexing expressions were checked too early. (#GH116105)
- Fixed an assertion failure caused by using ``consteval`` in condition in consumed analyses. (#GH117385)
- Fix a crash caused by incorrect argument position in merging deduced template arguments. (#GH113659)
- Fixed an assertion failure caused by mangled names with invalid identifiers. (#GH112205)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -723,6 +772,9 @@ Bug Fixes to AST Handling
sometimes incorrectly return null even if a comment was present. (#GH108145)
- Clang now correctly parses the argument of the ``relates``, ``related``, ``relatesalso``,
and ``relatedalso`` comment commands.
- Clang now uses the location of the begin of the member expression for ``CallExpr``
involving deduced ``this``. (#GH116928)
- Fixed printout of AST that uses pack indexing expression. (#GH116486)

Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -913,6 +965,8 @@ AST Matchers
- Ensure ``hasName`` matches template specializations across inline namespaces,
making `matchesNodeFullSlow` and `matchesNodeFullFast` consistent.

- Add ``exportDecl`` matcher to match export declaration.

clang-format
------------

Expand Down Expand Up @@ -964,6 +1018,10 @@ Improvements
Moved checkers
^^^^^^^^^^^^^^

- The checker ``alpha.core.IdenticalExpr`` was deleted because it was
duplicated in the clang-tidy checkers ``misc-redundant-expression`` and
``bugprone-branch-clone``.

- The checker ``alpha.security.MallocOverflow`` was deleted because it was
badly implemented and its agressive logic produced too many false positives.
To detect too large arguments passed to malloc, consider using the checker
Expand All @@ -975,6 +1033,16 @@ Moved checkers
original checkers were implemented only using AST matching and make more
sense as a single clang-tidy check.

- The checker ``alpha.unix.Chroot`` was modernized, improved and moved to
``unix.Chroot``. Testing was done on open source projects that use chroot(),
and false issues addressed in the improvements based on real use cases. Open
source projects used for testing include nsjail, lxroot, dive and ruri.
This checker conforms to SEI Cert C recommendation `POS05-C. Limit access to
files by creating a jail
<https://wiki.sei.cmu.edu/confluence/display/c/POS05-C.+Limit+access+to+files+by+creating+a+jail>`_.
Fixes (#GH34697).
(#GH117791) [Documentation](https://clang.llvm.org/docs/analyzer/checkers.html#unix-chroot-c).

.. _release-notes-sanitizers:

Sanitizers
Expand Down
14 changes: 14 additions & 0 deletions clang/docs/SanitizerCoverage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,20 @@ Users need to implement a single function to capture the CF table at startup:
// the collected control flow.
}
Gated Trace Callbacks
=====================

Gate the invocation of the tracing callbacks with
``-sanitizer-coverage-gated-trace-callbacks``.

When this option is enabled, the instrumentation will not call into the
runtime-provided callbacks for tracing, thus only incurring in a trivial
branch without going through a function call.

It is up to the runtime to toggle the value of the global variable in order to
enable tracing.

This option is only supported for trace-pc-guard and trace-cmp.

Disabling instrumentation with ``__attribute__((no_sanitize("coverage")))``
===========================================================================
Expand Down
4 changes: 2 additions & 2 deletions clang/docs/UsersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2660,7 +2660,7 @@ usual build cycle when using sample profilers for optimization:
> clang-cl /O2 -gdwarf -gline-tables-only ^
/clang:-fdebug-info-for-profiling /clang:-funique-internal-linkage-names ^
/fprofile-sample-use=code.prof code.cc /Fe:code /fuse-ld=lld /link /debug:dwarf
-fprofile-sample-use=code.prof code.cc /Fe:code -fuse-ld=lld /link /debug:dwarf
[OPTIONAL] Sampling-based profiles can have inaccuracies or missing block/
edge counters. The profile inference algorithm (profi) can be used to infer
Expand All @@ -2679,7 +2679,7 @@ usual build cycle when using sample profilers for optimization:
> clang-cl /clang:-fsample-profile-use-profi /O2 -gdwarf -gline-tables-only ^
/clang:-fdebug-info-for-profiling /clang:-funique-internal-linkage-names ^
/fprofile-sample-use=code.prof code.cc /Fe:code /fuse-ld=lld /link /debug:dwarf
-fprofile-sample-use=code.prof code.cc /Fe:code -fuse-ld=lld /link /debug:dwarf
Sample Profile Formats
""""""""""""""""""""""
Expand Down
122 changes: 54 additions & 68 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,37 @@ Critical section handling functions modeled by this checker:
}
}
.. _unix-Chroot:
unix.Chroot (C)
"""""""""""""""
Check improper use of chroot described by SEI Cert C recommendation `POS05-C.
Limit access to files by creating a jail
<https://wiki.sei.cmu.edu/confluence/display/c/POS05-C.+Limit+access+to+files+by+creating+a+jail>`_.
The checker finds usage patterns where ``chdir("/")`` is not called immediately
after a call to ``chroot(path)``.
.. code-block:: c
void f();
void test_bad() {
chroot("/usr/local");
f(); // warn: no call of chdir("/") immediately after chroot
}
void test_bad_path() {
chroot("/usr/local");
chdir("/usr"); // warn: no call of chdir("/") immediately after chroot
f();
}
void test_good() {
chroot("/usr/local");
chdir("/"); // no warning
f();
}
.. _unix-Errno:
unix.Errno (C)
Expand Down Expand Up @@ -1899,6 +1930,29 @@ Check the size argument passed into C string functions for common erroneous patt
// warn: potential buffer overflow
}
.. _unix-cstring-NotNullTerminated:
unix.cstring.NotNullTerminated (C)
""""""""""""""""""""""""""""""""""
Check for arguments which are not null-terminated strings;
applies to the ``strlen``, ``strcpy``, ``strcat``, ``strcmp`` family of functions.
Only very fundamental cases are detected where the passed memory block is
absolutely different from a null-terminated string. This checker does not
find if a memory buffer is passed where the terminating zero character
is missing.
.. code-block:: c
void test1() {
int l = strlen((char *)&test1); // warn
}
void test2() {
label:
int l = strlen((char *)&&label); // warn
}
.. _unix-cstring-NullArg:
unix.cstring.NullArg (C)
Expand Down Expand Up @@ -2788,36 +2842,6 @@ Check for assignment of a fixed address to a pointer.
p = (int *) 0x10000; // warn
}
.. _alpha-core-IdenticalExpr:
alpha.core.IdenticalExpr (C, C++)
"""""""""""""""""""""""""""""""""
Warn about unintended use of identical expressions in operators.
.. code-block:: cpp
// C
void test() {
int a = 5;
int b = a | 4 | a; // warn: identical expr on both sides
}
// C++
bool f(void);
void test(bool b) {
int i = 10;
if (f()) { // warn: true and false branches are identical
do {
i--;
} while (f());
} else {
do {
i--;
} while (f());
}
}
.. _alpha-core-PointerArithm:
alpha.core.PointerArithm (C)
Expand Down Expand Up @@ -3275,21 +3299,6 @@ SEI CERT checkers which tries to find errors based on their `C coding rules <htt
alpha.unix
^^^^^^^^^^
.. _alpha-unix-Chroot:
alpha.unix.Chroot (C)
"""""""""""""""""""""
Check improper use of chroot.
.. code-block:: c
void f();
void test() {
chroot("/usr/local");
f(); // warn: no call of chdir("/") immediately after chroot
}
.. _alpha-unix-PthreadLock:
alpha.unix.PthreadLock (C)
Expand Down Expand Up @@ -3367,29 +3376,6 @@ Checks for overlap in two buffer arguments. Applies to: ``memcpy, mempcpy, wmem
memcpy(a + 2, a + 1, 8); // warn
}
.. _alpha-unix-cstring-NotNullTerminated:
alpha.unix.cstring.NotNullTerminated (C)
""""""""""""""""""""""""""""""""""""""""
Check for arguments which are not null-terminated strings;
applies to the ``strlen``, ``strcpy``, ``strcat``, ``strcmp`` family of functions.
Only very fundamental cases are detected where the passed memory block is
absolutely different from a null-terminated string. This checker does not
find if a memory buffer is passed where the terminating zero character
is missing.
.. code-block:: c
void test1() {
int l = strlen((char *)&test); // warn
}
void test2() {
label:
int l = strlen((char *)&&label); // warn
}
.. _alpha-unix-cstring-OutOfBounds:
alpha.unix.cstring.OutOfBounds (C)
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,13 @@ class CXXRecordDecl : public RecordDecl {
needsOverloadResolutionForDestructor()) &&
"destructor should not be deleted");
data().DefaultedDestructorIsDeleted = true;
// C++23 [dcl.constexpr]p3.2:
// if the function is a constructor or destructor, its class does not have
// any virtual base classes.
// C++20 [dcl.constexpr]p5:
// The definition of a constexpr destructor whose function-body is
// not = delete shall additionally satisfy...
data().DefaultedDestructorIsConstexpr = data().NumVBases == 0;
}

/// Determine whether this class should get an implicit move
Expand Down
14 changes: 9 additions & 5 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -4390,17 +4390,17 @@ class PackIndexingExpr final
unsigned TransformedExpressions : 31;

LLVM_PREFERRED_TYPE(bool)
unsigned ExpandedToEmptyPack : 1;
unsigned FullySubstituted : 1;

PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc,
SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr,
ArrayRef<Expr *> SubstitutedExprs = {},
bool ExpandedToEmptyPack = false)
bool FullySubstituted = false)
: Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary),
EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc),
SubExprs{PackIdExpr, IndexExpr},
TransformedExpressions(SubstitutedExprs.size()),
ExpandedToEmptyPack(ExpandedToEmptyPack) {
FullySubstituted(FullySubstituted) {

auto *Exprs = getTrailingObjects<Expr *>();
std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(),
Expand All @@ -4424,12 +4424,16 @@ class PackIndexingExpr final
SourceLocation RSquareLoc, Expr *PackIdExpr,
Expr *IndexExpr, std::optional<int64_t> Index,
ArrayRef<Expr *> SubstitutedExprs = {},
bool ExpandedToEmptyPack = false);
bool FullySubstituted = false);
static PackIndexingExpr *CreateDeserialized(ASTContext &Context,
unsigned NumTransformedExprs);

bool isFullySubstituted() const { return FullySubstituted; }

/// Determine if the expression was expanded to empty.
bool expandsToEmptyPack() const { return ExpandedToEmptyPack; }
bool expandsToEmptyPack() const {
return isFullySubstituted() && TransformedExpressions == 0;
}

/// Determine the location of the 'sizeof' keyword.
SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/OpenMPClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -2670,8 +2670,8 @@ class OMPCompareClause final : public OMPClause {
}
};

/// This represents 'seq_cst' clause in the '#pragma omp atomic'
/// directive.
/// This represents 'seq_cst' clause in the '#pragma omp atomic|flush'
/// directives.
///
/// \code
/// #pragma omp atomic seq_cst
Expand Down
12 changes: 7 additions & 5 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -5922,12 +5922,12 @@ class PackIndexingType final
unsigned Size : 31;

LLVM_PREFERRED_TYPE(bool)
unsigned ExpandsToEmptyPack : 1;
unsigned FullySubstituted : 1;

protected:
friend class ASTContext; // ASTContext creates these.
PackIndexingType(const ASTContext &Context, QualType Canonical,
QualType Pattern, Expr *IndexExpr, bool ExpandsToEmptyPack,
QualType Pattern, Expr *IndexExpr, bool FullySubstituted,
ArrayRef<QualType> Expansions = {});

public:
Expand All @@ -5951,7 +5951,9 @@ class PackIndexingType final

bool hasSelectedType() const { return getSelectedIndex() != std::nullopt; }

bool expandsToEmptyPack() const { return ExpandsToEmptyPack; }
bool isFullySubstituted() const { return FullySubstituted; }

bool expandsToEmptyPack() const { return isFullySubstituted() && Size == 0; }

ArrayRef<QualType> getExpansions() const {
return {getExpansionsPtr(), Size};
Expand All @@ -5965,10 +5967,10 @@ class PackIndexingType final
if (hasSelectedType())
getSelectedType().Profile(ID);
else
Profile(ID, Context, getPattern(), getIndexExpr(), expandsToEmptyPack());
Profile(ID, Context, getPattern(), getIndexExpr(), isFullySubstituted());
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
QualType Pattern, Expr *E, bool ExpandsToEmptyPack);
QualType Pattern, Expr *E, bool FullySubstituted);

private:
const QualType *getExpansionsPtr() const {
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -473,12 +473,12 @@ let Class = PackIndexingType in {
def : Property<"indexExpression", ExprRef> {
let Read = [{ node->getIndexExpr() }];
}
def : Property<"expandsToEmptyPack", Bool> {
let Read = [{ node->expandsToEmptyPack() }];
def : Property<"isFullySubstituted", Bool> {
let Read = [{ node->isFullySubstituted() }];
}

def : Creator<[{
return ctx.getPackIndexingType(pattern, indexExpression, expandsToEmptyPack);
return ctx.getPackIndexingType(pattern, indexExpression, isFullySubstituted);
}]>;
}

Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,17 @@ AST_MATCHER_P(TemplateArgument, equalsIntegralValue,
extern const internal::VariadicDynCastAllOfMatcher<Stmt,
ObjCAutoreleasePoolStmt> autoreleasePoolStmt;

/// Matches any export declaration.
///
/// Example matches following declarations.
/// \code
/// export void foo();
/// export { void foo(); }
/// export namespace { void foo(); }
/// export int v;
/// \endcode
extern const internal::VariadicDynCastAllOfMatcher<Decl, ExportDecl> exportDecl;

/// Matches any value declaration.
///
/// Example matches A, B, C and F
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Analysis/FlowSensitive/ASTOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ struct ReferencedDecls {
/// Free functions and member functions which are referenced (but not
/// necessarily called).
llvm::DenseSet<const FunctionDecl *> Functions;
/// When analyzing a lambda's call operator, the set of all parameters (from
/// the surrounding function) that the lambda captures. Captured local
/// variables are already included in `Locals` above.
llvm::DenseSet<const ParmVarDecl *> LambdaCapturedParams;
};

/// Returns declarations that are declared in or referenced from `FD`.
Expand Down
Loading