19 changes: 11 additions & 8 deletions bolt/lib/Passes/PLTCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ Error PLTCall::runOnFunctions(BinaryContext &BC) {
return Error::success();

uint64_t NumCallsOptimized = 0;
for (auto &It : BC.getBinaryFunctions()) {
BinaryFunction &Function = It.second;
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &Function = BFI.second;
if (!shouldOptimize(Function))
continue;

Expand All @@ -61,18 +61,21 @@ Error PLTCall::runOnFunctions(BinaryContext &BC) {
if (opts::PLT == OT_HOT && !BB.getKnownExecutionCount())
continue;

for (MCInst &Instr : BB) {
if (!BC.MIB->isCall(Instr))
for (auto II = BB.begin(); II != BB.end(); II++) {
if (!BC.MIB->isCall(*II))
continue;
const MCSymbol *CallSymbol = BC.MIB->getTargetSymbol(Instr);
const MCSymbol *CallSymbol = BC.MIB->getTargetSymbol(*II);
if (!CallSymbol)
continue;
const BinaryFunction *CalleeBF = BC.getFunctionForSymbol(CallSymbol);
if (!CalleeBF || !CalleeBF->isPLTFunction())
continue;
BC.MIB->convertCallToIndirectCall(Instr, CalleeBF->getPLTSymbol(),
BC.Ctx.get());
BC.MIB->addAnnotation(Instr, "PLTCall", true);
const InstructionListType NewCode = BC.MIB->createIndirectPltCall(
*II, CalleeBF->getPLTSymbol(), BC.Ctx.get());
II = BB.replaceInstruction(II, NewCode);
assert(!NewCode.empty() && "PLT Call replacement must be non-empty");
std::advance(II, NewCode.size() - 1);
BC.MIB->addAnnotation(*II, "PLTCall", true);
++NumCallsOptimized;
}
}
Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ std::error_code BoltAddressTranslation::parse(raw_ostream &OS, StringRef Buf) {

StringRef Name = Buf.slice(Offset, Offset + NameSz);
Offset = alignTo(Offset + NameSz, 4);
if (Name.substr(0, 4) != "BOLT")
if (!Name.starts_with("BOLT"))
return make_error_code(llvm::errc::io_error);

Error Err(Error::success());
Expand Down
234 changes: 34 additions & 200 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ namespace bolt {
/// Emits debug information into .debug_info or .debug_types section.
class DIEStreamer : public DwarfStreamer {
DIEBuilder *DIEBldr;
DWARFRewriter &Rewriter;
GDBIndex &GDBIndexSection;

private:
/// Emit the compilation unit header for \p Unit in the debug_info
Expand Down Expand Up @@ -247,7 +247,7 @@ class DIEStreamer : public DwarfStreamer {
const uint64_t TypeSignature = cast<DWARFTypeUnit>(Unit).getTypeHash();
DIE *TypeDIE = DIEBldr->getTypeDIE(Unit);
const DIEBuilder::DWARFUnitInfo &UI = DIEBldr->getUnitInfoByDwarfUnit(Unit);
Rewriter.addGDBTypeUnitEntry(
GDBIndexSection.addGDBTypeUnitEntry(
{UI.UnitOffset, TypeSignature, TypeDIE->getOffset()});
if (Unit.getVersion() < 5) {
// Switch the section to .debug_types section.
Expand Down Expand Up @@ -278,12 +278,12 @@ class DIEStreamer : public DwarfStreamer {
}

public:
DIEStreamer(DIEBuilder *DIEBldr, DWARFRewriter &Rewriter,
DIEStreamer(DIEBuilder *DIEBldr, GDBIndex &GDBIndexSection,
DWARFLinkerBase::OutputFileType OutFileType,
raw_pwrite_stream &OutFile,
DWARFLinkerBase::MessageHandlerTy Warning)
: DwarfStreamer(OutFileType, OutFile, Warning), DIEBldr(DIEBldr),
Rewriter(Rewriter){};
GDBIndexSection(GDBIndexSection) {};

using DwarfStreamer::emitCompileUnitHeader;

Expand Down Expand Up @@ -326,12 +326,11 @@ static cl::opt<bool> KeepARanges(
"keep or generate .debug_aranges section if .gdb_index is written"),
cl::Hidden, cl::cat(BoltCategory));

static cl::opt<bool>
DeterministicDebugInfo("deterministic-debuginfo",
cl::desc("disables parallel execution of tasks that may produce "
"nondeterministic debug info"),
cl::init(true),
cl::cat(BoltCategory));
static cl::opt<bool> DeterministicDebugInfo(
"deterministic-debuginfo",
cl::desc("disables parallel execution of tasks that may produce "
"nondeterministic debug info"),
cl::init(true), cl::cat(BoltCategory));

static cl::opt<std::string> DwarfOutputPath(
"dwarf-output-path",
Expand Down Expand Up @@ -460,10 +459,11 @@ static std::optional<uint64_t> getAsAddress(const DWARFUnit &DU,
static std::unique_ptr<DIEStreamer>
createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr,
DWARFRewriter &Rewriter) {
GDBIndex &GDBIndexSection) {

std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
&DIEBldr, Rewriter, DWARFLinkerBase::OutputFileType::Object, OutFile,
&DIEBldr, GDBIndexSection, DWARFLinkerBase::OutputFileType::Object,
OutFile,
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {});
Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName);
if (Err)
Expand All @@ -484,13 +484,12 @@ emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer, DWARFUnit &Unit) {
return {U.UnitOffset, U.UnitLength, TypeHash};
}

static void emitDWOBuilder(const std::string &DWOName,
DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter,
DWARFUnit &SplitCU, DWARFUnit &CU,
DWARFRewriter::DWPState &State,
DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter) {
static void
emitDWOBuilder(const std::string &DWOName, DIEBuilder &DWODIEBuilder,
DWARFRewriter &Rewriter, DWARFUnit &SplitCU, DWARFUnit &CU,
DWARFRewriter::DWPState &State, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, GDBIndex &GDBIndexSection) {
// Populate debug_info and debug_abbrev for current dwo into StringRef.
DWODIEBuilder.generateAbbrevs();
DWODIEBuilder.finish();
Expand All @@ -500,8 +499,9 @@ static void emitDWOBuilder(const std::string &DWOName,
std::make_shared<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = SplitCU.getContext().getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> Streamer = createDIEStreamer(
*TheTriple, *ObjOS, "DwoStreamerInitAug2", DWODIEBuilder, Rewriter);
std::unique_ptr<DIEStreamer> Streamer =
createDIEStreamer(*TheTriple, *ObjOS, "DwoStreamerInitAug2",
DWODIEBuilder, GDBIndexSection);
DWARFRewriter::UnitMetaVectorType TUMetaVector;
DWARFRewriter::UnitMeta CUMI = {0, 0, 0};
if (SplitCU.getContext().getMaxDWOVersion() >= 5) {
Expand Down Expand Up @@ -652,6 +652,7 @@ void DWARFRewriter::updateDebugInfo() {

DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
*StrWriter);
GDBIndex GDBIndexSection(BC);
DWPState State;
if (opts::WriteDWP)
initDWPState(State);
Expand Down Expand Up @@ -704,7 +705,8 @@ void DWARFRewriter::updateDebugInfo() {
TempRangesSectionWriter->finalizeSection();

emitDWOBuilder(DWOName, DWODIEBuilder, *this, **SplitCU, *Unit, State,
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter);
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter,
GDBIndexSection);
}

if (Unit->getVersion() >= 5) {
Expand All @@ -729,9 +731,10 @@ void DWARFRewriter::updateDebugInfo() {
std::make_unique<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> Streamer =
createDIEStreamer(*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, *this);
CUOffsetMap OffsetMap = finalizeTypeSections(DIEBlder, *Streamer);
std::unique_ptr<DIEStreamer> Streamer = createDIEStreamer(
*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);
CUOffsetMap OffsetMap =
finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);

const bool SingleThreadedMode =
opts::NoThreads || opts::DeterministicDebugInfo;
Expand Down Expand Up @@ -761,7 +764,8 @@ void DWARFRewriter::updateDebugInfo() {

finalizeDebugSections(DIEBlder, DebugNamesTable, *Streamer, *ObjOS,
OffsetMap);
updateGdbIndexSection(OffsetMap, CUIndex);
GDBIndexSection.updateGdbIndexSection(OffsetMap, CUIndex,
*ARangesSectionWriter);
}

void DWARFRewriter::updateUnitDebugInfo(
Expand Down Expand Up @@ -1429,7 +1433,8 @@ void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) {
}

CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
DIEStreamer &Streamer) {
DIEStreamer &Streamer,
GDBIndex &GDBIndexSection) {
// update TypeUnit DW_AT_stmt_list with new .debug_line information.
auto updateLineTable = [&](const DWARFUnit &Unit) -> void {
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(Unit);
Expand All @@ -1449,8 +1454,8 @@ CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
std::make_shared<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> TypeStreamer =
createDIEStreamer(*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, *this);
std::unique_ptr<DIEStreamer> TypeStreamer = createDIEStreamer(
*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);

// generate debug_info and CUMap
CUOffsetMap CUMap;
Expand Down Expand Up @@ -2055,177 +2060,6 @@ void DWARFRewriter::writeDWOFiles(
TempOut->keep();
}

void DWARFRewriter::addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry) {
std::lock_guard<std::mutex> Lock(DWARFRewriterMutex);
if (!BC.getGdbIndexSection())
return;
GDBIndexTUEntryVector.emplace_back(Entry);
}

void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap, uint32_t NumCUs) {
if (!BC.getGdbIndexSection())
return;

// See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
// for .gdb_index section format.

StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents();

const char *Data = GdbIndexContents.data();

// Parse the header.
const uint32_t Version = read32le(Data);
if (Version != 7 && Version != 8) {
errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n";
exit(1);
}

// Some .gdb_index generators use file offsets while others use section
// offsets. Hence we can only rely on offsets relative to each other,
// and ignore their absolute values.
const uint32_t CUListOffset = read32le(Data + 4);
const uint32_t CUTypesOffset = read32le(Data + 8);
const uint32_t AddressTableOffset = read32le(Data + 12);
const uint32_t SymbolTableOffset = read32le(Data + 16);
const uint32_t ConstantPoolOffset = read32le(Data + 20);
Data += 24;

// Map CUs offsets to indices and verify existing index table.
std::map<uint32_t, uint32_t> OffsetToIndexMap;
const uint32_t CUListSize = CUTypesOffset - CUListOffset;
const uint32_t TUListSize = AddressTableOffset - CUTypesOffset;
const unsigned NUmCUsEncoded = CUListSize / 16;
unsigned MaxDWARFVersion = BC.DwCtx->getMaxVersion();
unsigned NumDWARF5TUs =
getGDBIndexTUEntryVector().size() - BC.DwCtx->getNumTypeUnits();
bool SkipTypeUnits = false;
// For DWARF5 Types are in .debug_info.
// LLD doesn't generate Types CU List, and in CU list offset
// only includes CUs.
// GDB 11+ includes only CUs in CU list and generates Types
// list.
// GDB 9 includes CUs and TUs in CU list and generates TYpes
// list. The NumCUs is CUs + TUs, so need to modify the check.
// For split-dwarf
// GDB-11, DWARF5: TU units from dwo are not included.
// GDB-11, DWARF4: TU units from dwo are included.
if (MaxDWARFVersion >= 5)
SkipTypeUnits = !TUListSize ? true
: ((NUmCUsEncoded + NumDWARF5TUs) ==
BC.DwCtx->getNumCompileUnits());

if (!((CUListSize == NumCUs * 16) ||
(CUListSize == (NumCUs + NumDWARF5TUs) * 16))) {
errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n";
exit(1);
}
DenseSet<uint64_t> OriginalOffsets;
for (unsigned Index = 0, Units = BC.DwCtx->getNumCompileUnits();
Index < Units; ++Index) {
const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index);
if (SkipTypeUnits && CU->isTypeUnit())
continue;
const uint64_t Offset = read64le(Data);
Data += 16;
if (CU->getOffset() != Offset) {
errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n";
exit(1);
}

OriginalOffsets.insert(Offset);
OffsetToIndexMap[Offset] = Index;
}

// Ignore old address table.
const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset;
// Move Data to the beginning of symbol table.
Data += SymbolTableOffset - CUTypesOffset;

// Calculate the size of the new address table.
uint32_t NewAddressTableSize = 0;
for (const auto &CURangesPair : ARangesSectionWriter->getCUAddressRanges()) {
const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second;
NewAddressTableSize += Ranges.size() * 20;
}

// Difference between old and new table (and section) sizes.
// Could be negative.
int32_t Delta = NewAddressTableSize - OldAddressTableSize;

size_t NewGdbIndexSize = GdbIndexContents.size() + Delta;

// Free'd by ExecutableFileMemoryManager.
auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize];
uint8_t *Buffer = NewGdbIndexContents;

write32le(Buffer, Version);
write32le(Buffer + 4, CUListOffset);
write32le(Buffer + 8, CUTypesOffset);
write32le(Buffer + 12, AddressTableOffset);
write32le(Buffer + 16, SymbolTableOffset + Delta);
write32le(Buffer + 20, ConstantPoolOffset + Delta);
Buffer += 24;

using MapEntry = std::pair<uint32_t, CUInfo>;
std::vector<MapEntry> CUVector(CUMap.begin(), CUMap.end());
// Need to sort since we write out all of TUs in .debug_info before CUs.
std::sort(CUVector.begin(), CUVector.end(),
[](const MapEntry &E1, const MapEntry &E2) -> bool {
return E1.second.Offset < E2.second.Offset;
});
// Writing out CU List <Offset, Size>
for (auto &CUInfo : CUVector) {
// Skipping TU for DWARF5 when they are not included in CU list.
if (!OriginalOffsets.count(CUInfo.first))
continue;
write64le(Buffer, CUInfo.second.Offset);
// Length encoded in CU doesn't contain first 4 bytes that encode length.
write64le(Buffer + 8, CUInfo.second.Length + 4);
Buffer += 16;
}

// Rewrite TU CU List, since abbrevs can be different.
// Entry example:
// 0: offset = 0x00000000, type_offset = 0x0000001e, type_signature =
// 0x418503b8111e9a7b Spec says " triplet, the first value is the CU offset,
// the second value is the type offset in the CU, and the third value is the
// type signature" Looking at what is being generated by gdb-add-index. The
// first entry is TU offset, second entry is offset from it, and third entry
// is the type signature.
if (TUListSize)
for (const GDBIndexTUEntry &Entry : getGDBIndexTUEntryVector()) {
write64le(Buffer, Entry.UnitOffset);
write64le(Buffer + 8, Entry.TypeDIERelativeOffset);
write64le(Buffer + 16, Entry.TypeHash);
Buffer += sizeof(GDBIndexTUEntry);
}

// Generate new address table.
for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair :
ARangesSectionWriter->getCUAddressRanges()) {
const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first];
const DebugAddressRangesVector &Ranges = CURangesPair.second;
for (const DebugAddressRange &Range : Ranges) {
write64le(Buffer, Range.LowPC);
write64le(Buffer + 8, Range.HighPC);
write32le(Buffer + 16, CUIndex);
Buffer += 20;
}
}

const size_t TrailingSize =
GdbIndexContents.data() + GdbIndexContents.size() - Data;
assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize &&
"size calculation error");

// Copy over the rest of the original data.
memcpy(Buffer, Data, TrailingSize);

// Register the new section.
BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents,
NewGdbIndexSize);
}

std::unique_ptr<DebugBufferVector>
DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version) {
auto LocBuffer = std::make_unique<DebugBufferVector>();
Expand Down
77 changes: 70 additions & 7 deletions bolt/lib/Rewrite/LinuxKernelRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {

/// Handle alternative instruction info from .altinstructions.
Error readAltInstructions();
Error tryReadAltInstructions(uint32_t AltInstFeatureSize,
bool AltInstHasPadLen, bool ParseOnly);
Error rewriteAltInstructions();

/// Read .pci_fixup
Expand Down Expand Up @@ -1319,12 +1321,69 @@ Error LinuxKernelRewriter::rewriteBugTable() {
/// u8 padlen; // present in older kernels
/// } __packed;
///
/// Note the structures is packed.
/// Note that the structure is packed.
///
/// Since the size of the "feature" field could be either u16 or u32, and
/// "padlen" presence is unknown, we attempt to parse .altinstructions section
/// using all possible combinations (four at this time). Since we validate the
/// contents of the section and its size, the detection works quite well.
/// Still, we leave the user the opportunity to specify these features on the
/// command line and skip the guesswork.
Error LinuxKernelRewriter::readAltInstructions() {
AltInstrSection = BC.getUniqueSectionByName(".altinstructions");
if (!AltInstrSection)
return Error::success();

// Presence of "padlen" field.
std::vector<bool> PadLenVariants;
if (opts::AltInstHasPadLen.getNumOccurrences())
PadLenVariants.push_back(opts::AltInstHasPadLen);
else
PadLenVariants = {false, true};

// Size (in bytes) variants of "feature" field.
std::vector<uint32_t> FeatureSizeVariants;
if (opts::AltInstFeatureSize.getNumOccurrences())
FeatureSizeVariants.push_back(opts::AltInstFeatureSize);
else
FeatureSizeVariants = {2, 4};

for (bool AltInstHasPadLen : PadLenVariants) {
for (uint32_t AltInstFeatureSize : FeatureSizeVariants) {
LLVM_DEBUG({
dbgs() << "BOLT-DEBUG: trying AltInstHasPadLen = " << AltInstHasPadLen
<< "; AltInstFeatureSize = " << AltInstFeatureSize << ";\n";
});
if (Error E = tryReadAltInstructions(AltInstFeatureSize, AltInstHasPadLen,
/*ParseOnly*/ true)) {
consumeError(std::move(E));
continue;
}

LLVM_DEBUG(dbgs() << "Matched .altinstructions format\n");

if (!opts::AltInstHasPadLen.getNumOccurrences())
BC.outs() << "BOLT-INFO: setting --" << opts::AltInstHasPadLen.ArgStr
<< '=' << AltInstHasPadLen << '\n';

if (!opts::AltInstFeatureSize.getNumOccurrences())
BC.outs() << "BOLT-INFO: setting --" << opts::AltInstFeatureSize.ArgStr
<< '=' << AltInstFeatureSize << '\n';

return tryReadAltInstructions(AltInstFeatureSize, AltInstHasPadLen,
/*ParseOnly*/ false);
}
}

// We couldn't match the format. Read again to properly propagate the error
// to the user.
return tryReadAltInstructions(opts::AltInstFeatureSize,
opts::AltInstHasPadLen, /*ParseOnly*/ false);
}

Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize,
bool AltInstHasPadLen,
bool ParseOnly) {
const uint64_t Address = AltInstrSection->getAddress();
DataExtractor DE = DataExtractor(AltInstrSection->getContents(),
BC.AsmInfo->isLittleEndian(),
Expand All @@ -1336,12 +1395,12 @@ Error LinuxKernelRewriter::readAltInstructions() {
Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
const uint64_t AltInstAddress =
Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
const uint64_t Feature = DE.getUnsigned(Cursor, opts::AltInstFeatureSize);
const uint64_t Feature = DE.getUnsigned(Cursor, AltInstFeatureSize);
const uint8_t OrgSize = DE.getU8(Cursor);
const uint8_t AltSize = DE.getU8(Cursor);

// Older kernels may have the padlen field.
const uint8_t PadLen = opts::AltInstHasPadLen ? DE.getU8(Cursor) : 0;
const uint8_t PadLen = AltInstHasPadLen ? DE.getU8(Cursor) : 0;

if (!Cursor)
return createStringError(
Expand All @@ -1358,7 +1417,7 @@ Error LinuxKernelRewriter::readAltInstructions() {
<< "\n\tFeature: 0x" << Twine::utohexstr(Feature)
<< "\n\tOrgSize: " << (int)OrgSize
<< "\n\tAltSize: " << (int)AltSize << '\n';
if (opts::AltInstHasPadLen)
if (AltInstHasPadLen)
BC.outs() << "\tPadLen: " << (int)PadLen << '\n';
}

Expand All @@ -1375,7 +1434,7 @@ Error LinuxKernelRewriter::readAltInstructions() {

BinaryFunction *AltBF =
BC.getBinaryFunctionContainingAddress(AltInstAddress);
if (AltBF && BC.shouldEmit(*AltBF)) {
if (!ParseOnly && AltBF && BC.shouldEmit(*AltBF)) {
BC.errs()
<< "BOLT-WARNING: alternative instruction sequence found in function "
<< *AltBF << '\n';
Expand All @@ -1397,6 +1456,9 @@ Error LinuxKernelRewriter::readAltInstructions() {
" referenced by .altinstructions entry %d",
OrgInstAddress, EntryID);

if (ParseOnly)
continue;

// There could be more than one alternative instruction sequences for the
// same original instruction. Annotate each alternative separately.
std::string AnnotationName = "AltInst";
Expand All @@ -1417,8 +1479,9 @@ Error LinuxKernelRewriter::readAltInstructions() {
}
}

BC.outs() << "BOLT-INFO: parsed " << EntryID
<< " alternative instruction entries\n";
if (!ParseOnly)
BC.outs() << "BOLT-INFO: parsed " << EntryID
<< " alternative instruction entries\n";

return Error::success();
}
Expand Down
41 changes: 41 additions & 0 deletions bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,47 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
return true;
}

InstructionListType createIndirectPltCall(const MCInst &DirectCall,
const MCSymbol *TargetLocation,
MCContext *Ctx) override {
const bool IsTailCall = isTailCall(DirectCall);
assert((DirectCall.getOpcode() == AArch64::BL ||
(DirectCall.getOpcode() == AArch64::B && IsTailCall)) &&
"64-bit direct (tail) call instruction expected");

InstructionListType Code;
// Code sequence for indirect plt call:
// adrp x16 <symbol>
// ldr x17, [x16, #<offset>]
// blr x17 ; or 'br' for tail calls

MCInst InstAdrp;
InstAdrp.setOpcode(AArch64::ADRP);
InstAdrp.addOperand(MCOperand::createReg(AArch64::X16));
InstAdrp.addOperand(MCOperand::createImm(0));
setOperandToSymbolRef(InstAdrp, /* OpNum */ 1, TargetLocation,
/* Addend */ 0, Ctx, ELF::R_AARCH64_ADR_GOT_PAGE);
Code.emplace_back(InstAdrp);

MCInst InstLoad;
InstLoad.setOpcode(AArch64::LDRXui);
InstLoad.addOperand(MCOperand::createReg(AArch64::X17));
InstLoad.addOperand(MCOperand::createReg(AArch64::X16));
InstLoad.addOperand(MCOperand::createImm(0));
setOperandToSymbolRef(InstLoad, /* OpNum */ 2, TargetLocation,
/* Addend */ 0, Ctx, ELF::R_AARCH64_LD64_GOT_LO12_NC);
Code.emplace_back(InstLoad);

MCInst InstCall;
InstCall.setOpcode(IsTailCall ? AArch64::BR : AArch64::BLR);
InstCall.addOperand(MCOperand::createReg(AArch64::X17));
if (IsTailCall)
setTailCall(InstCall);
Code.emplace_back(InstCall);

return Code;
}

bool lowerTailCall(MCInst &Inst) override {
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
if (getConditionalTailCall(Inst))
Expand Down
16 changes: 11 additions & 5 deletions bolt/lib/Target/X86/X86MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1639,11 +1639,16 @@ class X86MCPlusBuilder : public MCPlusBuilder {
return true;
}

bool convertCallToIndirectCall(MCInst &Inst, const MCSymbol *TargetLocation,
MCContext *Ctx) override {
assert((Inst.getOpcode() == X86::CALL64pcrel32 ||
(Inst.getOpcode() == X86::JMP_4 && isTailCall(Inst))) &&
InstructionListType createIndirectPltCall(const MCInst &DirectCall,
const MCSymbol *TargetLocation,
MCContext *Ctx) override {
assert((DirectCall.getOpcode() == X86::CALL64pcrel32 ||
(DirectCall.getOpcode() == X86::JMP_4 && isTailCall(DirectCall))) &&
"64-bit direct (tail) call instruction expected");

InstructionListType Code;
// Create a new indirect call by converting the previous direct call.
MCInst Inst = DirectCall;
const auto NewOpcode =
(Inst.getOpcode() == X86::CALL64pcrel32) ? X86::CALL64m : X86::JMP32m;
Inst.setOpcode(NewOpcode);
Expand All @@ -1664,7 +1669,8 @@ class X86MCPlusBuilder : public MCPlusBuilder {
Inst.insert(Inst.begin(),
MCOperand::createReg(X86::RIP)); // BaseReg

return true;
Code.emplace_back(Inst);
return Code;
}

void convertIndirectCallToLoad(MCInst &Inst, MCPhysReg Reg) override {
Expand Down
15 changes: 15 additions & 0 deletions bolt/test/AArch64/plt-call.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Verify that PLTCall optimization works.

RUN: %clang %cflags %p/../Inputs/plt-tailcall.c \
RUN: -o %t -Wl,-q
RUN: llvm-bolt %t -o %t.bolt --plt=all --print-plt --print-only=foo | FileCheck %s

// Call to printf
CHECK: adrp x16, printf@GOT
CHECK: ldr x17, [x16, :lo12:printf@GOT]
CHECK: blr x17 # PLTCall: 1

// Call to puts, that was tail-call optimized
CHECK: adrp x16, puts@GOT
CHECK: ldr x17, [x16, :lo12:puts@GOT]
CHECK: br x17 # TAILCALL # PLTCall: 1
8 changes: 8 additions & 0 deletions bolt/test/Inputs/plt-tailcall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "stub.h"

int foo(char *c) {
printf("");
__attribute__((musttail)) return puts(c);
}

int main() { return foo("a"); }
20 changes: 13 additions & 7 deletions bolt/test/X86/linux-alt-instruction.s
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,30 @@
## Older kernels used to have padlen field in alt_instr. Check compatibility.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --defsym PADLEN=1 \
# RUN: %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: %s -o %t.padlen.o
# RUN: %clang %cflags -nostdlib %t.padlen.o -o %t.padlen.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --alt-inst-has-padlen -o %t.out \
# RUN: llvm-bolt %t.padlen.exe --print-normalized --alt-inst-has-padlen -o %t.padlen.out \
# RUN: | FileCheck %s

## Check with a larger size of "feature" field in alt_instr.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \
# RUN: --defsym FEATURE_SIZE_4=1 %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: --defsym FEATURE_SIZE_4=1 %s -o %t.fs4.o
# RUN: %clang %cflags -nostdlib %t.fs4.o -o %t.fs4.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --alt-inst-feature-size=4 -o %t.out \
# RUN: llvm-bolt %t.fs4.exe --print-normalized --alt-inst-feature-size=4 -o %t.fs4.out \
# RUN: | FileCheck %s

## Check that out-of-bounds read is handled properly.

# RUN: not llvm-bolt %t.exe --print-normalized --alt-inst-feature-size=2 -o %t.out
# RUN: not llvm-bolt %t.fs4.exe --alt-inst-feature-size=2 -o %t.fs4.out

## Check that BOLT automatically detects structure fields in .altinstructions.

# RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s
# RUN: llvm-bolt %t.exe --print-normalized -o %t.padlen.out | FileCheck %s
# RUN: llvm-bolt %t.exe --print-normalized -o %t.fs4.out | FileCheck %s

# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 alternative instruction entries
Expand Down
11 changes: 11 additions & 0 deletions bolt/test/X86/plt-call.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Verify that PLTCall optimization works.

RUN: %clang %cflags %p/../Inputs/plt-tailcall.c \
RUN: -o %t -Wl,-q
RUN: llvm-bolt %t -o %t.bolt --plt=all --print-plt --print-only=foo | FileCheck %s

// Call to printf
CHECK: callq *printf@GOT(%rip) # PLTCall: 1

// Call to puts, that was tail-call optimized
CHECK: jmpl *puts@GOT(%rip) # TAILCALL # PLTCall: 1
6 changes: 2 additions & 4 deletions clang-tools-extra/clang-query/QueryParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,11 @@ QueryRef QueryParser::endQuery(QueryRef Q) {
StringRef Extra = Line;
StringRef ExtraTrimmed = Extra.ltrim(" \t\v\f\r");

if ((!ExtraTrimmed.empty() && ExtraTrimmed[0] == '\n') ||
(ExtraTrimmed.size() >= 2 && ExtraTrimmed[0] == '\r' &&
ExtraTrimmed[1] == '\n'))
if (ExtraTrimmed.starts_with('\n') || ExtraTrimmed.starts_with("\r\n"))
Q->RemainingContent = Extra;
else {
StringRef TrailingWord = lexWord();
if (!TrailingWord.empty() && TrailingWord.front() == '#') {
if (TrailingWord.starts_with('#')) {
Line = Line.drop_until([](char c) { return c == '\n'; });
Line = Line.drop_while([](char c) { return c == '\n'; });
return endQuery(Q);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,21 @@ AST_MATCHER(ImplicitCastExpr, isMultiLevelPointerConversion) {
return SourcePtrLevel != TargetPtrLevel;
}

AST_MATCHER(QualType, isPointerType) {
const QualType Type =
Node.getCanonicalType().getNonReferenceType().getUnqualifiedType();

return !Type.isNull() && Type->isPointerType();
}

} // namespace

void MultiLevelImplicitPointerConversionCheck::registerMatchers(
MatchFinder *Finder) {
Finder->addMatcher(
implicitCastExpr(hasCastKind(CK_BitCast), isMultiLevelPointerConversion())
implicitCastExpr(hasCastKind(CK_BitCast), isMultiLevelPointerConversion(),
unless(hasParent(explicitCastExpr(
hasDestinationType(isPointerType())))))
.bind("expr"),
this);
}
Expand Down
115 changes: 70 additions & 45 deletions clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
WarnOnSizeOfCompareToConstant(
Options.get("WarnOnSizeOfCompareToConstant", true)),
WarnOnSizeOfPointerToAggregate(
Options.get("WarnOnSizeOfPointerToAggregate", true)) {}
Options.get("WarnOnSizeOfPointerToAggregate", true)),
WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)) {}

void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
Expand All @@ -78,6 +79,7 @@ void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
WarnOnSizeOfCompareToConstant);
Options.store(Opts, "WarnOnSizeOfPointerToAggregate",
WarnOnSizeOfPointerToAggregate);
Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer);
}

void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
Expand Down Expand Up @@ -127,17 +129,30 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
const auto ConstStrLiteralDecl =
varDecl(isDefinition(), hasType(hasCanonicalType(CharPtrType)),
hasInitializer(ignoringParenImpCasts(stringLiteral())));
const auto VarWithConstStrLiteralDecl = expr(
hasType(hasCanonicalType(CharPtrType)),
ignoringParenImpCasts(declRefExpr(hasDeclaration(ConstStrLiteralDecl))));
Finder->addMatcher(
sizeOfExpr(has(ignoringParenImpCasts(
expr(hasType(hasCanonicalType(CharPtrType)),
ignoringParenImpCasts(declRefExpr(
hasDeclaration(ConstStrLiteralDecl)))))))
sizeOfExpr(has(ignoringParenImpCasts(VarWithConstStrLiteralDecl)))
.bind("sizeof-charp"),
this);

// Detect sizeof(ptr) where ptr points to an aggregate (i.e. sizeof(&S)).
// Do not find it if RHS of a 'sizeof(arr) / sizeof(arr[0])' expression.
if (WarnOnSizeOfPointerToAggregate) {
// Detect sizeof(ptr) where ptr is a pointer (CWE-467).
//
// In WarnOnSizeOfPointerToAggregate mode only report cases when ptr points
// to an aggregate type or ptr is an expression that (implicitly or
// explicitly) casts an array to a pointer type. (These are more suspicious
// than other sizeof(ptr) expressions because they can appear as distorted
// forms of the common sizeof(aggregate) expressions.)
//
// To avoid false positives, the check doesn't report expressions like
// 'sizeof(pp[0])' and 'sizeof(*pp)' where `pp` is a pointer-to-pointer or
// array of pointers. (This filters out both `sizeof(arr) / sizeof(arr[0])`
// expressions and other cases like `p = realloc(p, newsize * sizeof(*p));`.)
//
// Moreover this generic message is suppressed in cases that are also matched
// by the more concrete matchers 'sizeof-this' and 'sizeof-charp'.
if (WarnOnSizeOfPointerToAggregate || WarnOnSizeOfPointer) {
const auto ArrayExpr =
ignoringParenImpCasts(hasType(hasCanonicalType(arrayType())));
const auto ArrayCastExpr = expr(anyOf(
Expand All @@ -149,32 +164,31 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {

const auto PointerToStructType =
hasUnqualifiedDesugaredType(pointerType(pointee(recordType())));
const auto PointerToStructExpr = expr(
hasType(hasCanonicalType(PointerToStructType)), unless(cxxThisExpr()));

const auto ArrayOfPointersExpr = ignoringParenImpCasts(
hasType(hasCanonicalType(arrayType(hasElementType(pointerType()))
.bind("type-of-array-of-pointers"))));
const auto ArrayOfSamePointersExpr =
ignoringParenImpCasts(hasType(hasCanonicalType(
arrayType(equalsBoundNode("type-of-array-of-pointers")))));
const auto PointerToStructTypeWithBinding =
type(PointerToStructType).bind("struct-type");
const auto PointerToStructExpr =
expr(hasType(hasCanonicalType(PointerToStructType)));

const auto PointerToDetectedExpr =
WarnOnSizeOfPointer
? expr(hasType(hasUnqualifiedDesugaredType(pointerType())))
: expr(anyOf(ArrayCastExpr, PointerToArrayExpr,
PointerToStructExpr));

const auto ZeroLiteral = ignoringParenImpCasts(integerLiteral(equals(0)));
const auto ArrayOfSamePointersZeroSubscriptExpr =
ignoringParenImpCasts(arraySubscriptExpr(
hasBase(ArrayOfSamePointersExpr), hasIndex(ZeroLiteral)));
const auto ArrayLengthExprDenom =
expr(hasParent(binaryOperator(hasOperatorName("/"),
hasLHS(ignoringParenImpCasts(sizeOfExpr(
has(ArrayOfPointersExpr)))))),
sizeOfExpr(has(ArrayOfSamePointersZeroSubscriptExpr)));
const auto SubscriptExprWithZeroIndex =
arraySubscriptExpr(hasIndex(ZeroLiteral));
const auto DerefExpr =
ignoringParenImpCasts(unaryOperator(hasOperatorName("*")));

Finder->addMatcher(
expr(sizeOfExpr(anyOf(
has(ignoringParenImpCasts(anyOf(
ArrayCastExpr, PointerToArrayExpr, PointerToStructExpr))),
has(PointerToStructType))),
unless(ArrayLengthExprDenom))
.bind("sizeof-pointer-to-aggregate"),
expr(sizeOfExpr(anyOf(has(ignoringParenImpCasts(
expr(PointerToDetectedExpr, unless(DerefExpr),
unless(SubscriptExprWithZeroIndex),
unless(VarWithConstStrLiteralDecl),
unless(cxxThisExpr())))),
has(PointerToStructTypeWithBinding))))
.bind("sizeof-pointer"),
this);
}

Expand Down Expand Up @@ -292,11 +306,17 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
diag(E->getBeginLoc(),
"suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
<< E->getSourceRange();
} else if (const auto *E =
Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
diag(E->getBeginLoc(),
"suspicious usage of 'sizeof(A*)'; pointer to aggregate")
<< E->getSourceRange();
} else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-pointer")) {
if (Result.Nodes.getNodeAs<Type>("struct-type")) {
diag(E->getBeginLoc(),
"suspicious usage of 'sizeof(A*)' on pointer-to-aggregate type; did "
"you mean 'sizeof(A)'?")
<< E->getSourceRange();
} else {
diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
"that results in a pointer")
<< E->getSourceRange();
}
} else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
"sizeof-compare-constant")) {
diag(E->getOperatorLoc(),
Expand Down Expand Up @@ -332,18 +352,23 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
" numerator is not a multiple of denominator")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (NumTy && DenomTy && NumTy == DenomTy) {
// FIXME: This message is wrong, it should not refer to sizeof "pointer"
// usage (and by the way, it would be to clarify all the messages).
diag(E->getOperatorLoc(),
"suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (PointedTy && DenomTy && PointedTy == DenomTy) {
diag(E->getOperatorLoc(),
"suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (NumTy && DenomTy && NumTy->isPointerType() &&
DenomTy->isPointerType()) {
diag(E->getOperatorLoc(),
"suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (!WarnOnSizeOfPointer) {
// When 'WarnOnSizeOfPointer' is enabled, these messages become redundant:
if (PointedTy && DenomTy && PointedTy == DenomTy) {
diag(E->getOperatorLoc(),
"suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
} else if (NumTy && DenomTy && NumTy->isPointerType() &&
DenomTy->isPointerType()) {
diag(E->getOperatorLoc(),
"suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'")
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
}
}
} else if (const auto *E =
Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class SizeofExpressionCheck : public ClangTidyCheck {
const bool WarnOnSizeOfThis;
const bool WarnOnSizeOfCompareToConstant;
const bool WarnOnSizeOfPointerToAggregate;
const bool WarnOnSizeOfPointer;
};

} // namespace clang::tidy::bugprone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
//===----------------------------------------------------------------------===//

#include "MacroUsageCheck.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Regex.h"
#include <algorithm>
#include <cctype>
#include <functional>

Expand All @@ -37,7 +37,10 @@ class MacroUsageCallbacks : public PPCallbacks {
const MacroDirective *MD) override {
if (SM.isWrittenInBuiltinFile(MD->getLocation()) ||
MD->getMacroInfo()->isUsedForHeaderGuard() ||
MD->getMacroInfo()->getNumTokens() == 0)
MD->getMacroInfo()->tokens_empty() ||
llvm::any_of(MD->getMacroInfo()->tokens(), [](const Token &T) {
return T.isOneOf(tok::TokenKind::hash, tok::TokenKind::hashhash);
}))
return;

if (IgnoreCommandLineMacros &&
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_clang_library(clangTidyMiscModule
UnusedParametersCheck.cpp
UnusedUsingDeclsCheck.cpp
UseAnonymousNamespaceCheck.cpp
UseInternalLinkageCheck.cpp

LINK_LIBS
clangTidy
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "UnusedParametersCheck.h"
#include "UnusedUsingDeclsCheck.h"
#include "UseAnonymousNamespaceCheck.h"
#include "UseInternalLinkageCheck.h"

namespace clang::tidy {
namespace misc {
Expand Down Expand Up @@ -78,6 +79,8 @@ class MiscModule : public ClangTidyModule {
"misc-unused-using-decls");
CheckFactories.registerCheck<UseAnonymousNamespaceCheck>(
"misc-use-anonymous-namespace");
CheckFactories.registerCheck<UseInternalLinkageCheck>(
"misc-use-internal-linkage");
}
};

Expand Down
95 changes: 95 additions & 0 deletions clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===--- UseInternalLinkageCheck.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 "UseInternalLinkageCheck.h"
#include "../utils/FileExtensionsUtils.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/STLExtras.h"

using namespace clang::ast_matchers;

namespace clang::tidy::misc {

namespace {

AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); }

static bool isInMainFile(SourceLocation L, SourceManager &SM,
const FileExtensionsSet &HeaderFileExtensions) {
for (;;) {
if (utils::isSpellingLocInHeaderFile(L, SM, HeaderFileExtensions))
return false;
if (SM.isInMainFile(L))
return true;
// not in header file but not in main file
L = SM.getIncludeLoc(SM.getFileID(L));
if (L.isValid())
continue;
// Conservative about the unknown
return false;
}
}

AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet,
HeaderFileExtensions) {
return llvm::all_of(Node.redecls(), [&](const Decl *D) {
return isInMainFile(D->getLocation(),
Finder->getASTContext().getSourceManager(),
HeaderFileExtensions);
});
}

AST_POLYMORPHIC_MATCHER(isExternStorageClass,
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
VarDecl)) {
return Node.getStorageClass() == SC_Extern;
}

} // namespace

void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) {
auto Common =
allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions),
unless(anyOf(
// 1. internal linkage
isStaticStorageClass(), isInAnonymousNamespace(),
// 2. explicit external linkage
isExternStorageClass(), isExternC(),
// 3. template
isExplicitTemplateSpecialization(),
// 4. friend
hasAncestor(friendDecl()))));
Finder->addMatcher(
functionDecl(Common, unless(cxxMethodDecl()), unless(isMain()))
.bind("fn"),
this);
Finder->addMatcher(varDecl(Common, hasGlobalStorage()).bind("var"), this);
}

static constexpr StringRef Message =
"%0 %1 can be made static or moved into an anonymous namespace "
"to enforce internal linkage";

void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) {
diag(FD->getLocation(), Message) << "function" << FD;
return;
}
if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) {
diag(VD->getLocation(), Message) << "variable" << VD;
return;
}
llvm_unreachable("");
}

} // namespace clang::tidy::misc
38 changes: 38 additions & 0 deletions clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- UseInternalLinkageCheck.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_MISC_USEINTERNALLINKAGECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::misc {

/// Detects variables and functions that can be marked as static or moved into
/// an anonymous namespace to enforce internal linkage.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-internal-linkage.html
class UseInternalLinkageCheck : public ClangTidyCheck {
public:
UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}
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:
FileExtensionsSet HeaderFileExtensions;
};

} // namespace clang::tidy::misc

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ static constexpr bool RestrictToPODTypesDefault = false;
static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
static constexpr bool IgnoreMacrosDefault = true;

static constexpr char StrictCStandardComplianceName[] =
"StrictCStandardCompliance";
static constexpr bool StrictCStandardComplianceDefault = true;

static constexpr char StrictCppStandardComplianceName[] =
"StrictCppStandardCompliance";
static constexpr bool StrictCppStandardComplianceDefault = true;

namespace {

struct Designators {
Expand Down Expand Up @@ -97,7 +105,12 @@ UseDesignatedInitializersCheck::UseDesignatedInitializersCheck(
RestrictToPODTypes(
Options.get(RestrictToPODTypesName, RestrictToPODTypesDefault)),
IgnoreMacros(
Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)) {}
Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)),
StrictCStandardCompliance(Options.get(StrictCStandardComplianceName,
StrictCStandardComplianceDefault)),
StrictCppStandardCompliance(
Options.get(StrictCppStandardComplianceName,
StrictCppStandardComplianceDefault)) {}

void UseDesignatedInitializersCheck::registerMatchers(MatchFinder *Finder) {
const auto HasBaseWithFields =
Expand Down Expand Up @@ -179,6 +192,9 @@ void UseDesignatedInitializersCheck::storeOptions(
IgnoreSingleElementAggregates);
Options.store(Opts, RestrictToPODTypesName, RestrictToPODTypes);
Options.store(Opts, IgnoreMacrosName, IgnoreMacros);
Options.store(Opts, StrictCStandardComplianceName, StrictCStandardCompliance);
Options.store(Opts, StrictCppStandardComplianceName,
StrictCppStandardCompliance);
}

} // namespace clang::tidy::modernize
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@ class UseDesignatedInitializersCheck : public ClangTidyCheck {
return TK_IgnoreUnlessSpelledInSource;
}

bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus20 || LangOpts.C99 ||
(LangOpts.CPlusPlus && !StrictCppStandardCompliance) ||
(!LangOpts.CPlusPlus && !LangOpts.ObjC &&
!StrictCStandardCompliance);
}

private:
bool IgnoreSingleElementAggregates;
bool RestrictToPODTypes;
bool IgnoreMacros;
bool StrictCStandardCompliance;
bool StrictCppStandardCompliance;
};

} // namespace clang::tidy::modernize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
}
}

AST_MATCHER_FUNCTION_P(StatementMatcher, isConstRefReturningMethodCall,
AST_MATCHER_FUNCTION_P(StatementMatcher,
isRefReturningMethodCallWithConstOverloads,
std::vector<StringRef>, ExcludedContainerTypes) {
// Match method call expressions where the `this` argument is only used as
// const, this will be checked in `check()` part. This returned const
// reference is highly likely to outlive the local const reference of the
// variable being declared. The assumption is that the const reference being
// returned either points to a global static variable or to a member of the
// called object.
// const, this will be checked in `check()` part. This returned reference is
// highly likely to outlive the local const reference of the variable being
// declared. The assumption is that the reference being returned either points
// to a global static variable or to a member of the called object.
const auto MethodDecl =
cxxMethodDecl(returns(hasCanonicalType(matchers::isReferenceToConst())))
cxxMethodDecl(returns(hasCanonicalType(referenceType())))
.bind(MethodDeclId);
const auto ReceiverExpr =
ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ObjectArgId))));
Expand Down Expand Up @@ -121,7 +121,7 @@ AST_MATCHER_FUNCTION_P(StatementMatcher, initializerReturnsReferenceToConst,
declRefExpr(to(varDecl(hasLocalStorage()).bind(OldVarDeclId)));
return expr(
anyOf(isConstRefReturningFunctionCall(),
isConstRefReturningMethodCall(ExcludedContainerTypes),
isRefReturningMethodCallWithConstOverloads(ExcludedContainerTypes),
ignoringImpCasts(OldVarDeclRef),
ignoringImpCasts(unaryOperator(hasOperatorName("&"),
hasUnaryOperand(OldVarDeclRef)))));
Expand Down Expand Up @@ -259,10 +259,11 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
.bind("blockStmt");
};

