100 changes: 64 additions & 36 deletions bolt/lib/Passes/SplitFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,12 @@ struct SplitCacheDirected final : public SplitStrategy {
void fragment(const BlockIt Start, const BlockIt End) override {
BasicBlockOrder BlockOrder(Start, End);
BinaryFunction &BF = *BlockOrder.front()->getFunction();
// No need to re-split small functions.
if (BlockOrder.size() <= 2)
return;

size_t BestSplitIndex = findSplitIndex(BF, BlockOrder);
assert(BestSplitIndex < BlockOrder.size());

// Assign fragments based on the computed best split index.
// All basic blocks with index up to the best split index become hot.
Expand All @@ -200,10 +204,12 @@ struct SplitCacheDirected final : public SplitStrategy {
};

struct SplitScore {
size_t SplitIndex;
size_t SplitIndex = size_t(-1);
size_t HotSizeReduction = 0;
double LocalScore = 0;
double CoverCallScore = 0;

double sum() const { return LocalScore + CoverCallScore; }
};

// Auxiliary variables used by the algorithm.
Expand Down Expand Up @@ -303,7 +309,7 @@ struct SplitCacheDirected final : public SplitStrategy {
const size_t SplitIndex) {
assert(SplitIndex < BlockOrder.size() && "Invalid split index");

// Update function layout assuming hot-warm splitting at SplitIndex
// Update function layout assuming hot-warm splitting at SplitIndex.
for (size_t Index = 0; Index < BlockOrder.size(); Index++) {
BinaryBasicBlock *BB = BlockOrder[Index];
if (BB->getFragmentNum() == FragmentNum::cold())
Expand All @@ -319,8 +325,8 @@ struct SplitCacheDirected final : public SplitStrategy {
// Populate BB.OutputAddressRange with estimated new start and end addresses
// and compute the old end address of the hot section and the new end
// address of the hot section.
size_t OldHotEndAddr;
size_t NewHotEndAddr;
size_t OldHotEndAddr{0};
size_t NewHotEndAddr{0};
size_t CurrentAddr = BBOffsets[BlockOrder[0]];
for (BinaryBasicBlock *BB : BlockOrder) {
// We only care about new addresses of blocks in hot/warm.
Expand Down Expand Up @@ -492,20 +498,15 @@ struct SplitCacheDirected final : public SplitStrategy {
}

/// Compute the split score of splitting a function at a given index.
/// The split score consists of local score and cover score. Cover call score
/// is expensive to compute. As a result, we pass in a \p ReferenceScore and
/// compute cover score only when the local score exceeds that in the
/// ReferenceScore or that the size reduction of the hot fragment is larger
/// than that achieved by the split index of the ReferenceScore. This function
/// returns \p Score of SplitScore type. It contains the local score and cover
/// score (if computed) of the current splitting index. For easier book
/// keeping and comparison, it also stores the split index and the resulting
/// reduction in hot fragment size.
/// The split score consists of local score and cover score. This function
/// returns \p Score of SplitScore type. It contains the local score and
/// cover score of the current splitting index. For easier book keeping and
/// comparison, it also stores the split index and the resulting reduction
/// in hot fragment size.
SplitScore computeSplitScore(const BinaryFunction &BF,
const BasicBlockOrder &BlockOrder,
const size_t SplitIndex,
const std::vector<CallInfo> &CoverCalls,
const SplitScore &ReferenceScore) {
const std::vector<CallInfo> &CoverCalls) {
// Populate BinaryBasicBlock::OutputAddressRange with estimated
// new start and end addresses after hot-warm splitting at SplitIndex.
size_t OldHotEnd;
Expand Down Expand Up @@ -533,47 +534,74 @@ struct SplitCacheDirected final : public SplitStrategy {
// increamented in place.
computeJumpScore(BlockOrder, SplitIndex, Score);

// There is no need to compute CoverCallScore if we have already found
// another split index with a bigger LocalScore and bigger HotSizeReduction.
if (Score.LocalScore <= ReferenceScore.LocalScore &&
Score.HotSizeReduction <= ReferenceScore.HotSizeReduction)
return Score;

// Compute CoverCallScore and store in Score in place.
computeCoverCallScore(BlockOrder, SplitIndex, CoverCalls, Score);
return Score;
}

/// Find the most likely successor of a basic block when it has one or two
/// successors. Return nullptr otherwise.
const BinaryBasicBlock *getMostLikelySuccessor(const BinaryBasicBlock *BB) {
if (BB->succ_size() == 1)
return BB->getSuccessor();
if (BB->succ_size() == 2) {
uint64_t TakenCount = BB->getTakenBranchInfo().Count;
assert(TakenCount != BinaryBasicBlock::COUNT_NO_PROFILE);
uint64_t NonTakenCount = BB->getFallthroughBranchInfo().Count;
assert(NonTakenCount != BinaryBasicBlock::COUNT_NO_PROFILE);
if (TakenCount > NonTakenCount)
return BB->getConditionalSuccessor(true);
else if (TakenCount < NonTakenCount)
return BB->getConditionalSuccessor(false);
}
return nullptr;
}

/// Find the best index for splitting. The returned value is the index of the
/// last hot basic block. Hence, "no splitting" is equivalent to returning the
/// value which is one less than the size of the function.
size_t findSplitIndex(const BinaryFunction &BF,
const BasicBlockOrder &BlockOrder) {
assert(BlockOrder.size() > 2);
// Find all function calls that can be shortened if we move blocks of the
// current function to warm/cold
const std::vector<CallInfo> CoverCalls = extractCoverCalls(BF);

// Try all possible split indices (blocks with Index <= SplitIndex are in
// hot) and find the one maximizing the splitting score.
// Find the existing hot-cold splitting index.
size_t HotColdIndex = 0;
while (HotColdIndex + 1 < BlockOrder.size()) {
if (BlockOrder[HotColdIndex + 1]->getFragmentNum() == FragmentNum::cold())
break;
HotColdIndex++;
}
assert(HotColdIndex + 1 == BlockOrder.size() ||
(BlockOrder[HotColdIndex]->getFragmentNum() == FragmentNum::main() &&
BlockOrder[HotColdIndex + 1]->getFragmentNum() ==
FragmentNum::cold()));

// Try all possible split indices up to HotColdIndex (blocks that have
// Index <= SplitIndex are in hot) and find the one maximizing the
// splitting score.
SplitScore BestScore;
double BestScoreSum = -1.0;
SplitScore ReferenceScore;
for (size_t Index = 0; Index < BlockOrder.size(); Index++) {
for (size_t Index = 0; Index <= HotColdIndex; Index++) {
const BinaryBasicBlock *LastHotBB = BlockOrder[Index];
// No need to keep cold blocks in the hot section.
if (LastHotBB->getFragmentNum() == FragmentNum::cold())
break;
assert(LastHotBB->getFragmentNum() != FragmentNum::cold());

// Do not break jump to the most likely successor.
if (Index + 1 < BlockOrder.size() &&
BlockOrder[Index + 1] == getMostLikelySuccessor(LastHotBB))
continue;

const SplitScore Score =
computeSplitScore(BF, BlockOrder, Index, CoverCalls, ReferenceScore);
double ScoreSum = Score.LocalScore + Score.CoverCallScore;
if (ScoreSum > BestScoreSum) {
BestScoreSum = ScoreSum;
computeSplitScore(BF, BlockOrder, Index, CoverCalls);
if (Score.sum() > BestScore.sum())
BestScore = Score;
}
if (Score.LocalScore > ReferenceScore.LocalScore)
ReferenceScore = Score;
}

// If we don't find a good splitting point, fallback to the original one.
if (BestScore.SplitIndex == size_t(-1))
return HotColdIndex;

return BestScore.SplitIndex;
}
};
Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
MC
Object
Support
DWARFLinkerBase
DWARFLinker
AsmPrinter
TargetParser
Expand Down
11 changes: 7 additions & 4 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DWARFLinker/DWARFStreamer.h"
#include "llvm/DWARFLinker/Classic/DWARFStreamer.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
Expand Down Expand Up @@ -178,6 +178,9 @@ translateInputToOutputLocationList(const BinaryFunction &BF,
return MergedLL;
}

using namespace dwarf_linker;
using namespace dwarf_linker::classic;

namespace llvm {
namespace bolt {
/// Emits debug information into .debug_info or .debug_types section.
Expand Down Expand Up @@ -278,10 +281,10 @@ class DIEStreamer : public DwarfStreamer {

public:
DIEStreamer(DIEBuilder *DIEBldr, DWARFRewriter &Rewriter,
DWARFLinker::OutputFileType OutFileType,
DWARFLinkerBase::OutputFileType OutFileType,
raw_pwrite_stream &OutFile,
std::function<StringRef(StringRef Input)> Translator,
DWARFLinker::messageHandler Warning)
DWARFLinkerBase::MessageHandlerTy Warning)
: DwarfStreamer(OutFileType, OutFile, Translator, Warning),
DIEBldr(DIEBldr), Rewriter(Rewriter){};

Expand Down Expand Up @@ -457,7 +460,7 @@ createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
DWARFRewriter &Rewriter) {

std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
&DIEBldr, Rewriter, llvm::DWARFLinker::OutputFileType::Object, OutFile,
&DIEBldr, Rewriter, DWARFLinkerBase::OutputFileType::Object, OutFile,
[](StringRef Input) -> StringRef { return Input; },
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {});
Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName);
Expand Down
3 changes: 2 additions & 1 deletion bolt/lib/Rewrite/JITLinkLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ struct JITLinkLinker::Context : jitlink::JITLinkContext {

void notifyFinalized(
jitlink::JITLinkMemoryManager::FinalizedAlloc Alloc) override {
Linker.Allocs.push_back(std::move(Alloc));
if (Alloc)
Linker.Allocs.push_back(std::move(Alloc));
++Linker.MM->ObjectsLoaded;
}
};
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/RISCV/relax.s
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// CHECK: Binary Function "_start" after building cfg {
// CHECK: jal ra, near_f
// CHECK-NEXT: auipc ra, far_f@plt
// CHECK-NEXT: auipc ra, far_f
// CHECK-NEXT: jalr ra, 0xc(ra)
// CHECK-NEXT: j near_f

Expand Down
9 changes: 4 additions & 5 deletions bolt/test/X86/cdsplit-call-scale.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# When -call-scale=0.0, the tested function is 2-way splitted.
# When -call-scale=1.0, the tested function is 3-way splitted with 5 blocks
# in warm because of the increased benefit of shortening the call edges.
# When -call-scale=1000.0, the tested function is 3-way splitted with 7 blocks
# in warm because of the strong benefit of shortening the call edges.
# When -call-scale=1000.0, the tested function is still 3-way splitted with
# 5 blocks in warm because cdsplit does not allow hot-warm splitting to break
# a fall through branch from a basic block to its most likely successor.

# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.o
# RUN: link_fdata %s %t.o %t.fdata
Expand Down Expand Up @@ -39,12 +40,10 @@
# MEDINCENTIVE: {{^\.Ltmp5}}

# HIGHINCENTIVE: Binary Function "chain" after split-functions
# HIGHINCENTIVE: {{^\.LBB00}}
# HIGHINCENTIVE: {{^\.Ltmp1}}
# HIGHINCENTIVE: ------- HOT-COLD SPLIT POINT -------
# HIGHINCENTIVE: {{^\.LFT1}}
# HIGHINCENTIVE: ------- HOT-COLD SPLIT POINT -------
# HIGHINCENTIVE: {{^\.LFT0}}
# HIGHINCENTIVE: {{^\.Ltmp1}}
# HIGHINCENTIVE: {{^\.Ltmp0}}
# HIGHINCENTIVE: {{^\.Ltmp2}}
# HIGHINCENTIVE: {{^\.Ltmp3}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks(
/*OwnsHeaderSearch=*/false);
PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget());
InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(),
Compiler.getFrontendOpts());
Compiler.getFrontendOpts(), Compiler.getCodeGenOpts());
ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts,
Compiler.getTarget().getTriple());
}
Expand Down
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 @@ -83,6 +83,7 @@
#include "UnhandledSelfAssignmentCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
#include "UseAfterMoveCheck.h"
Expand Down Expand Up @@ -235,6 +236,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-unique-ptr-array-mismatch");
CheckFactories.registerCheck<UnsafeFunctionsCheck>(
"bugprone-unsafe-functions");
CheckFactories.registerCheck<UnusedLocalNonTrivialVariableCheck>(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck<UnusedRaiiCheck>("bugprone-unused-raii");
CheckFactories.registerCheck<UnusedReturnValueCheck>(
"bugprone-unused-return-value");
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ add_clang_library(clangTidyBugproneModule
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
UseAfterMoveCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//===--- UnusedLocalNonTrivialVariableCheck.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 "UnusedLocalNonTrivialVariableCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"

using namespace clang::ast_matchers;
using namespace clang::tidy::matchers;

namespace clang::tidy::bugprone {

namespace {
static constexpr StringRef DefaultIncludeTypeRegex =
"::std::.*mutex;::std::future;::std::basic_string;::std::basic_regex;"
"::std::basic_istringstream;::std::basic_stringstream;::std::bitset;"
"::std::filesystem::path";

AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); }
AST_MATCHER(VarDecl, isReferenced) { return Node.isReferenced(); }
AST_MATCHER(Type, isReferenceType) { return Node.isReferenceType(); }
AST_MATCHER(QualType, isTrivial) {
return Node.isTrivialType(Finder->getASTContext()) ||
Node.isTriviallyCopyableType(Finder->getASTContext());
}
} // namespace

UnusedLocalNonTrivialVariableCheck::UnusedLocalNonTrivialVariableCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeTypes(utils::options::parseStringList(
Options.get("IncludeTypes", DefaultIncludeTypeRegex))),
ExcludeTypes(
utils::options::parseStringList(Options.get("ExcludeTypes", ""))) {}

void UnusedLocalNonTrivialVariableCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeTypes",
utils::options::serializeStringList(IncludeTypes));
Options.store(Opts, "ExcludeTypes",
utils::options::serializeStringList(ExcludeTypes));
}

