29 changes: 15 additions & 14 deletions bolt/lib/Core/ParallelUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,20 @@ void runOnEachFunctionWithUniqueAllocId(
LLVM_DEBUG(T.stopTimer());
};

unsigned AllocId = 1;
auto EnsureAllocatorExists = [&BC](unsigned AllocId) {
if (!BC.MIB->checkAllocatorExists(AllocId)) {
MCPlusBuilder::AllocatorIdTy Id =
BC.MIB->initializeNewAnnotationAllocator();
(void)Id;
assert(AllocId == Id && "unexpected allocator id created");
}
};

if (opts::NoThreads || ForceSequential) {
runBlock(BC.getBinaryFunctions().begin(), BC.getBinaryFunctions().end(), 0);
EnsureAllocatorExists(AllocId);
runBlock(BC.getBinaryFunctions().begin(), BC.getBinaryFunctions().end(),
AllocId);
return;
}
// This lock is used to postpone task execution
Expand All @@ -205,32 +217,21 @@ void runOnEachFunctionWithUniqueAllocId(
ThreadPoolInterface &Pool = getThreadPool();
auto BlockBegin = BC.getBinaryFunctions().begin();
unsigned CurrentCost = 0;
unsigned AllocId = 1;
for (auto It = BC.getBinaryFunctions().begin();
It != BC.getBinaryFunctions().end(); ++It) {
BinaryFunction &BF = It->second;
CurrentCost += computeCostFor(BF, SkipPredicate, SchedPolicy);

if (CurrentCost >= BlockCost) {
if (!BC.MIB->checkAllocatorExists(AllocId)) {
MCPlusBuilder::AllocatorIdTy Id =
BC.MIB->initializeNewAnnotationAllocator();
(void)Id;
assert(AllocId == Id && "unexpected allocator id created");
}
EnsureAllocatorExists(AllocId);
Pool.async(runBlock, BlockBegin, std::next(It), AllocId);
AllocId++;
BlockBegin = std::next(It);
CurrentCost = 0;
}
}

if (!BC.MIB->checkAllocatorExists(AllocId)) {
MCPlusBuilder::AllocatorIdTy Id =
BC.MIB->initializeNewAnnotationAllocator();
(void)Id;
assert(AllocId == Id && "unexpected allocator id created");
}
EnsureAllocatorExists(AllocId);

Pool.async(runBlock, BlockBegin, BC.getBinaryFunctions().end(), AllocId);
Lock.unlock();
Expand Down
13 changes: 13 additions & 0 deletions bolt/lib/Passes/SplitFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,12 @@ Error SplitFunctions::runOnFunctions(BinaryContext &BC) {
if (!opts::SplitFunctions)
return Error::success();

if (BC.IsLinuxKernel && BC.BOLTReserved.empty()) {
BC.errs() << "BOLT-ERROR: split functions require reserved space in the "
"Linux kernel binary\n";
exit(1);
}

// If split strategy is not CDSplit, then a second run of the pass is not
// needed after function reordering.
if (BC.HasFinalizedFunctionOrder &&
Expand Down Expand Up @@ -829,6 +835,13 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
}
}
}

// Outlining blocks with dynamic branches is not supported yet.
if (BC.IsLinuxKernel) {
if (llvm::any_of(
*BB, [&](MCInst &Inst) { return BC.MIB->isDynamicBranch(Inst); }))
BB->setCanOutline(false);
}
}

BF.getLayout().updateLayoutIndices();
Expand Down
8 changes: 4 additions & 4 deletions bolt/lib/Passes/ValidateMemRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst,
if (!BD)
return false;

JumpTable *JT = BC.getJumpTableContainingAddress(BD->getAddress());
const uint64_t TargetAddress = BD->getAddress() + Offset;
JumpTable *JT = BC.getJumpTableContainingAddress(TargetAddress);
if (!JT)
return false;

Expand All @@ -42,9 +43,8 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst,
// the jump table label with a regular rodata reference. Get a
// non-JT reference by fetching the symbol 1 byte before the JT
// label.
MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(BD->getAddress() - 1, "DATAat");
BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, Offset + 1, &*BC.Ctx,
0);
MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(TargetAddress - 1, "DATAat");
BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, 1, &*BC.Ctx, 0);
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: replaced reference @" << BF.getPrintName()
<< " from " << BD->getName() << " to " << NewSym->getName()
<< " + 1\n");
Expand Down
36 changes: 13 additions & 23 deletions bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,14 +861,17 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
return true;
}

bool DataAggregator::recordTrace(
BinaryFunction &BF, const LBREntry &FirstLBR, const LBREntry &SecondLBR,
uint64_t Count,
SmallVector<std::pair<uint64_t, uint64_t>, 16> &Branches) const {
std::optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>>
DataAggregator::getFallthroughsInTrace(BinaryFunction &BF,
const LBREntry &FirstLBR,
const LBREntry &SecondLBR,
uint64_t Count) const {
SmallVector<std::pair<uint64_t, uint64_t>, 16> Branches;

BinaryContext &BC = BF.getBinaryContext();

if (!BF.isSimple())
return false;
return std::nullopt;

assert(BF.hasCFG() && "can only record traces in CFG state");

Expand All @@ -877,13 +880,13 @@ bool DataAggregator::recordTrace(
const uint64_t To = SecondLBR.From - BF.getAddress();

if (From > To)
return false;
return std::nullopt;

const BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(From);
const BinaryBasicBlock *ToBB = BF.getBasicBlockContainingOffset(To);

if (!FromBB || !ToBB)
return false;
return std::nullopt;

// Adjust FromBB if the first LBR is a return from the last instruction in
// the previous block (that instruction should be a call).
Expand All @@ -907,7 +910,7 @@ bool DataAggregator::recordTrace(
// within the same basic block, e.g. when two call instructions are in the
// same block. In this case we skip the processing.
if (FromBB == ToBB)
return true;
return Branches;

// Process blocks in the original layout order.
BinaryBasicBlock *BB = BF.getLayout().getBlock(FromBB->getIndex());
Expand All @@ -921,7 +924,7 @@ bool DataAggregator::recordTrace(
LLVM_DEBUG(dbgs() << "no fall-through for the trace:\n"
<< " " << FirstLBR << '\n'
<< " " << SecondLBR << '\n');
return false;
return std::nullopt;
}

const MCInst *Instr = BB->getLastNonPseudoInstr();
Expand All @@ -945,20 +948,7 @@ bool DataAggregator::recordTrace(
BI.Count += Count;
}

return true;
}

std::optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>>
DataAggregator::getFallthroughsInTrace(BinaryFunction &BF,
const LBREntry &FirstLBR,
const LBREntry &SecondLBR,
uint64_t Count) const {
SmallVector<std::pair<uint64_t, uint64_t>, 16> Res;

if (!recordTrace(BF, FirstLBR, SecondLBR, Count, Res))
return std::nullopt;

return Res;
return Branches;
}

bool DataAggregator::recordEntry(BinaryFunction &BF, uint64_t To, bool Mispred,
Expand Down
80 changes: 45 additions & 35 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -582,19 +582,51 @@ static void emitDWOBuilder(const std::string &DWOName,
Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter);
}

void DWARFRewriter::addStringHelper(DIEBuilder &DIEBldr, DIE &Die,
const DWARFUnit &Unit,
DIEValue &DIEAttrInfo, StringRef Str) {
uint32_t NewOffset = StrWriter->addString(Str);
/// Adds a \p Str to .debug_str section.
/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using
/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets
/// for this contribution of \p Unit.
static void addStringHelper(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, DIEBuilder &DIEBldr,
DIE &Die, const DWARFUnit &Unit,
DIEValue &DIEAttrInfo, StringRef Str) {
uint32_t NewOffset = StrWriter.addString(Str);
if (Unit.getVersion() >= 5) {
StrOffstsWriter->updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(),
NewOffset);
StrOffstsWriter.updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(),
NewOffset);
return;
}
DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(),
DIEInteger(NewOffset));
}

static std::string
updateDWONameCompDir(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter,
std::unordered_map<std::string, uint32_t> &NameToIndexMap,
DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &UnitDIE) {
DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name);
if (!DWONameAttrInfo)
DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name);
assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU.");
std::string ObjectName;

ObjectName = getDWOName(Unit, NameToIndexMap);
addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit,
DWONameAttrInfo, ObjectName.c_str());

DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir);
assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU.");

if (!opts::DwarfOutputPath.empty()) {
if (!sys::fs::exists(opts::DwarfOutputPath))
sys::fs::create_directory(opts::DwarfOutputPath);
addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit,
CompDirAttrInfo, opts::DwarfOutputPath.c_str());
}
return ObjectName;
}

using DWARFUnitVec = std::vector<DWARFUnit *>;
using CUPartitionVector = std::vector<DWARFUnitVec>;
/// Partitions CUs in to buckets. Bucket size is controlled by
Expand Down Expand Up @@ -692,33 +724,6 @@ void DWARFRewriter::updateDebugInfo() {
// specified.
std::unordered_map<std::string, uint32_t> NameToIndexMap;

auto updateDWONameCompDir = [&](DWARFUnit &Unit, DIEBuilder &DIEBldr,
DIE &UnitDIE) -> std::string {
DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name);
if (!DWONameAttrInfo)
DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name);
assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU.");
std::string ObjectName;

{
std::lock_guard<std::mutex> Lock(AccessMutex);
ObjectName = getDWOName(Unit, NameToIndexMap);
}
addStringHelper(DIEBldr, UnitDIE, Unit, DWONameAttrInfo,
ObjectName.c_str());

DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir);
assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU.");

if (!opts::DwarfOutputPath.empty()) {
if (!sys::fs::exists(opts::DwarfOutputPath))
sys::fs::create_directory(opts::DwarfOutputPath);
addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo,
opts::DwarfOutputPath.c_str());
}
return ObjectName;
};

DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
*StrWriter);
DWPState State;
Expand All @@ -741,8 +746,13 @@ void DWARFRewriter::updateDebugInfo() {
DIEBuilder DWODIEBuilder(BC, &(*SplitCU)->getContext(), DebugNamesTable,
Unit);
DWODIEBuilder.buildDWOUnit(**SplitCU);
std::string DWOName = updateDWONameCompDir(
*Unit, *DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit));
std::string DWOName = "";
{
std::lock_guard<std::mutex> Lock(AccessMutex);
DWOName = updateDWONameCompDir(*StrOffstsWriter, *StrWriter,
NameToIndexMap, *Unit, *DIEBlder,
*DIEBlder->getUnitDIEbyUnit(*Unit));
}

DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true);
DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter;
Expand Down
274 changes: 149 additions & 125 deletions bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Large diffs are not rendered by default.

82 changes: 44 additions & 38 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,35 @@ void RewriteInstance::discoverFileObjects() {

registerFragments();
FileSymbols.clear();

discoverBOLTReserved();
}