Finder->addMatcher(LocalVarCopiedFrom(anyOf(isConstRefReturningFunctionCall(),
isConstRefReturningMethodCall(
ExcludedContainerTypes))),
this);
Finder->addMatcher(
LocalVarCopiedFrom(anyOf(
isConstRefReturningFunctionCall(),
isRefReturningMethodCallWithConstOverloads(ExcludedContainerTypes))),
this);

Finder->addMatcher(LocalVarCopiedFrom(declRefExpr(
to(varDecl(hasLocalStorage()).bind(OldVarDeclId)))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ static StringRef getControlFlowString(const Stmt &Stmt) {
return "break";
if (isa<CXXThrowExpr>(Stmt))
return "throw";
llvm_unreachable("Unknown control flow interruptor");
llvm_unreachable("Unknown control flow interrupter");
}

void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
Expand All @@ -247,12 +247,12 @@ void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
return;

bool IsLastInScope = OuterScope->body_back() == If;
StringRef ControlFlowInterruptor = getControlFlowString(*Interrupt);
const StringRef ControlFlowInterrupter = getControlFlowString(*Interrupt);

if (!IsLastInScope && containsDeclInScope(Else)) {
if (WarnOnUnfixable) {
// Warn, but don't attempt an autofix.
diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
diag(ElseLoc, WarningMessage) << ControlFlowInterrupter;
}
return;
}
Expand All @@ -264,7 +264,7 @@ void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
// If the if statement is the last statement of its enclosing statements
// scope, we can pull the decl out of the if statement.
DiagnosticBuilder Diag = diag(ElseLoc, WarningMessage)
<< ControlFlowInterruptor
<< ControlFlowInterrupter
<< SourceRange(ElseLoc);
if (checkInitDeclUsageInElse(If) != nullptr) {
Diag << tooling::fixit::createReplacement(
Expand All @@ -288,7 +288,7 @@ void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
} else if (WarnOnUnfixable) {
// Warn, but don't attempt an autofix.
diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
diag(ElseLoc, WarningMessage) << ControlFlowInterrupter;
}
return;
}
Expand All @@ -300,7 +300,7 @@ void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
// If the if statement is the last statement of its enclosing statements
// scope, we can pull the decl out of the if statement.
DiagnosticBuilder Diag = diag(ElseLoc, WarningMessage)
<< ControlFlowInterruptor
<< ControlFlowInterrupter
<< SourceRange(ElseLoc);
Diag << tooling::fixit::createReplacement(
SourceRange(If->getIfLoc()),
Expand All @@ -312,13 +312,13 @@ void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
} else if (WarnOnUnfixable) {
// Warn, but don't attempt an autofix.
diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
diag(ElseLoc, WarningMessage) << ControlFlowInterrupter;
}
return;
}

DiagnosticBuilder Diag = diag(ElseLoc, WarningMessage)
<< ControlFlowInterruptor << SourceRange(ElseLoc);
<< ControlFlowInterrupter << SourceRange(ElseLoc);
removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
hasParent(callExpr()),
hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!="))));

auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(
isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));