void UnusedLocalNonTrivialVariableCheck::registerMatchers(MatchFinder *Finder) {
if (IncludeTypes.empty())
return;

Finder->addMatcher(
varDecl(isLocalVarDecl(), unless(isReferenced()),
unless(isExceptionVariable()), hasLocalStorage(), isDefinition(),
unless(hasType(isReferenceType())), unless(hasType(isTrivial())),
hasType(hasUnqualifiedDesugaredType(
anyOf(recordType(hasDeclaration(namedDecl(
matchesAnyListedName(IncludeTypes),
unless(matchesAnyListedName(ExcludeTypes))))),
templateSpecializationType(hasDeclaration(namedDecl(
matchesAnyListedName(IncludeTypes),
unless(matchesAnyListedName(ExcludeTypes)))))))))
.bind("var"),
this);
}

void UnusedLocalNonTrivialVariableCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("var");
diag(MatchedDecl->getLocation(), "unused local variable %0 of type %1")
<< MatchedDecl << MatchedDecl->getType();
}

bool UnusedLocalNonTrivialVariableCheck::isLanguageVersionSupported(
const LangOptions &LangOpts) const {
return LangOpts.CPlusPlus;
}

std::optional<TraversalKind>
UnusedLocalNonTrivialVariableCheck::getCheckTraversalKind() const {
return TK_IgnoreUnlessSpelledInSource;
}

} // namespace clang::tidy::bugprone
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===--- UnusedLocalNonTrivialVariableCheck.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_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::bugprone {

/// Warns when a local non trivial variable is unused within a function. By
/// default std::.*mutex and std::future are included.
///
/// The check supports these options:
/// - 'IncludeTypes': a semicolon-separated list of regular expressions
/// matching types to ensure must be used.
/// - 'ExcludeTypes': a semicolon-separated list of regular expressions
/// matching types that are excluded from the
/// 'IncludeTypes' matches.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.html
class UnusedLocalNonTrivialVariableCheck : public ClangTidyCheck {
public:
UnusedLocalNonTrivialVariableCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
std::optional<TraversalKind> getCheckTraversalKind() const override;

private:
const std::vector<StringRef> IncludeTypes;
const std::vector<StringRef> ExcludeTypes;
};

} // namespace clang::tidy::bugprone

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,72 @@ AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) {
FuncTemplate->getTemplateParameters()->getDepth();
}

AST_MATCHER_P(NamedDecl, hasSameNameAsBoundNode, std::string, BindingID) {
IdentifierInfo *II = Node.getIdentifier();
if (nullptr == II)
return false;
StringRef Name = II->getName();

return Builder->removeBindings(
[this, Name](const ast_matchers::internal::BoundNodesMap &Nodes) {
const DynTypedNode &BN = Nodes.getNode(this->BindingID);
if (const auto *ND = BN.get<NamedDecl>()) {
if (!isa<FieldDecl, CXXMethodDecl, VarDecl>(ND))
return true;
return ND->getName() != Name;
}
return true;
});
}

AST_MATCHER_P(LambdaCapture, hasCaptureKind, LambdaCaptureKind, Kind) {
return Node.getCaptureKind() == Kind;
}

AST_MATCHER_P(LambdaExpr, hasCaptureDefaultKind, LambdaCaptureDefault, Kind) {
return Node.getCaptureDefault() == Kind;
}

} // namespace

void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
auto RefToParmImplicit = allOf(
equalsBoundNode("var"), hasInitializer(ignoringParenImpCasts(
declRefExpr(to(equalsBoundNode("param"))))));
auto RefToParm = capturesVar(
varDecl(anyOf(hasSameNameAsBoundNode("param"), RefToParmImplicit)));
auto HasRefToParm = hasAnyCapture(RefToParm);

auto CaptureInRef =
allOf(hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByRef),
unless(hasAnyCapture(
capturesVar(varDecl(hasSameNameAsBoundNode("param"))))));
auto CaptureInCopy = allOf(
hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByCopy), HasRefToParm);
auto CaptureByRefExplicit = hasAnyCapture(
allOf(hasCaptureKind(LambdaCaptureKind::LCK_ByRef), RefToParm));

auto CapturedInBody =
lambdaExpr(anyOf(CaptureInRef, CaptureInCopy, CaptureByRefExplicit));
auto CapturedInCaptureList = hasAnyCapture(capturesVar(
varDecl(hasInitializer(ignoringParenImpCasts(equalsBoundNode("call"))))));

auto CapturedInLambda = hasDeclContext(cxxRecordDecl(
isLambda(),
hasParent(lambdaExpr(forCallable(equalsBoundNode("func")),
anyOf(CapturedInCaptureList, CapturedInBody)))));

auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));

auto ForwardCallMatcher = callExpr(
forCallable(equalsBoundNode("func")), argumentCountIs(1),
callExpr().bind("call"), argumentCountIs(1),
hasArgument(
0, declRefExpr(to(
varDecl(optionally(equalsBoundNode("param"))).bind("var")))),
forCallable(anyOf(equalsBoundNode("func"), CapturedInLambda)),
callee(unresolvedLookupExpr(hasAnyDeclaration(
namedDecl(hasUnderlyingDecl(hasName("::std::forward")))))),
hasArgument(0, declRefExpr(to(equalsBoundNode("param"))).bind("ref")),

unless(anyOf(hasAncestor(typeLoc()),
hasAncestor(expr(hasUnevaluatedContext())))));

Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class UnusedUsingDeclsCheck : public ClangTidyCheck {
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void onEndOfTranslationUnit() override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}

private:
void removeFromFoundDecls(const Decl *D);
Expand Down
15 changes: 10 additions & 5 deletions clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ using namespace clang::ast_matchers;
namespace clang::tidy::modernize {

namespace {
AST_MATCHER_P(InitListExpr, initCountLeq, unsigned, N) {
return Node.getNumInits() <= N;
}

// Identical to hasAnyName, except it does not take template specifiers into
// account. This is used to match the functions names as in
// DefaultEmplacyFunctions below without caring about the template types of the
Expand Down Expand Up @@ -205,11 +209,12 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));

// allow for T{} to be replaced, even if no CTOR is declared
auto HasConstructInitListExpr = has(initListExpr(anyOf(
allOf(has(SoughtConstructExpr),
has(cxxConstructExpr(argumentCountIs(0)))),
has(cxxBindTemporaryExpr(has(SoughtConstructExpr),
has(cxxConstructExpr(argumentCountIs(0))))))));
auto HasConstructInitListExpr = has(initListExpr(
initCountLeq(1), anyOf(allOf(has(SoughtConstructExpr),
has(cxxConstructExpr(argumentCountIs(0)))),
has(cxxBindTemporaryExpr(
has(SoughtConstructExpr),
has(cxxConstructExpr(argumentCountIs(0))))))));
auto HasBracedInitListExpr =
anyOf(has(cxxBindTemporaryExpr(HasConstructInitListExpr)),
HasConstructInitListExpr);
Expand Down
31 changes: 25 additions & 6 deletions clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,39 @@
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;
namespace {

AST_MATCHER(clang::LinkageSpecDecl, isExternCLinkage) {
return Node.getLanguage() == clang::LinkageSpecLanguageIDs::C;
}
} // namespace

