3 changes: 2 additions & 1 deletion bolt/lib/Core/BinarySection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ BinarySection::hash(const BinaryData &BD,

void BinarySection::emitAsData(MCStreamer &Streamer,
const Twine &SectionName) const {
StringRef SectionContents = getContents();
StringRef SectionContents =
isFinalized() ? getOutputContents() : getContents();
MCSectionELF *ELFSection =
BC.Ctx->getELFSection(SectionName, getELFType(), getELFFlags());

Expand Down
68 changes: 31 additions & 37 deletions bolt/lib/Passes/BinaryPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,8 @@ void CheckLargeFunctions::runOnFunctions(BinaryContext &BC) {
if (BC.HasRelocations)
return;

if (!opts::UpdateDebugSections)
return;

// If the function wouldn't fit, mark it as non-simple. Otherwise, we may emit
// incorrect debug info.
// incorrect meta data.
ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
uint64_t HotSize, ColdSize;
std::tie(HotSize, ColdSize) =
Expand All @@ -582,50 +579,31 @@ bool CheckLargeFunctions::shouldOptimize(const BinaryFunction &BF) const {
}

void LowerAnnotations::runOnFunctions(BinaryContext &BC) {
// Convert GnuArgsSize annotations into CFIs.
for (BinaryFunction *BF : BC.getAllBinaryFunctions()) {
for (FunctionFragment &FF : BF->getLayout().fragments()) {
// Reset at the start of the new fragment.
int64_t CurrentGnuArgsSize = 0;

for (BinaryBasicBlock *const BB : FF) {
for (auto II = BB->begin(); II != BB->end(); ++II) {

// Convert GnuArgsSize annotations into CFIs.
if (BF->usesGnuArgsSize() && BC.MIB->isInvoke(*II)) {
const int64_t NewGnuArgsSize = BC.MIB->getGnuArgsSize(*II);
assert(NewGnuArgsSize >= 0 &&
"Expected non-negative GNU_args_size.");
if (NewGnuArgsSize != CurrentGnuArgsSize) {
auto InsertII = BF->addCFIInstruction(
BB, II,
MCCFIInstruction::createGnuArgsSize(nullptr, NewGnuArgsSize));
CurrentGnuArgsSize = NewGnuArgsSize;
II = std::next(InsertII);
}
}

// Preserve selected annotations and strip the rest.
std::optional<uint32_t> Offset = BF->requiresAddressTranslation()
? BC.MIB->getOffset(*II)
: std::nullopt;
std::optional<uint32_t> Size = BC.MIB->getSize(*II);
MCSymbol *Label = BC.MIB->getLabel(*II);

BC.MIB->stripAnnotations(*II);

if (Offset)
BC.MIB->setOffset(*II, *Offset);
if (Size)
BC.MIB->setSize(*II, *Size);
if (Label)
BC.MIB->setLabel(*II, Label);
if (!BF->usesGnuArgsSize() || !BC.MIB->isInvoke(*II))
continue;

const int64_t NewGnuArgsSize = BC.MIB->getGnuArgsSize(*II);
assert(NewGnuArgsSize >= 0 && "Expected non-negative GNU_args_size.");
if (NewGnuArgsSize == CurrentGnuArgsSize)
continue;

auto InsertII = BF->addCFIInstruction(
BB, II,
MCCFIInstruction::createGnuArgsSize(nullptr, NewGnuArgsSize));
CurrentGnuArgsSize = NewGnuArgsSize;
II = std::next(InsertII);
}
}
}
}

// Release all memory taken by annotations
BC.MIB->freeAnnotations();
}

// Check for dirty state in MCSymbol objects that might be a consequence
Expand Down Expand Up @@ -1420,6 +1398,12 @@ void PrintProgramStats::runOnFunctions(BinaryContext &BC) {
if (NumAllStaleFunctions) {
const float PctStale =
NumAllStaleFunctions / (float)NumAllProfiledFunctions * 100.0f;
const float PctStaleFuncsWithEqualBlockCount =
(float)BC.Stats.NumStaleFuncsWithEqualBlockCount /
NumAllStaleFunctions * 100.0f;
const float PctStaleBlocksWithEqualIcount =
(float)BC.Stats.NumStaleBlocksWithEqualIcount /
BC.Stats.NumStaleBlocks * 100.0f;
auto printErrorOrWarning = [&]() {
if (PctStale > opts::StaleThreshold)
errs() << "BOLT-ERROR: ";
Expand All @@ -1442,6 +1426,16 @@ void PrintProgramStats::runOnFunctions(BinaryContext &BC) {
<< "%) belong to functions with invalid"
" (possibly stale) profile.\n";
}
outs() << "BOLT-INFO: " << BC.Stats.NumStaleFuncsWithEqualBlockCount
<< " stale function"
<< (BC.Stats.NumStaleFuncsWithEqualBlockCount == 1 ? "" : "s")
<< format(" (%.1f%% of all stale)", PctStaleFuncsWithEqualBlockCount)
<< " have matching block count.\n";
outs() << "BOLT-INFO: " << BC.Stats.NumStaleBlocksWithEqualIcount
<< " stale block"
<< (BC.Stats.NumStaleBlocksWithEqualIcount == 1 ? "" : "s")
<< format(" (%.1f%% of all stale)", PctStaleBlocksWithEqualIcount)
<< " have matching icount.\n";
if (PctStale > opts::StaleThreshold) {
errs() << "BOLT-ERROR: stale functions exceed specified threshold of "
<< opts::StaleThreshold << "%. Exiting.\n";
Expand Down
3 changes: 3 additions & 0 deletions bolt/lib/Profile/StaleProfileMatching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ void matchWeightsByHashes(BinaryContext &BC,
if (MatchedBlock == nullptr && YamlBB.Index == 0)
MatchedBlock = Blocks[0];
if (MatchedBlock != nullptr) {
const BinaryBasicBlock *BB = BlockOrder[MatchedBlock->Index - 1];
MatchedBlocks[YamlBB.Index] = MatchedBlock;
BlendedBlockHash BinHash = BlendedHashes[MatchedBlock->Index - 1];
LLVM_DEBUG(dbgs() << "Matched yaml block (bid = " << YamlBB.Index << ")"
Expand All @@ -433,6 +434,8 @@ void matchWeightsByHashes(BinaryContext &BC,
} else {
LLVM_DEBUG(dbgs() << " loose match\n");
}
if (YamlBB.NumInstructions == BB->size())
++BC.Stats.NumStaleBlocksWithEqualIcount;
} else {
LLVM_DEBUG(
dbgs() << "Couldn't match yaml block (bid = " << YamlBB.Index << ")"
Expand Down
20 changes: 10 additions & 10 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,20 +246,20 @@ bool YAMLProfileReader::parseFunctionProfile(

ProfileMatched &= !MismatchedBlocks && !MismatchedCalls && !MismatchedEdges;

if (ProfileMatched)
BF.markProfiled(YamlBP.Header.Flags);
if (!ProfileMatched) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: " << MismatchedBlocks << " blocks, "
<< MismatchedCalls << " calls, and " << MismatchedEdges
<< " edges in profile did not match function " << BF << '\n';

if (!ProfileMatched && opts::Verbosity >= 1)
errs() << "BOLT-WARNING: " << MismatchedBlocks << " blocks, "
<< MismatchedCalls << " calls, and " << MismatchedEdges
<< " edges in profile did not match function " << BF << '\n';
if (YamlBF.NumBasicBlocks != BF.size())
++BC.Stats.NumStaleFuncsWithEqualBlockCount;

if (!ProfileMatched && opts::InferStaleProfile) {
if (inferStaleProfile(BF, YamlBF)) {
if (opts::InferStaleProfile && inferStaleProfile(BF, YamlBF))
ProfileMatched = true;
BF.markProfiled(YamlBP.Header.Flags);
}
}
if (ProfileMatched)
BF.markProfiled(YamlBP.Header.Flags);

return ProfileMatched;
}
Expand Down
107 changes: 52 additions & 55 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,12 +757,11 @@ Error RewriteInstance::run() {
if (opts::Instrument && !BC->IsStaticExecutable)
updateRtFiniReloc();

if (BC->IsLinuxKernel) {
errs() << "BOLT-WARNING: not writing the output file for Linux Kernel\n";
return Error::success();
} else if (opts::OutputFilename == "/dev/null") {
if (opts::OutputFilename == "/dev/null") {
outs() << "BOLT-INFO: skipping writing final binary to disk\n";
return Error::success();
} else if (BC->IsLinuxKernel) {
errs() << "BOLT-WARNING: Linux kernel support is experimental\n";
}

// Rewrite allocatable contents and copy non-allocatable parts with mods.
Expand Down Expand Up @@ -1470,25 +1469,29 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
setPLTSymbol(BF, Symbol->getName());
}

void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {
void RewriteInstance::disassemblePLTInstruction(const BinarySection &Section,
uint64_t InstrOffset,
MCInst &Instruction,
uint64_t &InstrSize) {
const uint64_t SectionAddress = Section.getAddress();
const uint64_t SectionSize = Section.getSize();
StringRef PLTContents = Section.getContents();
ArrayRef<uint8_t> PLTData(
reinterpret_cast<const uint8_t *>(PLTContents.data()), SectionSize);

auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction,
uint64_t &InstrSize) {
const uint64_t InstrAddr = SectionAddress + InstrOffset;
if (!BC->DisAsm->getInstruction(Instruction, InstrSize,
PLTData.slice(InstrOffset), InstrAddr,
nulls())) {
errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section "
<< Section.getName() << " at offset 0x"
<< Twine::utohexstr(InstrOffset) << '\n';
exit(1);
}
};
const uint64_t InstrAddr = SectionAddress + InstrOffset;
if (!BC->DisAsm->getInstruction(Instruction, InstrSize,
PLTData.slice(InstrOffset), InstrAddr,
nulls())) {
errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section "
<< Section.getName() << formatv(" at offset {0:x}\n", InstrOffset);
exit(1);
}
}

void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {
const uint64_t SectionAddress = Section.getAddress();
const uint64_t SectionSize = Section.getSize();

uint64_t InstrOffset = 0;
// Locate new plt entry
Expand All @@ -1500,7 +1503,7 @@ void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {
uint64_t InstrSize;
// Loop through entry instructions
while (InstrOffset < SectionSize) {
disassembleInstruction(InstrOffset, Instruction, InstrSize);
disassemblePLTInstruction(Section, InstrOffset, Instruction, InstrSize);
EntrySize += InstrSize;
if (!BC->MIB->isIndirectBranch(Instruction)) {
Instructions.emplace_back(Instruction);
Expand All @@ -1521,7 +1524,7 @@ void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {

// Skip nops if any
while (InstrOffset < SectionSize) {
disassembleInstruction(InstrOffset, Instruction, InstrSize);
disassemblePLTInstruction(Section, InstrOffset, Instruction, InstrSize);
if (!BC->MIB->isNoop(Instruction))
break;

Expand Down Expand Up @@ -1578,29 +1581,13 @@ void RewriteInstance::disassemblePLTSectionX86(BinarySection &Section,
uint64_t EntrySize) {
const uint64_t SectionAddress = Section.getAddress();
const uint64_t SectionSize = Section.getSize();
StringRef PLTContents = Section.getContents();
ArrayRef<uint8_t> PLTData(
reinterpret_cast<const uint8_t *>(PLTContents.data()), SectionSize);

auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction,
uint64_t &InstrSize) {
const uint64_t InstrAddr = SectionAddress + InstrOffset;
if (!BC->DisAsm->getInstruction(Instruction, InstrSize,
PLTData.slice(InstrOffset), InstrAddr,
nulls())) {
errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section "
<< Section.getName() << " at offset 0x"
<< Twine::utohexstr(InstrOffset) << '\n';
exit(1);
}
};

for (uint64_t EntryOffset = 0; EntryOffset + EntrySize <= SectionSize;
EntryOffset += EntrySize) {
MCInst Instruction;
uint64_t InstrSize, InstrOffset = EntryOffset;
while (InstrOffset < EntryOffset + EntrySize) {
disassembleInstruction(InstrOffset, Instruction, InstrSize);
disassemblePLTInstruction(Section, InstrOffset, Instruction, InstrSize);
// Check if the entry size needs adjustment.
if (EntryOffset == 0 && BC->MIB->isTerminateBranch(Instruction) &&
EntrySize == 8)
Expand Down Expand Up @@ -1860,6 +1847,11 @@ Error RewriteInstance::readSpecialSections() {
BC->HasRelocations =
HasTextRelocations && (opts::RelocationMode != cl::BOU_FALSE);

if (BC->IsLinuxKernel && BC->HasRelocations) {
outs() << "BOLT-INFO: disabling relocation mode for Linux kernel\n";
BC->HasRelocations = false;
}

BC->IsStripped = !HasSymbolTable;

if (BC->IsStripped && !opts::AllowStripped) {
Expand Down Expand Up @@ -2532,7 +2524,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
Expected<StringRef> SectionName = Section->getName();
if (SectionName && !SectionName->empty())
ReferencedSection = BC->getUniqueSectionByName(*SectionName);
} else if (ReferencedSymbol &&
} else if (ReferencedSymbol && ContainingBF &&
(cantFail(Symbol.getFlags()) & SymbolRef::SF_Absolute)) {
// This might be a relocation for an ABS symbols like __global_pointer$ on
// RISC-V
Expand Down Expand Up @@ -3639,6 +3631,7 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
Function.setImageAddress(FuncSection->getAllocAddress());
Function.setImageSize(FuncSection->getOutputSize());
if (Function.getImageSize() > Function.getMaxSize()) {
assert(!BC->isX86() && "Unexpected large function.");
TooLarge = true;
FailedAddresses.emplace_back(Function.getAddress());
}
Expand Down Expand Up @@ -4153,10 +4146,10 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,

// Keep track of section header entries attached to the corresponding section.
std::vector<std::pair<BinarySection *, ELFShdrTy>> OutputSections;
auto addSection = [&](const ELFShdrTy &Section, BinarySection *BinSec) {
auto addSection = [&](const ELFShdrTy &Section, BinarySection &BinSec) {
ELFShdrTy NewSection = Section;
NewSection.sh_name = SHStrTab.getOffset(BinSec->getOutputName());
OutputSections.emplace_back(BinSec, std::move(NewSection));
NewSection.sh_name = SHStrTab.getOffset(BinSec.getOutputName());
OutputSections.emplace_back(&BinSec, std::move(NewSection));
};

// Copy over entries for original allocatable sections using modified name.
Expand All @@ -4174,7 +4167,7 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
BinarySection *BinSec = BC->getSectionForSectionRef(SecRef);
assert(BinSec && "Matching BinarySection should exist.");

addSection(Section, BinSec);
addSection(Section, *BinSec);
}

for (BinarySection &Section : BC->allocatableSections()) {
Expand All @@ -4201,7 +4194,7 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
NewSection.sh_link = 0;
NewSection.sh_info = 0;
NewSection.sh_addralign = Section.getAlignment();
addSection(NewSection, &Section);
addSection(NewSection, Section);
}

// Sort all allocatable sections by their offset.
Expand All @@ -4215,19 +4208,19 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
for (auto &SectionKV : OutputSections) {
ELFShdrTy &Section = SectionKV.second;

// TBSS section does not take file or memory space. Ignore it for layout
// purposes.
if (Section.sh_type == ELF::SHT_NOBITS && (Section.sh_flags & ELF::SHF_TLS))
// Ignore TLS sections as they don't take any space in the file.
if (Section.sh_type == ELF::SHT_NOBITS)
continue;

// Note that address continuity is not guaranteed as sections could be
// placed in different loadable segments.
if (PrevSection &&
PrevSection->sh_addr + PrevSection->sh_size > Section.sh_addr) {
if (opts::Verbosity > 1)
PrevSection->sh_offset + PrevSection->sh_size > Section.sh_offset) {
if (opts::Verbosity > 1) {
outs() << "BOLT-INFO: adjusting size for section "
<< PrevBinSec->getOutputName() << '\n';
PrevSection->sh_size = Section.sh_addr > PrevSection->sh_addr
? Section.sh_addr - PrevSection->sh_addr
: 0;
}
PrevSection->sh_size = Section.sh_offset - PrevSection->sh_offset;
}

PrevSection = &Section;
Expand Down Expand Up @@ -4261,7 +4254,7 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
if (NewSection.sh_type == ELF::SHT_SYMTAB)
NewSection.sh_info = NumLocalSymbols;

addSection(NewSection, BinSec);
addSection(NewSection, *BinSec);

LastFileOffset = BinSec->getOutputFileOffset();
}
Expand All @@ -4286,7 +4279,7 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
NewSection.sh_info = 0;
NewSection.sh_addralign = Section.getAlignment();

addSection(NewSection, &Section);
addSection(NewSection, Section);
}

// Assign indices to sections.
Expand Down Expand Up @@ -4369,8 +4362,10 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile<ELFT> *File) {
assert((NewEhdr.e_entry || !Obj.getHeader().e_entry) &&
"cannot find new address for entry point");
}
NewEhdr.e_phoff = PHDRTableOffset;
NewEhdr.e_phnum = Phnum;
if (PHDRTableOffset) {
NewEhdr.e_phoff = PHDRTableOffset;
NewEhdr.e_phnum = Phnum;
}
NewEhdr.e_shoff = SHTOffset;
NewEhdr.e_shnum = OutputSections.size();
NewEhdr.e_shstrndx = NewSectionIndex[NewEhdr.e_shstrndx];
Expand Down Expand Up @@ -5373,6 +5368,7 @@ void RewriteInstance::rewriteFile() {
continue;

if (Function->getImageSize() > Function->getMaxSize()) {
assert(!BC->isX86() && "Unexpected large function.");
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: new function size (0x"
<< Twine::utohexstr(Function->getImageSize())
Expand Down Expand Up @@ -5505,7 +5501,8 @@ void RewriteInstance::rewriteFile() {
addBATSection();

// Patch program header table.
patchELFPHDRTable();
if (!BC->IsLinuxKernel)
patchELFPHDRTable();

// Finalize memory image of section string table.
finalizeSectionStringTable();
Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/Target/X86/X86MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2197,7 +2197,7 @@ class X86MCPlusBuilder : public MCPlusBuilder {
MO->BaseRegNum != X86::RIP && MO->BaseRegNum != X86::RBP &&
MO->BaseRegNum != X86::NoRegister &&
MO->IndexRegNum == X86::NoRegister &&
MO->SegRegNum == X86::NoRegister && MO->BaseRegNum != X86::RIP) {
MO->SegRegNum == X86::NoRegister) {
VtableRegNum = MO->BaseRegNum;
MethodOffset = MO->DispImm;
MethodFetchInsns.push_back(&CurInst);
Expand Down
5 changes: 4 additions & 1 deletion bolt/test/AArch64/ifunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@
// REL_CHECK: [[#REL_SYMB_ADDR]] {{.*}} FUNC {{.*}} resolver_foo

static void foo() {}
static void bar() {}

static void *resolver_foo(void) { return foo; }
extern int use_foo;

static void *resolver_foo(void) { return use_foo ? foo : bar; }

__attribute__((ifunc("resolver_foo"))) void ifoo();

Expand Down
2 changes: 2 additions & 0 deletions bolt/test/X86/linux-orc.s
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ bar:
.L4:
.size bar, .-bar

# CHECK: BOLT-WARNING: Linux kernel support is experimental

.section .orc_unwind,"a",@progbits
.align 4
.section .orc_unwind_ip,"a",@progbits
Expand Down
52 changes: 52 additions & 0 deletions bolt/test/X86/phdr-out-of-order.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## Check that llvm-bolt correctly processes a binary with program headers and
## corresponding sections specified in non-ascending address order.

RUN: split-file %s %t
RUN: yaml2obj %t/yaml -o %t.exe --max-size=0
RUN: llvm-bolt %t.exe -o %t.bolt --allow-stripped
RUN: llvm-readelf -WS %t.bolt | FileCheck %s

CHECK: .a PROGBITS 0000000000400000 [[#%.6x, OFFSET:]] 000001
CHECK-NEXT: .b PROGBITS 0000000000000000 [[#%.6x, OFFSET+1]] 000001
CHECK-NEXT: .c PROGBITS 0000000000600000 [[#%.6x, OFFSET+2]] 000001

#--- yaml
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
ProgramHeaders:
- Type: PT_LOAD
FirstSec: .a
LastSec: .a
VAddr: 0x400000
- Type: PT_LOAD
FirstSec: .b
LastSec: .b
VAddr: 0x0
- Type: PT_LOAD
FirstSec: .c
LastSec: .c
VAddr: 0x600000
Sections:
- Name: .a
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: 00
AddressAlign: 0x1
Address: 0x400000
- Name: .b
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: 00
AddressAlign: 0x1
Address: 0x0
- Name: .c
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: 00
AddressAlign: 0x1
Address: 0x600000
...
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ int main(int argc, char **argv) {

tooling::ApplyChangesSpec Spec;
Spec.Cleanup = true;
Spec.Style = FormatStyle;
Spec.Format = DoFormat ? tooling::ApplyChangesSpec::kAll
: tooling::ApplyChangesSpec::kNone;
Spec.Style = DoFormat ? FormatStyle : format::getNoStyle();

for (const auto &FileChange : Changes) {
FileEntryRef Entry = FileChange.first;
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-move/Move.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ class FindAllIncludes : public PPCallbacks {
CharSourceRange FilenameRange,
OptionalFileEntryRef /*File*/, StringRef SearchPath,
StringRef /*RelativePath*/,
const Module * /*Imported*/,
const Module * /*SuggestedModule*/,
bool /*ModuleImported*/,
SrcMgr::CharacteristicKind /*FileType*/) override {
if (auto FileEntry = SM.getFileEntryRefForID(SM.getFileID(HashLoc)))
MoveTool->addIncludes(FileName, IsAngled, SearchPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ void ExpandModularHeadersPPCallbacks::InclusionDirective(
SourceLocation DirectiveLoc, const Token &IncludeToken,
StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange,
OptionalFileEntryRef IncludedFile, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported,
SrcMgr::CharacteristicKind FileType) {
if (Imported) {
if (ModuleImported) {
serialization::ModuleFile *MF =
Compiler.getASTReader()->getModuleManager().lookup(
*Imported->getASTFile());
*SuggestedModule->getASTFile());
handleModuleFile(MF);
}
parseToLocation(DirectiveLoc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class ExpandModularHeadersPPCallbacks : public PPCallbacks {
bool IsAngled, CharSourceRange FilenameRange,
OptionalFileEntryRef IncludedFile,
StringRef SearchPath, StringRef RelativePath,
const Module *Imported,
const Module *SuggestedModule, bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;

void EndOfMainFile() override;
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clang-tidy/add_new_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ def write_implementation(module_path, module, namespace, check_name_camel):
//===----------------------------------------------------------------------===//
#include "%(check_name)s.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
Expand All @@ -146,7 +145,7 @@ def write_implementation(module_path, module, namespace, check_name_camel):
void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
// FIXME: Add callback implementation.
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_"))
if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().starts_with("awesome_"))
return;
diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
<< MatchedDecl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class KernelNameRestrictionPPCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FileNameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;

void EndOfMainFile() override;
Expand Down Expand Up @@ -61,7 +62,7 @@ void KernelNameRestrictionCheck::registerPPCallbacks(const SourceManager &SM,
void KernelNameRestrictionPPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &, StringRef FileName, bool,
CharSourceRange, OptionalFileEntryRef, StringRef, StringRef, const Module *,
SrcMgr::CharacteristicKind) {
bool, SrcMgr::CharacteristicKind) {
IncludeDirective ID = {HashLoc, FileName};
IncludeDirectives.push_back(std::move(ID));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,8 @@ AST_MATCHER(clang::VarDecl, hasConstantDeclaration) {

DynamicStaticInitializersCheck::DynamicStaticInitializersCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void DynamicStaticInitializersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void DynamicStaticInitializersCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,16 @@
namespace clang::tidy::bugprone {

/// Finds dynamically initialized static variables in header files.
///
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extensions should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
//
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
class DynamicStaticInitializersCheck : public ClangTidyCheck {
public:
DynamicStaticInitializersCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus && !LangOpts.ThreadsafeStatics;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
45 changes: 7 additions & 38 deletions clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class SuspiciousIncludePPCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;

private:
Expand All @@ -37,41 +38,9 @@ class SuspiciousIncludePPCallbacks : public PPCallbacks {

SuspiciousIncludeCheck::SuspiciousIncludeCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> ImplementationFileExtensionsOption =
Options.get("ImplementationFileExtensions");
RawStringImplementationFileExtensions =
ImplementationFileExtensionsOption.value_or(
utils::defaultImplementationFileExtensions());
if (ImplementationFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringImplementationFileExtensions,
ImplementationFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid implementation file extension: '%0'")
<< RawStringImplementationFileExtensions;
}
} else
ImplementationFileExtensions = Context->getImplementationFileExtensions();

std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void SuspiciousIncludeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "ImplementationFileExtensions",
RawStringImplementationFileExtensions);
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()),
ImplementationFileExtensions(Context->getImplementationFileExtensions()) {
}

void SuspiciousIncludeCheck::registerPPCallbacks(
Expand All @@ -83,8 +52,8 @@ void SuspiciousIncludeCheck::registerPPCallbacks(
void SuspiciousIncludePPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
if (IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import)
return;

Expand Down
15 changes: 0 additions & 15 deletions clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,16 @@ namespace clang::tidy::bugprone {
/// #include "bar.c" // warning
/// #include "baz.h" // no diagnostic
///
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extensions should not contain
/// "." prefix) ";h;hh;hpp;hxx" by default. For extension-less header
/// files, using an empty string or leaving an empty string between ";" if
/// there are other filename extensions.
///
/// - `ImplementationFileExtensions`: likewise, a semicolon-separated list of
/// filename extensions of implementation files. "c;cc;cpp;cxx" by default.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/suspicious-include.html
class SuspiciousIncludeCheck : public ClangTidyCheck {
public:
SuspiciousIncludeCheck(StringRef Name, ClangTidyContext *Context);
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;

FileExtensionsSet HeaderFileExtensions;
FileExtensionsSet ImplementationFileExtensions;

private:
StringRef RawStringHeaderFileExtensions;
StringRef RawStringImplementationFileExtensions;
};

} // namespace clang::tidy::bugprone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,6 @@ static bool isNoReturnCallStatement(const Stmt *S) {
return Func->isNoReturn();
}

static bool isLiteral(const Expr *E) {
return isa<StringLiteral, CharacterLiteral, IntegerLiteral, FloatingLiteral,
CXXBoolLiteralExpr, CXXNullPtrLiteralExpr>(E);
}

static bool isUnaryExprOfLiteral(const Expr *E) {
if (const auto *UnOp = dyn_cast<UnaryOperator>(E))
return isLiteral(UnOp->getSubExpr());
return false;
}

static bool shouldBeDefaultMemberInitializer(const Expr *Value) {
if (isLiteral(Value) || isUnaryExprOfLiteral(Value))
return true;

if (const auto *DRE = dyn_cast<DeclRefExpr>(Value))
return isa<EnumConstantDecl>(DRE->getDecl());

return false;
}

namespace {

AST_MATCHER_P(FieldDecl, indexNotLessThan, unsigned, Index) {
Expand Down Expand Up @@ -166,19 +145,7 @@ isAssignmentToMemberOf(const CXXRecordDecl *Rec, const Stmt *S,

PreferMemberInitializerCheck::PreferMemberInitializerCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IsUseDefaultMemberInitEnabled(
Context->isCheckEnabled("modernize-use-default-member-init")),
UseAssignment(
Options.get("UseAssignment",
OptionsView("modernize-use-default-member-init",
Context->getOptions().CheckOptions, Context)
.get("UseAssignment", false))) {}

void PreferMemberInitializerCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "UseAssignment", UseAssignment);
}
: ClangTidyCheck(Name, Context) {}

void PreferMemberInitializerCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(cxxConstructorDecl(hasBody(compoundStmt()),
Expand Down Expand Up @@ -230,139 +197,99 @@ void PreferMemberInitializerCheck::check(
updateAssignmentLevel(Field, InitValue, Ctor, AssignedFields);
if (!canAdvanceAssignment(AssignedFields[Field]))
continue;
const bool IsInDefaultMemberInitializer =
IsUseDefaultMemberInitEnabled && getLangOpts().CPlusPlus11 &&
Ctor->isDefaultConstructor() &&
(getLangOpts().CPlusPlus20 || !Field->isBitField()) &&
!Field->hasInClassInitializer() &&
(!isa<RecordDecl>(Class->getDeclContext()) ||
!cast<RecordDecl>(Class->getDeclContext())->isUnion()) &&
shouldBeDefaultMemberInitializer(InitValue);
if (IsInDefaultMemberInitializer) {
bool InvalidFix = false;
SourceLocation FieldEnd =
Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
*Result.SourceManager, getLangOpts());
InvalidFix |= FieldEnd.isInvalid() || FieldEnd.isMacroID();
SourceLocation SemiColonEnd;
if (auto NextToken = Lexer::findNextToken(
S->getEndLoc(), *Result.SourceManager, getLangOpts()))
SemiColonEnd = NextToken->getEndLoc();
else
InvalidFix = true;
auto Diag =
diag(S->getBeginLoc(), "%0 should be initialized in an in-class"
" default member initializer")
<< Field;
if (InvalidFix)
continue;
CharSourceRange StmtRange =
CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd);

SmallString<128> Insertion(
{UseAssignment ? " = " : "{",
Lexer::getSourceText(Result.SourceManager->getExpansionRange(
InitValue->getSourceRange()),
*Result.SourceManager, getLangOpts()),
UseAssignment ? "" : "}"});

Diag << FixItHint::CreateInsertion(FieldEnd, Insertion)
<< FixItHint::CreateRemoval(StmtRange);

} else {
StringRef InsertPrefix = "";
bool HasInitAlready = false;
SourceLocation InsertPos;
SourceRange ReplaceRange;
bool AddComma = false;
bool InvalidFix = false;
unsigned Index = Field->getFieldIndex();
const CXXCtorInitializer *LastInListInit = nullptr;
for (const CXXCtorInitializer *Init : Ctor->inits()) {
if (!Init->isWritten() || Init->isInClassMemberInitializer())
continue;
if (Init->getMember() == Field) {
HasInitAlready = true;
if (isa<ImplicitValueInitExpr>(Init->getInit()))
InsertPos = Init->getRParenLoc();
else {
ReplaceRange = Init->getInit()->getSourceRange();
}
break;
}
if (Init->isMemberInitializer() &&
Index < Init->getMember()->getFieldIndex()) {
InsertPos = Init->getSourceLocation();
// There are initializers after the one we are inserting, so add a
// comma after this insertion in order to not break anything.
AddComma = true;
break;
StringRef InsertPrefix = "";
bool HasInitAlready = false;
SourceLocation InsertPos;
SourceRange ReplaceRange;
bool AddComma = false;
bool InvalidFix = false;
unsigned Index = Field->getFieldIndex();
const CXXCtorInitializer *LastInListInit = nullptr;
for (const CXXCtorInitializer *Init : Ctor->inits()) {
if (!Init->isWritten() || Init->isInClassMemberInitializer())
continue;
if (Init->getMember() == Field) {
HasInitAlready = true;
if (isa<ImplicitValueInitExpr>(Init->getInit()))
InsertPos = Init->getRParenLoc();
else {
ReplaceRange = Init->getInit()->getSourceRange();
}
LastInListInit = Init;
break;
}
if (HasInitAlready) {
if (InsertPos.isValid())
InvalidFix |= InsertPos.isMacroID();
else
InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
ReplaceRange.getEnd().isMacroID();
} else {
if (InsertPos.isInvalid()) {
if (LastInListInit) {
InsertPos = Lexer::getLocForEndOfToken(
LastInListInit->getRParenLoc(), 0, *Result.SourceManager,
getLangOpts());
// Inserting after the last constructor initializer, so we need a
// comma.
InsertPrefix = ", ";
} else {
InsertPos = Lexer::getLocForEndOfToken(
Ctor->getTypeSourceInfo()
->getTypeLoc()
.getAs<clang::FunctionTypeLoc>()
.getLocalRangeEnd(),
0, *Result.SourceManager, getLangOpts());

// If this is first time in the loop, there are no initializers so
// `:` declares member initialization list. If this is a
// subsequent pass then we have already inserted a `:` so continue
// with a comma.
InsertPrefix = FirstToCtorInits ? " : " : ", ";
}
}
if (Init->isMemberInitializer() &&
Index < Init->getMember()->getFieldIndex()) {
InsertPos = Init->getSourceLocation();
// There are initializers after the one we are inserting, so add a
// comma after this insertion in order to not break anything.
AddComma = true;
break;
}
LastInListInit = Init;
}
if (HasInitAlready) {
if (InsertPos.isValid())
InvalidFix |= InsertPos.isMacroID();
else
InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
ReplaceRange.getEnd().isMacroID();
} else {
if (InsertPos.isInvalid()) {
if (LastInListInit) {
InsertPos =
Lexer::getLocForEndOfToken(LastInListInit->getRParenLoc(), 0,
*Result.SourceManager, getLangOpts());
// Inserting after the last constructor initializer, so we need a
// comma.
InsertPrefix = ", ";
} else {
InsertPos = Lexer::getLocForEndOfToken(
Ctor->getTypeSourceInfo()
->getTypeLoc()
.getAs<clang::FunctionTypeLoc>()
.getLocalRangeEnd(),
0, *Result.SourceManager, getLangOpts());

// If this is first time in the loop, there are no initializers so
// `:` declares member initialization list. If this is a
// subsequent pass then we have already inserted a `:` so continue
// with a comma.
InsertPrefix = FirstToCtorInits ? " : " : ", ";
}
}
InvalidFix |= InsertPos.isMacroID();
}

SourceLocation SemiColonEnd;
if (auto NextToken = Lexer::findNextToken(
S->getEndLoc(), *Result.SourceManager, getLangOpts()))
SemiColonEnd = NextToken->getEndLoc();
SourceLocation SemiColonEnd;
if (auto NextToken = Lexer::findNextToken(
S->getEndLoc(), *Result.SourceManager, getLangOpts()))
SemiColonEnd = NextToken->getEndLoc();
else
InvalidFix = true;

auto Diag = diag(S->getBeginLoc(), "%0 should be initialized in a member"
" initializer of the constructor")
<< Field;
if (InvalidFix)
continue;
StringRef NewInit = Lexer::getSourceText(
Result.SourceManager->getExpansionRange(InitValue->getSourceRange()),
*Result.SourceManager, getLangOpts());
if (HasInitAlready) {
if (InsertPos.isValid())
Diag << FixItHint::CreateInsertion(InsertPos, NewInit);
else
InvalidFix = true;

auto Diag = diag(S->getBeginLoc(), "%0 should be initialized in a member"
" initializer of the constructor")
<< Field;
if (InvalidFix)
continue;
StringRef NewInit = Lexer::getSourceText(
Result.SourceManager->getExpansionRange(InitValue->getSourceRange()),
*Result.SourceManager, getLangOpts());
if (HasInitAlready) {
if (InsertPos.isValid())
Diag << FixItHint::CreateInsertion(InsertPos, NewInit);
else
Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit);
} else {
SmallString<128> Insertion({InsertPrefix, Field->getName(), "(",
NewInit, AddComma ? "), " : ")"});
Diag << FixItHint::CreateInsertion(InsertPos, Insertion,
FirstToCtorInits);
FirstToCtorInits = areDiagsSelfContained();
}
Diag << FixItHint::CreateRemoval(
CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd));
Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit);
} else {
SmallString<128> Insertion({InsertPrefix, Field->getName(), "(", NewInit,
AddComma ? "), " : ")"});
Diag << FixItHint::CreateInsertion(InsertPos, Insertion,
FirstToCtorInits);
FirstToCtorInits = areDiagsSelfContained();
}
Diag << FixItHint::CreateRemoval(
CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@ class PreferMemberInitializerCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

const bool IsUseDefaultMemberInitEnabled;
const bool UseAssignment;
};

} // namespace clang::tidy::cppcoreguidelines
Expand Down
22 changes: 2 additions & 20 deletions clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,8 @@ namespace clang::tidy::google::readability {

GlobalNamesInHeadersCheck::GlobalNamesInHeadersCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void GlobalNamesInHeadersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void GlobalNamesInHeadersCheck::registerMatchers(
ast_matchers::MatchFinder *Finder) {
Expand Down
10 changes: 0 additions & 10 deletions clang-tools-extra/clang-tidy/google/GlobalNamesInHeadersCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,15 @@ namespace clang::tidy::google::readability {
/// Flag global namespace pollution in header files.
/// Right now it only triggers on using declarations and directives.
///
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (the filename extensions should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/google/global-names-in-headers.html
class GlobalNamesInHeadersCheck : public ClangTidyCheck {
public:
GlobalNamesInHeadersCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,8 @@ namespace clang::tidy::google::build {

UnnamedNamespaceInHeaderCheck::UnnamedNamespaceInHeaderCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void UnnamedNamespaceInHeaderCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void UnnamedNamespaceInHeaderCheck::registerMatchers(
ast_matchers::MatchFinder *Finder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ namespace clang::tidy::google::build {

/// Finds anonymous namespaces in headers.
///
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extensions should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
///
/// https://google.github.io/styleguide/cppguide.html#Namespaces
///
/// Corresponding cpplint.py check name: 'build/namespaces'.
Expand All @@ -36,12 +28,10 @@ class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
7 changes: 0 additions & 7 deletions clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ namespace clang::tidy::llvm_check {
/// Finds and fixes header guards that do not adhere to LLVM style.
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/llvm/header-guard.html
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
class LLVMHeaderGuardCheck : public utils::HeaderGuardCheck {
public:
LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context);
Expand Down
7 changes: 4 additions & 3 deletions clang-tools-extra/clang-tidy/llvm/IncludeOrderCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class IncludeOrderPPCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;
void EndOfMainFile() override;

Expand Down Expand Up @@ -81,8 +82,8 @@ static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule) {
void IncludeOrderPPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
// We recognize the first include as a special main module header and want
// to leave it in the top position.
IncludeDirective ID = {HashLoc, FilenameRange, std::string(FileName),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class RestrictedIncludesPPCallbacks
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;

private:
Expand All @@ -45,14 +46,14 @@ class RestrictedIncludesPPCallbacks
void RestrictedIncludesPPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
// Compiler provided headers are allowed (e.g stddef.h).
if (SrcMgr::isSystem(FileType) && SearchPath == CompilerIncudeDir)
return;
portability::RestrictedIncludesPPCallbacks::InclusionDirective(
HashLoc, IncludeTok, FileName, IsAngled, FilenameRange, File, SearchPath,
RelativePath, Imported, FileType);
RelativePath, SuggestedModule, ModuleImported, FileType);
}

void RestrictSystemLibcHeadersCheck::registerPPCallbacks(
Expand Down
39 changes: 5 additions & 34 deletions clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,45 +28,16 @@ AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, FileExtensionsSet,
DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void DefinitionsInHeadersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
auto DefinitionMatcher =
anyOf(functionDecl(isDefinition(), unless(isDeleted())),
varDecl(isDefinition()));
if (UseHeaderFileExtension) {
Finder->addMatcher(namedDecl(DefinitionMatcher,
usesHeaderFileExtension(HeaderFileExtensions))
.bind("name-decl"),
this);
} else {
Finder->addMatcher(
namedDecl(DefinitionMatcher,
anyOf(usesHeaderFileExtension(HeaderFileExtensions),
unless(isExpansionInMainFile())))
.bind("name-decl"),
this);
}
Finder->addMatcher(namedDecl(DefinitionMatcher,
usesHeaderFileExtension(HeaderFileExtensions))
.bind("name-decl"),
this);
}

void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
Expand Down
9 changes: 0 additions & 9 deletions clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ namespace clang::tidy::misc {
/// The check supports these options:
/// - `UseHeaderFileExtension`: Whether to use file extension to distinguish
/// header files. True by default.
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/definitions-in-headers.html
Expand All @@ -35,13 +29,10 @@ class DefinitionsInHeadersCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus11;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
const bool UseHeaderFileExtension;
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class CyclicDependencyCallbacks : public PPCallbacks {
void InclusionDirective(SourceLocation, const Token &, StringRef FilePath,
bool, CharSourceRange Range,
OptionalFileEntryRef File, StringRef, StringRef,
const Module *,
const Module *, bool,
SrcMgr::CharacteristicKind FileType) override {
if (FileType != clang::SrcMgr::C_User)
return;
Expand Down
17 changes: 2 additions & 15 deletions clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,8 @@ static bool shouldCheckDecl(const Decl *TargetDecl) {

UnusedUsingDeclsCheck::UnusedUsingDeclsCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this);
Expand Down
22 changes: 2 additions & 20 deletions clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,8 @@ AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }

UseAnonymousNamespaceCheck::UseAnonymousNamespaceCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void UseAnonymousNamespaceCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void UseAnonymousNamespaceCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
Expand Down
10 changes: 0 additions & 10 deletions clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ namespace clang::tidy::misc {
/// Warns when using 'static' functions or variables at global scope, and
/// suggests moving them to an anonymous namespace.
///
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-anonymous-namespace.html
class UseAnonymousNamespaceCheck : public ClangTidyCheck {
Expand All @@ -33,12 +25,10 @@ class UseAnonymousNamespaceCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class IncludeModernizePPCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;

private:
Expand Down Expand Up @@ -178,8 +179,8 @@ IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(
void IncludeModernizePPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {

// If we don't want to warn for non-main file reports and this is one, skip
// it.
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ class MacroToEnumCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override {
clearCurrentEnum(HashLoc);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ namespace clang::tidy::portability {
void RestrictedIncludesPPCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
if (!Check.contains(FileName) && SrcMgr::isSystem(FileType)) {
SmallString<256> FullPath;
llvm::sys::path::append(FullPath, SearchPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class RestrictedIncludesPPCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;
void EndOfMainFile() override;

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_clang_library(clangTidyReadabilityModule
UniqueptrDeleteReleaseCheck.cpp
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseStdMinMaxCheck.cpp

LINK_LIBS
clangTidy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class DuplicateIncludeCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;

void MacroDefined(const Token &MacroNameTok,
Expand Down Expand Up @@ -76,8 +77,8 @@ void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,
void DuplicateIncludeCallbacks::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
if (llvm::is_contained(Files.back(), FileName)) {
// We want to delete the entire line, so make sure that [Start,End] covers
// everything.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ std::string IdentifierNamingCheck::HungarianNotation::getEnumPrefix(
const auto *ED = cast<EnumDecl>(ECD->getDeclContext());

std::string Name = ED->getName().str();
if (std::string::npos != Name.find("enum")) {
if (StringRef(Name).contains("enum")) {
Name = Name.substr(strlen("enum"), Name.length() - strlen("enum"));
Name = Name.erase(0, Name.find_first_not_of(' '));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "UniqueptrDeleteReleaseCheck.h"
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseStdMinMaxCheck.h"

namespace clang::tidy {
namespace readability {
Expand Down Expand Up @@ -163,6 +164,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-uppercase-literal-suffix");
CheckFactories.registerCheck<UseAnyOfAllOfCheck>(
"readability-use-anyofallof");
CheckFactories.registerCheck<UseStdMinMaxCheck>(
"readability-use-std-min-max");
}
};

Expand Down
188 changes: 188 additions & 0 deletions clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//===--- UseStdMinMaxCheck.cpp - clang-tidy -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "UseStdMinMaxCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Preprocessor.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

namespace {

// Ignore if statements that are inside macros.
AST_MATCHER(IfStmt, isIfInMacro) {
return Node.getIfLoc().isMacroID() || Node.getEndLoc().isMacroID();
}

} // namespace

static const llvm::StringRef AlgorithmHeader("<algorithm>");

static bool minCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs,
const Expr *CondRhs, const Expr *AssignLhs,
const Expr *AssignRhs, const ASTContext &Context) {
if ((Op == BO_LT || Op == BO_LE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context)))
return true;

if ((Op == BO_GT || Op == BO_GE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context)))
return true;

return false;
}

static bool maxCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs,
const Expr *CondRhs, const Expr *AssignLhs,
const Expr *AssignRhs, const ASTContext &Context) {
if ((Op == BO_LT || Op == BO_LE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context)))
return true;

if ((Op == BO_GT || Op == BO_GE) &&
(tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) &&
tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context)))
return true;

return false;
}

QualType getNonTemplateAlias(QualType QT) {
while (true) {
// cast to a TypedefType
if (const TypedefType *TT = dyn_cast<TypedefType>(QT)) {
// check if the typedef is a template and if it is dependent
if (!TT->getDecl()->getDescribedTemplate() &&
!TT->getDecl()->getDeclContext()->isDependentContext())
return QT;
QT = TT->getDecl()->getUnderlyingType();
}
// cast to elaborated type
else if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) {
QT = ET->getNamedType();
} else {
break;
}
}
return QT;
}

static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs,
const Expr *AssignLhs,
const SourceManager &Source,
const LangOptions &LO,
StringRef FunctionName,
const BinaryOperator *BO) {
const llvm::StringRef CondLhsStr = Lexer::getSourceText(
Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO);
const llvm::StringRef CondRhsStr = Lexer::getSourceText(
Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO);
const llvm::StringRef AssignLhsStr = Lexer::getSourceText(
Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);

clang::QualType GlobalImplicitCastType;
clang::QualType LhsType = CondLhs->getType()
.getCanonicalType()
.getNonReferenceType()
.getUnqualifiedType();
clang::QualType RhsType = CondRhs->getType()
.getCanonicalType()
.getNonReferenceType()
.getUnqualifiedType();
if (LhsType != RhsType) {
GlobalImplicitCastType = getNonTemplateAlias(BO->getLHS()->getType());
}

return (AssignLhsStr + " = " + FunctionName +
(!GlobalImplicitCastType.isNull()
? "<" + GlobalImplicitCastType.getAsString() + ">("
: "(") +
CondLhsStr + ", " + CondRhsStr + ");")
.str();
}

UseStdMinMaxCheck::UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()) {}

void UseStdMinMaxCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
}

void UseStdMinMaxCheck::registerMatchers(MatchFinder *Finder) {
auto AssignOperator =
binaryOperator(hasOperatorName("="),
hasLHS(expr(unless(isTypeDependent())).bind("AssignLhs")),
hasRHS(expr(unless(isTypeDependent())).bind("AssignRhs")));
auto BinaryOperator =
binaryOperator(hasAnyOperatorName("<", ">", "<=", ">="),
hasLHS(expr(unless(isTypeDependent())).bind("CondLhs")),
hasRHS(expr(unless(isTypeDependent())).bind("CondRhs")))
.bind("binaryOp");
Finder->addMatcher(
ifStmt(stmt().bind("if"), unless(isIfInMacro()),
unless(hasElse(stmt())), // Ensure `if` has no `else`
hasCondition(BinaryOperator),
hasThen(
anyOf(stmt(AssignOperator),
compoundStmt(statementCountIs(1), has(AssignOperator)))),
hasParent(stmt(unless(ifStmt(hasElse(
equalsBoundNode("if"))))))), // Ensure `if` has no `else if`
this);
}

void UseStdMinMaxCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
}

void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) {
const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
const clang::LangOptions &LO = Result.Context->getLangOpts();
const auto *CondLhs = Result.Nodes.getNodeAs<Expr>("CondLhs");
const auto *CondRhs = Result.Nodes.getNodeAs<Expr>("CondRhs");
const auto *AssignLhs = Result.Nodes.getNodeAs<Expr>("AssignLhs");
const auto *AssignRhs = Result.Nodes.getNodeAs<Expr>("AssignRhs");
const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("binaryOp");
const clang::BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode();
const SourceLocation IfLocation = If->getIfLoc();
const SourceLocation ThenLocation = If->getEndLoc();

auto ReplaceAndDiagnose = [&](const llvm::StringRef FunctionName) {
const SourceManager &Source = *Result.SourceManager;
diag(IfLocation, "use `%0` instead of `%1`")
<< FunctionName << BinaryOp->getOpcodeStr()
<< FixItHint::CreateReplacement(
SourceRange(IfLocation, Lexer::getLocForEndOfToken(
ThenLocation, 0, Source, LO)),
createReplacement(CondLhs, CondRhs, AssignLhs, Source, LO,
FunctionName, BinaryOp))
<< IncludeInserter.createIncludeInsertion(
Source.getFileID(If->getBeginLoc()), AlgorithmHeader);
};

if (minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
(*Result.Context))) {
ReplaceAndDiagnose("std::min");
} else if (maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
(*Result.Context))) {
ReplaceAndDiagnose("std::max");
}
}

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

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H

#include "../ClangTidyCheck.h"
#include "../utils/IncludeInserter.h"

namespace clang::tidy::readability {

/// Replaces certain conditional statements with equivalent calls to
/// ``std::min`` or ``std::max``.
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/UseStdMinMax.html
class UseStdMinMaxCheck : public ClangTidyCheck {
public:
UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

private:
utils::IncludeInserter IncludeInserter;
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H
4 changes: 0 additions & 4 deletions clang-tools-extra/clang-tidy/utils/HeaderGuard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,6 @@ class HeaderGuardPPCallbacks : public PPCallbacks {
};
} // namespace

void HeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}

void HeaderGuardCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
Expand Down
27 changes: 3 additions & 24 deletions clang-tools-extra/clang-tidy/utils/HeaderGuard.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,12 @@
namespace clang::tidy::utils {

/// Finds and fixes header guards.
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
class HeaderGuardCheck : public ClangTidyCheck {
public:
HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions = HeaderFileExtensionsOption.value_or(
utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(
RawStringHeaderFileExtensions, HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;

Expand All @@ -65,7 +45,6 @@ class HeaderGuardCheck : public ClangTidyCheck {
StringRef OldGuard = StringRef()) = 0;

private:
std::string RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/utils/IncludeInserter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class IncludeInserterCallback : public PPCallbacks {
bool IsAngled, CharSourceRange FileNameRange,
OptionalFileEntryRef /*IncludedFile*/,
StringRef /*SearchPath*/, StringRef /*RelativePath*/,
const Module * /*ImportedModule*/,
const Module * /*SuggestedModule*/,
bool /*ModuleImported*/,
SrcMgr::CharacteristicKind /*FileType*/) override {
Inserter->addInclude(FileNameRef, IsAngled, HashLocation,
IncludeToken.getEndLoc());
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/Headers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class IncludeStructure::RecordHeaders : public PPCallbacks {
OptionalFileEntryRef File,
llvm::StringRef /*SearchPath*/,
llvm::StringRef /*RelativePath*/,
const clang::Module * /*Imported*/,
const clang::Module * /*SuggestedModule*/,
bool /*ModuleImported*/,
SrcMgr::CharacteristicKind FileKind) override {
auto MainFID = SM.getMainFileID();
// If an include is part of the preamble patch, translate #line directives.
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/ParsedAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class ReplayPreamble : private PPCallbacks {
SynthesizedFilenameTok.getEndLoc())
.toCharRange(SM),
File, "SearchPath", "RelPath",
/*Imported=*/nullptr, Inc.FileKind);
/*SuggestedModule=*/nullptr, /*ModuleImported=*/false, Inc.FileKind);
if (File)
Delegate->FileSkipped(*File, SynthesizedFilenameTok, Inc.FileKind);
}
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ std::optional<HighlightingKind> kindForDecl(const NamedDecl *D,
if (auto *OMD = dyn_cast<ObjCMethodDecl>(D))
return OMD->isClassMethod() ? HighlightingKind::StaticMethod
: HighlightingKind::Method;
if (isa<FieldDecl, ObjCPropertyDecl>(D))
if (isa<FieldDecl, IndirectFieldDecl, ObjCPropertyDecl>(D))
return HighlightingKind::Field;
if (isa<EnumDecl>(D))
return HighlightingKind::Enum;
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/index/IndexAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ struct IncludeGraphCollector : public PPCallbacks {
llvm::StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, llvm::StringRef SearchPath,
llvm::StringRef RelativePath, const Module *Imported,
llvm::StringRef RelativePath,
const Module *SuggestedModule, bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override {
auto IncludeURI = toURI(File);
if (!IncludeURI)
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/unittests/ReplayPeambleTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct ReplayPreamblePPCallback : public PPCallbacks {
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, OptionalFileEntryRef,
StringRef, StringRef, const clang::Module *,
StringRef, StringRef, const clang::Module *, bool,
SrcMgr::CharacteristicKind) override {
Includes.emplace_back(SM, HashLoc, IncludeTok, FileName, IsAngled,
FilenameRange);
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,22 @@ sizeof...($TemplateParameter[[Elements]]);
using $Class[[Y]]$Bracket[[<]]0$Bracket[[>]]::$Unknown_dependentName[[xxx]];
};
};
)cpp",
// Heuristically resolved IndirectFieldDecl
R"cpp(
template $Bracket[[<]]typename $TemplateParameter_def[[T]]$Bracket[[>]]
struct $Class_def[[Base]] {
struct {
int $Field_decl[[waldo]];
};
};
template $Bracket[[<]]typename $TemplateParameter_def[[T]]$Bracket[[>]]
struct $Class_def[[Derived]] : $Class[[Base]]$Bracket[[<]]$TemplateParameter[[T]]$Bracket[[>]] {
using $Class[[Base]]$Bracket[[<]]$TemplateParameter[[T]]$Bracket[[>]]::$Field_dependentName[[waldo]];
void $Method_def[[foo]]() {
$Field_dependentName[[waldo]];
}
};
)cpp"};
for (const auto &TestCase : TestCases)
// Mask off scope modifiers to keep the tests manageable.
Expand Down
52 changes: 52 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,57 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^

- New :doc:`readability-use-std-min-max
<clang-tidy/checks/readability/use-std-min-max>` check.

Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.

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

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

- Improved :doc:`bugprone-suspicious-include
<clang-tidy/checks/bugprone/suspicious-include>` check by replacing the local
options `HeaderFileExtensions` and `ImplementationFileExtensions` by the
global options of the same name.

- Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>`
by removing enforcement of rule `C.48
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c48-prefer-in-class-initializers-to-member-initializers-in-constructors-for-constant-initializers>`_,
which was deprecated since :program:`clang-tidy` 17. This rule is now covered
by :doc:`cppcoreguidelines-use-default-member-init
<clang-tidy/checks/cppcoreguidelines/use-default-member-init>`.

- Improved :doc:`google-build-namespaces
<clang-tidy/checks/google/build-namespaces>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`google-global-names-in-headers
<clang-tidy/checks/google/global-names-in-headers>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`llvm-header-guard
<clang-tidy/checks/llvm/header-guard>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.
Additionally, the option `UseHeaderFileExtensions` is removed, so that the
check uses the `HeaderFileExtensions` option unconditionally.

- Improved :doc:`misc-unused-using-decls
<clang-tidy/checks/misc/unused-using-decls>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`misc-use-anonymous-namespace
<clang-tidy/checks/misc/use-anonymous-namespace>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`modernize-avoid-c-arrays
<clang-tidy/checks/modernize/avoid-c-arrays>` check by introducing the new
`AllowStringArrays` option, enabling the exclusion of array types with deduced
Expand All @@ -114,9 +159,16 @@ Changes in existing checks
Removed checks
^^^^^^^^^^^^^^

Miscellaneous
^^^^^^^^^^^^^

- Removed `cert-dcl21-cpp`, which was deprecated since :program:`clang-tidy` 17,
since the rule DCL21-CPP has been removed from the CERT guidelines.

- Fixed incorrect formatting in ``clang-apply-repalcements`` when no ``--format``
option is specified. Now ``clang-apply-replacements`` applies formatting only with
the option.

Improvements to include-fixer
-----------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,3 @@ Examples:
#include "Pterodactyl.h" // OK, .h files tend not to have definitions.
#include "Velociraptor.cpp" // Warning, filename is suspicious.
#include_next <stdio.c> // Warning, filename is suspicious.

Options
-------
.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

Default value: ``";h;hh;hpp;hxx"``
A semicolon-separated list of filename extensions of header files (the
filename extensions should not contain a "." prefix). For extension-less
header files, use an empty string or leave an empty string between ";"
if there are other filename extensions.

.. option:: ImplementationFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`ImplementationFileExtensions`.

Default value: ``"c;cc;cpp;cxx"``
Likewise, a semicolon-separated list of filename extensions of
implementation files.
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,11 @@ This check implements `C.49
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c49-prefer-initialization-to-assignment-in-constructors>`_
from the C++ Core Guidelines.

If the language version is `C++ 11` or above, the constructor is the default
constructor of the class, the field is not a bitfield (only in case of earlier
language version than `C++ 20`), furthermore the assigned value is a literal,
negated literal or ``enum`` constant then the preferred place of the
initialization is at the class member declaration.

This latter rule is `C.48
Please note, that this check does not enforce rule `C.48
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c48-prefer-in-class-initializers-to-member-initializers-in-constructors-for-constant-initializers>`_
from the C++ Core Guidelines.

Please note, that this check does not enforce this latter rule for
initializations already implemented as member initializers. For that purpose
from the C++ Core Guidelines. For that purpose
see check :doc:`modernize-use-default-member-init <../modernize/use-default-member-init>`.

.. note::

Enforcement of rule C.48 in this check is deprecated, to be removed in
:program:`clang-tidy` version 19 (only C.49 will be enforced by this check then).
Please use :doc:`cppcoreguidelines-use-default-member-init <../cppcoreguidelines/use-default-member-init>`
to enforce rule C.48.

Example 1
---------

Expand All @@ -51,16 +35,16 @@ Example 1
}
};

Here ``n`` can be initialized using a default member initializer, unlike
Here ``n`` can be initialized in the constructor initializer list, unlike
``m``, as ``m``'s initialization follows a control statement (``if``):

.. code-block:: c++

class C {
int n{1};
int n;
int m;
public:
C() {
C(): n(1) {
if (dice())
return;
m = 1;
Expand All @@ -84,7 +68,7 @@ Example 2
}
};

Here ``n`` can be initialized in the constructor initialization list, unlike
Here ``n`` can be initialized in the constructor initializer list, unlike
``m``, as ``m``'s initialization follows a control statement (``if``):

.. code-block:: c++
Expand All @@ -94,29 +78,3 @@ Here ``n`` can be initialized in the constructor initialization list, unlike
return;
m = mm;
}

.. option:: UseAssignment

Note: this option is deprecated, to be removed in :program:`clang-tidy`
version 19. Please use the `UseAssignment` option from
:doc:`cppcoreguidelines-use-default-member-init <../cppcoreguidelines/use-default-member-init>`
instead.

If this option is set to `true` (by default `UseAssignment` from
:doc:`modernize-use-default-member-init
<../modernize/use-default-member-init>` will be used),
the check will initialize members with an assignment.
In this case the fix of the first example looks like this:

.. code-block:: c++

class C {
int n = 1;
int m;
public:
C() {
if (dice())
return;
m = 1;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,3 @@ Finds anonymous namespaces in headers.
https://google.github.io/styleguide/cppguide.html#Namespaces

Corresponding cpplint.py check name: `build/namespaces`.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,3 @@ Flag global namespace pollution in header files. Right now it only triggers on

The relevant style guide section is
https://google.github.io/styleguide/cppguide.html#Namespaces.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not contain "." prefix). Default is "h".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ Clang-Tidy Checks
:doc:`readability-uniqueptr-delete-release <readability/uniqueptr-delete-release>`, "Yes"
:doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes"
:doc:`readability-use-anyofallof <readability/use-anyofallof>`,
:doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
:doc:`zircon-temporary-objects <zircon/temporary-objects>`,


Expand Down
15 changes: 0 additions & 15 deletions clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,3 @@ llvm-header-guard
=================

Finds and fixes header guards that do not adhere to LLVM style.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).
Original file line number Diff line number Diff line change
Expand Up @@ -92,27 +92,3 @@ When :program:`clang-tidy` is invoked with the `--fix-notes` option, this check
provides fixes that automatically add the ``inline`` keyword to discovered
functions. Please note that the addition of the ``inline`` keyword to variables
is not currently supported by this check.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).

.. option:: UseHeaderFileExtension

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. The check will unconditionally use the global option
`HeaderFileExtensions`.

When `true`, the check will use the file extension to distinguish header
files. Default is `true`.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Finds unused ``using`` declarations.

Unused ``using``` declarations in header files will not be diagnosed since these
using declarations are part of the header's public API. Allowed header file
extensions can be configured via the `HeaderFileExtensions` option (see below).
extensions can be configured via the global option `HeaderFileExtensions`.

Example:

Expand All @@ -16,17 +16,3 @@ Example:
// main.cpp
namespace n { class C; }
using n::C; // Never actually used.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A semicolon-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For extension-less header files, use an empty string or leave an
empty string between "," if there are other filename extensions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The following uses of ``static`` will *not* be diagnosed:

* Functions or variables in header files, since anonymous namespaces in headers
is considered an antipattern. Allowed header file extensions can be configured
via the `HeaderFileExtensions` option (see below).
via the global option `HeaderFileExtensions`.
* ``const`` or ``constexpr`` variables, since they already have implicit internal
linkage in C++.

Expand All @@ -33,18 +33,4 @@ Examples:
int x;
} // namespace

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A semicolon-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is ";h;hh;hpp;hxx".
For extension-less header files, using an empty string or leaving an
empty string between ";" if there are other filename extensions.

[1] `Undeprecating static <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1012>`_
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.. title:: clang-tidy - readability-use-std-min-max

readability-use-std-min-max
===========================

Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
Note: This may impact performance in critical code due to potential
additional stores compared to the original if statement.

Before:

.. code-block:: c++

void foo() {
int a = 2, b = 3;
if (a < b)
a = b;
}


After:

.. code-block:: c++

void foo() {
int a = 2, b = 3;
a = std::max(a, b);
}
6 changes: 4 additions & 2 deletions clang-tools-extra/include-cleaner/lib/Record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class PPRecorder : public PPCallbacks {
StringRef SpelledFilename, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind) override {
if (!Active)
return;
Expand Down Expand Up @@ -214,7 +215,8 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
OptionalFileEntryRef File,
llvm::StringRef /*SearchPath*/,
llvm::StringRef /*RelativePath*/,
const clang::Module * /*Imported*/,
const clang::Module * /*SuggestedModule*/,
bool /*ModuleImported*/,
SrcMgr::CharacteristicKind FileKind) override {
FileID HashFID = SM.getFileID(HashLoc);
int HashLine = SM.getLineNumber(HashFID, SM.getFileOffset(HashLoc));
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/modularize/CoverageChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class CoverageCheckerCallbacks : public PPCallbacks {
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath, const Module *Imported,
StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override {
Checker.collectUmbrellaHeaderHeader(File->getName());
}
Expand Down
20 changes: 10 additions & 10 deletions clang-tools-extra/modularize/PreprocessorTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,15 +730,14 @@ class PreprocessorCallbacks : public clang::PPCallbacks {
~PreprocessorCallbacks() override {}

// Overridden handlers.
void InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token &IncludeTok,
llvm::StringRef FileName, bool IsAngled,
clang::CharSourceRange FilenameRange,
clang::OptionalFileEntryRef File,
llvm::StringRef SearchPath,
llvm::StringRef RelativePath,
const clang::Module *Imported,
clang::SrcMgr::CharacteristicKind FileType) override;
void
InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token &IncludeTok, llvm::StringRef FileName,
bool IsAngled, clang::CharSourceRange FilenameRange,
clang::OptionalFileEntryRef File,
llvm::StringRef SearchPath, llvm::StringRef RelativePath,
const clang::Module *SuggestedModule, bool ModuleImported,
clang::SrcMgr::CharacteristicKind FileType) override;
void FileChanged(clang::SourceLocation Loc,
clang::PPCallbacks::FileChangeReason Reason,
clang::SrcMgr::CharacteristicKind FileType,
Expand Down Expand Up @@ -1275,7 +1274,8 @@ void PreprocessorCallbacks::InclusionDirective(
llvm::StringRef FileName, bool IsAngled,
clang::CharSourceRange FilenameRange, clang::OptionalFileEntryRef File,
llvm::StringRef SearchPath, llvm::StringRef RelativePath,
const clang::Module *Imported, clang::SrcMgr::CharacteristicKind FileType) {
const clang::Module *SuggestedModule, bool ModuleImported,
clang::SrcMgr::CharacteristicKind FileType) {
int DirectiveLine, DirectiveColumn;
std::string HeaderPath = getSourceLocationFile(PP, HashLoc);
getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);
Expand Down
6 changes: 4 additions & 2 deletions clang-tools-extra/pp-trace/PPCallbacksTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ void PPCallbacksTracker::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, llvm::StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
llvm::StringRef SearchPath, llvm::StringRef RelativePath,
const Module *Imported, SrcMgr::CharacteristicKind FileType) {
const Module *SuggestedModule, bool ModuleImported,
SrcMgr::CharacteristicKind FileType) {
beginCallback("InclusionDirective");
appendArgument("HashLoc", HashLoc);
appendArgument("IncludeTok", IncludeTok);
Expand All @@ -145,7 +146,8 @@ void PPCallbacksTracker::InclusionDirective(
appendArgument("File", File);
appendFilePathArgument("SearchPath", SearchPath);
appendFilePathArgument("RelativePath", RelativePath);
appendArgument("Imported", Imported);
appendArgument("SuggestedModule", SuggestedModule);
appendArgument("ModuleImported", ModuleImported);
}

// Callback invoked whenever there was an explicit module-import
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/pp-trace/PPCallbacksTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ class PPCallbacksTracker : public PPCallbacks {
llvm::StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
OptionalFileEntryRef File, llvm::StringRef SearchPath,
llvm::StringRef RelativePath, const Module *Imported,
llvm::StringRef RelativePath,
const Module *SuggestedModule, bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override;
void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
const Module *Imported) override;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <string>
// CHECK: #include <string>
// CHECK-NEXT: #include <memory>
// CHECK-NEXT: #include "bar.h"
#include <memory>
#include "foo.h"
#include "bar.h"

void foo() {
}
Loading