Finder->addMatcher(
traverse(TK_AsIs,
implicitCastExpr(
Expand All @@ -299,7 +302,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
// additional parens in replacement.
optionally(hasParent(stmt().bind("parentStmt"))),
unless(isInTemplateInstantiation()),
unless(hasAncestor(functionTemplateDecl())))
unless(IsInCompilerGeneratedFunction))
.bind("implicitCastToBool")),
this);

Expand Down Expand Up @@ -331,7 +334,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
anything()),
unless(isInTemplateInstantiation()),
unless(hasAncestor(functionTemplateDecl())))),
unless(IsInCompilerGeneratedFunction))),
this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ static void addParantheses(const BinaryOperator *BinOp,
int Precedence1 = getPrecedence(BinOp);
int Precedence2 = getPrecedence(ParentBinOp);

if (ParentBinOp != nullptr && Precedence1 != Precedence2) {
if (ParentBinOp != nullptr && Precedence1 != Precedence2 && Precedence1 > 0 &&
Precedence2 > 0) {
const clang::SourceLocation StartLoc = BinOp->getBeginLoc();
const clang::SourceLocation EndLoc =
clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts);
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def main():
filename = None
lines_by_file = {}
for line in sys.stdin:
match = re.search('^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line)
match = re.search('^\\+\\+\\+\\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line)
if match:
filename = match.group(2)
if filename is None:
Expand All @@ -255,7 +255,7 @@ def main():
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
continue

match = re.search("^@@.*\+(\d+)(,(\d+))?", line)
match = re.search(r"^@@.*\+(\d+)(,(\d+))?", line)
if match:
start_line = int(match.group(1))
line_count = 1
Expand Down
168 changes: 161 additions & 7 deletions clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,116 @@ void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
Nodes.insert(Match.getNodeAs<Node>(ID));
}

// Returns true if both types refer to the same type,
// ignoring the const-qualifier.
bool isSameTypeIgnoringConst(QualType A, QualType B) {
A = A.getCanonicalType();
B = B.getCanonicalType();
A.addConst();
B.addConst();
return A == B;
}

// Returns true if `D` and `O` have the same parameter types.
bool hasSameParameterTypes(const CXXMethodDecl &D, const CXXMethodDecl &O) {
if (D.getNumParams() != O.getNumParams())
return false;
for (int I = 0, E = D.getNumParams(); I < E; ++I) {
if (!isSameTypeIgnoringConst(D.getParamDecl(I)->getType(),
O.getParamDecl(I)->getType()))
return false;
}
return true;
}

// If `D` has a const-qualified overload with otherwise identical
// ref-qualifiers and parameter types, returns that overload.
const CXXMethodDecl *findConstOverload(const CXXMethodDecl &D) {
assert(!D.isConst());

DeclContext::lookup_result LookupResult =
D.getParent()->lookup(D.getNameInfo().getName());
if (LookupResult.isSingleResult()) {
// No overload.
return nullptr;
}
for (const Decl *Overload : LookupResult) {
const auto *O = dyn_cast<CXXMethodDecl>(Overload);
if (O && !O->isDeleted() && O->isConst() &&
O->getRefQualifier() == D.getRefQualifier() &&
hasSameParameterTypes(D, *O))
return O;
}
return nullptr;
}

// Returns true if both types are pointers or reference to the same type,
// ignoring the const-qualifier.
bool pointsToSameTypeIgnoringConst(QualType A, QualType B) {
assert(A->isPointerType() || A->isReferenceType());
assert(B->isPointerType() || B->isReferenceType());
return isSameTypeIgnoringConst(A->getPointeeType(), B->getPointeeType());
}

// Return true if non-const member function `M` likely does not mutate `*this`.
//
// Note that if the member call selects a method/operator `f` that
// is not const-qualified, then we also consider that the object is
// not mutated if:
// - (A) there is a const-qualified overload `cf` of `f` that has
// the
// same ref-qualifiers;
// - (B) * `f` returns a value, or
// * if `f` returns a `T&`, `cf` returns a `const T&` (up to
// possible aliases such as `reference` and
// `const_reference`), or
// * if `f` returns a `T*`, `cf` returns a `const T*` (up to
// possible aliases).
// - (C) the result of the call is not mutated.
//
// The assumption that `cf` has the same semantics as `f`.
// For example:
// - In `std::vector<T> v; const T t = v[...];`, we consider that
// expression `v[...]` does not mutate `v` as
// `T& std::vector<T>::operator[]` has a const overload
// `const T& std::vector<T>::operator[] const`, and the
// result expression of type `T&` is only used as a `const T&`;
// - In `std::map<K, V> m; V v = m.at(...);`, we consider
// `m.at(...)` to be an immutable access for the same reason.
// However:
// - In `std::map<K, V> m; const V v = m[...];`, We consider that
// `m[...]` mutates `m` as `V& std::map<K, V>::operator[]` does
// not have a const overload.
// - In `std::vector<T> v; T& t = v[...];`, we consider that
// expression `v[...]` mutates `v` as the result is kept as a
// mutable reference.
//
// This function checks (A) ad (B), but the caller should make sure that the
// object is not mutated through the return value.
bool isLikelyShallowConst(const CXXMethodDecl &M) {
assert(!M.isConst());
// The method can mutate our variable.

// (A)
const CXXMethodDecl *ConstOverload = findConstOverload(M);
if (ConstOverload == nullptr) {
return false;
}

// (B)
const QualType CallTy = M.getReturnType().getCanonicalType();
const QualType OverloadTy = ConstOverload->getReturnType().getCanonicalType();
if (CallTy->isReferenceType()) {
return OverloadTy->isReferenceType() &&
pointsToSameTypeIgnoringConst(CallTy, OverloadTy);
}
if (CallTy->isPointerType()) {
return OverloadTy->isPointerType() &&
pointsToSameTypeIgnoringConst(CallTy, OverloadTy);
}
return isSameTypeIgnoringConst(CallTy, OverloadTy);
}

// A matcher that matches DeclRefExprs that are used in ways such that the
// underlying declaration is not modified.
// If the declaration is of pointer type, `Indirections` specifies the level
Expand All @@ -54,16 +164,15 @@ void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
// matches (A).
//
AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
// We walk up the parents of the DeclRefExpr recursively until we end up on a
// parent that cannot modify the underlying object. There are a few kinds of
// expressions:
// - Those that cannot be used to mutate the underlying object. We can stop
// We walk up the parents of the DeclRefExpr recursively. There are a few
// kinds of expressions:
// - Those that cannot be used to mutate the underlying variable. We can stop
// recursion there.
// - Those that can be used to mutate the underlying object in analyzable
// - Those that can be used to mutate the underlying variable in analyzable
// ways (such as taking the address or accessing a subobject). We have to
// examine the parents.
// - Those that we don't know how to analyze. In that case we stop there and
// we assume that they can mutate the underlying expression.
// we assume that they can modify the expression.

struct StackEntry {
StackEntry(const Expr *E, int Indirections)
Expand All @@ -90,7 +199,7 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
assert(Ty->isPointerType());
Ty = Ty->getPointeeType().getCanonicalType();
}
if (Ty.isConstQualified())
if (Ty->isVoidType() || Ty.isConstQualified())
continue;

// Otherwise we have to look at the parents to see how the expression is
Expand Down Expand Up @@ -159,11 +268,56 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
// The method call cannot mutate our variable.
continue;
}
if (isLikelyShallowConst(*Method)) {
// We still have to check that the object is not modified through
// the method's return value (C).
const auto MemberParents = Ctx.getParents(*Member);
assert(MemberParents.size() == 1);
const auto *Call = MemberParents[0].get<CallExpr>();
// If `o` is an object of class type and `f` is a member function,
// then `o.f` has to be used as part of a call expression.
assert(Call != nullptr && "member function has to be called");
Stack.emplace_back(
Call,
Method->getReturnType().getCanonicalType()->isPointerType()
? 1
: 0);
continue;
}
return false;
}
Stack.emplace_back(Member, 0);
continue;
}
if (const auto *const OpCall = dyn_cast<CXXOperatorCallExpr>(P)) {
// Operator calls have function call syntax. The `*this` parameter
// is the first parameter.
if (OpCall->getNumArgs() == 0 || OpCall->getArg(0) != Entry.E) {
return false;
}
const auto *const Method =
dyn_cast<CXXMethodDecl>(OpCall->getDirectCallee());

if (Method == nullptr) {
// This is not a member operator. Typically, a friend operator. These
// are handled like function calls.
return false;
}

if (Method->isConst() || Method->isStatic()) {
continue;
}
if (isLikelyShallowConst(*Method)) {
// We still have to check that the object is not modified through
// the operator's return value (C).
Stack.emplace_back(
OpCall,
Method->getReturnType().getCanonicalType()->isPointerType() ? 1
: 0);
continue;
}
return false;
}