namespace clang::tidy::modernize {

static constexpr llvm::StringLiteral ExternCDeclName = "extern-c-decl";
static constexpr llvm::StringLiteral ParentDeclName = "parent-decl";
static constexpr llvm::StringLiteral TagDeclName = "tag-decl";
static constexpr llvm::StringLiteral TypedefName = "typedef";

UseUsingCheck::UseUsingCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
IgnoreExternC(Options.get("IgnoreExternC", false)) {}

void UseUsingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreMacros", IgnoreMacros);
Options.store(Opts, "IgnoreExternC", IgnoreExternC);
}

void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(typedefDecl(unless(isInstantiated()),
hasParent(decl().bind(ParentDeclName)))
.bind(TypedefName),
this);
Finder->addMatcher(
typedefDecl(
unless(isInstantiated()),
optionally(hasAncestor(
linkageSpecDecl(isExternCLinkage()).bind(ExternCDeclName))),
hasParent(decl().bind(ParentDeclName)))
.bind(TypedefName),
this);

// This matcher is used to find tag declarations in source code within
// typedefs. They appear in the AST just *prior* to the typedefs.
Expand Down Expand Up @@ -70,6 +83,11 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
if (MatchedDecl->getLocation().isInvalid())
return;

const auto *ExternCDecl =
Result.Nodes.getNodeAs<LinkageSpecDecl>(ExternCDeclName);
if (ExternCDecl && IgnoreExternC)
return;

SourceLocation StartLoc = MatchedDecl->getBeginLoc();

if (StartLoc.isMacroID() && IgnoreMacros)
Expand Down Expand Up @@ -122,7 +140,8 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
Type = FirstTypedefName + Type.substr(FirstTypedefType.size() + 1);
}
if (!ReplaceRange.getEnd().isMacroID()) {
const SourceLocation::IntTy Offset = MatchedDecl->getFunctionType() ? 0 : Name.size();
const SourceLocation::IntTy Offset =
MatchedDecl->getFunctionType() ? 0 : Name.size();
LastReplacementEnd = ReplaceRange.getEnd().getLocWithOffset(Offset);
}

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/UseUsingCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace clang::tidy::modernize {
class UseUsingCheck : public ClangTidyCheck {

const bool IgnoreMacros;
const bool IgnoreExternC;
SourceLocation LastReplacementEnd;
llvm::DenseMap<const Decl *, SourceRange> LastTagDeclRanges;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
if (!AlgDecl)
return;

if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
if (Unordered && AlgDecl->getName().contains("bound"))
return;

const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===--- AvoidReturnWithVoidValueCheck.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 "AvoidReturnWithVoidValueCheck.h"
#include "clang/AST/Stmt.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

static constexpr auto IgnoreMacrosName = "IgnoreMacros";
static constexpr auto IgnoreMacrosDefault = true;

static constexpr auto StrictModeName = "StrictMode";
static constexpr auto StrictModeDefault = true;

AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreMacros(
Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)),
StrictMode(Options.getLocalOrGlobal(StrictModeName, StrictModeDefault)) {}

void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(
hasReturnValue(allOf(hasType(voidType()), unless(initListExpr()))),
optionally(hasParent(compoundStmt().bind("compound_parent"))))
.bind("void_return"),
this);
}

void AvoidReturnWithVoidValueCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *VoidReturn = Result.Nodes.getNodeAs<ReturnStmt>("void_return");
if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID())
return;
if (!StrictMode && !Result.Nodes.getNodeAs<CompoundStmt>("compound_parent"))
return;
diag(VoidReturn->getBeginLoc(), "return statement within a void function "
"should not have a specified return value");
}

void AvoidReturnWithVoidValueCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, IgnoreMacrosName, IgnoreMacros);
Options.store(Opts, StrictModeName, StrictMode);
}

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

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDRETURNWITHVOIDVALUECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDRETURNWITHVOIDVALUECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// Finds return statements with `void` values used within functions with `void`
/// result types.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/avoid-return-with-void-value.html
class AvoidReturnWithVoidValueCheck : public ClangTidyCheck {
public:
AvoidReturnWithVoidValueCheck(StringRef Name, ClangTidyContext *Context);

void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;

private:
bool IgnoreMacros;
bool StrictMode;
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDRETURNWITHVOIDVALUECHECK_H
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS

add_clang_library(clangTidyReadabilityModule
AvoidConstParamsInDecls.cpp
AvoidReturnWithVoidValueCheck.cpp
AvoidUnconditionalPreprocessorIfCheck.cpp
BracesAroundStatementsCheck.cpp
ConstReturnTypeCheck.cpp
Expand Down
219 changes: 130 additions & 89 deletions clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "IdentifierNamingCheck.h"

#include "../GlobList.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
Expand Down Expand Up @@ -286,7 +287,9 @@ IdentifierNamingCheck::FileStyle IdentifierNamingCheck::getFileStyleFromOptions(
HPTOpt.value_or(IdentifierNamingCheck::HPT_Off));
}
bool IgnoreMainLike = Options.get("IgnoreMainLikeFunctions", false);
return {std::move(Styles), std::move(HNOption), IgnoreMainLike};
bool CheckAnonFieldInParent = Options.get("CheckAnonFieldInParent", false);
return {std::move(Styles), std::move(HNOption), IgnoreMainLike,
CheckAnonFieldInParent};
}

std::string IdentifierNamingCheck::HungarianNotation::getDeclTypeName(
Expand Down Expand Up @@ -859,6 +862,8 @@ void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit);
Options.store(Opts, "IgnoreMainLikeFunctions",
MainFileStyle->isIgnoringMainLikeFunction());
Options.store(Opts, "CheckAnonFieldInParent",
MainFileStyle->isCheckingAnonFieldInParentScope());
}

bool IdentifierNamingCheck::matchesStyle(
Expand Down Expand Up @@ -1111,7 +1116,7 @@ std::string IdentifierNamingCheck::fixupWithStyle(
StyleKind IdentifierNamingCheck::findStyleKind(
const NamedDecl *D,
ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
bool IgnoreMainLikeFunctions) const {
bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope) const {
assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() &&
"Decl must be an explicit identifier with a name.");

Expand Down Expand Up @@ -1185,29 +1190,14 @@ StyleKind IdentifierNamingCheck::findStyleKind(
}

if (const auto *Decl = dyn_cast<FieldDecl>(D)) {
QualType Type = Decl->getType();

if (!Type.isNull() && Type.isConstQualified()) {
if (NamingStyles[SK_ConstantMember])
return SK_ConstantMember;

if (NamingStyles[SK_Constant])
return SK_Constant;
if (CheckAnonFieldInParentScope) {
const RecordDecl *Record = Decl->getParent();
if (Record->isAnonymousStructOrUnion()) {
return findStyleKindForAnonField(Decl, NamingStyles);
}
}

if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember])
return SK_PrivateMember;

if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember])
return SK_ProtectedMember;

if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember])
return SK_PublicMember;

if (NamingStyles[SK_Member])
return SK_Member;

return SK_Invalid;
return findStyleKindForField(Decl, Decl->getType(), NamingStyles);
}

if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) {
Expand Down Expand Up @@ -1244,66 +1234,7 @@ StyleKind IdentifierNamingCheck::findStyleKind(
}

if (const auto *Decl = dyn_cast<VarDecl>(D)) {
QualType Type = Decl->getType();

if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
return SK_ConstexprVariable;

if (!Type.isNull() && Type.isConstQualified()) {
if (Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant])
return SK_ClassConstant;

if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_GlobalConstantPointer])
return SK_GlobalConstantPointer;

if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant])
return SK_GlobalConstant;

if (Decl->isStaticLocal() && NamingStyles[SK_StaticConstant])
return SK_StaticConstant;

if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_LocalConstantPointer])
return SK_LocalConstantPointer;

if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant])
return SK_LocalConstant;

if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant])
return SK_LocalConstant;

if (NamingStyles[SK_Constant])
return SK_Constant;
}

if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember])
return SK_ClassMember;

if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_GlobalPointer])
return SK_GlobalPointer;

if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable])
return SK_GlobalVariable;

if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable])
return SK_StaticVariable;

if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_LocalPointer])
return SK_LocalPointer;

if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable])
return SK_LocalVariable;

if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable])
return SK_LocalVariable;

if (NamingStyles[SK_Variable])
return SK_Variable;

return SK_Invalid;
return findStyleKindForVar(Decl, Decl->getType(), NamingStyles);
}

if (const auto *Decl = dyn_cast<CXXMethodDecl>(D)) {
Expand Down Expand Up @@ -1442,12 +1373,13 @@ IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl,
if (!FileStyle.isActive())
return std::nullopt;

return getFailureInfo(HungarianNotation.getDeclTypeName(Decl),
Decl->getName(), Decl, Loc, FileStyle.getStyles(),
FileStyle.getHNOption(),
findStyleKind(Decl, FileStyle.getStyles(),
FileStyle.isIgnoringMainLikeFunction()),
SM, IgnoreFailedSplit);
return getFailureInfo(
HungarianNotation.getDeclTypeName(Decl), Decl->getName(), Decl, Loc,
FileStyle.getStyles(), FileStyle.getHNOption(),
findStyleKind(Decl, FileStyle.getStyles(),
FileStyle.isIgnoringMainLikeFunction(),
FileStyle.isCheckingAnonFieldInParentScope()),
SM, IgnoreFailedSplit);
}

std::optional<RenamerClangTidyCheck::FailureInfo>
Expand Down Expand Up @@ -1496,5 +1428,114 @@ IdentifierNamingCheck::getStyleForFile(StringRef FileName) const {
return It.first->getValue();
}