void RewriteInstance::discoverBOLTReserved() {
BinaryData *StartBD = BC->getBinaryDataByName(getBOLTReservedStart());
BinaryData *EndBD = BC->getBinaryDataByName(getBOLTReservedEnd());
if (!StartBD != !EndBD) {
BC->errs() << "BOLT-ERROR: one of the symbols is missing from the binary: "
<< getBOLTReservedStart() << ", " << getBOLTReservedEnd()
<< '\n';
exit(1);
}

if (!StartBD)
return;

if (StartBD->getAddress() >= EndBD->getAddress()) {
BC->errs() << "BOLT-ERROR: invalid reserved space boundaries\n";
exit(1);
}
BC->BOLTReserved = AddressRange(StartBD->getAddress(), EndBD->getAddress());
BC->outs() << "BOLT-INFO: using reserved space for allocating new sections\n";

PHDRTableOffset = 0;
PHDRTableAddress = 0;
NewTextSegmentAddress = 0;
NewTextSegmentOffset = 0;
NextAvailableAddress = BC->BOLTReserved.start();
}

Error RewriteInstance::discoverRtFiniAddress() {
Expand Down Expand Up @@ -3617,26 +3646,6 @@ void RewriteInstance::updateMetadata() {
void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) {
BC->deregisterUnusedSections();

// Check if the input has a space reserved for BOLT.
BinaryData *StartBD = BC->getBinaryDataByName(getBOLTReservedStart());
BinaryData *EndBD = BC->getBinaryDataByName(getBOLTReservedEnd());
if (!StartBD != !EndBD) {
BC->errs() << "BOLT-ERROR: one of the symbols is missing from the binary: "
<< getBOLTReservedStart() << ", " << getBOLTReservedEnd()
<< '\n';
exit(1);
}

if (StartBD) {
PHDRTableOffset = 0;
PHDRTableAddress = 0;
NewTextSegmentAddress = 0;
NewTextSegmentOffset = 0;
NextAvailableAddress = StartBD->getAddress();
BC->outs()
<< "BOLT-INFO: using reserved space for allocating new sections\n";
}

// If no new .eh_frame was written, remove relocated original .eh_frame.
BinarySection *RelocatedEHFrameSection =
getSection(".relocated" + getEHFrameSectionName());
Expand All @@ -3657,12 +3666,12 @@ void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) {
// Map the rest of the sections.
mapAllocatableSections(MapSection);

if (StartBD) {
const uint64_t ReservedSpace = EndBD->getAddress() - StartBD->getAddress();
const uint64_t AllocatedSize = NextAvailableAddress - StartBD->getAddress();
if (ReservedSpace < AllocatedSize) {
BC->errs() << "BOLT-ERROR: reserved space (" << ReservedSpace << " byte"
<< (ReservedSpace == 1 ? "" : "s")
if (!BC->BOLTReserved.empty()) {
const uint64_t AllocatedSize =
NextAvailableAddress - BC->BOLTReserved.start();
if (BC->BOLTReserved.size() < AllocatedSize) {
BC->errs() << "BOLT-ERROR: reserved space (" << BC->BOLTReserved.size()
<< " byte" << (BC->BOLTReserved.size() == 1 ? "" : "s")
<< ") is smaller than required for new allocations ("
<< AllocatedSize << " bytes)\n";
exit(1);
Expand Down Expand Up @@ -4047,6 +4056,7 @@ void RewriteInstance::patchELFPHDRTable() {
NewWritableSegmentSize = NextAvailableAddress - NewWritableSegmentAddress;
}

const uint64_t SavedPos = OS.tell();
OS.seek(PHDRTableOffset);

auto createNewTextPhdr = [&]() {
Expand Down Expand Up @@ -4151,6 +4161,8 @@ void RewriteInstance::patchELFPHDRTable() {
<< "BOLT-ERROR: could not find PT_GNU_STACK program header to modify\n";
exit(1);
}

OS.seek(SavedPos);
}

namespace {
Expand Down Expand Up @@ -5041,10 +5053,6 @@ void RewriteInstance::patchELFSymTabs(ELFObjectFile<ELFT> *File) {
assert((DynSymSection || BC->IsStaticExecutable) &&
"dynamic symbol table expected");
if (DynSymSection) {
// Set pointer to the end of the section, so we can use pwrite to update
// the dynamic symbol table.
Out->os().seek(DynSymSection->sh_offset + DynSymSection->sh_size);

updateELFSymbolTable(
File,
/*IsDynSym=*/true,
Expand Down Expand Up @@ -5853,13 +5861,11 @@ void RewriteInstance::writeEHFrameHeader() {

NextAvailableAddress += EHFrameHdrSec.getOutputSize();

if (const BinaryData *ReservedEnd =
BC->getBinaryDataByName(getBOLTReservedEnd())) {
if (NextAvailableAddress > ReservedEnd->getAddress()) {
BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
<< " into reserved space\n";
exit(1);
}
if (!BC->BOLTReserved.empty() &&
(NextAvailableAddress > BC->BOLTReserved.end())) {
BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
<< " into reserved space\n";
exit(1);
}

// Merge new .eh_frame with the relocated original so that gdb can locate all
Expand Down Expand Up @@ -5893,7 +5899,7 @@ uint64_t RewriteInstance::getNewValueForSymbol(const StringRef Name) {

uint64_t RewriteInstance::getFileOffsetForAddress(uint64_t Address) const {
// Check if it's possibly part of the new segment.
if (Address >= NewTextSegmentAddress)
if (NewTextSegmentAddress && Address >= NewTextSegmentAddress)
return Address - NewTextSegmentAddress + NewTextSegmentOffset;

// Find an existing segment that matches the address.
Expand Down
60 changes: 0 additions & 60 deletions bolt/test/X86/jt-symbol-disambiguation-4.s

This file was deleted.

40 changes: 40 additions & 0 deletions bolt/test/X86/linux-smp-locks.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# REQUIRES: system-linux

## Check that BOLT correctly parses and updates the Linux kernel .smp_locks
## section.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops=0 --bolt-info=0 -o %t.out \
# RUN: |& FileCheck %s

## Check the output of BOLT with NOPs removed.

# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s

# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 SMP lock entries

.text
.globl _start
.type _start, %function
_start:
nop
nop
.L0:
lock incl (%rdi)
# CHECK: lock {{.*}} SMPLock
.L1:
lock orb $0x40, 0x4(%rsi)
# CHECK: lock {{.*}} SMPLock
ret
.size _start, .-_start

.section .smp_locks,"a",@progbits
.long .L0 - .
.long .L1 - .

## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits
29 changes: 23 additions & 6 deletions bolt/test/X86/linux-static-keys.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## Check that BOLT correctly updates the Linux kernel static keys jump table.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: link_fdata %s %t.o %t.fdata
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr

Expand All @@ -11,6 +13,12 @@
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \
# RUN: --bolt-info=0 |& FileCheck %s

## Verify that profile is matched correctly.

# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \
# RUN: --bolt-info=0 --data %t.fdata |& \
# RUN: FileCheck --check-prefix=CHECK-FDATA %s

## Verify the bindings again on the rewritten binary with nops removed.

# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s
Expand All @@ -25,15 +33,24 @@ _start:
# CHECK: Binary Function "_start"
nop
.L0:
jmp .L1
jmp L1
# CHECK: jit
# CHECK-SAME: # ID: 1 {{.*}} # Likely: 0 # InitValue: 1
nop
.L1:
L1:
.nops 5
jmp .L0
# CHECK: jit
# CHECK-SAME: # ID: 2 {{.*}} # Likely: 1 # InitValue: 1
.L2:

## Check that a branch profile associated with a NOP is handled properly when
## dynamic branch is created.

# FDATA: 1 _start #L1# 1 _start #L2# 3 42
# CHECK-FDATA: jit {{.*}} # ID: 2
# CHECK-FDATA-NEXT: jmp
# CHECK-FDATA-NEXT: Successors: {{.*}} (mispreds: 3, count: 42)
L2:
nop
.size _start, .-_start

Expand All @@ -51,11 +68,11 @@ foo:
__start___jump_table:

.long .L0 - . # Jump address
.long .L1 - . # Target address
.long L1 - . # Target address
.quad 1 # Key address

.long .L1 - . # Jump address
.long .L2 - . # Target address
.long L1 - . # Jump address
.long L2 - . # Target address
.quad 0 # Key address

.globl __stop___jump_table
Expand Down
40 changes: 40 additions & 0 deletions bolt/test/runtime/bolt-reserved.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// REQUIRES: system-linux

/*
* Check that llvm-bolt uses reserved space in a binary for allocating
* new sections.
*/

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

// CHECK: BOLT-INFO: using reserved space

/*
* Check that llvm-bolt detects a condition when the reserved space is
* not enough for allocating new sections.
*/

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

// CHECK-TINY: BOLT-ERROR: reserved space (1 byte) is smaller than required

#ifdef TINY
#define RSIZE "1"
#else
#define RSIZE "8192 * 1024"
#endif

asm(".pushsection .text \n\
.globl __bolt_reserved_start \n\
.type __bolt_reserved_start, @object \n\
__bolt_reserved_start: \n\
.space " RSIZE " \n\
.globl __bolt_reserved_end \n\
__bolt_reserved_end: \n\
.popsection");

int main() { return 0; }
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap() {
static const HeaderMapCollector::RegexHeaderMap STLPostfixHeaderMap = {
{"include/__stdarg___gnuc_va_list.h$", "<cstdarg>"},
{"include/__stdarg___va_copy.h$", "<cstdarg>"},
{"include/__stdarg_header_macro.h$", "<cstdarg>"},
{"include/__stdarg_va_arg.h$", "<cstdarg>"},
{"include/__stdarg_va_copy.h$", "<cstdarg>"},
{"include/__stdarg_va_list.h$", "<cstdarg>"},
{"include/__stddef_header_macro.h$", "<cstddef>"},
{"include/__stddef_max_align_t.h$", "<cstddef>"},
{"include/__stddef_null.h$", "<cstddef>"},
{"include/__stddef_nullptr_t.h$", "<cstddef>"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
//===----------------------------------------------------------------------===//

#include "CastingThroughVoidCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "llvm/ADT/StringSet.h"

using namespace clang::ast_matchers;

Expand All @@ -27,7 +25,8 @@ void CastingThroughVoidCheck::registerMatchers(MatchFinder *Finder) {
hasSourceExpression(
explicitCastExpr(
hasSourceExpression(
expr(hasType(qualType().bind("source_type")))),
expr(hasType(qualType(unless(pointsTo(voidType())))
.bind("source_type")))),
hasDestinationType(
qualType(pointsTo(voidType())).bind("void_type")))
.bind("cast"))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,11 @@ std::optional<RenamerClangTidyCheck::FailureInfo>
ReservedIdentifierCheck::getDeclFailureInfo(const NamedDecl *Decl,
const SourceManager &) const {
assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&
!Decl->isImplicit() &&
"Decl must be an explicit identifier with a name.");
// Implicit identifiers cannot fail.
if (Decl->isImplicit())
return std::nullopt;

return getFailureInfoImpl(
Decl->getName(), isa<TranslationUnitDecl>(Decl->getDeclContext()),
/*IsMacro = */ false, getLangOpts(), Invert, AllowedIdentifiers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,25 @@ namespace clang::tidy::bugprone {

void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(hasReturnValue(declRefExpr(to(parmVarDecl(hasType(
hasCanonicalType(matchers::isReferenceToConst())))))))
returnStmt(
hasReturnValue(declRefExpr(to(parmVarDecl(hasType(hasCanonicalType(
qualType(matchers::isReferenceToConst()).bind("type"))))))),
hasAncestor(functionDecl(hasReturnTypeLoc(
loc(qualType(hasCanonicalType(equalsBoundNode("type"))))))))
.bind("ret"),
this);
}

void ReturnConstRefFromParameterCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
diag(R->getRetValue()->getBeginLoc(),
"returning a constant reference parameter may cause a use-after-free "
"when the parameter is constructed from a temporary");
const SourceRange Range = R->getRetValue()->getSourceRange();
if (Range.isInvalid())
return;
diag(Range.getBegin(),
"returning a constant reference parameter may cause use-after-free "
"when the parameter is constructed from a temporary")
<< Range;
}

} // namespace clang::tidy::bugprone
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "../misc/StaticAssertCheck.h"
#include "../misc/ThrowByValueCatchByReferenceCheck.h"
#include "../performance/MoveConstructorInitCheck.h"
#include "../readability/EnumInitialValueCheck.h"
#include "../readability/UppercaseLiteralSuffixCheck.h"
#include "CommandProcessorCheck.h"
#include "DefaultOperatorNewAlignmentCheck.h"
Expand Down Expand Up @@ -299,6 +300,9 @@ class CERTModule : public ClangTidyModule {
"cert-flp37-c");
// FIO
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
// INT
CheckFactories.registerCheck<readability::EnumInitialValueCheck>(
"cert-int09-c");
// MSC
CheckFactories.registerCheck<bugprone::UnsafeFunctionsCheck>(
"cert-msc24-c");
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
if (!Converter.canApply()) {
diag(PrintfCall->getBeginLoc(),
"unable to use '%0' instead of %1 because %2")
<< ReplacementFunction << OldFunction->getIdentifier()
<< PrintfCall->getSourceRange() << ReplacementFunction
<< OldFunction->getIdentifier()
<< Converter.conversionNotPossibleReason();
return;
}
Expand Down
21 changes: 4 additions & 17 deletions clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,6 @@ AST_MATCHER(QualType, isLocalConstQualified) {
return Node.isLocalConstQualified();
}

AST_MATCHER(QualType, isTypeOfType) {
return isa<TypeOfType>(Node.getTypePtr());
}

AST_MATCHER(QualType, isTypeOfExprType) {
return isa<TypeOfExprType>(Node.getTypePtr());
}

struct CheckResult {
// Source range of the relevant `const` token in the definition being checked.
CharSourceRange ConstRange;
Expand Down Expand Up @@ -110,16 +102,11 @@ void ConstReturnTypeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void ConstReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
// Find all function definitions for which the return types are `const`
// qualified, ignoring decltype types.
auto NonLocalConstType =
qualType(unless(isLocalConstQualified()),
anyOf(decltypeType(), autoType(), isTypeOfType(),
isTypeOfExprType(), substTemplateTypeParmType()));
Finder->addMatcher(
functionDecl(
returns(allOf(isConstQualified(), unless(NonLocalConstType))),
anyOf(isDefinition(), cxxMethodDecl(isPure())),
// Overridden functions are not actionable.
unless(cxxMethodDecl(isOverride())))
functionDecl(returns(isLocalConstQualified()),
anyOf(isDefinition(), cxxMethodDecl(isPure())),
// Overridden functions are not actionable.
unless(cxxMethodDecl(isOverride())))
.bind("func"),
this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,10 @@ IdentifierNamingCheck::getFailureInfo(
std::optional<RenamerClangTidyCheck::FailureInfo>
IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl,
const SourceManager &SM) const {
// Implicit identifiers cannot be renamed.
if (Decl->isImplicit())
return std::nullopt;

SourceLocation Loc = Decl->getLocation();
const FileStyle &FileStyle = getStyleForFile(SM.getFilename(Loc));
if (!FileStyle.isActive())
Expand Down
196 changes: 105 additions & 91 deletions clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct DenseMapInfo<clang::tidy::RenamerClangTidyCheck::NamingCheckId> {
namespace clang::tidy {

namespace {

class NameLookup {
llvm::PointerIntPair<const NamedDecl *, 1, bool> Data;

Expand All @@ -78,6 +79,7 @@ class NameLookup {
operator bool() const { return !hasMultipleResolutions(); }
const NamedDecl *operator*() const { return getDecl(); }
};

} // namespace

static const NamedDecl *findDecl(const RecordDecl &RecDecl,
Expand All @@ -91,6 +93,44 @@ static const NamedDecl *findDecl(const RecordDecl &RecDecl,
return nullptr;
}

/// Returns the function that \p Method is overridding. If There are none or
/// multiple overrides it returns nullptr. If the overridden function itself is
/// overridding then it will recurse up to find the first decl of the function.
static const CXXMethodDecl *getOverrideMethod(const CXXMethodDecl *Method) {
if (Method->size_overridden_methods() != 1)
return nullptr;

while (true) {
Method = *Method->begin_overridden_methods();
assert(Method && "Overridden method shouldn't be null");
unsigned NumOverrides = Method->size_overridden_methods();
if (NumOverrides == 0)
return Method;
if (NumOverrides > 1)
return nullptr;
}
}

static bool hasNoName(const NamedDecl *Decl) {
return !Decl->getIdentifier() || Decl->getName().empty();
}

static const NamedDecl *getFailureForNamedDecl(const NamedDecl *ND) {
const auto *Canonical = cast<NamedDecl>(ND->getCanonicalDecl());
if (Canonical != ND)
return Canonical;

if (const auto *Method = dyn_cast<CXXMethodDecl>(ND)) {
if (const CXXMethodDecl *Overridden = getOverrideMethod(Method))
Canonical = cast<NamedDecl>(Overridden->getCanonicalDecl());

if (Canonical != ND)
return Canonical;
}

return ND;
}

/// Returns a decl matching the \p DeclName in \p Parent or one of its base
/// classes. If \p AggressiveTemplateLookup is `true` then it will check
/// template dependent base classes as well.
Expand Down Expand Up @@ -132,24 +172,6 @@ static NameLookup findDeclInBases(const CXXRecordDecl &Parent,
return NameLookup(Found); // If nullptr, decl wasn't found.
}

/// Returns the function that \p Method is overridding. If There are none or
/// multiple overrides it returns nullptr. If the overridden function itself is
/// overridding then it will recurse up to find the first decl of the function.
static const CXXMethodDecl *getOverrideMethod(const CXXMethodDecl *Method) {
if (Method->size_overridden_methods() != 1)
return nullptr;

while (true) {
Method = *Method->begin_overridden_methods();
assert(Method && "Overridden method shouldn't be null");
unsigned NumOverrides = Method->size_overridden_methods();
if (NumOverrides == 0)
return Method;
if (NumOverrides > 1)
return nullptr;
}
}

namespace {

/// Callback supplies macros to RenamerClangTidyCheck::checkMacro
Expand Down Expand Up @@ -192,10 +214,6 @@ class RenamerClangTidyVisitor
: Check(Check), SM(SM),
AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {}

static bool hasNoName(const NamedDecl *Decl) {
return !Decl->getIdentifier() || Decl->getName().empty();
}

bool shouldVisitTemplateInstantiations() const { return true; }

bool shouldVisitImplicitCode() const { return false; }
Expand Down Expand Up @@ -246,29 +264,10 @@ class RenamerClangTidyVisitor
}

bool VisitNamedDecl(NamedDecl *Decl) {
if (hasNoName(Decl))
return true;

const auto *Canonical = cast<NamedDecl>(Decl->getCanonicalDecl());
if (Canonical != Decl) {
Check->addUsage(Canonical, Decl->getLocation(), SM);
return true;
}

// Fix overridden methods
if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) {
Check->addUsage(Overridden, Method->getLocation(), SM);
return true; // Don't try to add the actual decl as a Failure.
}
}

// Ignore ClassTemplateSpecializationDecl which are creating duplicate
// replacements with CXXRecordDecl.
if (isa<ClassTemplateSpecializationDecl>(Decl))
return true;

Check->checkNamedDecl(Decl, SM);
SourceRange UsageRange =
DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
.getSourceRange();
Check->addUsage(Decl, UsageRange, SM);
return true;
}

Expand Down Expand Up @@ -413,82 +412,97 @@ void RenamerClangTidyCheck::registerPPCallbacks(
std::make_unique<RenamerClangTidyCheckPPCallbacks>(SM, this));
}

void RenamerClangTidyCheck::addUsage(
const RenamerClangTidyCheck::NamingCheckId &Decl, SourceRange Range,
const SourceManager &SourceMgr) {
std::pair<RenamerClangTidyCheck::NamingCheckFailureMap::iterator, bool>
RenamerClangTidyCheck::addUsage(
const RenamerClangTidyCheck::NamingCheckId &FailureId,
SourceRange UsageRange, const SourceManager &SourceMgr) {
// Do nothing if the provided range is invalid.
if (Range.isInvalid())
return;
if (UsageRange.isInvalid())
return {NamingCheckFailures.end(), false};

// If we have a source manager, use it to convert to the spelling location for
// performing the fix. This is necessary because macros can map the same
// spelling location to different source locations, and we only want to fix
// the token once, before it is expanded by the macro.
SourceLocation FixLocation = Range.getBegin();
// Get the spelling location for performing the fix. This is necessary because
// macros can map the same spelling location to different source locations,
// and we only want to fix the token once, before it is expanded by the macro.
SourceLocation FixLocation = UsageRange.getBegin();
FixLocation = SourceMgr.getSpellingLoc(FixLocation);
if (FixLocation.isInvalid())
return;
return {NamingCheckFailures.end(), false};

auto EmplaceResult = NamingCheckFailures.try_emplace(FailureId);
NamingCheckFailure &Failure = EmplaceResult.first->second;

// Try to insert the identifier location in the Usages map, and bail out if it
// is already in there
RenamerClangTidyCheck::NamingCheckFailure &Failure =
NamingCheckFailures[Decl];
if (!Failure.RawUsageLocs.insert(FixLocation).second)
return;
return EmplaceResult;

if (!Failure.shouldFix())
return;
if (Failure.FixStatus != RenamerClangTidyCheck::ShouldFixStatus::ShouldFix)
return EmplaceResult;

if (SourceMgr.isWrittenInScratchSpace(FixLocation))
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;

if (!utils::rangeCanBeFixed(Range, &SourceMgr))
if (!utils::rangeCanBeFixed(UsageRange, &SourceMgr))
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;

return EmplaceResult;
}

void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, SourceRange Range,
void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl,
SourceRange UsageRange,
const SourceManager &SourceMgr) {
// Don't keep track for non-identifier names.
auto *II = Decl->getIdentifier();
if (!II)
if (hasNoName(Decl))
return;

// Ignore ClassTemplateSpecializationDecl which are creating duplicate
// replacements with CXXRecordDecl.
if (isa<ClassTemplateSpecializationDecl>(Decl))
return;
if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
if (const CXXMethodDecl *Overridden = getOverrideMethod(Method))
Decl = Overridden;
}
Decl = cast<NamedDecl>(Decl->getCanonicalDecl());
return addUsage(
RenamerClangTidyCheck::NamingCheckId(Decl->getLocation(), II->getName()),
Range, SourceMgr);
}

void RenamerClangTidyCheck::checkNamedDecl(const NamedDecl *Decl,
const SourceManager &SourceMgr) {
std::optional<FailureInfo> MaybeFailure = getDeclFailureInfo(Decl, SourceMgr);
// We don't want to create a failure for every NamedDecl we find. Ideally
// there is just one NamedDecl in every group of "related" NamedDecls that
// becomes the failure. This NamedDecl and all of its related NamedDecls
// become usages. E.g. Since NamedDecls are Redeclarable, only the canonical
// NamedDecl becomes the failure and all redeclarations become usages.
const NamedDecl *FailureDecl = getFailureForNamedDecl(Decl);

std::optional<FailureInfo> MaybeFailure =
getDeclFailureInfo(FailureDecl, SourceMgr);
if (!MaybeFailure)
return;

FailureInfo &Info = *MaybeFailure;
NamingCheckFailure &Failure =
NamingCheckFailures[NamingCheckId(Decl->getLocation(), Decl->getName())];
SourceRange Range =
DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
.getSourceRange();

const IdentifierTable &Idents = Decl->getASTContext().Idents;
auto CheckNewIdentifier = Idents.find(Info.Fixup);
NamingCheckId FailureId(FailureDecl->getLocation(), FailureDecl->getName());

auto [FailureIter, NewFailure] = addUsage(FailureId, UsageRange, SourceMgr);

if (FailureIter == NamingCheckFailures.end()) {
// Nothing to do if the usage wasn't accepted.
return;
}
if (!NewFailure) {
// FailureInfo has already been provided.
return;
}

// Update the stored failure with info regarding the FailureDecl.
NamingCheckFailure &Failure = FailureIter->second;
Failure.Info = std::move(*MaybeFailure);

// Don't overwritte the failure status if it was already set.
if (!Failure.shouldFix()) {
return;
}
const IdentifierTable &Idents = FailureDecl->getASTContext().Idents;
auto CheckNewIdentifier = Idents.find(Failure.Info.Fixup);
if (CheckNewIdentifier != Idents.end()) {
const IdentifierInfo *Ident = CheckNewIdentifier->second;
if (Ident->isKeyword(getLangOpts()))
Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword;
else if (Ident->hasMacroDefinition())
Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition;
} else if (!isValidAsciiIdentifier(Info.Fixup)) {
} else if (!isValidAsciiIdentifier(Failure.Info.Fixup)) {
Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier;
}

Failure.Info = std::move(Info);
addUsage(Decl, Range, SourceMgr);
}

void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) {
Expand Down
14 changes: 8 additions & 6 deletions clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,9 @@ class RenamerClangTidyCheck : public ClangTidyCheck {
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI,
const SourceManager &SourceMgr);

void addUsage(const RenamerClangTidyCheck::NamingCheckId &Decl,
SourceRange Range, const SourceManager &SourceMgr);

/// Convenience method when the usage to be added is a NamedDecl.
void addUsage(const NamedDecl *Decl, SourceRange Range,
const SourceManager &SourceMgr);

void checkNamedDecl(const NamedDecl *Decl, const SourceManager &SourceMgr);

protected:
/// Overridden by derived classes, returns information about if and how a Decl
/// failed the check. A 'std::nullopt' result means the Decl did not fail the
Expand Down Expand Up @@ -158,6 +152,14 @@ class RenamerClangTidyCheck : public ClangTidyCheck {
const NamingCheckFailure &Failure) const = 0;

private:
// Manage additions to the Failure/usage map
//
// return the result of NamingCheckFailures::try_emplace() if the usage was
// accepted.
std::pair<NamingCheckFailureMap::iterator, bool>
addUsage(const RenamerClangTidyCheck::NamingCheckId &FailureId,
SourceRange UsageRange, const SourceManager &SourceMgr);

NamingCheckFailureMap NamingCheckFailures;
const bool AggressiveDependentMemberLookup;
};
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/index/CanonicalIncludes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ namespace {
const std::pair<llvm::StringRef, llvm::StringRef> IncludeMappings[] = {
{"include/__stdarg___gnuc_va_list.h", "<cstdarg>"},
{"include/__stdarg___va_copy.h", "<cstdarg>"},
{"include/__stdarg_header_macro.h", "<cstdarg>"},
{"include/__stdarg_va_arg.h", "<cstdarg>"},
{"include/__stdarg_va_copy.h", "<cstdarg>"},
{"include/__stdarg_va_list.h", "<cstdarg>"},
{"include/__stddef_header_macro.h", "<cstddef>"},
{"include/__stddef_max_align_t.h", "<cstddef>"},
{"include/__stddef_null.h", "<cstddef>"},
{"include/__stddef_nullptr_t.h", "<cstddef>"},
Expand Down
99 changes: 55 additions & 44 deletions clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,12 @@ namespace {
/// void f() { E e1 = EV1; }
///
/// After:
/// enum class E { EV1, EV2 };
/// void f() { E e1 = E::EV1; }
/// enum class E { V1, V2 };
/// void f() { E e1 = E::V1; }
///
/// Note that the respective project code might not compile anymore
/// if it made use of the now-gone implicit conversion to int.
/// This is out of scope for this tweak.
///
/// TODO: In the above example, we could detect that the values
/// start with the enum name, and remove that prefix.

class ScopifyEnum : public Tweak {
const char *id() const final;
Expand All @@ -63,14 +60,13 @@ class ScopifyEnum : public Tweak {
std::function<tooling::Replacement(StringRef, StringRef, unsigned)>;
llvm::Error addClassKeywordToDeclarations();
llvm::Error scopifyEnumValues();
llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef Prefix);
llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef EnumName,
bool StripPrefix);
llvm::Expected<StringRef> getContentForFile(StringRef FilePath);
unsigned getOffsetFromPosition(const Position &Pos, StringRef Content) const;
llvm::Error addReplacementForReference(const ReferencesResult::Reference &Ref,
const MakeReplacement &GetReplacement);
llvm::Error addReplacement(StringRef FilePath, StringRef Content,
const tooling::Replacement &Replacement);
Position getPosition(const Decl &D) const;

const EnumDecl *D = nullptr;
const Selection *S = nullptr;
Expand Down Expand Up @@ -109,7 +105,8 @@ Expected<Tweak::Effect> ScopifyEnum::apply(const Selection &Inputs) {

llvm::Error ScopifyEnum::addClassKeywordToDeclarations() {
for (const auto &Ref :
findReferences(*S->AST, getPosition(*D), 0, S->Index, false)
findReferences(*S->AST, sourceLocToPosition(*SM, D->getBeginLoc()), 0,
S->Index, false)
.References) {
if (!(Ref.Attributes & ReferencesResult::Declaration))
continue;
Expand All @@ -125,25 +122,46 @@ llvm::Error ScopifyEnum::addClassKeywordToDeclarations() {
}

llvm::Error ScopifyEnum::scopifyEnumValues() {
std::string PrefixToInsert(D->getName());
PrefixToInsert += "::";
for (auto E : D->enumerators()) {
if (auto Err = scopifyEnumValue(*E, PrefixToInsert))
StringRef EnumName(D->getName());
bool StripPrefix = true;
for (const EnumConstantDecl *E : D->enumerators()) {
if (!E->getName().starts_with(EnumName)) {
StripPrefix = false;
break;
}
}
for (const EnumConstantDecl *E : D->enumerators()) {
if (auto Err = scopifyEnumValue(*E, EnumName, StripPrefix))
return Err;
}
return llvm::Error::success();
}

llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD,
StringRef Prefix) {
StringRef EnumName,
bool StripPrefix) {
for (const auto &Ref :
findReferences(*S->AST, getPosition(CD), 0, S->Index, false)
findReferences(*S->AST, sourceLocToPosition(*SM, CD.getBeginLoc()), 0,
S->Index, false)
.References) {
if (Ref.Attributes & ReferencesResult::Declaration)
if (Ref.Attributes & ReferencesResult::Declaration) {
if (StripPrefix) {
const auto MakeReplacement = [&EnumName](StringRef FilePath,
StringRef Content,
unsigned Offset) {
unsigned Length = EnumName.size();
if (Content[Offset + Length] == '_')
++Length;
return tooling::Replacement(FilePath, Offset, Length, {});
};
if (auto Err = addReplacementForReference(Ref, MakeReplacement))
return Err;
}
continue;
}

const auto MakeReplacement = [&Prefix](StringRef FilePath,
StringRef Content, unsigned Offset) {
const auto MakeReplacement = [&](StringRef FilePath, StringRef Content,
unsigned Offset) {
const auto IsAlreadyScoped = [Content, Offset] {
if (Offset < 2)
return false;
Expand All @@ -164,9 +182,18 @@ llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD,
}
return false;
};
return IsAlreadyScoped()
? tooling::Replacement()
: tooling::Replacement(FilePath, Offset, 0, Prefix);
if (StripPrefix) {
const int ExtraLength =
Content[Offset + EnumName.size()] == '_' ? 1 : 0;
if (IsAlreadyScoped())
return tooling::Replacement(FilePath, Offset,
EnumName.size() + ExtraLength, {});
return tooling::Replacement(FilePath, Offset + EnumName.size(),
ExtraLength, "::");
}
return IsAlreadyScoped() ? tooling::Replacement()
: tooling::Replacement(FilePath, Offset, 0,
EnumName.str() + "::");
};
if (auto Err = addReplacementForReference(Ref, MakeReplacement))
return Err;
Expand All @@ -187,27 +214,19 @@ llvm::Expected<StringRef> ScopifyEnum::getContentForFile(StringRef FilePath) {
return Content;
}

unsigned int ScopifyEnum::getOffsetFromPosition(const Position &Pos,
StringRef Content) const {
unsigned int Offset = 0;

for (std::size_t LinesRemaining = Pos.line;
Offset < Content.size() && LinesRemaining;) {
if (Content[Offset++] == '\n')
--LinesRemaining;
}
return Offset + Pos.character;
}

llvm::Error
ScopifyEnum::addReplacementForReference(const ReferencesResult::Reference &Ref,
const MakeReplacement &GetReplacement) {
StringRef FilePath = Ref.Loc.uri.file();
auto Content = getContentForFile(FilePath);
llvm::Expected<StringRef> Content = getContentForFile(FilePath);
if (!Content)
return Content.takeError();
unsigned Offset = getOffsetFromPosition(Ref.Loc.range.start, *Content);
tooling::Replacement Replacement = GetReplacement(FilePath, *Content, Offset);
llvm::Expected<size_t> Offset =
positionToOffset(*Content, Ref.Loc.range.start);
if (!Offset)
return Offset.takeError();
tooling::Replacement Replacement =
GetReplacement(FilePath, *Content, *Offset);
if (Replacement.isApplicable())
return addReplacement(FilePath, *Content, Replacement);
return llvm::Error::success();
Expand All @@ -223,13 +242,5 @@ ScopifyEnum::addReplacement(StringRef FilePath, StringRef Content,
return llvm::Error::success();
}

Position ScopifyEnum::getPosition(const Decl &D) const {
const SourceLocation Loc = D.getLocation();
Position Pos;
Pos.line = SM->getSpellingLineNumber(Loc) - 1;
Pos.character = SM->getSpellingColumnNumber(Loc) - 1;
return Pos;
}

} // namespace
} // namespace clang::clangd
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# RUN: clangd -input-style=delimited -sync -input-mirror-file %t < %s
# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t
#
# RUN: clangd -lit-test -input-mirror-file %t < %s
# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t
#
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}
# RUN: clangd -input-style=delimited -sync -input-mirror-file %t < %s
# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t
#
# RUN: clangd -lit-test -input-mirror-file %t < %s
# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t
#
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}
114 changes: 57 additions & 57 deletions clang-tools-extra/clangd/test/hover.test
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
# RUN: clangd -lit-test < %s | FileCheck %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}}
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":27}}}
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "contents": {
# CHECK-NEXT: "kind": "plaintext",
# CHECK-NEXT: "value": "function foo\n\n→ void\n\nvoid foo()"
# CHECK-NEXT: },
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 28,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 25,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":10}}}
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": null
---
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main2.cpp","languageId":"cpp","version":1,"text":"enum foo{}; int main() { foo f; }\n"}}}
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main2.cpp"},"position":{"line":0,"character":27}}}
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "contents": {
# CHECK-NEXT: "kind": "plaintext",
# CHECK-NEXT: "value": "enum foo\n\nenum foo {}"
# CHECK-NEXT: },
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 28,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 25,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}
# RUN: clangd -lit-test < %s | FileCheck %s
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}}
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":27}}}
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "contents": {
# CHECK-NEXT: "kind": "plaintext",
# CHECK-NEXT: "value": "function foo\n\n→ void\n\nvoid foo()"
# CHECK-NEXT: },
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 28,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 25,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":10}}}
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": null
---
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main2.cpp","languageId":"cpp","version":1,"text":"enum foo{}; int main() { foo f; }\n"}}}
---
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main2.cpp"},"position":{"line":0,"character":27}}}
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "contents": {
# CHECK-NEXT: "kind": "plaintext",
# CHECK-NEXT: "value": "enum foo\n\nenum foo {}"
# CHECK-NEXT: },
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 28,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 25,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT:}
---
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}
26 changes: 13 additions & 13 deletions clang-tools-extra/clangd/test/spaces-in-delimited-input.test
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# RUN: clangd -input-style=delimited -sync < %s 2>&1 | FileCheck %s
# RUN: clangd -lit-test -sync < %s 2>&1 | FileCheck %s
#
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
---
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
---
{"jsonrpc":"2.0","method":"exit"}
# CHECK-NOT: JSON parse error
# RUN: clangd -input-style=delimited -sync < %s 2>&1 | FileCheck %s
# RUN: clangd -lit-test -sync < %s 2>&1 | FileCheck %s
#
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}

---

{"jsonrpc":"2.0","id":3,"method":"shutdown"}

---

{"jsonrpc":"2.0","method":"exit"}
# CHECK-NOT: JSON parse error
13 changes: 5 additions & 8 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,10 +642,7 @@ TEST_F(TargetDeclTest, RewrittenBinaryOperator) {
bool x = (Foo(1) [[!=]] Foo(2));
)cpp";
EXPECT_DECLS("CXXRewrittenBinaryOperator",
{"std::strong_ordering operator<=>(const Foo &) const = default",
Rel::TemplatePattern},
{"bool operator==(const Foo &) const noexcept = default",
Rel::TemplateInstantiation});
{"bool operator==(const Foo &) const noexcept = default"});
}

TEST_F(TargetDeclTest, FunctionTemplate) {
Expand Down Expand Up @@ -854,7 +851,7 @@ TEST_F(TargetDeclTest, DependentExprs) {
}
};
)cpp";
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
EXPECT_DECLS("MemberExpr", "void foo()");

// Similar to above but base expression involves a function call.
Code = R"cpp(
Expand All @@ -872,7 +869,7 @@ TEST_F(TargetDeclTest, DependentExprs) {
}
};
)cpp";
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
EXPECT_DECLS("MemberExpr", "void foo()");

// Similar to above but uses a function pointer.
Code = R"cpp(
Expand All @@ -891,7 +888,7 @@ TEST_F(TargetDeclTest, DependentExprs) {
}
};
)cpp";
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
EXPECT_DECLS("MemberExpr", "void foo()");

// Base expression involves a member access into this.
Code = R"cpp(
Expand Down Expand Up @@ -962,7 +959,7 @@ TEST_F(TargetDeclTest, DependentExprs) {
void Foo() { this->[[find]](); }
};
)cpp";
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void find()");
EXPECT_DECLS("MemberExpr", "void find()");
}

TEST_F(TargetDeclTest, DependentTypes) {
Expand Down
17 changes: 15 additions & 2 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,19 @@ class Foo final {})cpp";
// Bindings are in theory public members of an anonymous struct.
HI.AccessSpecifier = "public";
}},
{// Don't crash on invalid decl with invalid init expr.
R"cpp(
Unknown [[^abc]] = invalid;
// error-ok
)cpp",
[](HoverInfo &HI) {
HI.Name = "abc";
HI.Kind = index::SymbolKind::Variable;
HI.NamespaceScope = "";
HI.Definition = "int abc = <recovery - expr>()";
HI.Type = "int";
HI.AccessSpecifier = "public";
}},
{// Extra info for function call.
R"cpp(
void fun(int arg_a, int &arg_b) {};
Expand Down Expand Up @@ -3078,7 +3091,7 @@ TEST(Hover, All) {
HI.NamespaceScope = "";
HI.Definition =
"bool operator==(const Foo &) const noexcept = default";
HI.Documentation = "Foo spaceship";
HI.Documentation = "";
}},
};

Expand Down Expand Up @@ -3881,7 +3894,7 @@ TEST(Hover, SpaceshipTemplateNoCrash) {
TU.ExtraArgs.push_back("-std=c++20");
auto AST = TU.build();
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
EXPECT_EQ(HI->Documentation, "Foo bar baz");
EXPECT_EQ(HI->Documentation, "");
}

TEST(Hover, ForwardStructNoCrash) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ sizeof...($TemplateParameter[[Elements]]);
struct $Class_def[[Foo]] {
int $Field_decl[[Waldo]];
void $Method_def[[bar]]() {
$Class[[Foo]]().$Field_dependentName[[Waldo]];
$Class[[Foo]]().$Field[[Waldo]];
}
template $Bracket[[<]]typename $TemplateParameter_def[[U]]$Bracket[[>]]
void $Method_def[[bar1]]() {
Expand Down
66 changes: 61 additions & 5 deletions clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enum ^E;
)cpp");
}

TEST_F(ScopifyEnumTest, ApplyTest) {
TEST_F(ScopifyEnumTest, ApplyTestWithPrefix) {
std::string Original = R"cpp(
enum ^E { EV1, EV2, EV3 };
enum E;
Expand All @@ -39,13 +39,69 @@ E func(E in)
}
)cpp";
std::string Expected = R"cpp(
enum class E { EV1, EV2, EV3 };
enum class E { V1, V2, V3 };
enum class E;
E func(E in)
{
E out = E::EV1;
if (in == E::EV2)
out = E::EV3;
E out = E::V1;
if (in == E::V2)
out = E::V3;
return out;
}
)cpp";
FileName = "Test.cpp";
SCOPED_TRACE(Original);
EXPECT_EQ(apply(Original), Expected);
}

TEST_F(ScopifyEnumTest, ApplyTestWithPrefixAndUnderscore) {
std::string Original = R"cpp(
enum ^E { E_V1, E_V2, E_V3 };
enum E;
E func(E in)
{
E out = E_V1;
if (in == E_V2)
out = E::E_V3;
return out;
}
)cpp";
std::string Expected = R"cpp(
enum class E { V1, V2, V3 };
enum class E;
E func(E in)
{
E out = E::V1;
if (in == E::V2)
out = E::V3;
return out;
}
)cpp";
FileName = "Test.cpp";
SCOPED_TRACE(Original);
EXPECT_EQ(apply(Original), Expected);
}

TEST_F(ScopifyEnumTest, ApplyTestWithoutPrefix) {
std::string Original = R"cpp(
enum ^E { V1, V2, V3 };
enum E;
E func(E in)
{
E out = V1;
if (in == V2)
out = E::V3;
return out;
}
)cpp";
std::string Expected = R"cpp(
enum class E { V1, V2, V3 };
enum class E;
E func(E in)
{
E out = E::V1;
if (in == E::V2)
out = E::V3;
return out;
}
)cpp";
Expand Down
19 changes: 18 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ Code completion
Code actions
^^^^^^^^^^^^

- The tweak for turning unscoped into scoped enums now removes redundant prefixes
from the enum values.

Signature help
^^^^^^^^^^^^^^

Expand Down Expand Up @@ -166,13 +169,22 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^

- New alias :doc:`cert-int09-c <clang-tidy/checks/cert/int09-c>` to
:doc:`readability-enum-initial-value <clang-tidy/checks/readability/enum-initial-value>`
was added.

Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^

- Improved :doc:`bugprone-assert-side-effect
<clang-tidy/checks/bugprone/assert-side-effect>` check by detecting side
effect from calling a method with non-const reference parameters.

- Improved :doc:`bugprone-casting-through-void
<clang-tidy/checks/bugprone/casting-through-void>` check by ignoring casts
where source is already a ``void``` pointer, making middle ``void`` pointer
casts bug-free.

- Improved :doc:`bugprone-forwarding-reference-overload
<clang-tidy/checks/bugprone/forwarding-reference-overload>`
check to ignore deleted constructors which won't hide other overloads.
Expand Down Expand Up @@ -261,7 +273,8 @@ Changes in existing checks

- Improved :doc:`misc-const-correctness
<clang-tidy/checks/misc/const-correctness>` check by avoiding infinite recursion
for recursive forwarding reference.
for recursive functions with forwarding reference parameters and reference
variables which refer to themselves.

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by replacing the local
Expand Down Expand Up @@ -312,6 +325,10 @@ Changes in existing checks
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
fix-its.

- Improved :doc:`readability-const-return-type
<clang-tidy/checks/readability/const-return-type>` check to eliminate false
positives when returning types with const not at the top level.

- Improved :doc:`readability-duplicate-include
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ just the individual thread. Use any signal except ``SIGTERM``.
This check corresponds to the CERT C Coding Standard rule
`POS44-C. Do not use signals to terminate threads
<https://wiki.sei.cmu.edu/confluence/display/c/POS44-C.+Do+not+use+signals+to+terminate+threads>`_.

`cert-pos44-c` redirects here as an alias of this check.
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ completely before it is used.
It is also recommended to surround macro arguments in the replacement list
with parentheses. This ensures that the argument value is calculated
properly.

This check corresponds to the CERT C Coding Standard rule
`PRE20-C. Macro replacement lists should be parenthesized.
<https://wiki.sei.cmu.edu/confluence/display/c/PRE02-C.+Macro+replacement+lists+should+be+parenthesized>`_
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ This check is also related to and partially overlaps the CERT C++ Coding Standar
and
`EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation
<https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP62-CPP.+Do+not+access+the+bits+of+an+object+representation+that+are+not+part+of+the+object%27s+value+representation>`_

`cert-exp42-c` redirects here as an alias of this check.
10 changes: 10 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/cert/int09-c.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. title:: clang-tidy - cert-int09-c
.. meta::
:http-equiv=refresh: 5;URL=../readability/enum-initial-value.html

cert-int09-c
============

The `cert-int09-c` check is an alias, please see
:doc:`readability-enum-initial-value <../readability/enum-initial-value>` for
more information.
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ be acted upon and the effect is as if it was an asynchronous cancellation.
This check corresponds to the CERT C Coding Standard rule
`POS47-C. Do not use threads that can be canceled asynchronously
<https://wiki.sei.cmu.edu/confluence/display/c/POS47-C.+Do+not+use+threads+that+can+be+canceled+asynchronously>`_.

`cert-pos47-c` redirects here as an alias of this check.
7 changes: 5 additions & 2 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Clang-Tidy Checks
:doc:`bugprone-posix-return <bugprone/posix-return>`, "Yes"
:doc:`bugprone-redundant-branch-condition <bugprone/redundant-branch-condition>`, "Yes"
:doc:`bugprone-reserved-identifier <bugprone/reserved-identifier>`, "Yes"
:doc:`bugprone-return-const-ref-from-parameter <bugprone/return-const-ref-from-parameter>`
:doc:`bugprone-return-const-ref-from-parameter <bugprone/return-const-ref-from-parameter>`,
:doc:`bugprone-shared-ptr-array-mismatch <bugprone/shared-ptr-array-mismatch>`, "Yes"
:doc:`bugprone-signal-handler <bugprone/signal-handler>`,
:doc:`bugprone-signed-char-misuse <bugprone/signed-char-misuse>`,
Expand Down Expand Up @@ -395,8 +395,10 @@ Clang-Tidy Checks
:doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
:doc:`zircon-temporary-objects <zircon/temporary-objects>`,

Check aliases
-------------

.. csv-table:: Aliases..
.. csv-table::
:header: "Name", "Redirect", "Offers fixes"

:doc:`bugprone-narrowing-conversions <bugprone/narrowing-conversions>`, :doc:`cppcoreguidelines-narrowing-conversions <cppcoreguidelines/narrowing-conversions>`,
Expand All @@ -413,6 +415,7 @@ Clang-Tidy Checks
:doc:`cert-exp42-c <cert/exp42-c>`, :doc:`bugprone-suspicious-memory-comparison <bugprone/suspicious-memory-comparison>`,
:doc:`cert-fio38-c <cert/fio38-c>`, :doc:`misc-non-copyable-objects <misc/non-copyable-objects>`,
:doc:`cert-flp37-c <cert/flp37-c>`, :doc:`bugprone-suspicious-memory-comparison <bugprone/suspicious-memory-comparison>`,
:doc:`cert-int09-c <cert/int09-c>`, :doc:`readability-enum-initial-value <readability/enum-initial-value>`, "Yes"
:doc:`cert-msc24-c <cert/msc24-c>`, :doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`,
:doc:`cert-msc30-c <cert/msc30-c>`, :doc:`cert-msc50-cpp <cert/msc50-cpp>`,
:doc:`cert-msc32-c <cert/msc32-c>`, :doc:`cert-msc51-cpp <cert/msc51-cpp>`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
misc-throw-by-value-catch-by-reference
======================================

`cert-err09-cpp` redirects here as an alias for this check.
`cert-err61-cpp` redirects here as an alias for this check.
`cert-err09-cpp` and `cert-err61-cpp` redirect here as aliases of this check.

Finds violations of the rule "Throw by value, catch by reference" presented for
example in "C++ Coding Standards" by H. Sutter and A. Alexandrescu, as well as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Options

.. option:: PrintfLikeFunctions

A semicolon-separated list of (fully qualified) extra function names to
A semicolon-separated list of (fully qualified) function names to
replace, with the requirement that the first parameter contains the
printf-style format string and the arguments to be formatted follow
immediately afterwards. If neither this option nor
Expand All @@ -128,7 +128,7 @@ Options

.. option:: FprintfLikeFunctions

A semicolon-separated list of (fully qualified) extra function names to
A semicolon-separated list of (fully qualified) function names to
replace, with the requirement that the first parameter is retained, the
second parameter contains the printf-style format string and the
arguments to be formatted follow immediately afterwards. If neither this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,70 +6,83 @@ readability-enum-initial-value
Enforces consistent style for enumerators' initialization, covering three
styles: none, first only, or all initialized explicitly.

When adding new enumerations, inconsistent initial value will cause potential
enumeration value conflicts.
An inconsistent style and strictness to defining the initializing value of
enumerators may cause issues if the enumeration is extended with new
enumerators that obtain their integer representation implicitly.

In an enumeration, the following three cases are accepted.
1. none of enumerators are explicit initialized.
2. the first enumerator is explicit initialized.
3. all of enumerators are explicit initialized.
The following three cases are accepted:

#. **No** enumerators are explicit initialized.
#. Exactly **the first** enumerator is explicit initialized.
#. **All** enumerators are explicit initialized.

.. code-block:: c++

// valid, none of enumerators are initialized.
enum A {
e0,
e1,
e2,
enum A { // (1) Valid, none of enumerators are initialized.
a0,
a1,
a2,
};

// valid, the first enumerator is initialized.
enum A {
e0 = 0,
e1,
e2,
enum B { // (2) Valid, the first enumerator is initialized.
b0 = 0,
b1,
b2,
};

// valid, all of enumerators are initialized.
enum A {
e0 = 0,
e1 = 1,
e2 = 2,
enum C { // (3) Valid, all of enumerators are initialized.
c0 = 0,
c1 = 1,
c2 = 2,
};

// invalid, e1 is not explicit initialized.
enum A {
enum D { // Invalid, d1 is not explicitly initialized!
d0 = 0,
d1,
d2 = 2,
};

enum E { // Invalid, e1, e3, and e5 are not explicitly initialized.
e0 = 0,
e1,
e2 = 2,
e3, // Dangerous, as the numeric values of e3 and e5 are both 3, and this is not explicitly visible in the code!
e4 = 2,
e5,
};

This check corresponds to the CERT C Coding Standard recommendation `INT09-C. Ensure enumeration constants map to unique values
<https://wiki.sei.cmu.edu/confluence/display/c/INT09-C.+Ensure+enumeration+constants+map+to+unique+values>`_.

`cert-int09-c` redirects here as an alias of this check.

Options
-------

.. option:: AllowExplicitZeroFirstInitialValue

If set to `false`, the first enumerator must not be explicitly initialized.
See examples below. Default is `true`.
If set to `false`, the first enumerator must not be explicitly initialized to
a literal ``0``.
Default is `true`.

.. code-block:: c++

enum A {
e0 = 0, // not allowed if AllowExplicitZeroFirstInitialValue is false
e1,
e2,
enum F {
f0 = 0, // Not allowed if AllowExplicitZeroFirstInitialValue is false.
f1,
f2,
};


.. option:: AllowExplicitSequentialInitialValues

If set to `false`, sequential initializations are not allowed.
See examples below. Default is `true`.
If set to `false`, explicit initialization to sequential values are not
allowed.
Default is `true`.

.. code-block:: c++

enum A {
e0 = 1, // not allowed if AllowExplicitSequentialInitialValues is false
e1 = 2,
e2 = 3,
};
enum G {
g0 = 1, // Not allowed if AllowExplicitSequentialInitialValues is false.
g1 = 2,
g2 = 3,
45 changes: 21 additions & 24 deletions clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,30 @@ foreach(dep ${LLVM_UTILS_DEPS})
endif()
endforeach()

if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if (NOT WIN32 OR NOT LLVM_LINK_LLVM_DYLIB)
llvm_add_library(
CTTestTidyModule
MODULE clang-tidy/CTTestTidyModule.cpp
PLUGIN_TOOL clang-tidy
DEPENDS clang-tidy-headers)
endif()
if (NOT WIN32 OR NOT LLVM_LINK_LLVM_DYLIB)
llvm_add_library(
CTTestTidyModule
MODULE clang-tidy/CTTestTidyModule.cpp
PLUGIN_TOOL clang-tidy)
endif()

if(CLANG_BUILT_STANDALONE)
# LLVMHello library is needed below
if (EXISTS ${LLVM_MAIN_SRC_DIR}/lib/Transforms/Hello
AND NOT TARGET LLVMHello)
add_subdirectory(${LLVM_MAIN_SRC_DIR}/lib/Transforms/Hello
lib/Transforms/Hello)
endif()
if(CLANG_BUILT_STANDALONE)
# LLVMHello library is needed below
if (EXISTS ${LLVM_MAIN_SRC_DIR}/lib/Transforms/Hello
AND NOT TARGET LLVMHello)
add_subdirectory(${LLVM_MAIN_SRC_DIR}/lib/Transforms/Hello
lib/Transforms/Hello)
endif()
endif()

if(TARGET CTTestTidyModule)
list(APPEND CLANG_TOOLS_TEST_DEPS CTTestTidyModule LLVMHello)
target_include_directories(CTTestTidyModule PUBLIC BEFORE "${CLANG_TOOLS_SOURCE_DIR}")
if(CLANG_PLUGIN_SUPPORT AND (WIN32 OR CYGWIN))
set(LLVM_LINK_COMPONENTS
Support
)
endif()
endif()
if(TARGET CTTestTidyModule)
list(APPEND CLANG_TOOLS_TEST_DEPS CTTestTidyModule LLVMHello)
target_include_directories(CTTestTidyModule PUBLIC BEFORE "${CLANG_TOOLS_SOURCE_DIR}")
if(CLANG_PLUGIN_SUPPORT AND (WIN32 OR CYGWIN))
set(LLVM_LINK_COMPONENTS
Support
)
endif()
endif()

add_lit_testsuite(check-clang-extra "Running clang-tools-extra/test"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,10 @@ void bit_cast() {
__builtin_bit_cast(int *, static_cast<void *>(&d));
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
}

namespace PR87069 {
void castconstVoidToVoid() {
const void* ptr = nullptr;
int* numberPtr = static_cast<int*>(const_cast<void*>(ptr));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t
// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t -- -- -fno-delayed-template-parsing

using T = int;
using TConst = int const;
using TConstRef = int const&;

template <typename T>
struct Wrapper { Wrapper(T); };

template <typename T>
struct Identity { using type = T; };

template <typename T>
struct ConstRef { using type = const T&; };

namespace invalid {

int const &f1(int const &a) { return a; }
Expand All @@ -18,8 +27,59 @@ int const &f3(TConstRef a) { return a; }
int const &f4(TConst &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter

template <typename T>
const T& tf1(const T &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter

template <typename T>
const T& itf1(const T &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: returning a constant reference parameter

template <typename T>
typename ConstRef<T>::type itf2(const T &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter

template <typename T>
typename ConstRef<T>::type itf3(typename ConstRef<T>::type a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:72: warning: returning a constant reference parameter

template <typename T>
const T& itf4(typename ConstRef<T>::type a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter

void instantiate(const int &param, const float &paramf, int &mut_param, float &mut_paramf) {
itf1(0);
itf1(param);
itf1(paramf);
itf2(0);
itf2(param);
itf2(paramf);
itf3<int>(0);
itf3<int>(param);
itf3<float>(paramf);
itf4<int>(0);
itf4<int>(param);
itf4<float>(paramf);
}

struct C {
const C& foo(const C&c) { return c; }
// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: returning a constant reference parameter
};

} // namespace invalid

namespace false_negative_because_dependent_and_not_instantiated {
template <typename T>
typename ConstRef<T>::type tf2(const T &a) { return a; }

template <typename T>
typename ConstRef<T>::type tf3(typename ConstRef<T>::type a) { return a; }

template <typename T>
const T& tf4(typename ConstRef<T>::type a) { return a; }
} // false_negative_because_dependent_and_not_instantiated

namespace valid {

int const &f1(int &a) { return a; }
Expand All @@ -28,4 +88,58 @@ int const &f2(int &&a) { return a; }

int f1(int const &a) { return a; }

template <typename T>
T tf1(T a) { return a; }

template <typename T>
T tf2(const T a) { return a; }

template <typename T>
T tf3(const T &a) { return a; }

template <typename T>
Identity<T>::type tf4(const T &a) { return a; }

template <typename T>
T itf1(T a) { return a; }

template <typename T>
T itf2(const T a) { return a; }

template <typename T>
T itf3(const T &a) { return a; }

template <typename T>
Wrapper<T> itf4(const T& a) { return a; }

template <typename T>
const T& itf5(T& a) { return a; }

template <typename T>
T itf6(T& a) { return a; }

void instantiate(const int &param, const float &paramf, int &mut_param, float &mut_paramf) {
itf1(0);
itf1(param);
itf1(paramf);
itf2(0);
itf2(param);
itf2(paramf);
itf3(0);
itf3(param);
itf3(paramf);
itf2(0);
itf2(param);
itf2(paramf);
itf3(0);
itf3(param);
itf3(paramf);
itf4(param);
itf4(paramf);
itf5(mut_param);
itf5(mut_paramf);
itf6(mut_param);
itf6(mut_paramf);
}

} // namespace valid
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ struct HeapArray { // Ok, since destruc

HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok
other._data = nullptr; // Ok
// CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'std::nullptr_t'
// FIXME: This warning is emitted because an ImplicitCastExpr for the NullToPointer conversion isn't created for dependent types.
other.size = 0;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ template <class T>
struct Template {
Template() = default;
Template(const Template &Other) : Field(Other.Field) {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
// CHECK-FIXES: Template(const Template &Other) = default;
Template &operator=(const Template &Other);
void foo(const T &t);
int Field;
Expand All @@ -269,8 +271,12 @@ Template<T> &Template<T>::operator=(const Template<T> &Other) {
Field = Other.Field;
return *this;
}
// CHECK-MESSAGES: :[[@LINE-4]]:27: warning: use '= default'
// CHECK-FIXES: Template<T> &Template<T>::operator=(const Template<T> &Other) = default;

Template<int> T1;


// Dependent types.
template <class T>
struct DT1 {
Expand All @@ -284,6 +290,9 @@ DT1<T> &DT1<T>::operator=(const DT1<T> &Other) {
Field = Other.Field;
return *this;
}
// CHECK-MESSAGES: :[[@LINE-4]]:17: warning: use '= default'
// CHECK-FIXES: DT1<T> &DT1<T>::operator=(const DT1<T> &Other) = default;

DT1<int> Dt1;

template <class T>
Expand All @@ -303,6 +312,9 @@ DT2<T> &DT2<T>::operator=(const DT2<T> &Other) {
struct T {
typedef int TT;
};
// CHECK-MESSAGES: :[[@LINE-8]]:17: warning: use '= default'
// CHECK-FIXES: DT2<T> &DT2<T>::operator=(const DT2<T> &Other) = default;

DT2<T> Dt2;

// Default arguments.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#ifndef READABILITY_DUPLICATE_INCLUDE_H
#define READABILITY_DUPLICATE_INCLUDE_H

extern int g;
#include "duplicate-include2.h"
extern int h;
#include "duplicate-include2.h"
extern int i;
// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: duplicate include
// CHECK-FIXES: {{^extern int g;$}}
// CHECK-FIXES-NEXT: {{^#include "duplicate-include2.h"$}}
// CHECK-FIXES-NEXT: {{^extern int h;$}}
// CHECK-FIXES-NEXT: {{^extern int i;$}}

#endif
#ifndef READABILITY_DUPLICATE_INCLUDE_H
#define READABILITY_DUPLICATE_INCLUDE_H

extern int g;
#include "duplicate-include2.h"
extern int h;
#include "duplicate-include2.h"
extern int i;
// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: duplicate include
// CHECK-FIXES: {{^extern int g;$}}
// CHECK-FIXES-NEXT: {{^#include "duplicate-include2.h"$}}
// CHECK-FIXES-NEXT: {{^extern int h;$}}
// CHECK-FIXES-NEXT: {{^extern int i;$}}

#endif
Original file line number Diff line number Diff line change
@@ -1 +1 @@
// This file is intentionally empty.
// This file is intentionally empty.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
// This file is intentionally empty.
// This file is intentionally empty.
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,9 @@ CREATE_FUNCTION();

using ty = const int;
ty p21() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty' (aka 'const int') is

typedef const int ty2;
ty2 p22() {}
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty2' (aka 'const int') i

// Declaration uses a macro, while definition doesn't. In this case, we won't
// fix the declaration, and will instead issue a warning.
Expand Down Expand Up @@ -249,7 +247,6 @@ auto p27() -> int const { return 3; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu

std::add_const<int>::type p28() { return 3; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'std::add_const<int>::typ

// p29, p30 are based on
// llvm/projects/test-suite/SingleSource/Benchmarks/Misc-C++-EH/spirit.cpp:
Expand Down Expand Up @@ -355,3 +352,20 @@ struct p41 {
// CHECK-FIXES: T foo() const { return 2; }
};
template struct p41<int>;

namespace PR73270 {
template<typename K, typename V>
struct Pair {
using first_type = const K;
using second_type = V;
};

template<typename PairType>
typename PairType::first_type getFirst() {
return {};
}

void test() {
getFirst<Pair<int, int>>();
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++17
// Constexpr if is an exception to the rule, we cannot remove the else.
void f() {
if (sizeof(int) > 4)
return;
else
return;
// CHECK-MESSAGES: [[@LINE-2]]:3: warning: do not use 'else' after 'return'
if constexpr (sizeof(int) > 4)
return;
else
return;
if constexpr (sizeof(int) > 4)
return;
else if constexpr (sizeof(long) > 4)
return;
else
return;
}
// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++17

// Constexpr if is an exception to the rule, we cannot remove the else.
void f() {
if (sizeof(int) > 4)
return;
else
return;
// CHECK-MESSAGES: [[@LINE-2]]:3: warning: do not use 'else' after 'return'

if constexpr (sizeof(int) > 4)
return;
else
return;

if constexpr (sizeof(int) > 4)
return;
else if constexpr (sizeof(long) > 4)
return;
else
return;
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,5 @@
// RUN: clang-tidy --checks="-*,readability-identifier-naming" --dump-config %S/Inputs/config-files/- -- | grep "readability-identifier-naming\." | sort --check

// Dumped config does not overflow for unsigned options
// RUN: clang-tidy --dump-config \
// RUN: --checks="-*,misc-throw-by-value-catch-by-reference" \
// RUN: -- | grep -v -q "misc-throw-by-value-catch-by-reference.MaxSize: '-1'"

// RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- \
// RUN: -- | grep -q "misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976'"
// RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- -- | FileCheck %s -check-prefix=CHECK-OVERFLOW
// CHECK-OVERFLOW: misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976'
2 changes: 1 addition & 1 deletion clang-tools-extra/test/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ config.python_executable = "@Python3_EXECUTABLE@"
config.target_triple = "@LLVM_TARGET_TRIPLE@"
config.host_triple = "@LLVM_HOST_TRIPLE@"
config.clang_tidy_staticanalyzer = @CLANG_TIDY_ENABLE_STATIC_ANALYZER@
config.has_plugins = @CLANG_PLUGIN_SUPPORT@ & ~@LLVM_INSTALL_TOOLCHAIN_ONLY@
config.has_plugins = @CLANG_PLUGIN_SUPPORT@
# Support substitution of the tools and libs dirs with user parameters. This is
# used when we can't determine the tool dir at configuration time.
config.llvm_tools_dir = lit_config.substitute("@LLVM_TOOLS_DIR@")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// module.modulemap
module Level1A {
header "Level1A.h"
export *
}
module HasError {
header "HasError.h"
export *
}
// module.modulemap

module Level1A {
header "Level1A.h"
export *
}
module HasError {
header "HasError.h"
export *
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// module.modulemap
module Level1A {
header "Level1A.h"
export *
}
module Missing {
header "Missing.h"
export *
}
// module.modulemap

module Level1A {
header "Level1A.h"
export *
}
module Missing {
header "Missing.h"
export *
}
36 changes: 18 additions & 18 deletions clang-tools-extra/test/pp-trace/Inputs/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// module.modulemap
module Level1A {
header "Level1A.h"
export *
}
module Level1B {
header "Level1B.h"
export *
module Level2B {
header "Level2B.h"
export *
}
}
module Level2A {
header "Level2A.h"
export *
}
// module.modulemap

module Level1A {
header "Level1A.h"
export *
}
module Level1B {
header "Level1B.h"
export *
module Level2B {
header "Level2B.h"
export *
}
}
module Level2A {
header "Level2A.h"
export *
}
40 changes: 37 additions & 3 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,8 @@ the configuration (without a prefix: ``Auto``).

**AlignConsecutiveShortCaseStatements** (``ShortCaseStatementsAlignmentStyle``) :versionbadge:`clang-format 17` :ref:`¶ <AlignConsecutiveShortCaseStatements>`
Style of aligning consecutive short case labels.
Only applies if ``AllowShortCaseLabelsOnASingleLine`` is ``true``.
Only applies if ``AllowShortCaseExpressionOnASingleLine`` or
``AllowShortCaseLabelsOnASingleLine`` is ``true``.


.. code-block:: yaml
Expand Down Expand Up @@ -935,8 +936,26 @@ the configuration (without a prefix: ``Auto``).
default: return "";
}
* ``bool AlignCaseColons`` Whether aligned case labels are aligned on the colon, or on the
, or on the tokens after the colon.
* ``bool AlignCaseArrows`` Whether to align the case arrows when aligning short case expressions.

.. code-block:: java
true:
i = switch (day) {
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> 0;
};
false:
i = switch (day) {
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> 0;
};
* ``bool AlignCaseColons`` Whether aligned case labels are aligned on the colon, or on the tokens
after the colon.

.. code-block:: c++

Expand Down Expand Up @@ -1692,6 +1711,21 @@ the configuration (without a prefix: ``Auto``).



.. _AllowShortCaseExpressionOnASingleLine:

**AllowShortCaseExpressionOnASingleLine** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ <AllowShortCaseExpressionOnASingleLine>`
Whether to merge a short switch labeled rule into a single line.

.. code-block:: java
true: false:
switch (a) { vs. switch (a) {
case 1 -> 1; case 1 ->
default -> 0; 1;
}; default ->
0;
};
.. _AllowShortCaseLabelsOnASingleLine:

**AllowShortCaseLabelsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 3.6` :ref:`¶ <AllowShortCaseLabelsOnASingleLine>`
Expand Down
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
T __builtin_elementwise_ceil(T x) return the smallest integral value greater than or equal to x floating point types
T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types
T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types
T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types
T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types
T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types
T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types
Expand Down
4 changes: 2 additions & 2 deletions clang/docs/Multilib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,9 @@ For a more comprehensive example see
- Dir: thumb/v6-m
# List of one or more normalized command line options, as generated by Clang
# from the command line options or from Mappings below.
# Here, if the flags are a superset of {target=thumbv6m-none-unknown-eabi}
# Here, if the flags are a superset of {target=thumbv6m-unknown-none-eabi}
# then this multilib variant will be considered a match.
Flags: [--target=thumbv6m-none-unknown-eabi]
Flags: [--target=thumbv6m-unknown-none-eabi]

# Similarly, a multilib variant targeting Arm v7-M with an FPU (floating
# point unit).
Expand Down
100 changes: 96 additions & 4 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ C++ Specific Potentially Breaking Changes
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang.

- The behavior controlled by the `-frelaxed-template-template-args` flag is now
on by default, and the flag is deprecated. Until the flag is finally removed,
it's negative spelling can be used to obtain compatibility with previous
versions of clang.

- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).

ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
Expand All @@ -69,6 +76,9 @@ ABI Changes in This Version
returning a class in a register. This affects some uses of std::pair.
(#GH86384).

- Fixed Microsoft calling convention when returning classes that have a deleted
copy assignment operator. Such a class should be returned indirectly.

AST Dumping Potentially Breaking Changes
----------------------------------------

Expand All @@ -85,6 +95,25 @@ Clang Frontend Potentially Breaking Changes
of ``-Wno-gnu-binary-literal`` will no longer silence this pedantic warning,
which may break existing uses with ``-Werror``.

- The normalization of 3 element target triples where ``-none-`` is the middle
element has changed. For example, ``armv7m-none-eabi`` previously normalized
to ``armv7m-none-unknown-eabi``, with ``none`` for the vendor and ``unknown``
for the operating system. It now normalizes to ``armv7m-unknown-none-eabi``,
which has ``unknown`` vendor and ``none`` operating system.

The affected triples are primarily for bare metal Arm where it is intended
that ``none`` means that there is no operating system. As opposed to an unknown
type of operating system.

This change my cause clang to not find libraries, or libraries to be built at
different file system locations. This can be fixed by changing your builds to
use the new normalized triple. However, we recommend instead getting the
normalized triple from clang itself, as this will make your builds more
robust in case of future changes::

$ clang --target=<your target triple> -print-target-triple
<the normalized target triple>

What's New in Clang |release|?
==============================
Some of the major new features and improvements to Clang are listed
Expand All @@ -94,6 +123,17 @@ sections with improvements to Clang's support for those languages.

C++ Language Changes
--------------------
- C++17 support is now completed, with the enablement of the
relaxed temlate template argument matching rules introduced in P0522,
which was retroactively applied as a defect report.
While the implementation already existed since Clang 4, it was turned off by
default, and was controlled with the `-frelaxed-template-template-args` flag.
In this release, we implement provisional wording for a core defect on
P0522 (CWG2398), which avoids the most serious compatibility issues caused
by it, allowing us to enable it by default in this release.
The flag is now deprecated, and will be removed in the next release, but can
still be used to turn it off and regain compatibility with previous versions
(#GH36505).
- Implemented ``_BitInt`` literal suffixes ``__wb`` or ``__WB`` as a Clang extension with ``unsigned`` modifiers also allowed. (#GH85223).

C++17 Feature Support
Expand Down Expand Up @@ -153,6 +193,9 @@ C++2c Feature Support

- Implemented `P2748R5 Disallow Binding a Returned Glvalue to a Temporary <https://wg21.link/P2748R5>`_.

- Implemented `P2809R3: Trivial infinite loops are not Undefined Behavior <https://wg21.link/P2809R3>`_.


Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Substitute template parameter pack, when it is not explicitly specified
Expand All @@ -173,6 +216,9 @@ Resolutions to C++ Defect Reports
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).

- P0522 implementation is enabled by default in all language versions, and
provisional wording for CWG2398 is implemented.

C Language Changes
------------------

Expand Down Expand Up @@ -260,6 +306,11 @@ New Compiler Flags
allow late parsing certain attributes in specific contexts where they would
not normally be late parsed.

- ``-fseparate-named-sections`` uses separate unique sections for global
symbols in named special sections (i.e. symbols annotated with
``__attribute__((section(...)))``. This enables linker GC to collect unused
symbols without having to use a per-symbol section.

Deprecated Compiler Flags
-------------------------

Expand Down Expand Up @@ -294,6 +345,10 @@ Modified Compiler Flags
- Carved out ``-Wformat`` warning about scoped enums into a subwarning and
make it controlled by ``-Wformat-pedantic``. Fixes #GH88595.

- Trivial infinite loops (i.e loops with a constant controlling expresion
evaluating to ``true`` and an empty body such as ``while(1);``)
are considered infinite, even when the ``-ffinite-loop`` flag is set.

Removed Compiler Flags
-------------------------

Expand Down Expand Up @@ -414,6 +469,18 @@ Improvements to Clang's diagnostics

- Clang now diagnoses requires expressions with explicit object parameters.

- Clang now looks up members of the current instantiation in the template definition context
if the current instantiation has no dependent base classes.

.. code-block:: c++

template<typename T>
struct A {
int f() {
return this->x; // error: no member named 'x' in 'A<T>'
}
};

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

Expand Down Expand Up @@ -460,6 +527,9 @@ Bug Fixes in This Version
- Clang now correctly generates overloads for bit-precise integer types for
builtin operators in C++. Fixes #GH82998.

- Fix crash when destructor definition is preceded with an equals sign.
Fixes (#GH89544).

- When performing mixed arithmetic between ``_Complex`` floating-point types and integers,
Clang now correctly promotes the integer to its corresponding real floating-point
type only rather than to the complex type (e.g. ``_Complex float / int`` is now evaluated
Expand Down Expand Up @@ -487,6 +557,9 @@ Bug Fixes in This Version
The values of 0 and 1 block any unrolling of the loop. This keeps the same behavior with GCC.
Fixes (`#88624 <https://github.com/llvm/llvm-project/issues/88624>`_).

- Clang will no longer emit a duplicate -Wunused-value warning for an expression
`(A, B)` which evaluates to glvalue `B` that can be converted to non ODR-use. (#GH45783)

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -611,6 +684,21 @@ Bug Fixes to C++ Support
immediate function context.
- Fix CTAD for ``std::initializer_list``. This allows ``std::initializer_list{1, 2, 3}`` to be deduced as
``std::initializer_list<int>`` as intended.
- Fix a bug on template partial specialization whose template parameter is `decltype(auto)`.
- Fix a bug on template partial specialization with issue on deduction of nontype template parameter
whose type is `decltype(auto)`. Fixes (#GH68885).
- Clang now correctly treats the noexcept-specifier of a friend function to be a complete-class context.
- Fix an assertion failure when parsing an invalid members of an anonymous class. (#GH85447)
- Fixed a misuse of ``UnresolvedLookupExpr`` for ill-formed templated expressions. Fixes (#GH48673), (#GH63243)
and (#GH88832).
- Clang now defers all substitution into the exception specification of a function template specialization
until the noexcept-specifier is instantiated.
- Fix a crash when an implicitly declared ``operator==`` function with a trailing requires-clause has its
constraints compared to that of another declaration.
- Fix a bug where explicit specializations of member functions/function templates would have substitution
performed incorrectly when checking constraints. Fixes (#GH90349).
- Clang now allows constrained member functions to be explicitly specialized for an implicit instantiation
of a class template.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -663,6 +751,7 @@ Arm and AArch64 Support
* Arm Cortex-A78AE (cortex-a78ae).
* Arm Cortex-A520AE (cortex-a520ae).
* Arm Cortex-A720AE (cortex-a720ae).
* Arm Cortex-R82AE (cortex-r82ae).
* Arm Neoverse-N3 (neoverse-n3).
* Arm Neoverse-V3 (neoverse-v3).
* Arm Neoverse-V3AE (neoverse-v3ae).
Expand Down Expand Up @@ -722,10 +811,10 @@ AIX Support
WebAssembly Support
^^^^^^^^^^^^^^^^^^^

The -mcpu=generic configuration now enables multivalue and reference-types.These
proposals are standardized and available in all major engines. Enabling
multivalue here only enables the language feature but does not turn on the
multivalue ABI (this enables non-ABI uses of multivalue, like exnref).
The -mcpu=generic configuration now enables multivalue feature, which is
standardized and available in all major engines. Enabling multivalue here only
enables the language feature but does not turn on the multivalue ABI (this
enables non-ABI uses of multivalue, like exnref).

AVR Support
^^^^^^^^^^^
Expand Down Expand Up @@ -761,6 +850,9 @@ clang-format
``BreakTemplateDeclarations``.
- ``AlwaysBreakAfterReturnType`` is deprecated and renamed to
``BreakAfterReturnType``.
- Handles Java ``switch`` expressions.
- Adds ``AllowShortCaseExpressionOnASingleLine`` option.
- Adds ``AlignCaseArrows`` suboption to ``AlignConsecutiveShortCaseStatements``.

libclang
--------
Expand Down
3 changes: 1 addition & 2 deletions clang/docs/UsersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1696,7 +1696,6 @@ for more details.
* ``-fno-associative-math``
* ``-fno-reciprocal-math``
* ``-fsigned-zeros``
* ``-ftrapping-math``
* ``-ffp-contract=on``

There is ambiguity about how ``-ffp-contract``,
Expand Down Expand Up @@ -3505,7 +3504,7 @@ Differences between ``*17`` and ``*23`` modes:
- ``nullptr`` and ``nullptr_t`` are supported, only in ``*23`` mode.
- ``ATOMIC_VAR_INIT`` is removed from ``*23`` mode.
- ``bool``, ``true``, ``false``, ``alignas``, ``alignof``, ``static_assert``,
and ``thread_local` are now first-class keywords, only in ``*23`` mode.
and ``thread_local`` are now first-class keywords, only in ``*23`` mode.
- ``typeof`` and ``typeof_unqual`` are supported, only ``*23`` mode.
- Bit-precise integers (``_BitInt(N)``) are supported by default in ``*23``
mode, and as an extension in ``*17`` and earlier modes.
Expand Down
Loading