if (const auto *const Op = dyn_cast<UnaryOperator>(P)) {
switch (Op->getOpcode()) {
Expand Down
18 changes: 6 additions & 12 deletions clang-tools-extra/clangd/TidyProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ TidyProvider addTidyChecks(llvm::StringRef Checks,
}

TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) {
constexpr llvm::StringLiteral Seperator(",");
constexpr llvm::StringLiteral Separator(",");
static const std::string BadChecks = llvm::join_items(
Seperator,
// We want this list to start with a seperator to
Separator,
// We want this list to start with a separator to
// simplify appending in the lambda. So including an
// empty string here will force that.
"",
Expand All @@ -221,19 +221,13 @@ TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) {
"-hicpp-invalid-access-moved",
// Check uses dataflow analysis, which might hang/crash unexpectedly on
// incomplete code.
"-bugprone-unchecked-optional-access",

// ----- Performance problems -----

// This check runs expensive analysis for each variable.
// It has been observed to increase reparse time by 10x.
"-misc-const-correctness");
"-bugprone-unchecked-optional-access");

size_t Size = BadChecks.size();
for (const std::string &Str : ExtraBadChecks) {
if (Str.empty())
continue;
Size += Seperator.size();
Size += Separator.size();
if (LLVM_LIKELY(Str.front() != '-'))
++Size;
Size += Str.size();
Expand All @@ -244,7 +238,7 @@ TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) {
for (const std::string &Str : ExtraBadChecks) {
if (Str.empty())
continue;
DisableGlob += Seperator;
DisableGlob += Separator;
if (LLVM_LIKELY(Str.front() != '-'))
DisableGlob.push_back('-');
DisableGlob += Str;
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/clangd/index/remote/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ if (CLANGD_ENABLE_REMOTE)
clangdRemoteIndexProto
clangdRemoteIndexServiceProto
clangdRemoteMarshalling
clangBasic
clangDaemon
clangdSupport

Expand All @@ -35,6 +34,11 @@ if (CLANGD_ENABLE_REMOTE)
clangdRemoteIndexServiceProto
)

clang_target_link_libraries(clangdRemoteIndex
PRIVATE
clangBasic
)

add_subdirectory(marshalling)
add_subdirectory(server)
add_subdirectory(monitor)
Expand Down
31 changes: 29 additions & 2 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ New checks
to reading out-of-bounds data due to inadequate or incorrect string null
termination.

- New :doc:`misc-use-internal-linkage
<clang-tidy/checks/misc/use-internal-linkage>` check.

Detects variables and functions that can be marked as static or moved into
an anonymous namespace to enforce internal linkage.

- New :doc:`modernize-min-max-use-initializer-list
<clang-tidy/checks/modernize/min-max-use-initializer-list>` check.

Expand Down Expand Up @@ -218,6 +224,10 @@ Changes in existing checks
check by ignoring ``__func__`` macro in lambda captures, initializers of
default parameters and nested function declarations.

- Improved :doc:`bugprone-multi-level-implicit-pointer-conversion
<clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion>` check
by ignoring implicit pointer conversions that are part of a cast expression.

- Improved :doc:`bugprone-non-zero-enum-to-bool-conversion
<clang-tidy/checks/bugprone/non-zero-enum-to-bool-conversion>` check by
eliminating false positives resulting from direct usage of bitwise operators
Expand All @@ -227,6 +237,12 @@ Changes in existing checks
<clang-tidy/checks/bugprone/optional-value-conversion>` check by eliminating
false positives resulting from use of optionals in unevaluated context.

- Improved :doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` check by eliminating some
false positives and adding a new (off-by-default) option
`WarnOnSizeOfPointer` that reports all ``sizeof(pointer)`` expressions
(except for a few that are idiomatic).

- Improved :doc:`bugprone-suspicious-include
<clang-tidy/checks/bugprone/suspicious-include>` check by replacing the local
options `HeaderFileExtensions` and `ImplementationFileExtensions` by the
Expand All @@ -250,6 +266,10 @@ Changes in existing checks
<clang-tidy/checks/bugprone/use-after-move>` check to also handle
calls to ``std::forward``.

- Improved :doc:`cppcoreguidelines-macro-usage
<clang-tidy/checks/cppcoreguidelines/macro-usage>` check by ignoring macro with
hash preprocessing token.

- Improved :doc:`cppcoreguidelines-missing-std-forward
<clang-tidy/checks/cppcoreguidelines/missing-std-forward>` check by no longer
giving false positives for deleted functions, by fixing false negatives when only
Expand Down Expand Up @@ -321,6 +341,10 @@ Changes in existing checks
<clang-tidy/checks/misc/header-include-cycle>` check by avoiding crash for self
include cycles.

- Improved :doc:`misc-include-cleaner
<clang-tidy/checks/misc/include-cleaner>` check by avoiding false positives for
the functions with the same name as standard library functions.

- 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.
Expand Down Expand Up @@ -366,8 +390,10 @@ Changes in existing checks
- Improved :doc:`performance-unnecessary-copy-initialization
<clang-tidy/checks/performance/unnecessary-copy-initialization>` check by
detecting more cases of constant access. In particular, pointers can be
analyzed, se the check now handles the common patterns
analyzed, so the check now handles the common patterns
`const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`.
Calls to mutable function where there exists a `const` overload are also
handled.

- Improved :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
Expand Down Expand Up @@ -402,7 +428,8 @@ Changes in existing checks
valid fix suggestions for ``static_cast`` without a preceding space and
fixed problem with duplicate parentheses in double implicit casts. Corrected
the fix suggestions for C23 and later by using C-style casts instead of
``static_cast``.
``static_cast``. Fixed false positives in C++20 spaceship operator by ignoring
casts in implicit and defaulted functions.

- Improved :doc:`readability-redundant-inline-specifier
<clang-tidy/checks/readability/redundant-inline-specifier>` check to properly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ Options

.. option:: WarnOnSizeOfPointerToAggregate

When `true`, the check will warn on an expression like
``sizeof(expr)`` where the expression is a pointer
to aggregate. Default is `true`.
When `true`, the check will warn when the argument of ``sizeof`` is either a
pointer-to-aggregate type, an expression returning a pointer-to-aggregate
value or an expression that returns a pointer from an array-to-pointer
conversion (that may be implicit or explicit, for example ``array + 2`` or
``(int *)array``). Default is `true`.

.. option:: WarnOnSizeOfPointer

When `true`, the check will report all expressions where the argument of
``sizeof`` is an expression that produces a pointer (except for a few
idiomatic expressions that are probably intentional and correct).
This detects occurrences of CWE 467. Default is `false`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.. title:: clang-tidy - clang-analyzer-cplusplus.ArrayDelete
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete

clang-analyzer-cplusplus.ArrayDelete
====================================

Reports destructions of arrays of polymorphic objects that are destructed as
their base class.

The `clang-analyzer-cplusplus.ArrayDelete` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete>`_
for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. title:: clang-tidy - clang-analyzer-security.SetgidSetuidOrder

clang-analyzer-security.SetgidSetuidOrder
=========================================

Warn on possible reversed order of 'setgid(getgid()))' and 'setuid(getuid())'
(CERT: POS36-C).

The clang-analyzer-security.SetgidSetuidOrder check is an alias of
Clang Static Analyzer security.SetgidSetuidOrder.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. title:: clang-tidy - clang-analyzer-unix.Stream
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream

clang-analyzer-unix.Stream
==========================

Check stream handling functions.

The `clang-analyzer-unix.Stream` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream>`_
for more information.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Examples:
#define C 0
#define F1(x, y) ((a) > (b) ? (a) : (b))
#define F2(...) (__VA_ARGS__)
#define F3(x, y) x##y
#define COMMA ,
#define NORETURN [[noreturn]]
#define DEPRECATED attribute((deprecated))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_checkers(checkers_td, checkers_rst):
parent_package_ = package["ParentPackage"]
hidden = (checker["Hidden"] != 0) or (package["Hidden"] != 0)

while parent_package_ != None:
while parent_package_ is not None:
parent_package = table_entries[parent_package_["def"]]
checker_package_prefix = (
parent_package["PackageName"] + "." + checker_package_prefix
Expand All @@ -59,7 +59,7 @@ def get_checkers(checkers_td, checkers_rst):
"clang-analyzer-" + checker_package_prefix + "." + checker_name
)
anchor_url = re.sub(
"\.", "-", checker_package_prefix + "." + checker_name
r"\.", "-", checker_package_prefix + "." + checker_name
).lower()

if not hidden and "alpha" not in full_package_name.lower():
Expand Down Expand Up @@ -130,7 +130,7 @@ def generate_documentation(checker, has_documentation):
def update_documentation_list(checkers):
with open(os.path.join(__location__, "list.rst"), "r+") as f:
f_text = f.read()
check_text = f_text.split(".. csv-table:: Aliases..\n")[1]
check_text = f_text.split(':header: "Name", "Redirect", "Offers fixes"\n')[1]
checks = [x for x in check_text.split("\n") if ":header:" not in x and x]
old_check_text = "\n".join(checks)
checks = [x for x in checks if "clang-analyzer-" not in x]
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ Clang-Tidy Checks
:doc:`misc-unused-parameters <misc/unused-parameters>`, "Yes"
:doc:`misc-unused-using-decls <misc/unused-using-decls>`, "Yes"
:doc:`misc-use-anonymous-namespace <misc/use-anonymous-namespace>`,
:doc:`misc-use-internal-linkage <misc/use-internal-linkage>`,
:doc:`modernize-avoid-bind <modernize/avoid-bind>`, "Yes"
:doc:`modernize-avoid-c-arrays <modernize/avoid-c-arrays>`,
:doc:`modernize-concat-nested-namespaces <modernize/concat-nested-namespaces>`, "Yes"
Expand Down Expand Up @@ -442,6 +443,7 @@ Check aliases
:doc:`clang-analyzer-core.uninitialized.CapturedBlockVariable <clang-analyzer/core.uninitialized.CapturedBlockVariable>`, `Clang Static Analyzer core.uninitialized.CapturedBlockVariable <https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-capturedblockvariable>`_,
:doc:`clang-analyzer-core.uninitialized.NewArraySize <clang-analyzer/core.uninitialized.NewArraySize>`, `Clang Static Analyzer core.uninitialized.NewArraySize <https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-newarraysize>`_,
:doc:`clang-analyzer-core.uninitialized.UndefReturn <clang-analyzer/core.uninitialized.UndefReturn>`, `Clang Static Analyzer core.uninitialized.UndefReturn <https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-undefreturn>`_,
:doc:`clang-analyzer-cplusplus.ArrayDelete <clang-analyzer/cplusplus.ArrayDelete>`, `Clang Static Analyzer cplusplus.ArrayDelete <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete>`_,
:doc:`clang-analyzer-cplusplus.InnerPointer <clang-analyzer/cplusplus.InnerPointer>`, `Clang Static Analyzer cplusplus.InnerPointer <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-innerpointer>`_,
:doc:`clang-analyzer-cplusplus.Move <clang-analyzer/cplusplus.Move>`, Clang Static Analyzer cplusplus.Move,
:doc:`clang-analyzer-cplusplus.NewDelete <clang-analyzer/cplusplus.NewDelete>`, `Clang Static Analyzer cplusplus.NewDelete <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdelete>`_,
Expand Down Expand Up @@ -496,6 +498,7 @@ Check aliases
:doc:`clang-analyzer-osx.coreFoundation.containers.OutOfBounds <clang-analyzer/osx.coreFoundation.containers.OutOfBounds>`, `Clang Static Analyzer osx.coreFoundation.containers.OutOfBounds <https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-outofbounds>`_,
:doc:`clang-analyzer-osx.coreFoundation.containers.PointerSizedValues <clang-analyzer/osx.coreFoundation.containers.PointerSizedValues>`, `Clang Static Analyzer osx.coreFoundation.containers.PointerSizedValues <https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-pointersizedvalues>`_,
:doc:`clang-analyzer-security.FloatLoopCounter <clang-analyzer/security.FloatLoopCounter>`, `Clang Static Analyzer security.FloatLoopCounter <https://clang.llvm.org/docs/analyzer/checkers.html#security-floatloopcounter>`_,
:doc:`clang-analyzer-security.SetgidSetuidOrder <clang-analyzer/security.SetgidSetuidOrder>`, Clang Static Analyzer security.SetgidSetuidOrder,
:doc:`clang-analyzer-security.cert.env.InvalidPtr <clang-analyzer/security.cert.env.InvalidPtr>`, `Clang Static Analyzer security.cert.env.InvalidPtr <https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr>`_,
:doc:`clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling <clang-analyzer/security.insecureAPI.DeprecatedOrUnsafeBufferHandling>`, `Clang Static Analyzer security.insecureAPI.DeprecatedOrUnsafeBufferHandling <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-deprecatedorunsafebufferhandling>`_,
:doc:`clang-analyzer-security.insecureAPI.UncheckedReturn <clang-analyzer/security.insecureAPI.UncheckedReturn>`, `Clang Static Analyzer security.insecureAPI.UncheckedReturn <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-uncheckedreturn>`_,
Expand All @@ -516,6 +519,7 @@ Check aliases
:doc:`clang-analyzer-unix.MallocSizeof <clang-analyzer/unix.MallocSizeof>`, `Clang Static Analyzer unix.MallocSizeof <https://clang.llvm.org/docs/analyzer/checkers.html#unix-mallocsizeof>`_,
:doc:`clang-analyzer-unix.MismatchedDeallocator <clang-analyzer/unix.MismatchedDeallocator>`, `Clang Static Analyzer unix.MismatchedDeallocator <https://clang.llvm.org/docs/analyzer/checkers.html#unix-mismatcheddeallocator>`_,
:doc:`clang-analyzer-unix.StdCLibraryFunctions <clang-analyzer/unix.StdCLibraryFunctions>`, `Clang Static Analyzer unix.StdCLibraryFunctions <https://clang.llvm.org/docs/analyzer/checkers.html#unix-stdclibraryfunctions>`_,
:doc:`clang-analyzer-unix.Stream <clang-analyzer/unix.Stream>`, `Clang Static Analyzer unix.Stream <https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream>`_,
:doc:`clang-analyzer-unix.Vfork <clang-analyzer/unix.Vfork>`, `Clang Static Analyzer unix.Vfork <https://clang.llvm.org/docs/analyzer/checkers.html#unix-vfork>`_,
:doc:`clang-analyzer-unix.cstring.BadSizeArg <clang-analyzer/unix.cstring.BadSizeArg>`, `Clang Static Analyzer unix.cstring.BadSizeArg <https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-badsizearg>`_,
:doc:`clang-analyzer-unix.cstring.NullArg <clang-analyzer/unix.cstring.NullArg>`, `Clang Static Analyzer unix.cstring.NullArg <https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-nullarg>`_,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.. title:: clang-tidy - misc-use-internal-linkage

misc-use-internal-linkage
=========================

Detects variables and functions that can be marked as static or moved into
an anonymous namespace to enforce internal linkage.

Static functions and variables are scoped to a single file. Marking functions
and variables as static helps to better remove dead code. In addition, it gives
the compiler more information and allows for more aggressive optimizations.

Example:

.. code-block:: c++

int v1; // can be marked as static

void fn1(); // can be marked as static

namespace {
// already in anonymous namespace
int v2;
void fn2();
}
// already declared as extern
extern int v2;
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ declaration of ``S``.

Even when compiling in a language version older than C++20, depending on your
compiler, designated initializers are potentially supported. Therefore, the
check is not restricted to C++20 and newer versions. Check out the options
check is by default restricted to C99/C++20 and above. Check out the options
``-Wc99-designator`` to get support for mixed designators in initializer list in
C and ``-Wc++20-designator`` for support of designated initializers in older C++
language modes.
Expand All @@ -60,3 +60,13 @@ Options
The value `true` specifies that only Plain Old Data (POD) types shall be
checked. This makes the check applicable to even older C++ standards. The
default value is `false`.

.. option:: StrictCStandardCompliance

When set to `false`, the check will not restrict itself to C99 and above.
The default value is `true`.

.. option:: StrictCppStandardCompliance

When set to `false`, the check will not restrict itself to C++20 and above.
The default value is `true`.
11 changes: 9 additions & 2 deletions clang-tools-extra/include-cleaner/lib/IncludeSpeller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "clang-include-cleaner/IncludeSpeller.h"
#include "clang-include-cleaner/Types.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Registry.h"
#include <memory>
Expand All @@ -30,8 +31,14 @@ class DefaultIncludeSpeller : public IncludeSpeller {
return Input.H.verbatim().str();
case Header::Physical:
bool IsAngled = false;
std::string WorkingDir;
if (auto WD = Input.HS.getFileMgr()
.getVirtualFileSystem()
.getCurrentWorkingDirectory())
WorkingDir = *WD;
std::string FinalSpelling = Input.HS.suggestPathToFileForDiagnostics(
Input.H.physical(), Input.Main->tryGetRealPathName(), &IsAngled);
Input.H.resolvedPath(), WorkingDir, Input.Main->tryGetRealPathName(),
&IsAngled);
return IsAngled ? "<" + FinalSpelling + ">" : "\"" + FinalSpelling + "\"";
}
llvm_unreachable("Unknown clang::include_cleaner::Header::Kind enum");
Expand Down Expand Up @@ -60,4 +67,4 @@ std::string spellHeader(const IncludeSpeller::Input &Input) {
return Spelling;
}

} // namespace clang::include_cleaner
} // namespace clang::include_cleaner
8 changes: 6 additions & 2 deletions clang-tools-extra/include-cleaner/lib/LocateSymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Tooling/Inclusions/StandardLibrary.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
#include <vector>