StyleKind IdentifierNamingCheck::findStyleKindForAnonField(
const FieldDecl *AnonField,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const {
const IndirectFieldDecl *IFD =
utils::findOutermostIndirectFieldDeclForField(AnonField);
assert(IFD && "Found an anonymous record field without an IndirectFieldDecl");

QualType Type = AnonField->getType();

if (const auto *F = dyn_cast<FieldDecl>(IFD->chain().front())) {
return findStyleKindForField(F, Type, NamingStyles);
}

if (const auto *V = IFD->getVarDecl()) {
return findStyleKindForVar(V, Type, NamingStyles);
}

return SK_Invalid;
}

StyleKind IdentifierNamingCheck::findStyleKindForField(
const FieldDecl *Field, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const {
if (!Type.isNull() && Type.isConstQualified()) {
if (NamingStyles[SK_ConstantMember])
return SK_ConstantMember;

if (NamingStyles[SK_Constant])
return SK_Constant;
}

if (Field->getAccess() == AS_private && NamingStyles[SK_PrivateMember])
return SK_PrivateMember;

if (Field->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember])
return SK_ProtectedMember;

if (Field->getAccess() == AS_public && NamingStyles[SK_PublicMember])
return SK_PublicMember;

if (NamingStyles[SK_Member])
return SK_Member;

return SK_Invalid;
}

StyleKind IdentifierNamingCheck::findStyleKindForVar(
const VarDecl *Var, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const {
if (Var->isConstexpr() && NamingStyles[SK_ConstexprVariable])
return SK_ConstexprVariable;

if (!Type.isNull() && Type.isConstQualified()) {
if (Var->isStaticDataMember() && NamingStyles[SK_ClassConstant])
return SK_ClassConstant;

if (Var->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_GlobalConstantPointer])
return SK_GlobalConstantPointer;

if (Var->isFileVarDecl() && NamingStyles[SK_GlobalConstant])
return SK_GlobalConstant;

if (Var->isStaticLocal() && NamingStyles[SK_StaticConstant])
return SK_StaticConstant;

if (Var->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_LocalConstantPointer])
return SK_LocalConstantPointer;

if (Var->isLocalVarDecl() && NamingStyles[SK_LocalConstant])
return SK_LocalConstant;

if (Var->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant])
return SK_LocalConstant;

if (NamingStyles[SK_Constant])
return SK_Constant;
}

if (Var->isStaticDataMember() && NamingStyles[SK_ClassMember])
return SK_ClassMember;

if (Var->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_GlobalPointer])
return SK_GlobalPointer;

if (Var->isFileVarDecl() && NamingStyles[SK_GlobalVariable])
return SK_GlobalVariable;

if (Var->isStaticLocal() && NamingStyles[SK_StaticVariable])
return SK_StaticVariable;

if (Var->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
NamingStyles[SK_LocalPointer])
return SK_LocalPointer;

if (Var->isLocalVarDecl() && NamingStyles[SK_LocalVariable])
return SK_LocalVariable;

if (Var->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable])
return SK_LocalVariable;

if (NamingStyles[SK_Variable])
return SK_Variable;

return SK_Invalid;
}

} // namespace readability
} // namespace clang::tidy
26 changes: 23 additions & 3 deletions clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
struct FileStyle {
FileStyle() : IsActive(false), IgnoreMainLikeFunctions(false) {}
FileStyle(SmallVectorImpl<std::optional<NamingStyle>> &&Styles,
HungarianNotationOption HNOption, bool IgnoreMainLike)
HungarianNotationOption HNOption, bool IgnoreMainLike,
bool CheckAnonFieldInParent)
: Styles(std::move(Styles)), HNOption(std::move(HNOption)),
IsActive(true), IgnoreMainLikeFunctions(IgnoreMainLike) {}
IsActive(true), IgnoreMainLikeFunctions(IgnoreMainLike),
CheckAnonFieldInParentScope(CheckAnonFieldInParent) {}

ArrayRef<std::optional<NamingStyle>> getStyles() const {
assert(IsActive);
Expand All @@ -144,11 +146,16 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
bool isActive() const { return IsActive; }
bool isIgnoringMainLikeFunction() const { return IgnoreMainLikeFunctions; }

bool isCheckingAnonFieldInParentScope() const {
return CheckAnonFieldInParentScope;
}

private:
SmallVector<std::optional<NamingStyle>, 0> Styles;
HungarianNotationOption HNOption;
bool IsActive;
bool IgnoreMainLikeFunctions;
bool CheckAnonFieldInParentScope;
};

IdentifierNamingCheck::FileStyle
Expand All @@ -175,7 +182,7 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
StyleKind findStyleKind(
const NamedDecl *D,
ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
bool IgnoreMainLikeFunctions) const;
bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope) const;

