250 changes: 250 additions & 0 deletions bolt/lib/Passes/ContinuityStats.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
//===- bolt/Passes/ContinuityStats.cpp --------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the continuity stats calculation pass.
//
//===----------------------------------------------------------------------===//

#include "bolt/Passes/ContinuityStats.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/Support/CommandLine.h"
#include <queue>
#include <unordered_map>
#include <unordered_set>

#define DEBUG_TYPE "bolt-opts"

using namespace llvm;
using namespace bolt;

namespace opts {
extern cl::opt<unsigned> Verbosity;
cl::opt<unsigned> NumFunctionsForContinuityCheck(
"num-functions-for-continuity-check",
cl::desc("number of hottest functions to print aggregated "
"CFG discontinuity stats of."),
cl::init(1000), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltOptCategory));
} // namespace opts

namespace {
using FunctionListType = std::vector<const BinaryFunction *>;
using function_iterator = FunctionListType::iterator;

template <typename T>
void printDistribution(raw_ostream &OS, std::vector<T> &values,
bool Fraction = false) {
if (values.empty())
return;
// Sort values from largest to smallest and print the MAX, TOP 1%, 5%, 10%,
// 20%, 50%, 80%, MIN. If Fraction is true, then values are printed as
// fractions instead of integers.
std::sort(values.begin(), values.end());

auto printLine = [&](std::string Text, double Percent) {
int Rank = int(values.size() * (1.0 - Percent / 100));
if (Percent == 0)
Rank = values.size() - 1;
if (Fraction)
OS << " " << Text << std::string(9 - Text.length(), ' ') << ": "
<< format("%.2lf%%", values[Rank] * 100) << "\n";
else
OS << " " << Text << std::string(9 - Text.length(), ' ') << ": "
<< values[Rank] << "\n";
};

printLine("MAX", 0);
const int percentages[] = {1, 5, 10, 20, 50, 80};
for (size_t i = 0; i < sizeof(percentages) / sizeof(percentages[0]); ++i) {
printLine("TOP " + std::to_string(percentages[i]) + "%", percentages[i]);
}
printLine("MIN", 100);
}

void printCFGContinuityStats(raw_ostream &OS,
iterator_range<function_iterator> &Functions) {
// Given a perfect profile, every positive-execution-count BB should be
// connected to an entry of the function through a positive-execution-count
// directed path in the control flow graph.
std::vector<size_t> NumUnreachables;
std::vector<size_t> SumECUnreachables;
std::vector<double> FractionECUnreachables;

for (auto it = Functions.begin(); it != Functions.end(); ++it) {
const BinaryFunction *Function = *it;
if (Function->size() <= 1)
continue;

// Compute the sum of all BB execution counts (ECs).
size_t NumPosECBBs = 0;
size_t SumAllBBEC = 0;
for (const BinaryBasicBlock &BB : *Function) {
const size_t BBEC = BB.getKnownExecutionCount();
NumPosECBBs += BBEC > 0 ? 1 : 0;
SumAllBBEC += BBEC;
}

// Perform BFS on subgraph of CFG induced by positive weight edges.
// Compute the number of BBs reachable from the entry(s) of the function and
// the sum of their execution counts (ECs).
std::unordered_map<unsigned, const BinaryBasicBlock *> IndexToBB;
std::unordered_set<unsigned> Visited;
std::queue<unsigned> Queue;
for (const BinaryBasicBlock &BB : *Function) {
// Make sure BB.getIndex() is not already in IndexToBB.
assert(IndexToBB.find(BB.getIndex()) == IndexToBB.end());
IndexToBB[BB.getIndex()] = &BB;
if (BB.isEntryPoint() && BB.getKnownExecutionCount() > 0) {
Queue.push(BB.getIndex());
Visited.insert(BB.getIndex());
}
}
while (!Queue.empty()) {
const unsigned BBIndex = Queue.front();
const BinaryBasicBlock *BB = IndexToBB[BBIndex];
Queue.pop();
auto SuccBIIter = BB->branch_info_begin();
for (const BinaryBasicBlock *Succ : BB->successors()) {
const uint64_t Count = SuccBIIter->Count;
if (Count == BinaryBasicBlock::COUNT_NO_PROFILE || Count == 0) {
++SuccBIIter;
continue;
}
if (!Visited.insert(Succ->getIndex()).second) {
++SuccBIIter;
continue;
}
Queue.push(Succ->getIndex());
++SuccBIIter;
}
}

const size_t NumReachableBBs = Visited.size();

// Loop through Visited, and sum the corresponding BBs' execution counts
// (ECs).
size_t SumReachableBBEC = 0;
for (const unsigned BBIndex : Visited) {
const BinaryBasicBlock *BB = IndexToBB[BBIndex];
SumReachableBBEC += BB->getKnownExecutionCount();
}

const size_t NumPosECBBsUnreachableFromEntry =
NumPosECBBs - NumReachableBBs;
const size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC;
const double FractionECUnreachable =
(double)SumUnreachableBBEC / SumAllBBEC;

if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05) {
OS << "Non-trivial CFG discontinuity observed in function "
<< Function->getPrintName() << "\n";
LLVM_DEBUG(Function->dump());
}

NumUnreachables.push_back(NumPosECBBsUnreachableFromEntry);
SumECUnreachables.push_back(SumUnreachableBBEC);
FractionECUnreachables.push_back(FractionECUnreachable);
}

if (FractionECUnreachables.empty())
return;

std::sort(FractionECUnreachables.begin(), FractionECUnreachables.end());
const int Rank = int(FractionECUnreachables.size() * 0.95);
OS << format("top 5%% function CFG discontinuity is %.2lf%%\n",
FractionECUnreachables[Rank] * 100);

if (opts::Verbosity >= 1) {
OS << "abbreviations: EC = execution count, POS BBs = positive EC BBs\n"
<< "distribution of NUM(unreachable POS BBs) among all focal "
"functions\n";
printDistribution(OS, NumUnreachables);

OS << "distribution of SUM_EC(unreachable POS BBs) among all focal "
"functions\n";
printDistribution(OS, SumECUnreachables);

OS << "distribution of [(SUM_EC(unreachable POS BBs) / SUM_EC(all "
"POS BBs))] among all focal functions\n";
printDistribution(OS, FractionECUnreachables, /*Fraction=*/true);
}
}

void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
size_t NumTopFunctions) {
// Sort the list of functions by execution counts (reverse).
llvm::sort(ValidFunctions,
[&](const BinaryFunction *A, const BinaryFunction *B) {
return A->getKnownExecutionCount() > B->getKnownExecutionCount();
});

const size_t RealNumTopFunctions =
std::min(NumTopFunctions, ValidFunctions.size());

iterator_range<function_iterator> Functions(
ValidFunctions.begin(), ValidFunctions.begin() + RealNumTopFunctions);

BC.outs() << format("BOLT-INFO: among the hottest %zu functions ",
RealNumTopFunctions);
printCFGContinuityStats(BC.outs(), Functions);

// Print more detailed bucketed stats if requested.
if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5) {
const size_t PerBucketSize = RealNumTopFunctions / 5;
BC.outs() << format(
"Detailed stats for 5 buckets, each with %zu functions:\n",
PerBucketSize);

// For each bucket, print the CFG continuity stats of the functions in the
// bucket.
for (size_t BucketIndex = 0; BucketIndex < 5; ++BucketIndex) {
const size_t StartIndex = BucketIndex * PerBucketSize;
const size_t EndIndex = StartIndex + PerBucketSize;
iterator_range<function_iterator> Functions(
ValidFunctions.begin() + StartIndex,
ValidFunctions.begin() + EndIndex);
const size_t MaxFunctionExecutionCount =
ValidFunctions[StartIndex]->getKnownExecutionCount();
const size_t MinFunctionExecutionCount =
ValidFunctions[EndIndex - 1]->getKnownExecutionCount();
BC.outs() << format("----------------\n| Bucket %zu: "
"|\n----------------\n",
BucketIndex + 1)
<< format(
"execution counts of the %zu functions in the bucket: "
"%zu-%zu\n",
EndIndex - StartIndex, MinFunctionExecutionCount,
MaxFunctionExecutionCount);
printCFGContinuityStats(BC.outs(), Functions);
}
}
}
} // namespace

bool PrintContinuityStats::shouldOptimize(const BinaryFunction &BF) const {
if (BF.empty() || !BF.hasValidProfile())
return false;

return BinaryFunctionPass::shouldOptimize(BF);
}