Expand All @@ -40,8 +41,11 @@ Hints declHints(const Decl *D) {
std::vector<Hinted<SymbolLocation>> locateDecl(const Decl &D) {
std::vector<Hinted<SymbolLocation>> Result;
// FIXME: Should we also provide physical locations?
if (auto SS = tooling::stdlib::Recognizer()(&D))
return {{*SS, Hints::CompleteSymbol}};
if (auto SS = tooling::stdlib::Recognizer()(&D)) {
Result.push_back({*SS, Hints::CompleteSymbol});
if (!D.hasBody())
return Result;
}
// FIXME: Signal foreign decls, e.g. a forward declaration not owned by a
// library. Some useful signals could be derived by checking the DeclContext.
// Most incidental forward decls look like:
Expand Down
11 changes: 11 additions & 0 deletions clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,17 @@ TEST_F(HeadersForSymbolTest, StandardHeaders) {
tooling::stdlib::Header::named("<assert.h>")));
}

TEST_F(HeadersForSymbolTest, NonStandardHeaders) {
Inputs.Code = "void assert() {}";
buildAST();
EXPECT_THAT(
headersFor("assert"),
// Respect the ordering from the stdlib mapping.
UnorderedElementsAre(physicalHeader("input.mm"),
tooling::stdlib::Header::named("<cassert>"),
tooling::stdlib::Header::named("<assert.h>")));
}