std::optional<RenamerClangTidyCheck::FailureInfo> getFailureInfo(
StringRef Type, StringRef Name, const NamedDecl *ND,
Expand All @@ -199,6 +206,19 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {

const FileStyle &getStyleForFile(StringRef FileName) const;

/// Find the style kind of a field in an anonymous record.
StyleKind findStyleKindForAnonField(
const FieldDecl *AnonField,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const;

StyleKind findStyleKindForField(
const FieldDecl *Field, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const;

StyleKind
findStyleKindForVar(const VarDecl *Var, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const;

/// Stores the style options as a vector, indexed by the specified \ref
/// StyleKind, for a given directory.
mutable llvm::StringMap<FileStyle> NamingStylesCache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "MisleadingIndentationCheck.h"
#include "../utils/LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

Expand Down Expand Up @@ -51,8 +52,20 @@ void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
}

void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
const CompoundStmt *CStmt) {
static bool isAtStartOfLineIncludingEmptyMacro(SourceLocation NextLoc,
const SourceManager &SM,
const LangOptions &LangOpts) {
const SourceLocation BeforeLoc =
utils::lexer::getPreviousTokenAndStart(NextLoc, SM, LangOpts).second;
if (BeforeLoc.isInvalid())
return false;
return SM.getExpansionLineNumber(BeforeLoc) !=
SM.getExpansionLineNumber(NextLoc);
}

void MisleadingIndentationCheck::missingBracesCheck(
const SourceManager &SM, const CompoundStmt *CStmt,
const LangOptions &LangOpts) {
const static StringRef StmtNames[] = {"if", "for", "while"};
for (unsigned int I = 0; I < CStmt->size() - 1; I++) {
const Stmt *CurrentStmt = CStmt->body_begin()[I];
Expand Down Expand Up @@ -92,6 +105,8 @@ void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,

if (NextLoc.isInvalid() || NextLoc.isMacroID())
continue;
if (!isAtStartOfLineIncludingEmptyMacro(NextLoc, SM, LangOpts))
continue;

if (SM.getExpansionColumnNumber(InnerLoc) ==
SM.getExpansionColumnNumber(NextLoc)) {
Expand All @@ -117,7 +132,8 @@ void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
danglingElseCheck(*Result.SourceManager, Result.Context, If);

if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
missingBracesCheck(*Result.SourceManager, CStmt);
missingBracesCheck(*Result.SourceManager, CStmt,
Result.Context->getLangOpts());
}

} // namespace clang::tidy::readability
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class MisleadingIndentationCheck : public ClangTidyCheck {
private:
void danglingElseCheck(const SourceManager &SM, ASTContext *Context,
const IfStmt *If);
void missingBracesCheck(const SourceManager &SM, const CompoundStmt *CStmt);
void missingBracesCheck(const SourceManager &SM, const CompoundStmt *CStmt,
const LangOptions &LangOpts);
};

} // namespace clang::tidy::readability
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "AvoidConstParamsInDecls.h"
#include "AvoidReturnWithVoidValueCheck.h"
#include "AvoidUnconditionalPreprocessorIfCheck.h"
#include "BracesAroundStatementsCheck.h"
#include "ConstReturnTypeCheck.h"
Expand Down Expand Up @@ -63,6 +64,8 @@ class ReadabilityModule : public ClangTidyModule {
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<AvoidConstParamsInDecls>(
"readability-avoid-const-params-in-decls");
CheckFactories.registerCheck<AvoidReturnWithVoidValueCheck>(
"readability-avoid-return-with-void-value");
CheckFactories.registerCheck<AvoidUnconditionalPreprocessorIfCheck>(
"readability-avoid-unconditional-preprocessor-if");
CheckFactories.registerCheck<BracesAroundStatementsCheck>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {

static const char KDefaultTypes[] =
"::std::basic_string;::std::basic_string_view;::std::vector;::std::array";
"::std::basic_string;::std::basic_string_view;::std::vector;::std::array;::"
"std::span";

SimplifySubscriptExprCheck::SimplifySubscriptExprCheck(
StringRef Name, ClangTidyContext *Context)
Expand Down
8 changes: 8 additions & 0 deletions clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ def main():
help="checks filter, when not specified, use clang-tidy " "default",
default="",
)
parser.add_argument(
"-config-file",
dest="config_file",
help="Specify the path of .clang-tidy or custom config file",
default="",
)
parser.add_argument("-use-color", action="store_true", help="Use colors in output")
parser.add_argument(
"-path", dest="build_path", help="Path used to read a compile command database."
Expand Down Expand Up @@ -313,6 +319,8 @@ def main():
common_clang_tidy_args.append("-fix")
if args.checks != "":
common_clang_tidy_args.append("-checks=" + args.checks)
if args.config_file != "":
common_clang_tidy_args.append("-config-file=" + args.config_file)
if args.quiet:
common_clang_tidy_args.append("-quiet")
if args.build_path is not None:
Expand Down
24 changes: 24 additions & 0 deletions clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,28 @@ bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
return DataFirst == DataSecond;
}

const IndirectFieldDecl *
findOutermostIndirectFieldDeclForField(const FieldDecl *FD) {
const RecordDecl *Record = FD->getParent();
assert(Record->isAnonymousStructOrUnion() &&
"FD must be a field in an anonymous record");

const DeclContext *Context = Record;
while (isa<RecordDecl>(Context) &&
cast<RecordDecl>(Context)->isAnonymousStructOrUnion()) {
Context = Context->getParent();
}

// Search for the target IndirectFieldDecl within the located context.
for (const auto *D : Context->decls()) {
const auto *IFD = dyn_cast<IndirectFieldDecl>(D);
if (!IFD)
continue;
if (IFD->getAnonField() == FD)
return IFD;
}

return nullptr;
}

} // namespace clang::tidy::utils
5 changes: 5 additions & 0 deletions clang-tools-extra/clang-tidy/utils/ASTUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM);
bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
const ASTContext &Context, bool Canonical = false);

// Given a field of an anonymous record, find its corresponding
// IndirectFieldDecl in the outermost possible scope.
const IndirectFieldDecl *
findOutermostIndirectFieldDeclForField(const FieldDecl *FD);

} // namespace clang::tidy::utils

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/Hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,7 @@ void maybeAddSymbolProviders(ParsedAST &AST, HoverInfo &HI,

const SourceManager &SM = AST.getSourceManager();
llvm::SmallVector<include_cleaner::Header> RankedProviders =
include_cleaner::headersForSymbol(Sym, SM, AST.getPragmaIncludes().get());
include_cleaner::headersForSymbol(Sym, SM, &AST.getPragmaIncludes());
if (RankedProviders.empty())
return;

Expand Down Expand Up @@ -1254,7 +1254,7 @@ void maybeAddUsedSymbols(ParsedAST &AST, HoverInfo &HI, const Inclusion &Inc) {
llvm::DenseSet<include_cleaner::Symbol> UsedSymbols;
include_cleaner::walkUsed(
AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
AST.getPragmaIncludes().get(), AST.getPreprocessor(),
&AST.getPragmaIncludes(), AST.getPreprocessor(),
[&](const include_cleaner::SymbolReference &Ref,
llvm::ArrayRef<include_cleaner::Header> Providers) {
if (Ref.RT != include_cleaner::RefType::Explicit ||
Expand Down
36 changes: 3 additions & 33 deletions clang-tools-extra/clangd/IncludeCleaner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
#include <cassert>
#include <iterator>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
Expand Down Expand Up @@ -237,18 +236,6 @@ removeAllUnusedIncludes(llvm::ArrayRef<Diag> UnusedIncludes) {
Diag.Fixes.front().Edits.begin(),
Diag.Fixes.front().Edits.end());
}

// TODO(hokein): emit a suitable text for the label.
ChangeAnnotation Annotation = {/*label=*/"",
/*needsConfirmation=*/true,
/*description=*/""};
static const ChangeAnnotationIdentifier RemoveAllUnusedID =
"RemoveAllUnusedIncludes";
for (unsigned I = 0; I < RemoveAll.Edits.size(); ++I) {
ChangeAnnotationIdentifier ID = RemoveAllUnusedID + std::to_string(I);
RemoveAll.Edits[I].annotationId = ID;
RemoveAll.Annotations.push_back({ID, Annotation});
}
return RemoveAll;
}

Expand All @@ -268,20 +255,8 @@ addAllMissingIncludes(llvm::ArrayRef<Diag> MissingIncludeDiags) {
Edits.try_emplace(Edit.newText, Edit);
}
}
// FIXME(hokein): emit used symbol reference in the annotation.
ChangeAnnotation Annotation = {/*label=*/"",
/*needsConfirmation=*/true,
/*description=*/""};
static const ChangeAnnotationIdentifier AddAllMissingID =
"AddAllMissingIncludes";
unsigned I = 0;
for (auto &It : Edits) {
ChangeAnnotationIdentifier ID = AddAllMissingID + std::to_string(I++);
for (auto &It : Edits)
AddAllMissing.Edits.push_back(std::move(It.second));
AddAllMissing.Edits.back().annotationId = ID;

AddAllMissing.Annotations.push_back({ID, Annotation});
}
return AddAllMissing;
}
Fix fixAll(const Fix &RemoveAllUnused, const Fix &AddAllMissing) {
Expand All @@ -292,11 +267,6 @@ Fix fixAll(const Fix &RemoveAllUnused, const Fix &AddAllMissing) {
FixAll.Edits.push_back(F);
for (const auto &F : AddAllMissing.Edits)
FixAll.Edits.push_back(F);

for (const auto &A : RemoveAllUnused.Annotations)
FixAll.Annotations.push_back(A);
for (const auto &A : AddAllMissing.Annotations)
FixAll.Annotations.push_back(A);
return FixAll;
}

Expand All @@ -311,7 +281,7 @@ getUnused(ParsedAST &AST,
auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
if (ReferencedFiles.contains(IncludeID))
continue;
if (!mayConsiderUnused(MFI, AST, AST.getPragmaIncludes().get())) {
if (!mayConsiderUnused(MFI, AST, &AST.getPragmaIncludes())) {
dlog("{0} was not used, but is not eligible to be diagnosed as unused",
MFI.Written);
continue;
Expand Down Expand Up @@ -403,7 +373,7 @@ IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
.getBuiltinDir();
include_cleaner::walkUsed(
AST.getLocalTopLevelDecls(), /*MacroRefs=*/Macros,
AST.getPragmaIncludes().get(), AST.getPreprocessor(),
&AST.getPragmaIncludes(), AST.getPreprocessor(),
[&](const include_cleaner::SymbolReference &Ref,
llvm::ArrayRef<include_cleaner::Header> Providers) {
bool Satisfied = false;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ std::string summarizeExpr(const Expr *E) {
// Step through implicit nodes that clang doesn't classify as such.
std::string VisitCXXMemberCallExpr(const CXXMemberCallExpr *E) {
// Call to operator bool() inside if (X): dispatch to X.
if (E->getNumArgs() == 0 &&
if (E->getNumArgs() == 0 && E->getMethodDecl() &&
E->getMethodDecl()->getDeclName().getNameKind() ==
DeclarationName::CXXConversionFunctionName &&
E->getSourceRange() ==
Expand Down
21 changes: 12 additions & 9 deletions clang-tools-extra/clangd/ParsedAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,18 +653,23 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
}

IncludeStructure Includes;
include_cleaner::PragmaIncludes PI;
// If we are using a preamble, copy existing includes.
if (Preamble) {
Includes = Preamble->Includes;
Includes.MainFileIncludes = Patch->preambleIncludes();
// Replay the preamble includes so that clang-tidy checks can see them.
ReplayPreamble::attach(Patch->preambleIncludes(), *Clang,
Patch->modifiedBounds());
PI = *Preamble->Pragmas;
}
// Important: collectIncludeStructure is registered *after* ReplayPreamble!
// Otherwise we would collect the replayed includes again...
// (We can't *just* use the replayed includes, they don't have Resolved path).
Includes.collect(*Clang);
// Same for pragma-includes, we're already inheriting preamble includes, so we
// should only receive callbacks for non-preamble mainfile includes.
PI.record(*Clang);
// Copy over the macros in the preamble region of the main file, and combine
// with non-preamble macros below.
MainFileMacros Macros;
Expand Down Expand Up @@ -735,7 +740,7 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
ParsedAST Result(Filename, Inputs.Version, std::move(Preamble),
std::move(Clang), std::move(Action), std::move(Tokens),
std::move(Macros), std::move(Marks), std::move(ParsedDecls),
std::move(Diags), std::move(Includes));
std::move(Diags), std::move(Includes), std::move(PI));
llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
std::back_inserter(Result.Diags));
return std::move(Result);
Expand Down Expand Up @@ -828,23 +833,21 @@ ParsedAST::ParsedAST(PathRef TUPath, llvm::StringRef Version,
syntax::TokenBuffer Tokens, MainFileMacros Macros,
std::vector<PragmaMark> Marks,
std::vector<Decl *> LocalTopLevelDecls,
std::vector<Diag> Diags, IncludeStructure Includes)
std::vector<Diag> Diags, IncludeStructure Includes,
include_cleaner::PragmaIncludes PI)
: TUPath(TUPath), Version(Version), Preamble(std::move(Preamble)),
Clang(std::move(Clang)), Action(std::move(Action)),
Tokens(std::move(Tokens)), Macros(std::move(Macros)),
Marks(std::move(Marks)), Diags(std::move(Diags)),
LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
Includes(std::move(Includes)) {
Resolver = std::make_unique<HeuristicResolver>(getASTContext());
Includes(std::move(Includes)), PI(std::move(PI)),
Resolver(std::make_unique<HeuristicResolver>(getASTContext())) {
assert(this->Clang);
assert(this->Action);
}

std::shared_ptr<const include_cleaner::PragmaIncludes>
ParsedAST::getPragmaIncludes() const {
if (!Preamble)
return nullptr;
return Preamble->Pragmas;
const include_cleaner::PragmaIncludes &ParsedAST::getPragmaIncludes() const {
return PI;
}

std::optional<llvm::StringRef> ParsedAST::preambleVersion() const {
Expand Down
9 changes: 4 additions & 5 deletions clang-tools-extra/clangd/ParsedAST.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,8 @@ class ParsedAST {
/// Tokens recorded while parsing the main file.
/// (!) does not have tokens from the preamble.
const syntax::TokenBuffer &getTokens() const { return Tokens; }
/// Returns the PramaIncludes from the preamble.
/// Might be null if AST is built without a preamble.
std::shared_ptr<const include_cleaner::PragmaIncludes>
getPragmaIncludes() const;
/// Returns the PramaIncludes for preamble + main file includes.
const include_cleaner::PragmaIncludes &getPragmaIncludes() const;

/// Returns the version of the ParseInputs this AST was built from.
llvm::StringRef version() const { return Version; }
Expand All @@ -129,7 +127,7 @@ class ParsedAST {
std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
MainFileMacros Macros, std::vector<PragmaMark> Marks,
std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags,
IncludeStructure Includes);
IncludeStructure Includes, include_cleaner::PragmaIncludes PI);
Path TUPath;
std::string Version;
// In-memory preambles must outlive the AST, it is important that this member
Expand Down Expand Up @@ -159,6 +157,7 @@ class ParsedAST {
// top-level decls from the preamble.
std::vector<Decl *> LocalTopLevelDecls;
IncludeStructure Includes;
include_cleaner::PragmaIncludes PI;
std::unique_ptr<HeuristicResolver> Resolver;
};

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1339,7 +1339,7 @@ maybeFindIncludeReferences(ParsedAST &AST, Position Pos,
auto Converted = convertIncludes(AST);
include_cleaner::walkUsed(
AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
AST.getPragmaIncludes().get(), AST.getPreprocessor(),
&AST.getPragmaIncludes(), AST.getPreprocessor(),
[&](const include_cleaner::SymbolReference &Ref,
llvm::ArrayRef<include_cleaner::Header> Providers) {
if (Ref.RT != include_cleaner::RefType::Explicit ||
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/FileIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ FileShardedIndex::getShard(llvm::StringRef Uri) const {
SlabTuple indexMainDecls(ParsedAST &AST) {
return indexSymbols(
AST.getASTContext(), AST.getPreprocessor(), AST.getLocalTopLevelDecls(),
&AST.getMacros(), *AST.getPragmaIncludes(),
&AST.getMacros(), AST.getPragmaIncludes(),
/*IsIndexMainAST=*/true, AST.version(), /*CollectMainFileRefs=*/true);
}

Expand Down
8 changes: 4 additions & 4 deletions clang-tools-extra/clangd/index/SymbolCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,8 @@ void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation DefLoc,

// Use the expansion location to get the #include header since this is
// where the symbol is exposed.
IncludeFiles[S.ID] = SM.getDecomposedExpansionLoc(DefLoc).first;
if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
IncludeFiles[S.ID] = FID;

// We update providers for a symbol with each occurence, as SymbolCollector
// might run while parsing, rather than at the end of a translation unit.
Expand Down Expand Up @@ -879,16 +880,15 @@ void SymbolCollector::finish() {
const Symbol *S = Symbols.find(SID);
if (!S)
continue;
assert(IncludeFiles.contains(SID));

const auto FID = IncludeFiles.at(SID);
FileID FID = IncludeFiles.lookup(SID);
// Determine if the FID is #include'd or #import'ed.
Symbol::IncludeDirective Directives = Symbol::Invalid;
auto CollectDirectives = shouldCollectIncludePath(S->SymInfo.Kind);
if ((CollectDirectives & Symbol::Include) != 0)
Directives |= Symbol::Include;
// Only allow #import for symbols from ObjC-like files.
if ((CollectDirectives & Symbol::Import) != 0) {
if ((CollectDirectives & Symbol::Import) != 0 && FID.isValid()) {
auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
if (Inserted)
It->second = FilesWithObjCConstructs.contains(FID) ||
Expand Down
15 changes: 15 additions & 0 deletions clang-tools-extra/clangd/test/GH75115.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: rm -rf %t.dir && mkdir -p %t.dir
// RUN: echo '[{"directory": "%/t.dir", "command": "clang --target=x86_64-pc-windows-msvc -x c GH75115.test", "file": "GH75115.test"}]' > %t.dir/compile_commands.json
// RUN: clangd -enable-config=0 --compile-commands-dir=%t.dir -check=%s 2>&1 | FileCheck -strict-whitespace %s

// CHECK: Building preamble...
// CHECK-NEXT: Built preamble
// CHECK-NEXT: Indexing headers...
// CHECK-NEXT: Building AST...
// CHECK-NEXT: Indexing AST...
// CHECK-NEXT: Building inlay hints
// CHECK-NEXT: semantic highlighting
// CHECK-NEXT: Testing features at each token
// CHECK-NEXT: All checks completed, 0 errors

#define assert
68 changes: 0 additions & 68 deletions clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
Original file line number Diff line number Diff line change
Expand Up @@ -157,21 +157,10 @@
# CHECK-NEXT: {
# CHECK-NEXT: "arguments": [
# CHECK-NEXT: {
# CHECK-NEXT: "changeAnnotations": {
# CHECK-NEXT: "AddAllMissingIncludes0": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "AddAllMissingIncludes1": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: "documentChanges": [
# CHECK-NEXT: {
# CHECK-NEXT: "edits": [
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "AddAllMissingIncludes0",
# CHECK-NEXT: "newText": "#include {{.*}}bar.h{{.*}}",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -185,7 +174,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "AddAllMissingIncludes1",
# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand Down Expand Up @@ -213,29 +201,10 @@
# CHECK-NEXT: {
# CHECK-NEXT: "arguments": [
# CHECK-NEXT: {
# CHECK-NEXT: "changeAnnotations": {
# CHECK-NEXT: "AddAllMissingIncludes0": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "AddAllMissingIncludes1": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "RemoveAllUnusedIncludes0": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "RemoveAllUnusedIncludes1": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: "documentChanges": [
# CHECK-NEXT: {
# CHECK-NEXT: "edits": [
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes0",
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -249,7 +218,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes1",
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -263,7 +231,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "AddAllMissingIncludes0",
# CHECK-NEXT: "newText": "#include {{.*}}bar.h{{.*}}",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -277,7 +244,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "AddAllMissingIncludes1",
# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand Down Expand Up @@ -342,21 +308,10 @@
# CHECK-NEXT: {
# CHECK-NEXT: "arguments": [
# CHECK-NEXT: {
# CHECK-NEXT: "changeAnnotations": {
# CHECK-NEXT: "RemoveAllUnusedIncludes0": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "RemoveAllUnusedIncludes1": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: "documentChanges": [
# CHECK-NEXT: {
# CHECK-NEXT: "edits": [
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes0",
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -370,7 +325,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes1",
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand Down Expand Up @@ -398,29 +352,10 @@
# CHECK-NEXT: {
# CHECK-NEXT: "arguments": [
# CHECK-NEXT: {
# CHECK-NEXT: "changeAnnotations": {
# CHECK-NEXT: "AddAllMissingIncludes0": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "AddAllMissingIncludes1": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "RemoveAllUnusedIncludes0": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: },
# CHECK-NEXT: "RemoveAllUnusedIncludes1": {
# CHECK-NEXT: "label": "",
# CHECK-NEXT: "needsConfirmation": true
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: "documentChanges": [
# CHECK-NEXT: {
# CHECK-NEXT: "edits": [
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes0",
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -434,7 +369,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "RemoveAllUnusedIncludes1",
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -448,7 +382,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "AddAllMissingIncludes0",
# CHECK-NEXT: "newText": "#include {{.*}}bar.h{{.*}}",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand All @@ -462,7 +395,6 @@
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "annotationId": "AddAllMissingIncludes1",
# CHECK-NEXT: "newText": "#include {{.*}}foo.h{{.*}}",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand Down
56 changes: 56 additions & 0 deletions clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,62 @@ TEST(DiagnosticTest, MakeUnique) {
"no matching constructor for initialization of 'S'")));
}

TEST(DiagnosticTest, CoroutineInHeader) {
StringRef CoroutineH = R"cpp(
namespace std {
template <class Ret, typename... T>
struct coroutine_traits { using promise_type = typename Ret::promise_type; };

template <class Promise = void>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
static coroutine_handle from_promise(Promise &promise);
constexpr void* address() const noexcept;
};
template <>
struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
static coroutine_handle from_address(void *);
constexpr void* address() const noexcept;
};

struct awaitable {
bool await_ready() noexcept { return false; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
} // namespace std
)cpp";

StringRef Header = R"cpp(
#include "coroutine.h"
template <typename T> struct [[clang::coro_return_type]] Gen {
struct promise_type {
Gen<T> get_return_object() {
return {};
}
std::awaitable initial_suspend();
std::awaitable final_suspend() noexcept;
void unhandled_exception();
void return_value(T t);
};
};

Gen<int> foo_coro(int b) { co_return b; }
)cpp";
Annotations Main(R"cpp(
// error-ok
#include "header.hpp"
Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
TU.AdditionalFiles["header.hpp"] = std::string(Header);
TU.ExtraArgs.push_back("--std=c++20");
EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
}

TEST(DiagnosticTest, MakeShared) {
// We usually miss diagnostics from header functions as we don't parse them.
// std::make_shared is only parsed when --parse-forwarding-functions is set
Expand Down
12 changes: 6 additions & 6 deletions clang-tools-extra/clangd/unittests/FileIndexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
auto AST = File.build();
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());
}

TEST(FileIndexTest, CustomizedURIScheme) {
Expand Down Expand Up @@ -254,7 +254,7 @@ TEST(FileIndexTest, IWYUPragmaExport) {
auto AST = File.build();
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());

auto Symbols = runFuzzyFind(M, "");
EXPECT_THAT(
Expand Down Expand Up @@ -446,7 +446,7 @@ TEST(FileIndexTest, Relations) {
FileIndex Index;
Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());
SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
uint32_t Results = 0;
RelationsRequest Req;
Expand Down Expand Up @@ -567,15 +567,15 @@ TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
auto AST = File.build();
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());
EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("a")));

File.Filename = "f2.cpp";
File.HeaderCode = "int b;";
AST = File.build();
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());
EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("b")));
}

Expand Down Expand Up @@ -720,7 +720,7 @@ TEST(FileIndexTest, Profile) {
auto AST = TestTU::withHeaderCode("int a;").build();
FI.updateMain(FileName, AST);
FI.updatePreamble(FileName, "v1", AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());

llvm::BumpPtrAllocator Alloc;
MemoryTree MT(&Alloc);
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,10 @@ TEST(IncludeCleaner, IWYUPragmas) {
#include "public.h"

void bar() { foo(); }
#include "keep_main_file.h" // IWYU pragma: keep
)cpp";
TU.AdditionalFiles["behind_keep.h"] = guard("");
TU.AdditionalFiles["keep_main_file.h"] = guard("");
TU.AdditionalFiles["exported.h"] = guard("");
TU.AdditionalFiles["public.h"] = guard("#include \"private.h\"");
TU.AdditionalFiles["private.h"] = guard(R"cpp(
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2205,6 +2205,19 @@ TEST(BlockEndHints, Macro) {
ExpectedHint{" // struct S1", "S1"});
}

TEST(BlockEndHints, PointerToMemberFunction) {
// Do not crash trying to summarize `a->*p`.
assertBlockEndHints(R"cpp(
class A {};
using Predicate = bool(A::*)();
void foo(A* a, Predicate p) {
if ((a->*p)()) {
$ptrmem[[}]]
} // suppress
)cpp",
ExpectedHint{" // if", "ptrmem"});
}

// FIXME: Low-hanging fruit where we could omit a type hint:
// - auto x = TypeName(...);
// - auto x = (TypeName) (...);
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/unittests/TestTU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ SymbolSlab TestTU::headerSymbols() const {
auto AST = build();
return std::get<0>(indexHeaderSymbols(
/*Version=*/"null", AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes()));
AST.getPragmaIncludes()));
}

RefSlab TestTU::headerRefs() const {
Expand All @@ -177,7 +177,7 @@ std::unique_ptr<SymbolIndex> TestTU::index() const {
auto Idx = std::make_unique<FileIndex>();
Idx->updatePreamble(testPath(Filename), /*Version=*/"null",
AST.getASTContext(), AST.getPreprocessor(),
*AST.getPragmaIncludes());
AST.getPragmaIncludes());
Idx->updateMain(testPath(Filename), AST);
return std::move(Idx);
}
Expand Down
55 changes: 47 additions & 8 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,22 @@ Improvements to clang-tidy

- Improved `--dump-config` to print check options in alphabetical order.

- Improved :program:`clang-tidy-diff.py` script. It now returns exit code `1`
if any :program:`clang-tidy` subprocess exits with a non-zero code or if
exporting fixes fails. It now accepts a directory as a value for
`-export-fixes` to export individual yaml files for each compilation unit.
- Improved :program:`clang-tidy-diff.py` script.
* Return exit code `1` if any :program:`clang-tidy` subprocess exits with
a non-zero code or if exporting fixes fails.

* Accept a directory as a value for `-export-fixes` to export individual
yaml files for each compilation unit.

* Introduce a `-config-file` option that forwards a configuration file to
:program:`clang-tidy`. Corresponds to the `--config-file` option in
:program:`clang-tidy`.

- Improved :program:`run-clang-tidy.py` script. It now accepts a directory
as a value for `-export-fixes` to export individual yaml files for each
compilation unit.


New checks
^^^^^^^^^^

Expand Down Expand Up @@ -168,6 +175,11 @@ New checks
extracted from an optional-like type and then used to create a new instance
of the same optional-like type.

- New :doc:`bugprone-unused-local-non-trivial-variable
<clang-tidy/checks/bugprone/unused-local-non-trivial-variable>` check.

Warns when a local non trivial variable is unused within a function.

- New :doc:`cppcoreguidelines-no-suspend-with-lock
<clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock>` check.

Expand Down Expand Up @@ -218,6 +230,12 @@ New checks
Detects C++ code where a reference variable is used to extend the lifetime
of a temporary object that has just been constructed.

- New :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check.

Finds return statements with ``void`` values used within functions with
``void`` result types.

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

Expand Down Expand Up @@ -283,6 +301,10 @@ Changes in existing checks
coroutine functions and increase issue detection for cases involving type
aliases with references.

- Improved :doc:`cppcoreguidelines-missing-std-forward
<clang-tidy/checks/cppcoreguidelines/missing-std-forward>` check to
address false positives in the capture list and body of lambdas.

- Improved :doc:`cppcoreguidelines-narrowing-conversions
<clang-tidy/checks/cppcoreguidelines/narrowing-conversions>` check by
extending the `IgnoreConversionFromTypes` option to include types without a
Expand Down Expand Up @@ -346,7 +368,8 @@ Changes in existing checks
<clang-tidy/checks/misc/const-correctness>` check to avoid false positive when
using pointer to member function. Additionally, the check no longer emits
a diagnostic when a variable that is not type-dependent is an operand of a
type-dependent binary operator.
type-dependent binary operator. Improved performance of the check through
optimizations.

- Improved :doc:`misc-include-cleaner
<clang-tidy/checks/misc/include-cleaner>` check by adding option
Expand All @@ -360,7 +383,7 @@ Changes in existing checks

- Improved :doc:`misc-unused-using-decls
<clang-tidy/checks/misc/unused-using-decls>` check to avoid false positive when
using in elaborated type.
using in elaborated type and only check cpp files.

- Improved :doc:`modernize-avoid-bind
<clang-tidy/checks/modernize/avoid-bind>` check to
Expand All @@ -382,6 +405,10 @@ Changes in existing checks
false-positives when constructing the container with ``count`` copies of
elements with value ``value``.

- Improved :doc:`modernize-use-emplace
<clang-tidy/checks/modernize/use-emplace>` to not replace aggregates that
``emplace`` cannot construct with aggregate initialization.

- Improved :doc:`modernize-use-equals-delete
<clang-tidy/checks/modernize/use-equals-delete>` check to ignore
false-positives when special member function is actually used or implicit.
Expand All @@ -396,7 +423,8 @@ Changes in existing checks

- Improved :doc:`modernize-use-using
<clang-tidy/checks/modernize/use-using>` check to fix function pointer and
forward declared ``typedef`` correctly.
forward declared ``typedef`` correctly. Added option `IgnoreExternC` to ignore ``typedef``
declaration in ``extern "C"`` scope.

- Improved :doc:`performance-faster-string-find
<clang-tidy/checks/performance/faster-string-find>` check to properly escape
Expand Down Expand Up @@ -441,18 +469,29 @@ Changes in existing checks
has been enhanced, particularly within complex types like function pointers
and cases where style checks were omitted when functions started with macros.
Added support for C++20 ``concept`` declarations. ``Camel_Snake_Case`` and
``camel_Snake_Case`` now detect more invalid identifier names.
``camel_Snake_Case`` now detect more invalid identifier names. Fields in
anonymous records (i.e. anonymous structs and unions) now can be checked with
the naming rules associated with their enclosing scopes rather than the naming
rules of public struct/union members.

- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check to take
do-while loops into account for the `AllowIntegerConditions` and
`AllowPointerConditions` options. It also now provides more consistent
suggestions when parentheses are added to the return value.

- Improved :doc:`readability-misleading-indentation
<clang-tidy/checks/readability/misleading-indentation>` check to ignore
false-positives for line started with empty macro.

- Improved :doc:`readability-non-const-parameter
<clang-tidy/checks/readability/non-const-parameter>` check to ignore
false-positives in initializer list of record.

- Improved :doc:`readability-simplify-subscript-expr
<clang-tidy/checks/readability/simplify-subscript-expr>` check by extending
the default value of the `Types` option to include ``std::span``.

- Improved :doc:`readability-static-accessed-through-instance
<clang-tidy/checks/readability/static-accessed-through-instance>` check to
identify calls to static member functions with out-of-class inline definitions.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.. title:: clang-tidy - bugprone-unused-local-non-trivial-variable

bugprone-unused-local-non-trivial-variable
==========================================

Warns when a local non trivial variable is unused within a function.
The following types of variables are excluded from this check:

* trivial and trivially copyable
* references and pointers
* exception variables in catch clauses
* static or thread local
* structured bindings

This check can be configured to warn on all non-trivial variables by setting
`IncludeTypes` to `.*`, and excluding specific types using `ExcludeTypes`.

In the this example, `my_lock` would generate a warning that it is unused.

.. code-block:: c++

std::mutex my_lock;
// my_lock local variable is never used

In the next example, `future2` would generate a warning that it is unused.

.. code-block:: c++

std::future<MyObject> future1;
std::future<MyObject> future2;
// ...
MyObject foo = future1.get();
// future2 is not used.

Options
-------

.. option:: IncludeTypes

Semicolon-separated list of regular expressions matching types of variables
to check. By default the following types are checked:

* `::std::.*mutex`
* `::std::future`
* `::std::basic_string`
* `::std::basic_regex`
* `::std::basic_istringstream`
* `::std::basic_stringstream`
* `::std::bitset`
* `::std::filesystem::path`

.. option:: ExcludeTypes

A semicolon-separated list of regular expressions matching types that are
excluded from the `IncludeTypes` matches. By default it is an empty list.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. title:: clang-tidy - clang-analyzer-optin.core.EnumCastOutOfRange
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#optin-core-enumcastoutofrange

clang-analyzer-optin.core.EnumCastOutOfRange
============================================

Check integer to enumeration casts for out of range values.

The `clang-analyzer-optin.core.EnumCastOutOfRange` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#optin-core-enumcastoutofrange>`_
for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. title:: clang-tidy - clang-analyzer-security.cert.env.InvalidPtr
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr

clang-analyzer-security.cert.env.InvalidPtr
===========================================

Finds usages of possibly invalidated pointers.

The `clang-analyzer-security.cert.env.InvalidPtr` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr>`_
for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. title:: clang-tidy - clang-analyzer-unix.Errno
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno

clang-analyzer-unix.Errno
=========================

Check for improper use of 'errno'.

The `clang-analyzer-unix.Errno` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno>`_
for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.. title:: clang-tidy - clang-analyzer-unix.StdCLibraryFunctions
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#unix-stdclibraryfunctions

clang-analyzer-unix.StdCLibraryFunctions
========================================

Check for invalid arguments of C standard library functions, and apply relations
between arguments and return value.

The `clang-analyzer-unix.StdCLibraryFunctions` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#unix-stdclibraryfunctions>`_
for more information.
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Clang-Tidy Checks
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
:doc:`bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch>`, "Yes"
:doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`,
:doc:`bugprone-unused-local-non-trivial-variable <bugprone/unused-local-non-trivial-variable>`,
:doc:`bugprone-unused-raii <bugprone/unused-raii>`, "Yes"
:doc:`bugprone-unused-return-value <bugprone/unused-return-value>`,
:doc:`bugprone-use-after-move <bugprone/use-after-move>`,
Expand Down Expand Up @@ -336,6 +337,7 @@ Clang-Tidy Checks
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`,
:doc:`readability-avoid-unconditional-preprocessor-if <readability/avoid-unconditional-preprocessor-if>`,
:doc:`readability-braces-around-statements <readability/braces-around-statements>`, "Yes"
:doc:`readability-const-return-type <readability/const-return-type>`, "Yes"
Expand Down Expand Up @@ -439,6 +441,7 @@ Clang-Tidy Checks
:doc:`clang-analyzer-nullability.NullableDereferenced <clang-analyzer/nullability.NullableDereferenced>`, `Clang Static Analyzer nullability.NullableDereferenced <https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullabledereferenced>`_,
:doc:`clang-analyzer-nullability.NullablePassedToNonnull <clang-analyzer/nullability.NullablePassedToNonnull>`, `Clang Static Analyzer nullability.NullablePassedToNonnull <https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullablepassedtononnull>`_,
:doc:`clang-analyzer-nullability.NullableReturnedFromNonnull <clang-analyzer/nullability.NullableReturnedFromNonnull>`, `Clang Static Analyzer nullability.NullableReturnedFromNonnull <https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullablereturnedfromnonnull>`_,
:doc:`clang-analyzer-optin.core.EnumCastOutOfRange <clang-analyzer/optin.core.EnumCastOutOfRange>`, `Clang Static Analyzer optin.core.EnumCastOutOfRange <https://clang.llvm.org/docs/analyzer/checkers.html#optin-core-enumcastoutofrange>`_,
:doc:`clang-analyzer-optin.cplusplus.UninitializedObject <clang-analyzer/optin.cplusplus.UninitializedObject>`, `Clang Static Analyzer optin.cplusplus.UninitializedObject <https://clang.llvm.org/docs/analyzer/checkers.html#optin-cplusplus-uninitializedobject>`_,
:doc:`clang-analyzer-optin.cplusplus.VirtualCall <clang-analyzer/optin.cplusplus.VirtualCall>`, `Clang Static Analyzer optin.cplusplus.VirtualCall <https://clang.llvm.org/docs/analyzer/checkers.html#optin-cplusplus-virtualcall>`_,
:doc:`clang-analyzer-optin.mpi.MPI-Checker <clang-analyzer/optin.mpi.MPI-Checker>`, `Clang Static Analyzer optin.mpi.MPI-Checker <https://clang.llvm.org/docs/analyzer/checkers.html#optin-mpi-mpi-checker>`_,
Expand Down Expand Up @@ -478,6 +481,7 @@ Clang-Tidy Checks
:doc:`clang-analyzer-osx.coreFoundation.containers.OutOfBounds <clang-analyzer/osx.coreFoundation.containers.OutOfBounds>`, `Clang Static Analyzer osx.coreFoundation.containers.OutOfBounds <https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-outofbounds>`_,
:doc:`clang-analyzer-osx.coreFoundation.containers.PointerSizedValues <clang-analyzer/osx.coreFoundation.containers.PointerSizedValues>`, `Clang Static Analyzer osx.coreFoundation.containers.PointerSizedValues <https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-pointersizedvalues>`_,
:doc:`clang-analyzer-security.FloatLoopCounter <clang-analyzer/security.FloatLoopCounter>`, `Clang Static Analyzer security.FloatLoopCounter <https://clang.llvm.org/docs/analyzer/checkers.html#security-floatloopcounter>`_,
:doc:`clang-analyzer-security.cert.env.InvalidPtr <clang-analyzer/security.cert.env.InvalidPtr>`, `Clang Static Analyzer security.cert.env.InvalidPtr <https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr>`_,
:doc:`clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling <clang-analyzer/security.insecureAPI.DeprecatedOrUnsafeBufferHandling>`, `Clang Static Analyzer security.insecureAPI.DeprecatedOrUnsafeBufferHandling <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-deprecatedorunsafebufferhandling>`_,
:doc:`clang-analyzer-security.insecureAPI.UncheckedReturn <clang-analyzer/security.insecureAPI.UncheckedReturn>`, `Clang Static Analyzer security.insecureAPI.UncheckedReturn <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-uncheckedreturn>`_,
:doc:`clang-analyzer-security.insecureAPI.bcmp <clang-analyzer/security.insecureAPI.bcmp>`, `Clang Static Analyzer security.insecureAPI.bcmp <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bcmp>`_,
Expand All @@ -492,9 +496,11 @@ Clang-Tidy Checks
:doc:`clang-analyzer-security.insecureAPI.strcpy <clang-analyzer/security.insecureAPI.strcpy>`, `Clang Static Analyzer security.insecureAPI.strcpy <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-strcpy>`_,
:doc:`clang-analyzer-security.insecureAPI.vfork <clang-analyzer/security.insecureAPI.vfork>`, `Clang Static Analyzer security.insecureAPI.vfork <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-vfork>`_,
:doc:`clang-analyzer-unix.API <clang-analyzer/unix.API>`, `Clang Static Analyzer unix.API <https://clang.llvm.org/docs/analyzer/checkers.html#unix-api>`_,
:doc:`clang-analyzer-unix.Errno <clang-analyzer/unix.Errno>`, `Clang Static Analyzer unix.Errno <https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno>`_,
:doc:`clang-analyzer-unix.Malloc <clang-analyzer/unix.Malloc>`, `Clang Static Analyzer unix.Malloc <https://clang.llvm.org/docs/analyzer/checkers.html#unix-malloc>`_,
:doc:`clang-analyzer-unix.MallocSizeof <clang-analyzer/unix.MallocSizeof>`, `Clang Static Analyzer unix.MallocSizeof <https://clang.llvm.org/docs/analyzer/checkers.html#unix-mallocsizeof>`_,
:doc:`clang-analyzer-unix.MismatchedDeallocator <clang-analyzer/unix.MismatchedDeallocator>`, `Clang Static Analyzer unix.MismatchedDeallocator <https://clang.llvm.org/docs/analyzer/checkers.html#unix-mismatcheddeallocator>`_,
:doc:`clang-analyzer-unix.StdCLibraryFunctions <clang-analyzer/unix.StdCLibraryFunctions>`, `Clang Static Analyzer unix.StdCLibraryFunctions <https://clang.llvm.org/docs/analyzer/checkers.html#unix-stdclibraryfunctions>`_,
:doc:`clang-analyzer-unix.Vfork <clang-analyzer/unix.Vfork>`, `Clang Static Analyzer unix.Vfork <https://clang.llvm.org/docs/analyzer/checkers.html#unix-vfork>`_,
:doc:`clang-analyzer-unix.cstring.BadSizeArg <clang-analyzer/unix.cstring.BadSizeArg>`, `Clang Static Analyzer unix.cstring.BadSizeArg <https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-badsizearg>`_,
:doc:`clang-analyzer-unix.cstring.NullArg <clang-analyzer/unix.cstring.NullArg>`, `Clang Static Analyzer unix.cstring.NullArg <https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-nullarg>`_,
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/modernize/use-using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ After:
using R_t = struct { int a; };
using R_p = R_t*;

The checker ignores `typedef` within `extern "C" { ... }` blocks.

.. code-block:: c++

extern "C" {
typedef int InExternC; // Left intact.
}

This check requires using C++11 or higher to run.

Options
Expand All @@ -37,3 +45,8 @@ Options

If set to `true`, the check will not give warnings inside macros. Default
is `true`.

.. option:: IgnoreExternC

If set to `true`, the check will not give warning inside `extern "C"`scope.
Default is `false`
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.. title:: clang-tidy - readability-avoid-return-with-void-value

readability-avoid-return-with-void-value
========================================

Finds return statements with ``void`` values used within functions with
``void`` result types.

A function with a ``void`` return type is intended to perform a task without
producing a return value. Return statements with expressions could lead
to confusion and may miscommunicate the function's intended behavior.

Example:

.. code-block::
void g();
void f() {
// ...
return g();
}
In a long function body, the ``return`` statement suggests that the function
returns a value. However, ``return g();`` is a combination of two statements
that should be written as

.. code-block::
g();
return;
to make clear that ``g()`` is called and immediately afterwards the function
returns (nothing).

In C, the same issue is detected by the compiler if the ``-Wpedantic`` mode
is enabled.

Options
-------

.. option:: IgnoreMacros

The value `false` specifies that return statements expanded
from macros are not checked. The default value is `true`.

.. option:: StrictMode

The value `false` specifies that a direct return statement shall
be excluded from the analysis if it is the only statement not
contained in a block like ``if (cond) return g();``. The default
value is `true`.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The following options are described below:

- :option:`AbstractClassCase`, :option:`AbstractClassPrefix`, :option:`AbstractClassSuffix`, :option:`AbstractClassIgnoredRegexp`, :option:`AbstractClassHungarianPrefix`
- :option:`AggressiveDependentMemberLookup`
- :option:`CheckAnonFieldInParent`
- :option:`ClassCase`, :option:`ClassPrefix`, :option:`ClassSuffix`, :option:`ClassIgnoredRegexp`, :option:`ClassHungarianPrefix`
- :option:`ClassConstantCase`, :option:`ClassConstantPrefix`, :option:`ClassConstantSuffix`, :option:`ClassConstantIgnoredRegexp`, :option:`ClassConstantHungarianPrefix`
- :option:`ClassMemberCase`, :option:`ClassMemberPrefix`, :option:`ClassMemberSuffix`, :option:`ClassMemberIgnoredRegexp`, :option:`ClassMemberHungarianPrefix`
Expand Down Expand Up @@ -207,6 +208,32 @@ After if AggressiveDependentMemberLookup is `true`:
}
};

.. option:: CheckAnonFieldInParent

When set to `true`, fields in anonymous records (i.e. anonymous
unions and structs) will be treated as names in the enclosing scope
rather than public members of the anonymous record for the purpose
of name checking.

For example:

.. code-block:: c++

class Foo {
private:
union {
int iv_;
float fv_;
};
};

If :option:`CheckAnonFieldInParent` is `false`, you may get warnings
that ``iv_`` and ``fv_`` are not coherent to public member names, because
``iv_`` and ``fv_`` are public members of the anonymous union. When
:option:`CheckAnonFieldInParent` is `true`, ``iv_`` and ``fv_`` will be
treated as private data members of ``Foo`` for the purpose of name checking
and thus no warnings will be emitted.

.. option:: ClassCase

When defined, the check will ensure class names conform to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Options
.. option:: Types

The list of type(s) that triggers this check. Default is
`::std::basic_string;::std::basic_string_view;::std::vector;::std::array`
`::std::basic_string;::std::basic_string_view;::std::vector;::std::array;::std::span`
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ class PragmaIncludes {
llvm::DenseSet<llvm::sys::fs::UniqueID> ShouldKeep;

/// Owns the strings.
llvm::BumpPtrAllocator Arena;
/// Each record() pushes a new one, while keeping all the old strings alive.
std::vector<std::shared_ptr<const llvm::BumpPtrAllocator>> Arena;

// FIXME: add support for clang use_instead pragma
};
Expand Down
7 changes: 4 additions & 3 deletions clang-tools-extra/include-cleaner/lib/Record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
: RecordPragma(CI.getPreprocessor(), Out) {}
RecordPragma(const Preprocessor &P, PragmaIncludes *Out)
: SM(P.getSourceManager()), HeaderInfo(P.getHeaderSearchInfo()), Out(Out),
UniqueStrings(Arena) {}
Arena(std::make_shared<llvm::BumpPtrAllocator>()),
UniqueStrings(*Arena) {}

void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
Expand All @@ -204,7 +205,7 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
std::unique(It.getSecond().begin(), It.getSecond().end()),
It.getSecond().end());
}
Out->Arena = std::move(Arena);
Out->Arena.emplace_back(std::move(Arena));
}

void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
Expand Down Expand Up @@ -336,7 +337,7 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
const SourceManager &SM;
const HeaderSearch &HeaderInfo;
PragmaIncludes *Out;
llvm::BumpPtrAllocator Arena;
std::shared_ptr<llvm::BumpPtrAllocator> Arena;
/// Intern table for strings. Contents are on the arena.
llvm::StringSaver UniqueStrings;

Expand Down
Loading