Error PrintContinuityStats::runOnFunctions(BinaryContext &BC) {
// Create a list of functions with valid profiles.
FunctionListType ValidFunctions;
for (const auto &BFI : BC.getBinaryFunctions()) {
const BinaryFunction *Function = &BFI.second;
if (PrintContinuityStats::shouldOptimize(*Function))
ValidFunctions.push_back(Function);
}
if (ValidFunctions.empty() || opts::NumFunctionsForContinuityCheck == 0)
return Error::success();

printAll(BC, ValidFunctions, opts::NumFunctionsForContinuityCheck);
return Error::success();
}
6 changes: 2 additions & 4 deletions bolt/lib/Passes/Instrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,8 @@ static bool hasAArch64ExclusiveMemop(
BinaryBasicBlock *BB = BBQueue.front().first;
bool IsLoad = BBQueue.front().second;
BBQueue.pop();
if (Visited.find(BB) != Visited.end())
if (!Visited.insert(BB).second)
continue;
Visited.insert(BB);

for (const MCInst &Inst : *BB) {
// Two loads one after another - skip whole function
Expand All @@ -126,8 +125,7 @@ static bool hasAArch64ExclusiveMemop(
if (BC.MIB->isAArch64ExclusiveLoad(Inst))
IsLoad = true;

if (IsLoad && BBToSkip.find(BB) == BBToSkip.end()) {
BBToSkip.insert(BB);
if (IsLoad && BBToSkip.insert(BB).second) {
if (opts::Verbosity >= 2) {
outs() << "BOLT-INSTRUMENTER: skip BB " << BB->getName()
<< " due to exclusive instruction in function "
Expand Down
22 changes: 10 additions & 12 deletions bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {

// Output addresses are delta-encoded
uint64_t PrevAddress = 0;
writeMaps</*Cold=*/false>(Maps, PrevAddress, OS);
writeMaps</*Cold=*/true>(Maps, PrevAddress, OS);
writeMaps</*Cold=*/false>(PrevAddress, OS);
writeMaps</*Cold=*/true>(PrevAddress, OS);

BC.outs() << "BOLT-INFO: Wrote " << Maps.size() << " BAT maps\n";
BC.outs() << "BOLT-INFO: Wrote " << FuncHashes.getNumFunctions()
Expand Down Expand Up @@ -182,8 +182,7 @@ size_t BoltAddressTranslation::getNumEqualOffsets(const MapTy &Map,
}

template <bool Cold>
void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
uint64_t &PrevAddress, raw_ostream &OS) {
void BoltAddressTranslation::writeMaps(uint64_t &PrevAddress, raw_ostream &OS) {
const uint32_t NumFuncs =
llvm::count_if(llvm::make_first_range(Maps), [&](const uint64_t Address) {
return Cold == ColdPartSource.count(Address);
Expand Down Expand Up @@ -213,16 +212,17 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
: 0;
uint32_t Skew = 0;
if (Cold) {
auto HotEntryIt = Maps.find(ColdPartSource[Address]);
assert(HotEntryIt != Maps.end());
size_t HotIndex = std::distance(Maps.begin(), HotEntryIt);
auto HotEntryIt = llvm::lower_bound(HotFuncs, ColdPartSource[Address]);
assert(HotEntryIt != HotFuncs.end());
size_t HotIndex = std::distance(HotFuncs.begin(), HotEntryIt);
encodeULEB128(HotIndex - PrevIndex, OS);
PrevIndex = HotIndex;
// Skew of all input offsets for cold fragments is simply the first input
// offset.
Skew = Map.begin()->second >> 1;
encodeULEB128(Skew, OS);
} else {
HotFuncs.push_back(Address);
// Function hash
size_t BFHash = getBFHash(HotInputAddress);
LLVM_DEBUG(dbgs() << "Hash: " << formatv("{0:x}\n", BFHash));
Expand Down Expand Up @@ -311,17 +311,15 @@ std::error_code BoltAddressTranslation::parse(raw_ostream &OS, StringRef Buf) {
return make_error_code(llvm::errc::io_error);

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

template <bool Cold>
void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
uint64_t &PrevAddress, DataExtractor &DE,
void BoltAddressTranslation::parseMaps(uint64_t &PrevAddress, DataExtractor &DE,
uint64_t &Offset, Error &Err) {
const uint32_t NumFunctions = DE.getULEB128(&Offset, &Err);
LLVM_DEBUG(dbgs() << "Parsing " << NumFunctions << (Cold ? " cold" : "")
Expand Down
6 changes: 1 addition & 5 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,11 +643,7 @@ size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) {
// equal number of blocks.
if (NamespaceToProfiledBFSizesIt->second.count(BF->size()) == 0)
continue;
auto NamespaceToBFsIt = NamespaceToBFs.find(Namespace);
if (NamespaceToBFsIt == NamespaceToBFs.end())
NamespaceToBFs[Namespace] = {BF};
else
NamespaceToBFsIt->second.push_back(BF);
NamespaceToBFs[Namespace].push_back(BF);
}

// Iterates through all profiled functions and binary functions belonging to
Expand Down
3 changes: 3 additions & 0 deletions bolt/lib/Rewrite/BinaryPassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "bolt/Passes/AllocCombiner.h"
#include "bolt/Passes/AsmDump.h"
#include "bolt/Passes/CMOVConversion.h"
#include "bolt/Passes/ContinuityStats.h"
#include "bolt/Passes/FixRISCVCallsPass.h"
#include "bolt/Passes/FixRelaxationPass.h"
#include "bolt/Passes/FrameOptimizer.h"
Expand Down Expand Up @@ -373,6 +374,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
if (opts::PrintProfileStats)
Manager.registerPass(std::make_unique<PrintProfileStats>(NeverPrint));

Manager.registerPass(std::make_unique<PrintContinuityStats>(NeverPrint));

Manager.registerPass(std::make_unique<ValidateInternalCalls>(NeverPrint));

Manager.registerPass(std::make_unique<ValidateMemRefs>(NeverPrint));
Expand Down
49 changes: 49 additions & 0 deletions bolt/test/AArch64/adr-relaxation.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Check that llvm-bolt will not unnecessarily relax ADR instruction.

# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static
# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=random2
# RUN: llvm-objdump -d --disassemble-symbols=_start %t.bolt | FileCheck %s
# RUN: llvm-objdump -d --disassemble-symbols=foo.cold.0 %t.bolt \
# RUN: | FileCheck --check-prefix=CHECK-FOO %s

## ADR below references its containing function that is split. But ADR is always
## in the main fragment, thus there is no need to relax it.
.text
.globl _start
.type _start, %function
_start:
# CHECK: <_start>:
.cfi_startproc
adr x1, _start
# CHECK-NOT: adrp
# CHECK: adr
cmp x1, x11
b.hi .L1
mov x0, #0x0
.L1:
ret x30
.cfi_endproc
.size _start, .-_start


## In foo, ADR is in the split fragment but references the main one. Thus, it
## needs to be relaxed into ADRP + ADD.
.globl foo
.type foo, %function
foo:
.cfi_startproc
cmp x1, x11
b.hi .L2
mov x0, #0x0
.L2:
# CHECK-FOO: <foo.cold.0>:
adr x1, foo
# CHECK-FOO: adrp
# CHECK-FOO-NEXT: add
ret x30
.cfi_endproc
.size foo, .-foo

## Force relocation mode.
.reloc 0, R_AARCH64_NONE
4 changes: 4 additions & 0 deletions bolt/test/X86/cfg-discontinuity-reporting.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Check profile discontinuity reporting
RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe
RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt | FileCheck %s
CHECK: among the hottest 5 functions top 5% function CFG discontinuity is 100.00%
4 changes: 2 additions & 2 deletions bolt/tools/driver/llvm-bolt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ int main(int argc, char **argv) {

ToolName = argv[0];

if (llvm::sys::path::filename(ToolName) == "perf2bolt")
if (llvm::sys::path::filename(ToolName).starts_with("perf2bolt"))
perf2boltMode(argc, argv);
else if (llvm::sys::path::filename(ToolName) == "llvm-boltdiff")
else if (llvm::sys::path::filename(ToolName).starts_with("llvm-boltdiff"))
boltDiffMode(argc, argv);
else
boltMode(argc, argv);
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-apply-replacements/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangApplyReplacements
add_clang_library(clangApplyReplacements STATIC
lib/Tooling/ApplyReplacements.cpp

DEPENDS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,8 @@ groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,

if (auto Entry = SM.getFileManager().getOptionalFileRef(Path)) {
if (SourceTU) {
auto &Replaces = DiagReplacements[*Entry];
auto It = Replaces.find(R);
if (It == Replaces.end())
Replaces.emplace(R, SourceTU);
else if (It->second != SourceTU)
auto [It, Inserted] = DiagReplacements[*Entry].try_emplace(R, SourceTU);
if (!Inserted && It->second != SourceTU)
// This replacement is a duplicate of one suggested by another TU.
return;
}
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-change-namespace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangChangeNamespace
add_clang_library(clangChangeNamespace STATIC
ChangeNamespace.cpp

DEPENDS
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,9 +606,8 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
// If this reference has been processed as a function call, we do not
// process it again.
if (ProcessedFuncRefs.count(FuncRef))
if (!ProcessedFuncRefs.insert(FuncRef).second)
return;
ProcessedFuncRefs.insert(FuncRef);
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
assert(Func);
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangDoc
add_clang_library(clangDoc STATIC
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,7 @@ Example usage for a project using a compile commands database:
llvm::StringMap<std::vector<StringRef>> USRToBitcode;
Executor->get()->getToolResults()->forEachResult(
[&](StringRef Key, StringRef Value) {
auto R = USRToBitcode.try_emplace(Key, std::vector<StringRef>());
R.first->second.emplace_back(Value);
USRToBitcode[Key].emplace_back(Value);
});

// Collects all Infos according to their unique USR value. This map is added
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-include-fixer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
support
)

add_clang_library(clangIncludeFixer
add_clang_library(clangIncludeFixer STATIC
IncludeFixer.cpp
IncludeFixerContext.cpp
InMemorySymbolIndex.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(findAllSymbols
add_clang_library(findAllSymbols STATIC
FindAllSymbols.cpp
FindAllSymbolsAction.cpp
FindAllMacros.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_clang_library(clangIncludeFixerPlugin
add_clang_library(clangIncludeFixerPlugin STATIC
IncludeFixerPlugin.cpp

LINK_LIBS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-move/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangMove
add_clang_library(clangMove STATIC
Move.cpp
HelperDeclRefGraph.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-query/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangQuery
add_clang_library(clangQuery STATIC
Query.cpp
QueryParser.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-reorder-fields/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
support
)

add_clang_library(clangReorderFields
add_clang_library(clangReorderFields STATIC
ReorderFieldsAction.cpp

DEPENDS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ configure_file(
${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})

add_clang_library(clangTidy
add_clang_library(clangTidy STATIC
ClangTidy.cpp
ClangTidyCheck.cpp
ClangTidyModule.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/abseil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyAbseilModule
add_clang_library(clangTidyAbseilModule STATIC
AbseilTidyModule.cpp
CleanupCtadCheck.cpp
DurationAdditionCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/altera/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
support
)

add_clang_library(clangTidyAlteraModule
add_clang_library(clangTidyAlteraModule STATIC
AlteraTidyModule.cpp
IdDependentBackwardBranchCheck.cpp
KernelNameRestrictionCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyAndroidModule
add_clang_library(clangTidyAndroidModule STATIC
AndroidTidyModule.cpp
CloexecAccept4Check.cpp
CloexecAcceptCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/boost/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyBoostModule
add_clang_library(clangTidyBoostModule STATIC
BoostTidyModule.cpp
UseRangesCheck.cpp
UseToStringCheck.cpp
Expand Down
18 changes: 9 additions & 9 deletions clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
ReplacerMap Results;
static const Signature SingleSig = {{0}};
static const Signature TwoSig = {{0}, {2}};
static const auto AddFrom =
const auto AddFrom =
[&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
std::initializer_list<StringRef> Names, StringRef Prefix) {
llvm::SmallString<64> Buffer;
Expand All @@ -214,17 +214,17 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
}
};

static const auto AddFromStd =
[](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
std::initializer_list<StringRef> Names) {
const auto AddFromStd =
[&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
std::initializer_list<StringRef> Names) {
AddFrom(Replacer, Names, "std");
};

static const auto AddFromBoost =
[](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
std::initializer_list<
std::pair<StringRef, std::initializer_list<StringRef>>>
NamespaceAndNames) {
const auto AddFromBoost =
[&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
std::initializer_list<
std::pair<StringRef, std::initializer_list<StringRef>>>
NamespaceAndNames) {
for (auto [Namespace, Names] : NamespaceAndNames)
AddFrom(Replacer, Names,
SmallString<64>{"boost", (Namespace.empty() ? "" : "::"),
Expand Down
46 changes: 46 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===--- BitwisePointerCastCheck.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 "BitwisePointerCastCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::bugprone {

void BitwisePointerCastCheck::registerMatchers(MatchFinder *Finder) {
if (getLangOpts().CPlusPlus20) {
auto IsPointerType = refersToType(qualType(isAnyPointer()));
Finder->addMatcher(callExpr(hasDeclaration(functionDecl(allOf(
hasName("::std::bit_cast"),
hasTemplateArgument(0, IsPointerType),
hasTemplateArgument(1, IsPointerType)))))
.bind("bit_cast"),
this);
}

auto IsDoublePointerType =
hasType(qualType(pointsTo(qualType(isAnyPointer()))));
Finder->addMatcher(callExpr(hasArgument(0, IsDoublePointerType),
hasArgument(1, IsDoublePointerType),
hasDeclaration(functionDecl(hasName("::memcpy"))))
.bind("memcpy"),
this);
}

void BitwisePointerCastCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("bit_cast"))
diag(Call->getBeginLoc(),
"do not use 'std::bit_cast' to cast between pointers")
<< Call->getSourceRange();
else if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("memcpy"))
diag(Call->getBeginLoc(), "do not use 'memcpy' to cast between pointers")
<< Call->getSourceRange();
}

} // namespace clang::tidy::bugprone
34 changes: 34 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- BitwisePointerCastCheck.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_BUGPRONE_BITWISEPOINTERCASTCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::bugprone {

/// Warns about code that tries to cast between pointers by means of
/// ``std::bit_cast`` or ``memcpy``.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/bitwise-pointer-cast.html
class BitwisePointerCastCheck : public ClangTidyCheck {
public:
BitwisePointerCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
};

} // namespace clang::tidy::bugprone

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "AssertSideEffectCheck.h"
#include "AssignmentInIfConditionCheck.h"
#include "BadSignalToKillThreadCheck.h"
#include "BitwisePointerCastCheck.h"
#include "BoolPointerImplicitConversionCheck.h"
#include "BranchCloneCheck.h"
#include "CastingThroughVoidCheck.h"
Expand Down Expand Up @@ -109,6 +110,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-assignment-in-if-condition");
CheckFactories.registerCheck<BadSignalToKillThreadCheck>(
"bugprone-bad-signal-to-kill-thread");
CheckFactories.registerCheck<BitwisePointerCastCheck>(
"bugprone-bitwise-pointer-cast");
CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
"bugprone-bool-pointer-implicit-conversion");
CheckFactories.registerCheck<BranchCloneCheck>("bugprone-branch-clone");
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyBugproneModule
add_clang_library(clangTidyBugproneModule STATIC
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
AssignmentInIfConditionCheck.cpp
BadSignalToKillThreadCheck.cpp
BitwisePointerCastCheck.cpp
BoolPointerImplicitConversionCheck.cpp
BranchCloneCheck.cpp
BugproneTidyModule.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ void CrtpConstructorAccessibilityCheck::check(

WithFriendHintIfNeeded(
diag(Ctor->getLocation(),
"%0 contructor allows the CRTP to be %select{inherited "
"%0 constructor allows the CRTP to be %select{inherited "
"from|constructed}1 as a regular template class; consider making "
"it private%select{| and declaring the derived class as friend}2")
<< Access << IsPublic << NeedsFriend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,13 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
}
// Check if a definition in another namespace exists.
const auto DeclName = CurDecl->getName();
if (!DeclNameToDefinitions.contains(DeclName)) {
auto It = DeclNameToDefinitions.find(DeclName);
if (It == DeclNameToDefinitions.end()) {
continue; // No definition in this translation unit, we can skip it.
}
// Make a warning for each definition with the same name (in other
// namespaces).
const auto &Definitions = DeclNameToDefinitions[DeclName];
const auto &Definitions = It->second;
for (const auto *Def : Definitions) {
diag(CurDecl->getLocation(),
"no definition found for %0, but a definition with "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
Options.get("WarnOnSizeOfCompareToConstant", true)),
WarnOnSizeOfPointerToAggregate(
Options.get("WarnOnSizeOfPointerToAggregate", true)),
WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)) {}
WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)),
WarnOnOffsetDividedBySizeOf(
Options.get("WarnOnOffsetDividedBySizeOf", true)) {}

void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
Expand All @@ -82,6 +84,8 @@ void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfPointerToAggregate",
WarnOnSizeOfPointerToAggregate);
Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer);
Options.store(Opts, "WarnOnOffsetDividedBySizeOf",
WarnOnOffsetDividedBySizeOf);
}

void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
Expand Down Expand Up @@ -307,7 +311,8 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
offsetOfExpr()))
.bind("sizeof-in-ptr-arithmetic-scale-expr");
const auto PtrArithmeticIntegerScaleExpr = binaryOperator(
hasAnyOperatorName("*", "/"),
WarnOnOffsetDividedBySizeOf ? binaryOperator(hasAnyOperatorName("*", "/"))
: binaryOperator(hasOperatorName("*")),
// sizeof(...) * sizeof(...) and sizeof(...) / sizeof(...) is handled
// by this check on another path.
hasOperands(expr(hasType(isInteger()), unless(SizeofLikeScaleExpr)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SizeofExpressionCheck : public ClangTidyCheck {
const bool WarnOnSizeOfCompareToConstant;
const bool WarnOnSizeOfPointerToAggregate;
const bool WarnOnSizeOfPointer;
const bool WarnOnOffsetDividedBySizeOf;
};

} // namespace clang::tidy::bugprone
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/cert/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyCERTModule
add_clang_library(clangTidyCERTModule STATIC
CERTTidyModule.cpp
CommandProcessorCheck.cpp
DefaultOperatorNewAlignmentCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyConcurrencyModule
add_clang_library(clangTidyConcurrencyModule STATIC
ConcurrencyTidyModule.cpp
MtUnsafeCheck.cpp
ThreadCanceltypeAsynchronousCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyCppCoreGuidelinesModule
add_clang_library(clangTidyCppCoreGuidelinesModule STATIC
AvoidCapturingLambdaCoroutinesCheck.cpp
AvoidConstOrRefDataMembersCheck.cpp
AvoidDoWhileCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,8 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer(
// It only includes fields that have not been fixed
SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) {
if (!HasRecordClassMemberSet.contains(F)) {
if (HasRecordClassMemberSet.insert(F).second)
AllFieldsToInit.insert(F);
HasRecordClassMemberSet.insert(F);
}
});
if (FieldsToInit.empty())
return;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/darwin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyDarwinModule
add_clang_library(clangTidyDarwinModule STATIC
AvoidSpinlockCheck.cpp
DarwinTidyModule.cpp
DispatchOnceNonstaticCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyFuchsiaModule
add_clang_library(clangTidyFuchsiaModule STATIC
DefaultArgumentsCallsCheck.cpp
DefaultArgumentsDeclarationsCheck.cpp
FuchsiaTidyModule.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/google/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyGoogleModule
add_clang_library(clangTidyGoogleModule STATIC
AvoidCStyleCastsCheck.cpp
AvoidNSObjectNewCheck.cpp
AvoidThrowingObjCExceptionCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyHICPPModule
add_clang_library(clangTidyHICPPModule STATIC
ExceptionBaseclassCheck.cpp
HICPPTidyModule.cpp
IgnoredRemoveResultCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyLinuxKernelModule
add_clang_library(clangTidyLinuxKernelModule STATIC
LinuxKernelTidyModule.cpp
MustCheckErrsCheck.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyLLVMModule
add_clang_library(clangTidyLLVMModule STATIC
HeaderGuardCheck.cpp
IncludeOrderCheck.cpp
LLVMTidyModule.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyLLVMLibcModule
add_clang_library(clangTidyLLVMLibcModule STATIC
CalleeNamespaceCheck.cpp
ImplementationInNamespaceCheck.cpp
InlineFunctionDeclCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ add_custom_command(
add_custom_target(genconfusable DEPENDS Confusables.inc)
set_target_properties(genconfusable PROPERTIES FOLDER "Clang Tools Extra/Sourcegenning")

add_clang_library(clangTidyMiscModule
add_clang_library(clangTidyMiscModule STATIC
ConstCorrectnessCheck.cpp
CoroutineHostileRAIICheck.cpp
DefinitionsInHeadersCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ static bool mayShadow(const NamedDecl *ND0,
const ConfusableIdentifierCheck::ContextInfo *
ConfusableIdentifierCheck::getContextInfo(const DeclContext *DC) {
const DeclContext *PrimaryContext = DC->getPrimaryContext();
auto It = ContextInfos.find(PrimaryContext);
if (It != ContextInfos.end())
auto [It, Inserted] = ContextInfos.try_emplace(PrimaryContext);
if (!Inserted)
return &It->second;

ContextInfo &Info = ContextInfos[PrimaryContext];
ContextInfo &Info = It->second;
Info.PrimaryContext = PrimaryContext;
Info.NonTransparentContext = PrimaryContext;

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyModernizeModule
add_clang_library(clangTidyModernizeModule STATIC
AvoidBindCheck.cpp
AvoidCArraysCheck.cpp
ConcatNestedNamespacesCheck.cpp
Expand Down
226 changes: 135 additions & 91 deletions clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "UseStartsEndsWithCheck.h"

#include "../utils/ASTUtils.h"
#include "../utils/OptionsUtils.h"
#include "clang/Lex/Lexer.h"

Expand All @@ -16,13 +17,71 @@
using namespace clang::ast_matchers;

namespace clang::tidy::modernize {
struct NotLengthExprForStringNode {
NotLengthExprForStringNode(std::string ID, DynTypedNode Node,
ASTContext *Context)
: ID(std::move(ID)), Node(std::move(Node)), Context(Context) {}
bool operator()(const internal::BoundNodesMap &Nodes) const {
// Match a string literal and an integer size or strlen() call.
if (const auto *StringLiteralNode = Nodes.getNodeAs<StringLiteral>(ID)) {
if (const auto *IntegerLiteralSizeNode = Node.get<IntegerLiteral>()) {
return StringLiteralNode->getLength() !=
IntegerLiteralSizeNode->getValue().getZExtValue();
}

if (const auto *StrlenNode = Node.get<CallExpr>()) {
if (StrlenNode->getDirectCallee()->getName() != "strlen" ||
StrlenNode->getNumArgs() != 1) {
return true;
}

if (const auto *StrlenArgNode = dyn_cast<StringLiteral>(
StrlenNode->getArg(0)->IgnoreParenImpCasts())) {
return StrlenArgNode->getLength() != StringLiteralNode->getLength();
}
}
}

// Match a string variable and a call to length() or size().
if (const auto *ExprNode = Nodes.getNodeAs<Expr>(ID)) {
if (const auto *MemberCallNode = Node.get<CXXMemberCallExpr>()) {
const CXXMethodDecl *MethodDeclNode = MemberCallNode->getMethodDecl();
const StringRef Name = MethodDeclNode->getName();
if (!MethodDeclNode->isConst() || MethodDeclNode->getNumParams() != 0 ||
(Name != "size" && Name != "length")) {
return true;
}

if (const auto *OnNode =
dyn_cast<Expr>(MemberCallNode->getImplicitObjectArgument())) {
return !utils::areStatementsIdentical(OnNode->IgnoreParenImpCasts(),
ExprNode->IgnoreParenImpCasts(),
*Context);
}
}
}

return true;
}

private:
std::string ID;
DynTypedNode Node;
ASTContext *Context;
};

AST_MATCHER_P(Expr, lengthExprForStringNode, std::string, ID) {
return Builder->removeBindings(NotLengthExprForStringNode(
ID, DynTypedNode::create(Node), &(Finder->getASTContext())));
}

UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}

void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
const auto ZeroLiteral = integerLiteral(equals(0));

const auto HasStartsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
Expand All @@ -32,119 +91,104 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
anyOf(HasStartsWithMethodWithName("starts_with"),
HasStartsWithMethodWithName("startsWith"),
HasStartsWithMethodWithName("startswith"));
const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf(
HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(HasStartsWithMethod)))))));
const auto OnClassWithStartsWithFunction =
on(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasStartsWithMethod,
hasAnyBase(hasType(hasCanonicalType(
hasDeclaration(cxxRecordDecl(HasStartsWithMethod)))))))))));

const auto HasEndsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
.bind("ends_with_fun"));
};
const auto HasEndsWithMethod = anyOf(HasEndsWithMethodWithName("ends_with"),
HasEndsWithMethodWithName("endsWith"),
HasEndsWithMethodWithName("endswith"));
const auto OnClassWithEndsWithFunction =
on(expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasEndsWithMethod,
hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(HasEndsWithMethod)))))))))))
.bind("haystack"));

// Case 1: X.find(Y) [!=]= 0 -> starts_with.
const auto FindExpr = cxxMemberCallExpr(
// A method call with no second argument or the second argument is zero...
anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
// ... named find...
callee(cxxMethodDecl(hasName("find")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));

// Case 2: X.rfind(Y, 0) [!=]= 0 -> starts_with.
const auto RFindExpr = cxxMemberCallExpr(
// A method call with a second argument of zero...
hasArgument(1, ZeroLiteral),
// ... named rfind...
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));

// Match a string literal and an integer or strlen() call matching the length.
const auto HasStringLiteralAndLengthArgs = [](const auto StringArgIndex,
const auto LengthArgIndex) {
return allOf(
hasArgument(StringArgIndex, stringLiteral().bind("string_literal_arg")),
hasArgument(LengthArgIndex,
anyOf(integerLiteral().bind("integer_literal_size_arg"),
callExpr(callee(functionDecl(parameterCountIs(1),
hasName("strlen"))),
hasArgument(0, stringLiteral().bind(
"strlen_arg"))))));
};

// Match a string variable and a call to length() or size().
const auto HasStringVariableAndSizeCallArgs = [](const auto StringArgIndex,
const auto LengthArgIndex) {
return allOf(
hasArgument(StringArgIndex, declRefExpr(hasDeclaration(
decl().bind("string_var_decl")))),
hasArgument(LengthArgIndex,
cxxMemberCallExpr(
callee(cxxMethodDecl(isConst(), parameterCountIs(0),
hasAnyName("size", "length"))),
on(declRefExpr(
to(decl(equalsBoundNode("string_var_decl"))))))));
};

// Match either one of the two cases above.
const auto HasStringAndLengthArgs =
[HasStringLiteralAndLengthArgs, HasStringVariableAndSizeCallArgs](
const auto StringArgIndex, const auto LengthArgIndex) {
return anyOf(
HasStringLiteralAndLengthArgs(StringArgIndex, LengthArgIndex),
HasStringVariableAndSizeCallArgs(StringArgIndex, LengthArgIndex));
};
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));

// Case 3: X.compare(0, LEN(Y), Y) [!=]= 0 -> starts_with.
const auto CompareExpr = cxxMemberCallExpr(
// A method call with three arguments...
argumentCountIs(3),
// ... where the first argument is zero...
hasArgument(0, ZeroLiteral),
// ... named compare...
argumentCountIs(3), hasArgument(0, ZeroLiteral),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
// ... on a class with a starts_with function...
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// ... where the third argument is some string and the second a length.
HasStringAndLengthArgs(2, 1),
// Bind search expression.
hasArgument(2, expr().bind("search_expr")));
OnClassWithStartsWithFunction, hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")));

// Case 4: X.compare(LEN(X) - LEN(Y), LEN(Y), Y) [!=]= 0 -> ends_with.
const auto CompareEndsWithExpr = cxxMemberCallExpr(
argumentCountIs(3),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
OnClassWithEndsWithFunction, hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")),
hasArgument(0,
binaryOperator(hasOperatorName("-"),
hasLHS(lengthExprForStringNode("haystack")),
hasRHS(lengthExprForStringNode("needle")))));

// All cases comparing to 0.
Finder->addMatcher(
// Match [=!]= with a zero on one side and (r?)find|compare on the other.
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr))
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
CompareEndsWithExpr))
.bind("find_expr"),
ZeroLiteral))
.bind("expr"),
this);

// Case 5: X.rfind(Y) [!=]= LEN(X) - LEN(Y) -> ends_with.
Finder->addMatcher(
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(
cxxMemberCallExpr(
anyOf(
argumentCountIs(1),
allOf(argumentCountIs(2),
hasArgument(
1,
anyOf(declRefExpr(to(varDecl(hasName("npos")))),
memberExpr(member(hasName("npos"))))))),
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
OnClassWithEndsWithFunction,
hasArgument(0, expr().bind("needle")))
.bind("find_expr"),
binaryOperator(hasOperatorName("-"),
hasLHS(lengthExprForStringNode("haystack")),
hasRHS(lengthExprForStringNode("needle")))))
.bind("expr"),
this);
}

void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>("find_expr");
const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("find_fun");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("search_expr");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("needle");
const auto *StartsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("starts_with_fun");

const auto *StringLiteralArg =
Result.Nodes.getNodeAs<StringLiteral>("string_literal_arg");
const auto *IntegerLiteralSizeArg =
Result.Nodes.getNodeAs<IntegerLiteral>("integer_literal_size_arg");
const auto *StrlenArg = Result.Nodes.getNodeAs<StringLiteral>("strlen_arg");

// Filter out compare cases where the length does not match string literal.
if (StringLiteralArg && IntegerLiteralSizeArg &&
StringLiteralArg->getLength() !=
IntegerLiteralSizeArg->getValue().getZExtValue()) {
return;
}

if (StringLiteralArg && StrlenArg &&
StringLiteralArg->getLength() != StrlenArg->getLength()) {
return;
}
const auto *EndsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("ends_with_fun");
assert(bool(StartsWithFunction) != bool(EndsWithFunction));
const CXXMethodDecl *ReplacementFunction =
StartsWithFunction ? StartsWithFunction : EndsWithFunction;

if (ComparisonExpr->getBeginLoc().isMacroID()) {
return;
Expand All @@ -154,26 +198,26 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {

auto Diagnostic =
diag(FindExpr->getExprLoc(), "use %0 instead of %1() %select{==|!=}2 0")
<< StartsWithFunction->getName() << FindFun->getName() << Neg;
<< ReplacementFunction->getName() << FindFun->getName() << Neg;

// Remove possible arguments after search expression and ' [!=]= 0' suffix.
// Remove possible arguments after search expression and ' [!=]= .+' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
*Result.SourceManager, getLangOpts()),
ComparisonExpr->getEndLoc()),
")");

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

// Replace method name by 'starts_with'.
// Replace method name by '(starts|ends)_with'.
// Remove possible arguments before search expression.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(FindExpr->getExprLoc(),
SearchExpr->getBeginLoc()),
(StartsWithFunction->getName() + "(").str());
(ReplacementFunction->getName() + "(").str());

// Add possible negation '!'.
if (Neg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace clang::tidy::modernize {

/// Checks for common roundabout ways to express ``starts_with`` and
/// ``ends_with`` and suggests replacing with ``starts_with`` when the method is
/// ``ends_with`` and suggests replacing with the simpler method when it is
/// available. Notably, this will work with ``std::string`` and
/// ``std::string_view``.
///
Expand Down
7 changes: 4 additions & 3 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
this->PP = PP;
}

void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
Expand Down Expand Up @@ -75,9 +76,9 @@ void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) {

utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
utils::FormatStringConverter Converter(Result.Context, StrFormat,
FormatArgOffset, ConverterConfig,
getLangOpts());
utils::FormatStringConverter Converter(
Result.Context, StrFormat, FormatArgOffset, ConverterConfig,
getLangOpts(), *Result.SourceManager, *PP);
const Expr *StrFormatCall = StrFormat->getCallee();
if (!Converter.canApply()) {
diag(StrFormat->getBeginLoc(),
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class UseStdFormatCheck : public ClangTidyCheck {
StringRef ReplacementFormatFunction;
utils::IncludeInserter IncludeInserter;
std::optional<StringRef> MaybeHeaderToInclude;
Preprocessor *PP = nullptr;
};

} // namespace clang::tidy::modernize
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ void UseStdPrintCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
this->PP = PP;
}

static clang::ast_matchers::StatementMatcher
Expand Down Expand Up @@ -131,7 +132,8 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
ConverterConfig.StrictMode = StrictMode;
ConverterConfig.AllowTrailingNewlineRemoval = true;
utils::FormatStringConverter Converter(
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts());
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(),
*Result.SourceManager, *PP);
const Expr *PrintfCall = Printf->getCallee();
const StringRef ReplacementFunction = Converter.usePrintNewlineFunction()
? ReplacementPrintlnFunction
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class UseStdPrintCheck : public ClangTidyCheck {
}

private:
Preprocessor *PP;
bool StrictMode;
std::vector<StringRef> PrintfLikeFunctions;
std::vector<StringRef> FprintfLikeFunctions;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/mpi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyMPIModule
add_clang_library(clangTidyMPIModule STATIC
BufferDerefCheck.cpp
MPITidyModule.cpp
TypeMismatchCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/objc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyObjCModule
add_clang_library(clangTidyObjCModule STATIC
AssertEquals.cpp
AvoidNSErrorInitCheck.cpp
DeallocInCategoryCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/openmp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyOpenMPModule
add_clang_library(clangTidyOpenMPModule STATIC
ExceptionEscapeCheck.cpp
OpenMPTidyModule.cpp
UseDefaultNoneCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/performance/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyPerformanceModule
add_clang_library(clangTidyPerformanceModule STATIC
AvoidEndlCheck.cpp
EnumSizeCheck.cpp
FasterStringFindCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
}

if (const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();
RecordDecl && !(RecordDecl->hasMoveConstructor() &&
RecordDecl->hasMoveAssignment())) {
RecordDecl && RecordDecl->hasDefinition() &&
!(RecordDecl->hasMoveConstructor() &&
RecordDecl->hasMoveAssignment())) {
const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();
const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();
const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_clang_library(clangTidyPlugin
add_clang_library(clangTidyPlugin STATIC
ClangTidyPlugin.cpp

LINK_LIBS
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/portability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ set(LLVM_LINK_COMPONENTS
TargetParser
)

add_clang_library(clangTidyPortabilityModule
add_clang_library(clangTidyPortabilityModule STATIC
PortabilityTidyModule.cpp
RestrictSystemIncludesCheck.cpp
SIMDIntrinsicsCheck.cpp
StdAllocatorConstCheck.cpp
TemplateVirtualMemberFunctionCheck.cpp

LINK_LIBS
clangTidy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "RestrictSystemIncludesCheck.h"
#include "SIMDIntrinsicsCheck.h"
#include "StdAllocatorConstCheck.h"
#include "TemplateVirtualMemberFunctionCheck.h"

namespace clang::tidy {
namespace portability {
Expand All @@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule {
"portability-simd-intrinsics");
CheckFactories.registerCheck<StdAllocatorConstCheck>(
"portability-std-allocator-const");
CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
"portability-template-virtual-member-function");
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===--- TemplateVirtualMemberFunctionCheck.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 "TemplateVirtualMemberFunctionCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::portability {
namespace {
AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); }
} // namespace

void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
unless(isExplicitTemplateSpecialization()))
.bind("specialization")),
isVirtual(), unless(isUsed()),
unless(cxxDestructorDecl(isDefaulted())))
.bind("method"),
this);
}

void TemplateVirtualMemberFunctionCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *ImplicitSpecialization =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
const auto *MethodDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("method");

diag(MethodDecl->getLocation(),
"unspecified virtual member function instantiation; the virtual "
"member function is not instantiated but it might be with a "
"different compiler");
diag(ImplicitSpecialization->getPointOfInstantiation(),
"template instantiated here", DiagnosticIDs::Note);
}

} // namespace clang::tidy::portability
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- TemplateVirtualMemberFunctionCheck.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_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::portability {

/// Upon instantiating a template class, non-virtual member functions don't have
/// to be instantiated unless they are used. Virtual member function
/// instantiation on the other hand is unspecified and depends on the
/// implementation of the compiler. This check intends to find cases when a
/// virtual member function is not instantiated but it might be with a different
/// compiler.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
public:
TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
};

} // namespace clang::tidy::portability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyReadabilityModule
add_clang_library(clangTidyReadabilityModule STATIC
AvoidConstParamsInDecls.cpp
AvoidNestedConditionalOperatorCheck.cpp
AvoidReturnWithVoidValueCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,9 @@ StyleKind IdentifierNamingCheck::findStyleKind(
if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
return SK_TypeAlias;

if (isa<NamespaceAliasDecl>(D) && NamingStyles[SK_Namespace])
return SK_Namespace;

if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
if (Decl->isAnonymousNamespace())
return SK_Invalid;
Expand Down
32 changes: 18 additions & 14 deletions clang-tools-extra/clang-tidy/rename_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
#
# ===-----------------------------------------------------------------------===#

from __future__ import unicode_literals

import argparse
import glob
import io
import os
import re
import sys
from typing import List


def replaceInFileRegex(fileName, sFrom, sTo):
def replaceInFileRegex(fileName: str, sFrom: str, sTo: str) -> None:
if sFrom == sTo:
return

Expand All @@ -35,7 +35,7 @@ def replaceInFileRegex(fileName, sFrom, sTo):
f.write(txt)


def replaceInFile(fileName, sFrom, sTo):
def replaceInFile(fileName: str, sFrom: str, sTo: str) -> None:
if sFrom == sTo:
return
txt = None
Expand All @@ -51,7 +51,7 @@ def replaceInFile(fileName, sFrom, sTo):
f.write(txt)


def generateCommentLineHeader(filename):
def generateCommentLineHeader(filename: str) -> str:
return "".join(
[
"//===--- ",
Expand All @@ -63,7 +63,7 @@ def generateCommentLineHeader(filename):
)


def generateCommentLineSource(filename):
def generateCommentLineSource(filename: str) -> str:
return "".join(
[
"//===--- ",
Expand All @@ -75,7 +75,7 @@ def generateCommentLineSource(filename):
)


def fileRename(fileName, sFrom, sTo):
def fileRename(fileName: str, sFrom: str, sTo: str) -> str:
if sFrom not in fileName or sFrom == sTo:
return fileName
newFileName = fileName.replace(sFrom, sTo)
Expand All @@ -84,7 +84,7 @@ def fileRename(fileName, sFrom, sTo):
return newFileName


def deleteMatchingLines(fileName, pattern):
def deleteMatchingLines(fileName: str, pattern: str) -> bool:
lines = None
with io.open(fileName, "r", encoding="utf8") as f:
lines = f.readlines()
Expand All @@ -101,7 +101,7 @@ def deleteMatchingLines(fileName, pattern):
return True


def getListOfFiles(clang_tidy_path):
def getListOfFiles(clang_tidy_path: str) -> List[str]:
files = glob.glob(os.path.join(clang_tidy_path, "**"), recursive=True)
files += [
os.path.normpath(os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst"))
Expand All @@ -124,7 +124,7 @@ def getListOfFiles(clang_tidy_path):

# Adapts the module's CMakelist file. Returns 'True' if it could add a new
# entry and 'False' if the entry already existed.
def adapt_cmake(module_path, check_name_camel):
def adapt_cmake(module_path: str, check_name_camel: str) -> bool:
filename = os.path.join(module_path, "CMakeLists.txt")
with io.open(filename, "r", encoding="utf8") as f:
lines = f.readlines()
Expand Down Expand Up @@ -153,7 +153,9 @@ def adapt_cmake(module_path, check_name_camel):


# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
def adapt_module(
module_path: str, module: str, check_name: str, check_name_camel: str
) -> None:
modulecpp = next(
iter(
filter(
Expand Down Expand Up @@ -204,7 +206,9 @@ def adapt_module(module_path, module, check_name, check_name_camel):


# Adds a release notes entry.
def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
def add_release_notes(
clang_tidy_path: str, old_check_name: str, new_check_name: str
) -> None:
filename = os.path.normpath(
os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")
)
Expand Down Expand Up @@ -262,7 +266,7 @@ def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
f.write(line)


def main():
def main() -> None:
parser = argparse.ArgumentParser(description="Rename clang-tidy check.")
parser.add_argument("old_check_name", type=str, help="Old check name.")
parser.add_argument("new_check_name", type=str, help="New check name.")
Expand Down Expand Up @@ -311,7 +315,7 @@ def main():
"Check name '%s' not found in %s. Exiting."
% (check_name_camel, cmake_lists)
)
return 1
sys.exit(1)

modulecpp = next(
iter(
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set(LLVM_LINK_COMPONENTS
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES ClangTidyMain.cpp ClangTidyToolMain.cpp)

add_clang_library(clangTidyMain
add_clang_library(clangTidyMain STATIC
ClangTidyMain.cpp

LINK_LIBS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyUtils
add_clang_library(clangTidyUtils STATIC
Aliasing.cpp
ASTUtils.cpp
BracesAroundStatement.cpp
Expand Down
65 changes: 60 additions & 5 deletions clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/FixIt.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -195,11 +196,10 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {
return false;
}

FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
const Configuration ConfigIn,
const LangOptions &LO)
FormatStringConverter::FormatStringConverter(
ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset,
const Configuration ConfigIn, const LangOptions &LO, SourceManager &SM,
Preprocessor &PP)
: Context(ContextIn), Config(ConfigIn),
CastMismatchedIntegerTypes(
castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)),
Expand All @@ -208,11 +208,22 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
assert(ArgsOffset <= NumArgs);
FormatExpr = llvm::dyn_cast<StringLiteral>(
Args[FormatArgOffset]->IgnoreImplicitAsWritten());

if (!FormatExpr || !FormatExpr->isOrdinary()) {
// Function must have a narrow string literal as its first argument.
conversionNotPossible("first argument is not a narrow string literal");
return;
}

if (const std::optional<StringRef> MaybeMacroName =
formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
MaybeMacroName) {
conversionNotPossible(
("format string contains unreplaceable macro '" + *MaybeMacroName + "'")
.str());
return;
}

PrintfFormatString = FormatExpr->getString();

// Assume that the output will be approximately the same size as the input,
Expand All @@ -230,6 +241,50 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
finalizeFormatText();
}

std::optional<StringRef>
FormatStringConverter::formatStringContainsUnreplaceableMacro(
const CallExpr *Call, const StringLiteral *FormatExpr, SourceManager &SM,
Preprocessor &PP) {
// If a macro invocation surrounds the entire call then we don't want that to
// inhibit conversion. The whole format string will appear to come from that
// macro, as will the function call.
std::optional<StringRef> MaybeSurroundingMacroName;
if (SourceLocation BeginCallLoc = Call->getBeginLoc();
BeginCallLoc.isMacroID())
MaybeSurroundingMacroName =
Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());

for (auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
I != E; ++I) {
const SourceLocation &TokenLoc = *I;
if (TokenLoc.isMacroID()) {
const StringRef MacroName =
Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());

if (MaybeSurroundingMacroName != MacroName) {
// glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes
// for types that change size so we must look for multiple prefixes.
if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI"))
return MacroName;

const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
const OptionalFileEntryRef MaybeFileEntry =
SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
if (!MaybeFileEntry)
return MacroName;

HeaderSearch &HS = PP.getHeaderSearchInfo();
// Check if the file is a system header
if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
llvm::sys::path::filename(MaybeFileEntry->getName()) !=
"inttypes.h")
return MacroName;
}
}
}
return std::nullopt;
}

void FormatStringConverter::emitAlignment(const PrintfSpecifier &FS,
std::string &FormatSpec) {
ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
Expand Down
7 changes: 6 additions & 1 deletion clang-tools-extra/clang-tidy/utils/FormatStringConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class FormatStringConverter

FormatStringConverter(ASTContext *Context, const CallExpr *Call,
unsigned FormatArgOffset, Configuration Config,
const LangOptions &LO);
const LangOptions &LO, SourceManager &SM,
Preprocessor &PP);

bool canApply() const { return ConversionNotPossibleReason.empty(); }
const std::string &conversionNotPossibleReason() const {
Expand Down Expand Up @@ -110,6 +111,10 @@ class FormatStringConverter

void appendFormatText(StringRef Text);
void finalizeFormatText();
static std::optional<StringRef>
formatStringContainsUnreplaceableMacro(const CallExpr *CallExpr,
const StringLiteral *FormatExpr,
SourceManager &SM, Preprocessor &PP);
bool conversionNotPossible(std::string Reason) {
ConversionNotPossibleReason = std::move(Reason);
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/zircon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyZirconModule
add_clang_library(clangTidyZirconModule STATIC
TemporaryObjectsCheck.cpp
ZirconTidyModule.cpp

Expand Down
7 changes: 6 additions & 1 deletion clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,13 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
// since we stored inner-most parent first.
std::string Result;
llvm::raw_string_ostream OS(Result);
for (const auto *Parent : llvm::reverse(Parents))
for (const auto *Parent : llvm::reverse(Parents)) {
if (Parent != *Parents.rbegin() && Parent->isDependent() &&
Parent->getAsRecordDecl() &&
Parent->getAsRecordDecl()->getDescribedClassTemplate())
OS << "template ";
Parent->print(OS, Context.getPrintingPolicy());
}
return OS.str();
}

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ endif()
include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/../clang-tidy")
include_directories(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../include-cleaner/include")

add_clang_library(clangDaemon
add_clang_library(clangDaemon STATIC
AST.cpp
ASTSignals.cpp
ClangdLSPServer.cpp
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ClangdServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,

CodeCompleteOpts.MainFileSignals = IP->Signals;
CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes;
CodeCompleteOpts.ArgumentLists = Config::current().Completion.ArgumentLists;
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
// both the old and the new version in case only one of them matches.
CodeCompleteResult Result = clangd::codeComplete(
Expand Down
26 changes: 18 additions & 8 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "AST.h"
#include "CodeCompletionStrings.h"
#include "Compiler.h"
#include "Config.h"
#include "ExpectedTypes.h"
#include "Feature.h"
#include "FileDistance.h"
Expand Down Expand Up @@ -350,8 +351,7 @@ struct CodeCompletionBuilder {
CodeCompletionContext::Kind ContextKind,
const CodeCompleteOptions &Opts,
bool IsUsingDeclaration, tok::TokenKind NextTokenKind)
: ASTCtx(ASTCtx),
EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets),
: ASTCtx(ASTCtx), ArgumentLists(Opts.ArgumentLists),
IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) {
Completion.Deprecated = true; // cleared by any non-deprecated overload.
add(C, SemaCCS, ContextKind);
Expand Down Expand Up @@ -561,14 +561,23 @@ struct CodeCompletionBuilder {
}

std::string summarizeSnippet() const {
/// localize ArgumentLists tests for better readability
const bool None = ArgumentLists == Config::ArgumentListsPolicy::None;
const bool Open =
ArgumentLists == Config::ArgumentListsPolicy::OpenDelimiter;
const bool Delim = ArgumentLists == Config::ArgumentListsPolicy::Delimiters;
const bool Full =
ArgumentLists == Config::ArgumentListsPolicy::FullPlaceholders ||
(!None && !Open && !Delim); // <-- failsafe: Full is default

if (IsUsingDeclaration)
return "";
auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>();
if (!Snippet)
// All bundles are function calls.
// FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g.
// we need to complete 'forward<$1>($0)'.
return "($0)";
return None ? "" : (Open ? "(" : "($0)");

if (Snippet->empty())
return "";
Expand Down Expand Up @@ -607,7 +616,7 @@ struct CodeCompletionBuilder {
return "";
}
}
if (EnableFunctionArgSnippets)
if (Full)
return *Snippet;

// Replace argument snippets with a simplified pattern.
Expand All @@ -622,9 +631,9 @@ struct CodeCompletionBuilder {

bool EmptyArgs = llvm::StringRef(*Snippet).ends_with("()");
if (Snippet->front() == '<')
return EmptyArgs ? "<$1>()$0" : "<$1>($0)";
return None ? "" : (Open ? "<" : (EmptyArgs ? "<$1>()$0" : "<$1>($0)"));
if (Snippet->front() == '(')
return EmptyArgs ? "()" : "($0)";
return None ? "" : (Open ? "(" : (EmptyArgs ? "()" : "($0)"));
return *Snippet; // Not an arg snippet?
}
// 'CompletionItemKind::Interface' matches template type aliases.
Expand All @@ -638,7 +647,7 @@ struct CodeCompletionBuilder {
// e.g. Foo<${1:class}>.
if (llvm::StringRef(*Snippet).ends_with("<>"))
return "<>"; // can happen with defaulted template arguments.
return "<$0>";
return None ? "" : (Open ? "<" : "<$0>");
}
return *Snippet;
}
Expand All @@ -654,7 +663,8 @@ struct CodeCompletionBuilder {
ASTContext *ASTCtx;
CodeCompletion Completion;
llvm::SmallVector<BundledEntry, 1> Bundled;
bool EnableFunctionArgSnippets;
/// the way argument lists are handled.
Config::ArgumentListsPolicy ArgumentLists;
// No snippets will be generated for using declarations and when the function
// arguments are already present.
bool IsUsingDeclaration;
Expand Down
9 changes: 5 additions & 4 deletions clang-tools-extra/clangd/CodeComplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "ASTSignals.h"
#include "Compiler.h"
#include "Config.h"
#include "Protocol.h"
#include "Quality.h"
#include "index/Index.h"
Expand Down Expand Up @@ -101,17 +102,17 @@ struct CodeCompleteOptions {
/// '->' on member access etc.
bool IncludeFixIts = false;

/// Whether to generate snippets for function arguments on code-completion.
/// Needs snippets to be enabled as well.
bool EnableFunctionArgSnippets = true;

/// Whether to include index symbols that are not defined in the scopes
/// visible from the code completion point. This applies in contexts without
/// explicit scope qualifiers.
///
/// Such completions can insert scope qualifiers.
bool AllScopes = false;

/// The way argument list on calls '()' and generics '<>' are handled.
Config::ArgumentListsPolicy ArgumentLists =
Config::ArgumentListsPolicy::FullPlaceholders;

/// Whether to use the clang parser, or fallback to text-based completion
/// (using identifiers in the current file and symbol indexes).
enum CodeCompletionParse {
Expand Down
14 changes: 14 additions & 0 deletions clang-tools-extra/clangd/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,25 @@ struct Config {
std::vector<std::string> FullyQualifiedNamespaces;
} Style;

/// controls the completion options for argument lists.
enum class ArgumentListsPolicy {
/// nothing, no argument list and also NO Delimiters "()" or "<>".
None,
/// open, only opening delimiter "(" or "<".
OpenDelimiter,
/// empty pair of delimiters "()" or "<>".
Delimiters,
/// full name of both type and variable.
FullPlaceholders,
};

/// Configures code completion feature.
struct {
/// Whether code completion includes results that are not visible in current
/// scopes.
bool AllScopes = true;
/// controls the completion options for argument lists.
ArgumentListsPolicy ArgumentLists = ArgumentListsPolicy::FullPlaceholders;
} Completion;

/// Configures hover feature.
Expand Down
15 changes: 15 additions & 0 deletions clang-tools-extra/clangd/ConfigCompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,21 @@ struct FragmentCompiler {
C.Completion.AllScopes = AllScopes;
});
}
if (F.ArgumentLists) {
if (auto Val =
compileEnum<Config::ArgumentListsPolicy>("ArgumentLists",
*F.ArgumentLists)
.map("None", Config::ArgumentListsPolicy::None)
.map("OpenDelimiter",
Config::ArgumentListsPolicy::OpenDelimiter)
.map("Delimiters", Config::ArgumentListsPolicy::Delimiters)
.map("FullPlaceholders",
Config::ArgumentListsPolicy::FullPlaceholders)
.value())
Out.Apply.push_back([Val](const Params &, Config &C) {
C.Completion.ArgumentLists = *Val;
});
}
}

void compile(Fragment::HoverBlock &&F) {
Expand Down
8 changes: 8 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H

#include "Config.h"
#include "ConfigProvider.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
Expand Down Expand Up @@ -308,6 +309,13 @@ struct Fragment {
/// Whether code completion should include suggestions from scopes that are
/// not visible. The required scope prefix will be inserted.
std::optional<Located<bool>> AllScopes;
/// How to present the argument list between '()' and '<>':
/// valid values are enum Config::ArgumentListsPolicy values:
/// None: Nothing at all
/// OpenDelimiter: only opening delimiter "(" or "<"
/// Delimiters: empty pair of delimiters "()" or "<>"
/// FullPlaceholders: full name of both type and parameter
std::optional<Located<std::string>> ArgumentLists;
};
CompletionBlock Completion;

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/ConfigYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ class Parser {
if (auto AllScopes = boolValue(N, "AllScopes"))
F.AllScopes = *AllScopes;
});
Dict.handle("ArgumentLists", [&](Node &N) {
if (auto ArgumentLists = scalarValue(N, "ArgumentLists"))
F.ArgumentLists = *ArgumentLists;
});
Dict.parse(N);
}

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/FindSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit,
*Req.Limit *= 5;
}
TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(
Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
Req.Limit.value_or(std::numeric_limits<size_t>::max()));
FuzzyMatcher Filter(Req.Query);

Index->fuzzyFind(Req, [HintPath, &Top, &Filter, AnyScope = Req.AnyScope,
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/Headers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class IncludeStructure::RecordHeaders : public PPCallbacks {
IDs.push_back(HID);
}
}
Out->MainFileIncludesBySpelling.try_emplace(Inc.Written)
.first->second.push_back(Out->MainFileIncludes.size() - 1);
Out->MainFileIncludesBySpelling[Inc.Written].push_back(
Out->MainFileIncludes.size() - 1);
}

// Record include graph (not just for main-file includes)
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2282,8 +2282,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
return;
}
auto It = CallsIn.try_emplace(R.Container, std::vector<Range>{}).first;
It->second.push_back(Loc->range);
CallsIn[R.Container].push_back(Loc->range);

ContainerLookup.IDs.insert(R.Container);
});
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/MemIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bool MemIndex::fuzzyFind(
trace::Span Tracer("MemIndex fuzzyFind");

TopN<std::pair<float, const Symbol *>> Top(
Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
Req.Limit.value_or(std::numeric_limits<size_t>::max()));
FuzzyMatcher Filter(Req.Query);
bool More = false;
for (const auto &Pair : Index) {
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/dex/Dex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req,
return LHS.second > RHS.second;
};
TopN<IDAndScore, decltype(Compare)> Top(
Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max(), Compare);
Req.Limit.value_or(std::numeric_limits<size_t>::max()), Compare);
for (const auto &IDAndScore : IDAndScores) {
const DocID SymbolDocID = IDAndScore.first;
const auto *Sym = Symbols[SymbolDocID];
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/remote/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (CLANGD_ENABLE_REMOTE)
# target-local?
add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI=1)

add_clang_library(clangdRemoteIndex
add_clang_library(clangdRemoteIndex STATIC
Client.cpp

LINK_LIBS
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_clang_library(clangdRemoteMarshalling
add_clang_library(clangdRemoteMarshalling STATIC
Marshalling.cpp

LINK_LIBS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../)
# When compiled without Remote Index support, the real implementation index
# client is not present. Users will get a notification about this when trying
# to connect to remote index server instance.
add_clang_library(clangdRemoteIndex
add_clang_library(clangdRemoteIndex STATIC
UnimplementedClient.cpp

LINK_LIBS
Expand Down
50 changes: 44 additions & 6 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,28 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
SM.getBufferData(SM.getMainFileID()), Replacements);
if (!QualifiedFunc)
return QualifiedFunc.takeError();
return QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);

std::string TemplatePrefix;
if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(FD)) {
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
Parent =
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
if (const TemplateParameterList *Params =
Parent->getDescribedTemplateParams()) {
std::string S;
llvm::raw_string_ostream Stream(S);
Params->print(Stream, FD->getASTContext());
if (!S.empty())
*S.rbegin() = '\n'; // Replace space with newline
TemplatePrefix.insert(0, S);
}
}
}

auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
if (!TemplatePrefix.empty())
Source.insert(0, TemplatePrefix);
return Source;
}

// Returns replacements to delete tokens with kind `Kind` in the range
Expand Down Expand Up @@ -212,9 +233,13 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
}
}
const NamedDecl *ND = Ref.Targets.front();
const std::string Qualifier =
std::string Qualifier =
getQualification(AST, TargetContext,
SM.getLocForStartOfFile(SM.getMainFileID()), ND);
if (ND->getDeclContext()->isDependentContext() &&
llvm::isa<TypeDecl>(ND)) {
Qualifier.insert(0, "typename ");
}
if (auto Err = DeclarationCleanups.add(
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
Expand Down Expand Up @@ -407,10 +432,23 @@ class DefineOutline : public Tweak {
return !SameFile;
}

// Bail out in templated classes, as it is hard to spell the class name,
// i.e if the template parameter is unnamed.
if (MD->getParent()->isTemplated())
return false;
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
Parent =
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
if (const TemplateParameterList *Params =
Parent->getDescribedTemplateParams()) {

// Class template member functions must be defined in the
// same file.
SameFile = true;

// Bail out if the template parameter is unnamed.
for (NamedDecl *P : *Params) {
if (!P->getIdentifier())
return false;
}
}
}

// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB OR NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
list(APPEND CLANGD_ATOMIC_LIB "atomic")
endif()

add_clang_library(clangdSupport
add_clang_library(clangdSupport STATIC
Bracket.cpp
Cancellation.cpp
Context.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES ClangdToolMain.cpp)

add_clang_library(clangdMain
add_clang_library(clangdMain STATIC
ClangdMain.cpp
Check.cpp
)
Expand Down
18 changes: 13 additions & 5 deletions clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,13 @@ opt<std::string> FallbackStyle{
init(clang::format::DefaultFallbackStyle),
};

opt<bool> EnableFunctionArgSnippets{
opt<int> EnableFunctionArgSnippets{
"function-arg-placeholders",
cat(Features),
desc("When disabled, completions contain only parentheses for "
"function calls. When enabled, completions also contain "
desc("When disabled (0), completions contain only parentheses for "
"function calls. When enabled (1), completions also contain "
"placeholders for method parameters"),
init(CodeCompleteOptions().EnableFunctionArgSnippets),
init(-1),
};

opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
Expand Down Expand Up @@ -650,6 +650,7 @@ class FlagsConfigProvider : public config::Provider {
std::optional<Config::CDBSearchSpec> CDBSearch;
std::optional<Config::ExternalIndexSpec> IndexSpec;
std::optional<Config::BackgroundPolicy> BGPolicy;
std::optional<Config::ArgumentListsPolicy> ArgumentLists;

// If --compile-commands-dir arg was invoked, check value and override
// default path.
Expand Down Expand Up @@ -694,13 +695,21 @@ class FlagsConfigProvider : public config::Provider {
BGPolicy = Config::BackgroundPolicy::Skip;
}

if (EnableFunctionArgSnippets >= 0) {
ArgumentLists = EnableFunctionArgSnippets
? Config::ArgumentListsPolicy::FullPlaceholders
: Config::ArgumentListsPolicy::Delimiters;
}

Frag = [=](const config::Params &, Config &C) {
if (CDBSearch)
C.CompileFlags.CDBSearch = *CDBSearch;
if (IndexSpec)
C.Index.External = *IndexSpec;
if (BGPolicy)
C.Index.Background = *BGPolicy;
if (ArgumentLists)
C.Completion.ArgumentLists = *ArgumentLists;
if (AllScopesCompletion.getNumOccurrences())
C.Completion.AllScopes = AllScopesCompletion;

Expand Down Expand Up @@ -916,7 +925,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
Opts.CodeComplete.IncludeIndicator.Insert.clear();
Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
}
Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
Opts.CodeComplete.RunParser = CodeCompletionParse;
Opts.CodeComplete.RankingModel = RankingModel;
// FIXME: If we're using C++20 modules, force the lookup process to load
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,22 @@ TEST_F(LSPTest, ClangTidyRename) {
EXPECT_EQ(Params, std::vector{llvm::json::Value(std::move(ExpectedEdit))});
}

TEST_F(LSPTest, ClangTidyCrash_Issue109367) {
// This test requires clang-tidy checks to be linked in.
if (!CLANGD_TIDY_CHECKS)
return;
Opts.ClangTidyProvider = [](tidy::ClangTidyOptions &ClangTidyOpts,
llvm::StringRef) {
ClangTidyOpts.Checks = {"-*,boost-use-ranges"};
};
// Check that registering the boost-use-ranges checker's matchers
// on two different threads does not cause a crash.
auto &Client = start();
Client.didOpen("a.cpp", "");
Client.didOpen("b.cpp", "");
Client.sync();
}

TEST_F(LSPTest, IncomingCalls) {
Annotations Code(R"cpp(
void calle^e(int);
Expand Down
25 changes: 23 additions & 2 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ClangdServer.h"
#include "CodeComplete.h"
#include "Compiler.h"
#include "Config.h"
#include "Feature.h"
#include "Matchers.h"
#include "Protocol.h"
Expand Down Expand Up @@ -2595,10 +2596,10 @@ TEST(SignatureHelpTest, DynamicIndexDocumentation) {
ElementsAre(AllOf(sig("foo() -> int"), sigDoc("Member doc"))));
}

TEST(CompletionTest, CompletionFunctionArgsDisabled) {
TEST(CompletionTest, ArgumentListsPolicy) {
CodeCompleteOptions Opts;
Opts.EnableSnippets = true;
Opts.EnableFunctionArgSnippets = false;
Opts.ArgumentLists = Config::ArgumentListsPolicy::Delimiters;

{
auto Results = completions(
Expand Down Expand Up @@ -2670,6 +2671,26 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) {
EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf(
named("FOO"), snippetSuffix("($0)"))));
}
{
Opts.ArgumentLists = Config::ArgumentListsPolicy::None;
auto Results = completions(
R"cpp(
void xfoo(int x, int y);
void f() { xfo^ })cpp",
{}, Opts);
EXPECT_THAT(Results.Completions,
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix(""))));
}
{
Opts.ArgumentLists = Config::ArgumentListsPolicy::OpenDelimiter;
auto Results = completions(
R"cpp(
void xfoo(int x, int y);
void f() { xfo^ })cpp",
{}, Opts);
EXPECT_THAT(Results.Completions,
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix("("))));
}
}

TEST(CompletionTest, SuggestOverrides) {
Expand Down
55 changes: 42 additions & 13 deletions clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
F^oo(const Foo&) = delete;
};)cpp");

// Not available within templated classes, as it is hard to spell class name
// out-of-line in such cases.
// Not available within templated classes with unnamed parameters, as it is
// hard to spell class name out-of-line in such cases.
EXPECT_UNAVAILABLE(R"cpp(
template <typename> struct Foo { void fo^o(){} };
)cpp");
Expand Down Expand Up @@ -154,7 +154,6 @@ TEST_F(DefineOutlineTest, FailsWithoutSource) {
}

TEST_F(DefineOutlineTest, ApplyTest) {
llvm::StringMap<std::string> EditedFiles;
ExtraFiles["Test.cpp"] = "";
FileName = "Test.hpp";

Expand Down Expand Up @@ -229,17 +228,18 @@ TEST_F(DefineOutlineTest, ApplyTest) {
// Ctor initializer with attribute.
{
R"cpp(
class Foo {
F^oo(int z) __attribute__((weak)) : bar(2){}
template <typename T> class Foo {
F^oo(T z) __attribute__((weak)) : bar(2){}
int bar;
};)cpp",
R"cpp(
class Foo {
Foo(int z) __attribute__((weak)) ;
template <typename T> class Foo {
Foo(T z) __attribute__((weak)) ;
int bar;
};)cpp",
"Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
},
};template <typename T>
Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
)cpp",
""},
// Virt specifiers.
{
R"cpp(
Expand Down Expand Up @@ -369,7 +369,31 @@ TEST_F(DefineOutlineTest, ApplyTest) {
};)cpp",
" void A::foo(int) {}\n",
},
// Destrctors
// Complex class template
{
R"cpp(
template <typename T, typename ...U> struct O1 {
template <class V, int A> struct O2 {
enum E { E1, E2 };
struct I {
E f^oo(T, U..., V, E) { return E1; }
};
};
};)cpp",
R"cpp(
template <typename T, typename ...U> struct O1 {
template <class V, int A> struct O2 {
enum E { E1, E2 };
struct I {
E foo(T, U..., V, E) ;
};
};
};template <typename T, typename ...U>
template <class V, int A>
typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
)cpp",
""},
// Destructors
{
"class A { ~A^(){} };",
"class A { ~A(); };",
Expand All @@ -378,9 +402,14 @@ TEST_F(DefineOutlineTest, ApplyTest) {
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
llvm::StringMap<std::string> EditedFiles;
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
if (Case.ExpectedSource.empty()) {
EXPECT_TRUE(EditedFiles.empty());
} else {
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/xpc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ set(LLVM_LINK_COMPONENTS
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES Conversion.cpp XPCTransport.cpp)

add_clang_library(clangdXpcJsonConversions
add_clang_library(clangdXpcJsonConversions STATIC
Conversion.cpp
LINK_LIBS clangDaemon clangdSupport
)

add_clang_library(clangdXpcTransport
add_clang_library(clangdXpcTransport STATIC
XPCTransport.cpp
LINK_LIBS clangDaemon clangdSupport clangdXpcJsonConversions
DEPENDS ClangDriverOptions
Expand Down
34 changes: 31 additions & 3 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ Objective-C
Miscellaneous
^^^^^^^^^^^^^

- The DefineOutline tweak now handles member functions of class templates.

Improvements to clang-doc
-------------------------

Expand All @@ -109,12 +111,24 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^

- New :doc:`bugprone-bitwise-pointer-cast
<clang-tidy/checks/bugprone/bitwise-pointer-cast>` check.

Warns about code that tries to cast between pointers by means of
``std::bit_cast`` or ``memcpy``.

- New :doc:`bugprone-tagged-union-member-count
<clang-tidy/checks/bugprone/tagged-union-member-count>` check.

Gives warnings for tagged unions, where the number of tags is
different from the number of data members inside the union.

- New :doc:`portability-template-virtual-member-function
<clang-tidy/checks/portability/template-virtual-member-function>` check.

Finds cases when an uninstantiated virtual member function in a template class
causes cross-compiler incompatibility.

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

Expand Down Expand Up @@ -144,7 +158,7 @@ Changes in existing checks
- Improved :doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` check to find suspicious
usages of ``sizeof()``, ``alignof()``, and ``offsetof()`` when adding or
subtracting from a pointer.
subtracting from a pointer directly or when used to scale a numeric value.

- Improved :doc:`bugprone-unchecked-optional-access
<clang-tidy/checks/bugprone/unchecked-optional-access>` to support
Expand Down Expand Up @@ -190,18 +204,28 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-nullptr>` check to also recognize
``NULL``/``__null`` (but not ``0``) when used with a templated type.

- Improved :doc:`modernize-use-starts-ends-with
<clang-tidy/checks/modernize/use-starts-ends-with>` check to handle two cases
that can be replaced with ``ends_with``

- Improved :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check to support replacing
member function calls too.
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.

- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/modernize/use-std-print>` check to support replacing
member function calls too.
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.

- Improved :doc:`performance-avoid-endl
<clang-tidy/checks/performance/avoid-endl>` check to use ``std::endl`` as
placeholder when lexer cannot get source text.

- Improved :doc:`performance-move-const-arg
<clang-tidy/checks/performance/move-const-arg>` check to fix a crash when
an argument type is declared but not defined.

- Improved :doc:`readability-container-contains
<clang-tidy/checks/readability/container-contains>` check to let it work on
any class that has a ``contains`` method.
Expand All @@ -220,6 +244,10 @@ Changes in existing checks
<clang-tidy/checks/readability/redundant-smartptr-get>` check to
remove `->`, when redundant `get()` is removed.

- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check to
validate ``namespace`` aliases.

Removed checks
^^^^^^^^^^^^^^

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.. title:: clang-tidy - bugprone-bitwise-pointer-cast

bugprone-bitwise-pointer-cast
=============================

Warns about code that tries to cast between pointers by means of
``std::bit_cast`` or ``memcpy``.

The motivation is that ``std::bit_cast`` is advertised as the safe alternative
to type punning via ``reinterpret_cast`` in modern C++. However, one should not
blindly replace ``reinterpret_cast`` with ``std::bit_cast``, as follows:

.. code-block:: c++

int x{};
-float y = *reinterpret_cast<float*>(&x);
+float y = *std::bit_cast<float*>(&x);

The drop-in replacement behaves exactly the same as ``reinterpret_cast``, and
Undefined Behavior is still invoked. ``std::bit_cast`` is copying the bytes of
the input pointer, not the pointee, into an output pointer of a different type,
which may violate the strict aliasing rules. However, simply looking at the
code, it looks "safe", because it uses ``std::bit_cast`` which is advertised as
safe.

The solution to safe type punning is to apply ``std::bit_cast`` on value types,
not on pointer types:

.. code-block:: c++

int x{};
float y = std::bit_cast<float>(x);

This way, the bytes of the input object are copied into the output object, which
is much safer. Do note that Undefined Behavior can still occur, if there is no
value of type ``To`` corresponding to the value representation produced.
Compilers may be able to optimize this copy and generate identical assembly to
the original ``reinterpret_cast`` version.

Code before C++20 may backport ``std::bit_cast`` by means of ``memcpy``, or
simply call ``memcpy`` directly, which is equally problematic. This is also
detected by this check:

.. code-block:: c++

int* x{};
float* y{};
std::memcpy(&y, &x, sizeof(x));

Alternatively, if a cast between pointers is truly wanted, ``reinterpret_cast``
should be used, to clearly convey the intent and enable warnings from compilers
and linters, which should be addressed accordingly.
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ and the result is typically unintended, often out of bounds.
``Ptr + sizeof(T)`` will offset the pointer by ``sizeof(T)`` elements,
effectively exponentiating the scaling factor to the power of 2.

Similarly, multiplying or dividing a numeric value with the ``sizeof`` of an
element or the whole buffer is suspicious, because the dimensional connection
between the numeric value and the actual ``sizeof`` result can not always be
deduced.
While scaling an integer up (multiplying) with ``sizeof`` is likely **always**
an issue, a scaling down (division) is not always inherently dangerous, in case
the developer is aware that the division happens between an appropriate number
of _bytes_ and a ``sizeof`` value.
Turning :option:`WarnOnOffsetDividedBySizeOf` off will restrict the
warnings to the multiplication case.

This case also checks suspicious ``alignof`` and ``offsetof`` usages in
pointer arithmetic, as both return the "size" in bytes and not elements,
potentially resulting in doubly-scaled offsets.
Expand Down Expand Up @@ -299,3 +310,9 @@ Options
``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`.

.. option:: WarnOnOffsetDividedBySizeOf

When `true`, the check will warn on pointer arithmetic where the
element count is obtained from a division with ``sizeof(...)``,
e.g., ``Ptr + Bytes / sizeof(*T)``. Default is `true`.
2 changes: 2 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Clang-Tidy Checks
:doc:`bugprone-assert-side-effect <bugprone/assert-side-effect>`,
:doc:`bugprone-assignment-in-if-condition <bugprone/assignment-in-if-condition>`,
:doc:`bugprone-bad-signal-to-kill-thread <bugprone/bad-signal-to-kill-thread>`,
:doc:`bugprone-bitwise-pointer-cast <bugprone/bitwise-pointer-cast>`,
:doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
:doc:`bugprone-branch-clone <bugprone/branch-clone>`,
:doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
Expand Down Expand Up @@ -347,6 +348,7 @@ Clang-Tidy Checks
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
:doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ modernize-use-starts-ends-with
==============================

Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
and suggests replacing with ``starts_with`` when the method is available.
Notably, this will work with ``std::string`` and ``std::string_view``.
and suggests replacing with the simpler method when it is available. Notably,
this will work with ``std::string`` and ``std::string_view``.

.. code-block:: c++

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

Expand All @@ -22,3 +28,5 @@ becomes
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ into:

The check uses the same format-string-conversion algorithm as
`modernize-use-std-print <../modernize/use-std-print.html>`_ and its
shortcomings are described in the documentation for that check.
shortcomings and behaviour in combination with macros are described in the
documentation for that check.

Options
-------
Expand Down
Loading