TEST_F(HeadersForSymbolTest, ExporterNoNameMatch) {
Inputs.Code = R"cpp(
#include "exporter/foo.h"
Expand Down
18 changes: 18 additions & 0 deletions clang-tools-extra/include-cleaner/unittests/IncludeSpellerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ TEST(IncludeSpeller, CanOverrideSystemHeaders) {
HS, MainFile}));
}

TEST(IncludeSpeller, RelativeIncludeSearchPath) {
TestInputs Inputs;

Inputs.WorkingDir = "/root/inner";
Inputs.ExtraArgs.push_back("-I..");
Inputs.ExtraFiles["/root/foo.h"] = "";
TestAST AST{Inputs};

auto &FM = AST.fileManager();
auto &HS = AST.preprocessor().getHeaderSearchInfo();
const auto *MainFile = AST.sourceManager().getFileEntryForID(
AST.sourceManager().getMainFileID());

EXPECT_EQ("\"foo.h\"",
spellHeader(
{Header{*FM.getOptionalFileRef("/root/foo.h")}, HS, MainFile}));
}

IncludeSpellingStrategy::Add<DummyIncludeSpeller>
Speller("dummy", "Dummy Include Speller");

Expand Down
8 changes: 6 additions & 2 deletions clang-tools-extra/pseudo/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ add_clang_library(clangPseudo
Token.cpp

LINK_LIBS
clangBasic
clangLex
clangPseudoGrammar

DEPENDS
Expand All @@ -25,3 +23,9 @@ add_clang_library(clangPseudo
target_include_directories(clangPseudo INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
)

clang_target_link_libraries(clangPseudo
PRIVATE
clangBasic
clangLex
)
6 changes: 5 additions & 1 deletion clang-tools-extra/pseudo/lib/cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ add_clang_library(clangPseudoCXX
cxx_gen

LINK_LIBS
clangBasic
clangPseudo
clangPseudoGrammar
)

clang_target_link_libraries(clangPseudoCXX
PRIVATE
clangBasic
)
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,15 @@ void test()

takeSecondLevelVoidPtr(getSecondLevelVoidPtr());
}

namespace PR93959 {
void free(void*);

void test() {
char **p = nullptr;
free(p);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: multilevel pointer conversion from 'char **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
free((void *)p);
free(static_cast<void *>(p));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,24 @@ int Test5() {

int sum = 0;
sum += sizeof(&S);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(__typeof(&S));
sum += sizeof(&TS);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(__typeof(&TS));
sum += sizeof(STRKWD MyStruct*);
sum += sizeof(__typeof(STRKWD MyStruct*));
sum += sizeof(TypedefStruct*);
sum += sizeof(__typeof(TypedefStruct*));
sum += sizeof(PTTS);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PMyStruct);
sum += sizeof(PS);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PS2);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&A10);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

#ifdef __cplusplus
MyStruct &rS = S;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t -- -config="{CheckOptions: {bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression: true, bugprone-sizeof-expression.WarnOnSizeOfPointer: true}}" --

class C {
int size() { return sizeof(this); }
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(this)'
};

#define LEN 8

int X;
extern int A[10];
extern short B[10];

#pragma pack(1)
struct S { char a, b, c; };

enum E { E_VALUE = 0 };
enum class EC { VALUE = 0 };

bool AsBool() { return false; }
int AsInt() { return 0; }
E AsEnum() { return E_VALUE; }
EC AsEnumClass() { return EC::VALUE; }
S AsStruct() { return {}; }

struct M {
int AsInt() { return 0; }
E AsEnum() { return E_VALUE; }
S AsStruct() { return {}; }
};

int Test1(const char* ptr) {
int sum = 0;
sum += sizeof(LEN);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
sum += sizeof(LEN + 1);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
sum += sizeof(sum, LEN);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(..., ...)'
sum += sizeof(AsBool());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(AsInt());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(AsEnum());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(AsEnumClass());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(M{}.AsInt());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(M{}.AsEnum());
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer
sum += sizeof(sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + LEN + sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + (LEN + sizeof(X)));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + -sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(LEN + - + -sizeof(X));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
sum += sizeof(char) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
sum += sizeof(A) / sizeof(S);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(char) / sizeof(int);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(char) / sizeof(A);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(B[0]) / sizeof(A);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
sum += sizeof(ptr) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(ptr[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(char*);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(void*);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(const void volatile*);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(ptr) / sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(int) * sizeof(char);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += sizeof(ptr) * sizeof(ptr[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += sizeof(int) * (2 * sizeof(char));
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication
sum += (2 * sizeof(char)) * sizeof(int);
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious 'sizeof' by 'sizeof' multiplication
if (sizeof(A) < 0x100000) sum += 42;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant
if (sizeof(A) <= 0xFFFFFFFEU) sum += 42;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant
return sum;
}

int Test5() {
typedef int Array10[10];
typedef C ArrayC[10];

struct MyStruct {
Array10 arr;
Array10* ptr;
};
typedef const MyStruct TMyStruct;
typedef const MyStruct *PMyStruct;
typedef TMyStruct *PMyStruct2;

static TMyStruct kGlocalMyStruct = {};
static TMyStruct volatile * kGlocalMyStructPtr = &kGlocalMyStruct;

MyStruct S;
PMyStruct PS;
PMyStruct2 PS2;
Array10 A10;
C *PtrArray[10];
C *PC;

char *PChar;
int *PInt, **PPInt;
MyStruct **PPMyStruct;

int sum = 0;
sum += sizeof(&S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&kGlocalMyStruct.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&kGlocalMyStructPtr->arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(S.arr + 0);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(+ S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof((int*)S.arr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

sum += sizeof(S.ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(kGlocalMyStruct.ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(kGlocalMyStructPtr->ptr);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

sum += sizeof(&kGlocalMyStruct);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&S);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(MyStruct*);
sum += sizeof(PMyStruct);
sum += sizeof(PS);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PS2);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(&A10);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PtrArray) / sizeof(PtrArray[1]);
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(A10) / sizeof(PtrArray[0]);
sum += sizeof(PC) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
// CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
sum += sizeof(ArrayC) / sizeof(PtrArray[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator

sum += sizeof(PChar);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PInt);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PPInt);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(PPMyStruct);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer

return sum;
}

void some_generic_function(const void *arg, int argsize);
int *IntP, **IntPP;
C *ClassP, **ClassPP;

void GenericFunctionTest() {
// The `sizeof(pointer)` checks ignore situations where the pointer is
// produced by dereferencing a pointer-to-pointer, because this is unlikely
// to be an accident and can appear in legitimate code that tries to call
// a generic function which emulates dynamic typing within C.
some_generic_function(IntPP, sizeof(*IntPP));
some_generic_function(ClassPP, sizeof(*ClassPP));
// Using `...[0]` instead of the dereference operator is another common
// variant, which is also widespread in the idiomatic array-size calculation:
// `sizeof(array) / sizeof(array[0])`.
some_generic_function(IntPP, sizeof(IntPP[0]));
some_generic_function(ClassPP, sizeof(ClassPP[0]));
// FIXME: There is a third common pattern where the generic function is
// called with `&Variable` and `sizeof(Variable)`. Right now these are
// reported by the `sizeof(pointer)` checks, but this causes some false
// positives, so it would be good to create an exception for them.
some_generic_function(&IntPP, sizeof(IntP));
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
some_generic_function(&ClassPP, sizeof(ClassP));
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
}

int ValidExpressions() {
int A[] = {1, 2, 3, 4};
static const char str[] = "hello";
static const char* ptr[] { "aaa", "bbb", "ccc" };
typedef C *CA10[10];
C *PtrArray[10];
CA10 PtrArray1;

int sum = 0;
if (sizeof(A) < 10)
sum += sizeof(A);
sum += sizeof(int);
sum += sizeof(AsStruct());
sum += sizeof(M{}.AsStruct());
sum += sizeof(A[sizeof(A) / sizeof(int)]);
// Here the outer sizeof is reported, but the inner ones are accepted:
sum += sizeof(&A[sizeof(A) / sizeof(int)]);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer
sum += sizeof(sizeof(0)); // Special case: sizeof size_t.
sum += sizeof(void*);
sum += sizeof(void const *);
sum += sizeof(void const *) / 4;
sum += sizeof(str);
sum += sizeof(str) / sizeof(char);
sum += sizeof(str) / sizeof(str[0]);
sum += sizeof(ptr) / sizeof(ptr[0]);
sum += sizeof(ptr) / sizeof(*(ptr));
sum += sizeof(PtrArray) / sizeof(PtrArray[0]);
// Canonical type of PtrArray1 is same as PtrArray.
sum = sizeof(PtrArray) / sizeof(PtrArray1[0]);
// There is no warning for 'sizeof(T*)/sizeof(Q)' case.
sum += sizeof(PtrArray) / sizeof(A[0]);
return sum;
}
Loading