274 changes: 149 additions & 125 deletions bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Large diffs are not rendered by default.

377 changes: 266 additions & 111 deletions bolt/lib/Rewrite/RewriteInstance.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

// RUN: %clang %cflags -g -Wl,-q -o %t %s

/// Verify that the binary indeed contains an unnamed symbol at _start
/// Verify that the binary indeed contains a fake label ".L0 " at _start.
// RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=CHECK-ELF
// CHECK-ELF-DAG: [[#%x,START:]] {{.*}} FUNC GLOBAL DEFAULT [[#%d,SECTION:]] _start{{$}}
// CHECK-ELF-DAG: [[#%x,START]] {{.*}} NOTYPE LOCAL DEFAULT [[#SECTION]] .L0 {{$}}

/// Verify that BOLT did not create an extra entry point for the unnamed symbol
/// Verify that BOLT did not create an extra entry point for the fake label.
// RUN: llvm-bolt -o %t.bolt %t --print-cfg | FileCheck %s
// CHECK: Binary Function "_start" after building cfg {
// CHECK: IsMultiEntry: 0
Expand Down
1 change: 1 addition & 0 deletions bolt/test/X86/cdsplit-symbol-names.s
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# RUN: --call-scale=2 --data=%t.fdata --reorder-blocks=ext-tsp
# RUN: llvm-objdump --syms %t.bolt | FileCheck %s --check-prefix=CHECK-SYMS-WARM

# CHECK-SYMS-WARM: 0000000000000000 l df *ABS* 0000000000000000 bolt-pseudo.o
# CHECK-SYMS-WARM: .text.warm
# CHECK-SYMS-WARM-SAME: chain.warm
# CHECK-SYMS-WARM: .text.cold
Expand Down
54 changes: 44 additions & 10 deletions bolt/test/X86/fragment-lite.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,42 @@
# RUN: split-file %s %t
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/baz.s -o %t.baz.o
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/baz2.s -o %t.baz2.o
# RUN: link_fdata %s %t.o %t.main.fdata
# RUN: link_fdata %s %t.baz.o %t.baz.fdata
# RUN: merge-fdata %t.main.fdata %t.baz.fdata > %t.fdata
# RUN: %clang %cflags %t.o %t.baz.o -o %t.exe -Wl,-q
# RUN: link_fdata %s %t.baz2.o %t.baz2.fdata
# RUN: merge-fdata %t.main.fdata %t.baz.fdata %t.baz2.fdata > %t.fdata
# RUN: %clang %cflags %t.o %t.baz.o %t.baz2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.out --lite=1 --data %t.fdata -v=1 -print-cfg \
# RUN: 2>&1 | FileCheck %s

# CHECK: BOLT-INFO: processing main.cold.1 as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing foo.cold.1/1 as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing bar.cold.1/1 as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing foo.cold.1/1(*2) as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing bar.cold.1/1(*2) as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing baz.cold.1 as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing baz.cold.1/1 as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing baz.cold.1/1(*2) as a sibling of non-ignored function
# CHECK: BOLT-INFO: processing baz.cold.1/2(*2) as a sibling of non-ignored function

# CHECK: Binary Function "main.cold.1" after building cfg
# CHECK: Parent : main

# CHECK: Binary Function "foo.cold.1/1" after building cfg
# CHECK: Binary Function "foo.cold.1/1(*2)" after building cfg
# CHECK: Parent : foo

# CHECK: Binary Function "bar.cold.1/1" after building cfg
# CHECK: Parent : bar/1
# CHECK: Binary Function "bar.cold.1/1(*2)" after building cfg
# CHECK: Parent : bar/1(*2)

# CHECK: Binary Function "baz.cold.1" after building cfg
# CHECK: Parent : baz{{$}}

# CHECK: Binary Function "baz.cold.1/1" after building cfg
# CHECK: Parent : baz/1
# CHECK: Binary Function "baz.cold.1/1(*2)" after building cfg
# CHECK: Parent : baz/1(*2)

# CHECK: Binary Function "baz.cold.1/2(*2)" after building cfg
# CHECK: Parent : baz/2(*2)

#--- main.s
.file "main.s"
.globl main
.type main, %function
main:
Expand Down Expand Up @@ -126,6 +133,7 @@ baz.cold.1:
.size baz.cold.1, .-baz.cold.1

#--- baz.s
.file "baz.s"
.local baz
.type baz, %function
baz:
Expand All @@ -149,3 +157,29 @@ baz.cold.1:
retq
.cfi_endproc
.size baz.cold.1, .-baz.cold.1

#--- baz2.s
.file "baz2.s"
.local baz
.type baz, %function
baz:
.cfi_startproc
# FDATA: 0 [unknown] 0 1 baz/2 0 1 0
cmpl $0x0, %eax
je baz.cold.1
retq
.cfi_endproc
.size baz, .-baz

.section .text.cold
.local baz.cold.1
.type baz.cold.1, %function
baz.cold.1:
.cfi_startproc
pushq %rbp
movq %rsp, %rbp
movl $0x0, %eax
popq %rbp
retq
.cfi_endproc
.size baz.cold.1, .-baz.cold.1
40 changes: 40 additions & 0 deletions bolt/test/X86/linux-smp-locks.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# REQUIRES: system-linux

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

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

## Check the output of BOLT with NOPs removed.

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

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

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

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

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

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

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

## Verify that profile is matched correctly.

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

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

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

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

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

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

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

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

.globl __stop___jump_table
Expand Down
9 changes: 8 additions & 1 deletion bolt/test/X86/pre-aggregated-perf.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ REQUIRES: system-linux

RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe
RUN: perf2bolt %t.exe -o %t --pa -p %p/Inputs/pre-aggregated.txt -w %t.new \
RUN: --profile-use-dfs
RUN: --profile-use-dfs | FileCheck %s

RUN: llvm-bolt %t.exe -data %t -o %t.null | FileCheck %s
RUN: llvm-bolt %t.exe -data %t.new -o %t.null | FileCheck %s
RUN: llvm-bolt %t.exe -p %p/Inputs/pre-aggregated.txt --pa -o %t.null | FileCheck %s

CHECK: BOLT-INFO: 4 out of 7 functions in the binary (57.1%) have non-empty execution profile

RUN: cat %t | sort | FileCheck %s -check-prefix=PERF2BOLT
RUN: cat %t.new | FileCheck %s -check-prefix=NEWFORMAT

Expand Down
32 changes: 32 additions & 0 deletions bolt/test/X86/register-fragments-bolt-symbols.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Test the heuristics for matching BOLT-added split functions.

# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %S/cdsplit-symbol-names.s -o %t.main.o
# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.chain.o
# RUN: link_fdata %S/cdsplit-symbol-names.s %t.main.o %t.fdata
# RUN: sed -i 's|chain|chain/2|g' %t.fdata
# RUN: llvm-strip --strip-unneeded %t.main.o
# RUN: llvm-objcopy --localize-symbol=chain %t.main.o
# RUN: %clang %cflags %t.chain.o %t.main.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=randomN \
# RUN: --reorder-blocks=ext-tsp --enable-bat --bolt-seed=7 --data=%t.fdata
# RUN: llvm-objdump --syms %t.bolt | FileCheck %s --check-prefix=CHECK-SYMS

# RUN: link_fdata %s %t.bolt %t.preagg PREAGG
# PREAGG: B X:0 #chain.cold.0# 1 0
# RUN: perf2bolt %t.bolt -p %t.preagg --pa -o %t.bat.fdata -w %t.bat.yaml -v=1 \
# RUN: | FileCheck %s --check-prefix=CHECK-REGISTER

# CHECK-SYMS: l df *ABS* [[#]] chain.s
# CHECK-SYMS: l F .bolt.org.text [[#]] chain
# CHECK-SYMS: l F .text.cold [[#]] chain.cold.0
# CHECK-SYMS: l F .text [[#]] chain
# CHECK-SYMS: l df *ABS* [[#]] bolt-pseudo.o

# CHECK-REGISTER: BOLT-INFO: marking chain.cold.0/1(*2) as a fragment of chain/2(*2)

.file "chain.s"
.text
.type chain, @function
chain:
ret
.size chain, .-chain
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
//===----------------------------------------------------------------------===//

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

using namespace clang::ast_matchers;

Expand All @@ -27,7 +25,8 @@ void CastingThroughVoidCheck::registerMatchers(MatchFinder *Finder) {
hasSourceExpression(
explicitCastExpr(
hasSourceExpression(
expr(hasType(qualType().bind("source_type")))),
expr(hasType(qualType(unless(pointsTo(voidType())))
.bind("source_type")))),
hasDestinationType(
qualType(pointsTo(voidType())).bind("void_type")))
.bind("cast"))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,8 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
// Get out the qualifiers of the original type. This will always be
// re-applied to the WorkType to ensure it is the same qualification as the
// original From was.
auto QualifiersToApply = From.split().Quals.getAsOpaqueValue();
auto FastQualifiersToApply = static_cast<unsigned>(
From.split().Quals.getAsOpaqueValue() & Qualifiers::FastMask);

// LValue->RValue is irrelevant for the check, because it is a thing to be
// done at a call site, and will be performed if need be performed.
Expand All @@ -993,7 +994,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
// "const double -> double".
LLVM_DEBUG(llvm::dbgs()
<< "--- approximateStdConv. Conversion between numerics.\n");
WorkType = QualType{ToBuiltin, QualifiersToApply};
WorkType = QualType{ToBuiltin, FastQualifiersToApply};
}

const auto *FromEnum = WorkType->getAs<EnumType>();
Expand All @@ -1002,7 +1003,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
// Unscoped enumerations (or enumerations in C) convert to numerics.
LLVM_DEBUG(llvm::dbgs()
<< "--- approximateStdConv. Unscoped enum to numeric.\n");
WorkType = QualType{ToBuiltin, QualifiersToApply};
WorkType = QualType{ToBuiltin, FastQualifiersToApply};
} else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) {
// Numeric types convert to enumerations only in C.
if (Ctx.getLangOpts().CPlusPlus) {
Expand All @@ -1013,7 +1014,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,

LLVM_DEBUG(llvm::dbgs()
<< "--- approximateStdConv. Numeric to unscoped enum.\n");
WorkType = QualType{ToEnum, QualifiersToApply};
WorkType = QualType{ToEnum, FastQualifiersToApply};
}

// Check for pointer conversions.
Expand All @@ -1022,14 +1023,14 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
if (FromPtr && ToPtr) {
if (ToPtr->isVoidPointerType()) {
LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. To void pointer.\n");
WorkType = QualType{ToPtr, QualifiersToApply};
WorkType = QualType{ToPtr, FastQualifiersToApply};
}

const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl();
const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl();
if (isDerivedToBase(FromRecordPtr, ToRecordPtr)) {
LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived* to Base*\n");
WorkType = QualType{ToPtr, QualifiersToApply};
WorkType = QualType{ToPtr, FastQualifiersToApply};
}
}

Expand All @@ -1039,7 +1040,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
const auto *ToRecord = To->getAsCXXRecordDecl();
if (isDerivedToBase(FromRecord, ToRecord)) {
LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n");
WorkType = QualType{ToRecord->getTypeForDecl(), QualifiersToApply};
WorkType = QualType{ToRecord->getTypeForDecl(), FastQualifiersToApply};
}

if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
Expand All @@ -1054,7 +1055,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From,
!ToFunctionPtr->hasNoexceptExceptionSpec()) {
LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. noexcept function "
"pointer to non-noexcept.\n");
WorkType = QualType{ToPtr, QualifiersToApply};
WorkType = QualType{ToPtr, FastQualifiersToApply};
}
}

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "../misc/StaticAssertCheck.h"
#include "../misc/ThrowByValueCatchByReferenceCheck.h"
#include "../performance/MoveConstructorInitCheck.h"
#include "../readability/EnumInitialValueCheck.h"
#include "../readability/UppercaseLiteralSuffixCheck.h"
#include "CommandProcessorCheck.h"
#include "DefaultOperatorNewAlignmentCheck.h"
Expand Down Expand Up @@ -299,6 +300,9 @@ class CERTModule : public ClangTidyModule {
"cert-flp37-c");
// FIO
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
// INT
CheckFactories.registerCheck<readability::EnumInitialValueCheck>(
"cert-int09-c");
// MSC
CheckFactories.registerCheck<bugprone::UnsafeFunctionsCheck>(
"cert-msc24-c");
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_clang_library(clangTidyModernizeModule
MakeSharedCheck.cpp
MakeSmartPtrCheck.cpp
MakeUniqueCheck.cpp
MinMaxUseInitializerListCheck.cpp
ModernizeTidyModule.cpp
PassByValueCheck.cpp
RawStringLiteralCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
//===--- MinMaxUseInitializerListCheck.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 "MinMaxUseInitializerListCheck.h"
#include "../utils/ASTUtils.h"
#include "../utils/LexerUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"

using namespace clang;

namespace {

struct FindArgsResult {
const Expr *First;
const Expr *Last;
const Expr *Compare;
SmallVector<const clang::Expr *, 2> Args;
};

} // anonymous namespace

using namespace clang::ast_matchers;

namespace clang::tidy::modernize {

static FindArgsResult findArgs(const CallExpr *Call) {
FindArgsResult Result;
Result.First = nullptr;
Result.Last = nullptr;
Result.Compare = nullptr;

// check if the function has initializer list argument
if (Call->getNumArgs() < 3) {
auto ArgIterator = Call->arguments().begin();

const auto *InitListExpr =
dyn_cast<CXXStdInitializerListExpr>(*ArgIterator);
const auto *InitList =
InitListExpr != nullptr
? dyn_cast<clang::InitListExpr>(
InitListExpr->getSubExpr()->IgnoreImplicit())
: nullptr;

if (InitList) {
Result.Args.append(InitList->inits().begin(), InitList->inits().end());
Result.First = *ArgIterator;
Result.Last = *ArgIterator;

// check if there is a comparison argument
std::advance(ArgIterator, 1);
if (ArgIterator != Call->arguments().end())
Result.Compare = *ArgIterator;

return Result;
}
Result.Args = SmallVector<const Expr *>(Call->arguments());
} else {
// if it has 3 arguments then the last will be the comparison
Result.Compare = *(std::next(Call->arguments().begin(), 2));
Result.Args = SmallVector<const Expr *>(llvm::drop_end(Call->arguments()));
}
Result.First = Result.Args.front();
Result.Last = Result.Args.back();

return Result;
}

static SmallVector<FixItHint>
generateReplacements(const MatchFinder::MatchResult &Match,
const CallExpr *TopCall, const FindArgsResult &Result,
const bool IgnoreNonTrivialTypes,
const std::uint64_t IgnoreTrivialTypesOfSizeAbove) {
SmallVector<FixItHint> FixItHints;
const SourceManager &SourceMngr = *Match.SourceManager;
const LangOptions &LanguageOpts = Match.Context->getLangOpts();

const QualType ResultType = TopCall->getDirectCallee()
->getReturnType()
.getCanonicalType()
.getNonReferenceType()
.getUnqualifiedType();

// check if the type is trivial
const bool IsResultTypeTrivial = ResultType.isTrivialType(*Match.Context);

if ((!IsResultTypeTrivial && IgnoreNonTrivialTypes))
return FixItHints;

if (IsResultTypeTrivial &&
static_cast<std::uint64_t>(
Match.Context->getTypeSizeInChars(ResultType).getQuantity()) >
IgnoreTrivialTypesOfSizeAbove)
return FixItHints;

for (const Expr *Arg : Result.Args) {
const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts());

// If the argument is not a nested call
if (!InnerCall) {
// check if typecast is required
const QualType ArgType = Arg->IgnoreParenImpCasts()
->getType()
.getCanonicalType()
.getUnqualifiedType();

if (ArgType == ResultType)
continue;

const StringRef ArgText = Lexer::getSourceText(
CharSourceRange::getTokenRange(Arg->getSourceRange()), SourceMngr,
LanguageOpts);

const auto Replacement = Twine("static_cast<")
.concat(ResultType.getAsString(LanguageOpts))
.concat(">(")
.concat(ArgText)
.concat(")")
.str();

FixItHints.push_back(
FixItHint::CreateReplacement(Arg->getSourceRange(), Replacement));
continue;
}

const FindArgsResult InnerResult = findArgs(InnerCall);

// if the nested call doesn't have arguments skip it
if (!InnerResult.First || !InnerResult.Last)
continue;

// if the nested call is not the same as the top call
if (InnerCall->getDirectCallee()->getQualifiedNameAsString() !=
TopCall->getDirectCallee()->getQualifiedNameAsString())
continue;

// if the nested call doesn't have the same compare function
if ((Result.Compare || InnerResult.Compare) &&
!utils::areStatementsIdentical(Result.Compare, InnerResult.Compare,
*Match.Context))
continue;

// remove the function call
FixItHints.push_back(
FixItHint::CreateRemoval(InnerCall->getCallee()->getSourceRange()));

// remove the parentheses
const auto LParen = utils::lexer::findNextTokenSkippingComments(
InnerCall->getCallee()->getEndLoc(), SourceMngr, LanguageOpts);
if (LParen.has_value() && LParen->is(tok::l_paren))
FixItHints.push_back(
FixItHint::CreateRemoval(SourceRange(LParen->getLocation())));
FixItHints.push_back(
FixItHint::CreateRemoval(SourceRange(InnerCall->getRParenLoc())));

// if the inner call has an initializer list arg
if (InnerResult.First == InnerResult.Last) {
// remove the initializer list braces
FixItHints.push_back(FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(InnerResult.First->getBeginLoc())));
FixItHints.push_back(FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(InnerResult.First->getEndLoc())));
}

const SmallVector<FixItHint> InnerReplacements = generateReplacements(
Match, InnerCall, InnerResult, IgnoreNonTrivialTypes,
IgnoreTrivialTypesOfSizeAbove);

FixItHints.append(InnerReplacements);

if (InnerResult.Compare) {
// find the comma after the value arguments
const auto Comma = utils::lexer::findNextTokenSkippingComments(
InnerResult.Last->getEndLoc(), SourceMngr, LanguageOpts);

// remove the comma and the comparison
if (Comma.has_value() && Comma->is(tok::comma))
FixItHints.push_back(
FixItHint::CreateRemoval(SourceRange(Comma->getLocation())));

FixItHints.push_back(
FixItHint::CreateRemoval(InnerResult.Compare->getSourceRange()));
}
}

return FixItHints;
}

MinMaxUseInitializerListCheck::MinMaxUseInitializerListCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreNonTrivialTypes(Options.get("IgnoreNonTrivialTypes", true)),
IgnoreTrivialTypesOfSizeAbove(
Options.get("IgnoreTrivialTypesOfSizeAbove", 32L)),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()) {}

void MinMaxUseInitializerListCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreNonTrivialTypes", IgnoreNonTrivialTypes);
Options.store(Opts, "IgnoreTrivialTypesOfSizeAbove",
IgnoreTrivialTypesOfSizeAbove);
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
}

void MinMaxUseInitializerListCheck::registerMatchers(MatchFinder *Finder) {
auto CreateMatcher = [](const StringRef FunctionName) {
auto FuncDecl = functionDecl(hasName(FunctionName));
auto Expression = callExpr(callee(FuncDecl));

return callExpr(callee(FuncDecl),
anyOf(hasArgument(0, Expression),
hasArgument(1, Expression),
hasArgument(0, cxxStdInitializerListExpr())),
unless(hasParent(Expression)))
.bind("topCall");
};

Finder->addMatcher(CreateMatcher("::std::max"), this);
Finder->addMatcher(CreateMatcher("::std::min"), this);
}

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

void MinMaxUseInitializerListCheck::check(
const MatchFinder::MatchResult &Match) {

const auto *TopCall = Match.Nodes.getNodeAs<CallExpr>("topCall");

const FindArgsResult Result = findArgs(TopCall);
const SmallVector<FixItHint> Replacements =
generateReplacements(Match, TopCall, Result, IgnoreNonTrivialTypes,
IgnoreTrivialTypesOfSizeAbove);

if (Replacements.empty())
return;

const DiagnosticBuilder Diagnostic =
diag(TopCall->getBeginLoc(),
"do not use nested 'std::%0' calls, use an initializer list instead")
<< TopCall->getDirectCallee()->getName()
<< Inserter.createIncludeInsertion(
Match.SourceManager->getFileID(TopCall->getBeginLoc()),
"<algorithm>");

// if the top call doesn't have an initializer list argument
if (Result.First != Result.Last) {
// add { and } insertions
Diagnostic << FixItHint::CreateInsertion(Result.First->getBeginLoc(), "{");

Diagnostic << FixItHint::CreateInsertion(
Lexer::getLocForEndOfToken(Result.Last->getEndLoc(), 0,
*Match.SourceManager,
Match.Context->getLangOpts()),
"}");
}

Diagnostic << Replacements;
}

} // namespace clang::tidy::modernize
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===--- MinMaxUseInitializerListCheck.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_MODERNIZE_MINMAXUSEINITIALIZERLISTCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MINMAXUSEINITIALIZERLISTCHECK_H

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

namespace clang::tidy::modernize {

/// Replaces nested ``std::min`` and ``std::max`` calls with an initializer list
/// where applicable.
///
/// For example:
///
/// \code
/// int a = std::max(std::max(i, j), k);
/// \endcode
///
/// This code is transformed to:
///
/// \code
/// int a = std::max({i, j, k});
/// \endcode
class MinMaxUseInitializerListCheck : public ClangTidyCheck {
public:
MinMaxUseInitializerListCheck(StringRef Name, ClangTidyContext *Context);

void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
void check(const ast_matchers::MatchFinder::MatchResult &Match) override;

bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus11;
}
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

private:
bool IgnoreNonTrivialTypes;
std::uint64_t IgnoreTrivialTypesOfSizeAbove;
utils::IncludeInserter Inserter;
};

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MINMAXUSEINITIALIZERLISTCHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "MacroToEnumCheck.h"
#include "MakeSharedCheck.h"
#include "MakeUniqueCheck.h"
#include "MinMaxUseInitializerListCheck.h"
#include "PassByValueCheck.h"
#include "RawStringLiteralCheck.h"
#include "RedundantVoidArgCheck.h"
Expand Down Expand Up @@ -68,6 +69,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<MacroToEnumCheck>("modernize-macro-to-enum");
CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
CheckFactories.registerCheck<MinMaxUseInitializerListCheck>(
"modernize-min-max-use-initializer-list");
CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
CheckFactories.registerCheck<UseDesignatedInitializersCheck>(
"modernize-use-designated-initializers");
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class UseNullptrCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
// FIXME this should be CPlusPlus11 but that causes test cases to
// erroneously fail.
return LangOpts.CPlusPlus;
return LangOpts.CPlusPlus || LangOpts.C23;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ void AvoidReturnWithVoidValueCheck::check(
<< BraceInsertionHints.closingBraceFixIt();
}
Diag << FixItHint::CreateRemoval(VoidReturn->getReturnLoc());
if (!Result.Nodes.getNodeAs<FunctionDecl>("function_parent") ||
SurroundingBlock->body_back() != VoidReturn)
const auto *FunctionParent =
Result.Nodes.getNodeAs<FunctionDecl>("function_parent");
if (!FunctionParent ||
(SurroundingBlock && SurroundingBlock->body_back() != VoidReturn))
// If this is not the last statement in a function body, we add a `return`.
Diag << FixItHint::CreateInsertion(SemicolonPos.getLocWithOffset(1),
" return;", true);
}
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ add_clang_library(clangTidyReadabilityModule
IsolateDeclarationCheck.cpp
MagicNumbersCheck.cpp
MakeMemberFunctionConstCheck.cpp
MathMissingParenthesesCheck.cpp
MisleadingIndentationCheck.cpp
MisplacedArrayIndexCheck.cpp
NamedParameterCheck.cpp
Expand Down
21 changes: 4 additions & 17 deletions clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,6 @@ AST_MATCHER(QualType, isLocalConstQualified) {
return Node.isLocalConstQualified();
}

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

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

struct CheckResult {
// Source range of the relevant `const` token in the definition being checked.
CharSourceRange ConstRange;
Expand Down Expand Up @@ -110,16 +102,11 @@ void ConstReturnTypeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void ConstReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
// Find all function definitions for which the return types are `const`
// qualified, ignoring decltype types.
auto NonLocalConstType =
qualType(unless(isLocalConstQualified()),
anyOf(decltypeType(), autoType(), isTypeOfType(),
isTypeOfExprType(), substTemplateTypeParmType()));
Finder->addMatcher(
functionDecl(
returns(allOf(isConstQualified(), unless(NonLocalConstType))),
anyOf(isDefinition(), cxxMethodDecl(isPure())),
// Overridden functions are not actionable.
unless(cxxMethodDecl(isOverride())))
functionDecl(returns(isLocalConstQualified()),
anyOf(isDefinition(), cxxMethodDecl(isPure())),
// Overridden functions are not actionable.
unless(cxxMethodDecl(isOverride())))
.bind("func"),
this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===--- MathMissingParenthesesCheck.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 "MathMissingParenthesesCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

void MathMissingParenthesesCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(binaryOperator(unless(hasParent(binaryOperator())),
unless(isAssignmentOperator()),
unless(isComparisonOperator()),
unless(hasAnyOperatorName("&&", "||")),
hasDescendant(binaryOperator()))
.bind("binOp"),
this);
}

static int getPrecedence(const BinaryOperator *BinOp) {
if (!BinOp)
return 0;
switch (BinOp->getOpcode()) {
case BO_Mul:
case BO_Div:
case BO_Rem:
return 5;
case BO_Add:
case BO_Sub:
return 4;
case BO_And:
return 3;
case BO_Xor:
return 2;
case BO_Or:
return 1;
default:
return 0;
}
}
static void addParantheses(const BinaryOperator *BinOp,
const BinaryOperator *ParentBinOp,
ClangTidyCheck *Check,
const clang::SourceManager &SM,
const clang::LangOptions &LangOpts) {
if (!BinOp)
return;

int Precedence1 = getPrecedence(BinOp);
int Precedence2 = getPrecedence(ParentBinOp);

if (ParentBinOp != nullptr && Precedence1 != Precedence2) {
const clang::SourceLocation StartLoc = BinOp->getBeginLoc();
const clang::SourceLocation EndLoc =
clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts);

auto Diag =
Check->diag(StartLoc,
"'%0' has higher precedence than '%1'; add parentheses to "
"explicitly specify the order of operations")
<< (Precedence1 > Precedence2 ? BinOp->getOpcodeStr()
: ParentBinOp->getOpcodeStr())
<< (Precedence1 > Precedence2 ? ParentBinOp->getOpcodeStr()
: BinOp->getOpcodeStr())
<< SourceRange(StartLoc, EndLoc);

if (EndLoc.isValid()) {
Diag << FixItHint::CreateInsertion(StartLoc, "(")
<< FixItHint::CreateInsertion(EndLoc, ")");
}
}

addParantheses(dyn_cast<BinaryOperator>(BinOp->getLHS()->IgnoreImpCasts()),
BinOp, Check, SM, LangOpts);
addParantheses(dyn_cast<BinaryOperator>(BinOp->getRHS()->IgnoreImpCasts()),
BinOp, Check, SM, LangOpts);
}

void MathMissingParenthesesCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binOp");
std::vector<
std::pair<clang::SourceRange, std::pair<const clang::BinaryOperator *,
const clang::BinaryOperator *>>>
Insertions;
const SourceManager &SM = *Result.SourceManager;
const clang::LangOptions &LO = Result.Context->getLangOpts();
addParantheses(BinOp, nullptr, this, SM, LO);
}

} // namespace clang::tidy::readability
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- MathMissingParenthesesCheck.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_MATHMISSINGPARENTHESESCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// Check for mising parantheses in mathematical expressions that involve
/// operators of different priorities.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/math-missing-parentheses.html
class MathMissingParenthesesCheck : public ClangTidyCheck {
public:
MathMissingParenthesesCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "IsolateDeclarationCheck.h"
#include "MagicNumbersCheck.h"
#include "MakeMemberFunctionConstCheck.h"
#include "MathMissingParenthesesCheck.h"
#include "MisleadingIndentationCheck.h"
#include "MisplacedArrayIndexCheck.h"
#include "NamedParameterCheck.h"
Expand Down Expand Up @@ -105,6 +106,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-identifier-naming");
CheckFactories.registerCheck<ImplicitBoolConversionCheck>(
"readability-implicit-bool-conversion");
CheckFactories.registerCheck<MathMissingParenthesesCheck>(
"readability-math-missing-parentheses");
CheckFactories.registerCheck<RedundantInlineSpecifierCheck>(
"readability-redundant-inline-specifier");
CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/CodeCompletionStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
if (!IncludeFunctionArguments &&
ResultKind == CodeCompletionResult::RK_Declaration)
TruncateSnippetAt.emplace(Snippet->size());
LLVM_FALLTHROUGH;
[[fallthrough]];
case CodeCompletionString::CK_RightParen:
case CodeCompletionString::CK_LeftBracket:
case CodeCompletionString::CK_RightBracket:
Expand Down
99 changes: 55 additions & 44 deletions clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,12 @@ namespace {
/// void f() { E e1 = EV1; }
///
/// After:
/// enum class E { EV1, EV2 };
/// void f() { E e1 = E::EV1; }
/// enum class E { V1, V2 };
/// void f() { E e1 = E::V1; }
///
/// Note that the respective project code might not compile anymore
/// if it made use of the now-gone implicit conversion to int.
/// This is out of scope for this tweak.
///
/// TODO: In the above example, we could detect that the values
/// start with the enum name, and remove that prefix.

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

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

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

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

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

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

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

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

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

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

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

---

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

---

{"jsonrpc":"2.0","method":"exit"}
# CHECK-NOT: JSON parse error
8 changes: 4 additions & 4 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ TEST_F(TargetDeclTest, DependentExprs) {
}
};
)cpp";
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
EXPECT_DECLS("MemberExpr", "void foo()");

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

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

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

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

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

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

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

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

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

Expand Down Expand Up @@ -100,13 +103,15 @@ Improvements to clang-tidy
- Improved :program:`run-clang-tidy.py` script. Added argument `-source-filter`
to filter source files from the compilation database, via a RegEx. In a
similar fashion to what `-header-filter` does for header files.

- Improved :program:`check_clang_tidy.py` script. Added argument `-export-fixes`
to aid in clang-tidy and test development.

- Fixed bug where big values for unsigned check options overflowed into negative values
when being printed with ``--dump-config``.
when being printed with `--dump-config`.

- Fixed ``--verify-config`` option not properly parsing checks when using the
literal operator in the ``.clang-tidy`` config.
- Fixed `--verify-config` option not properly parsing checks when using the
literal operator in the `.clang-tidy` config.

New checks
^^^^^^^^^^
Expand All @@ -131,6 +136,12 @@ New checks
to reading out-of-bounds data due to inadequate or incorrect string null
termination.

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

Replaces nested ``std::min`` and ``std::max`` calls with an initializer list
where applicable.

- New :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check.

Expand All @@ -143,6 +154,12 @@ New checks
Enforces consistent style for enumerators' initialization, covering three
styles: none, first only, or all initialized explicitly.

- New :doc:`readability-math-missing-parentheses
<clang-tidy/checks/readability/math-missing-parentheses>` check.

Check for missing parentheses in mathematical expressions that involve
operators of different priorities.

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

Expand All @@ -152,13 +169,22 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^

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

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

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

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

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

- Improved :doc:`google-explicit-constructor
<clang-tidy/checks/google/explicit-constructor>` check to better handle
``C++-20`` `explicit(bool)`.
C++20 `explicit(bool)`.

- Improved :doc:`google-global-names-in-headers
<clang-tidy/checks/google/global-names-in-headers>` check by replacing the local
Expand All @@ -237,13 +263,18 @@ Changes in existing checks
check by ignoring other functions with same prefixes as the target specific
functions.

- Improved :doc:`linuxkernel-must-check-errs
<clang-tidy/checks/linuxkernel/must-check-errs>` check documentation to
consistently use the check's proper name.

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

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

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by replacing the local
Expand All @@ -269,6 +300,10 @@ Changes in existing checks
don't remove parentheses used in ``sizeof`` calls when they have array index
accesses as arguments.

- Improved :doc:`modernize-use-nullptr
<clang-tidy/checks/modernize/use-nullptr>` check to include support for C23,
which also has introduced the ``nullptr`` keyword.

- Improved :doc:`modernize-use-override
<clang-tidy/checks/modernize/use-override>` check to also remove any trailing
whitespace when deleting the ``virtual`` keyword.
Expand All @@ -290,6 +325,10 @@ Changes in existing checks
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
fix-its.

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

- Improved :doc:`readability-duplicate-include
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.
Expand Down Expand Up @@ -324,13 +363,9 @@ Miscellaneous
^^^^^^^^^^^^^

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

- Fixed the :doc:`linuxkernel-must-check-errs
<clang-tidy/checks/linuxkernel/must-check-errs>` documentation to consistently
use the check's proper name.

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ just the individual thread. Use any signal except ``SIGTERM``.
This check corresponds to the CERT C Coding Standard rule
`POS44-C. Do not use signals to terminate threads
<https://wiki.sei.cmu.edu/confluence/display/c/POS44-C.+Do+not+use+signals+to+terminate+threads>`_.

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

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

`cert-exp42-c` redirects here as an alias of this check.
2 changes: 1 addition & 1 deletion clang-tools-extra/docs/clang-tidy/checks/cert/env33-c.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ but does not actually attempt to execute a command.

This check corresponds to the CERT C Coding Standard rule
`ENV33-C. Do not call system()
<https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=2130132>`_.
<https://www.securecoding.cert.org/confluence/display/c/ENV33-C.+Do+not+call+system()>`_.
10 changes: 10 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/cert/int09-c.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. title:: clang-tidy - cert-int09-c
.. meta::
:http-equiv=refresh: 5;URL=../readability/enum-initial-value.html

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

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

`cert-pos47-c` redirects here as an alias of this check.
9 changes: 7 additions & 2 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Clang-Tidy Checks
:doc:`bugprone-posix-return <bugprone/posix-return>`, "Yes"
:doc:`bugprone-redundant-branch-condition <bugprone/redundant-branch-condition>`, "Yes"
:doc:`bugprone-reserved-identifier <bugprone/reserved-identifier>`, "Yes"
:doc:`bugprone-return-const-ref-from-parameter <bugprone/return-const-ref-from-parameter>`
:doc:`bugprone-return-const-ref-from-parameter <bugprone/return-const-ref-from-parameter>`,
:doc:`bugprone-shared-ptr-array-mismatch <bugprone/shared-ptr-array-mismatch>`, "Yes"
:doc:`bugprone-signal-handler <bugprone/signal-handler>`,
:doc:`bugprone-signed-char-misuse <bugprone/signed-char-misuse>`,
Expand Down Expand Up @@ -276,6 +276,7 @@ Clang-Tidy Checks
:doc:`modernize-macro-to-enum <modernize/macro-to-enum>`, "Yes"
:doc:`modernize-make-shared <modernize/make-shared>`, "Yes"
:doc:`modernize-make-unique <modernize/make-unique>`, "Yes"
:doc:`modernize-min-max-use-initializer-list <modernize/min-max-use-initializer-list>`, "Yes"
:doc:`modernize-pass-by-value <modernize/pass-by-value>`, "Yes"
:doc:`modernize-raw-string-literal <modernize/raw-string-literal>`, "Yes"
:doc:`modernize-redundant-void-arg <modernize/redundant-void-arg>`, "Yes"
Expand Down Expand Up @@ -363,6 +364,7 @@ Clang-Tidy Checks
:doc:`readability-isolate-declaration <readability/isolate-declaration>`, "Yes"
:doc:`readability-magic-numbers <readability/magic-numbers>`,
:doc:`readability-make-member-function-const <readability/make-member-function-const>`, "Yes"
:doc:`readability-math-missing-parentheses <readability/math-missing-parentheses>`, "Yes"
:doc:`readability-misleading-indentation <readability/misleading-indentation>`,
:doc:`readability-misplaced-array-index <readability/misplaced-array-index>`, "Yes"
:doc:`readability-named-parameter <readability/named-parameter>`, "Yes"
Expand Down Expand Up @@ -393,8 +395,10 @@ Clang-Tidy Checks
:doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
:doc:`zircon-temporary-objects <zircon/temporary-objects>`,

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

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

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

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

Finds violations of the rule "Throw by value, catch by reference" presented for
example in "C++ Coding Standards" by H. Sutter and A. Alexandrescu, as well as
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.. title:: clang-tidy - modernize-min-max-use-initializer-list

modernize-min-max-use-initializer-list
======================================

Replaces nested ``std::min`` and ``std::max`` calls with an initializer list
where applicable.

For instance, consider the following code:

.. code-block:: cpp
int a = std::max(std::max(i, j), k);
The check will transform the above code to:

.. code-block:: cpp
int a = std::max({i, j, k});
Performance Considerations
==========================

While this check simplifies the code and makes it more readable, it may cause
performance degradation for non-trivial types due to the need to copy objects
into the initializer list.

To avoid this, it is recommended to use `std::ref` or `std::cref` for
non-trivial types:

.. code-block:: cpp
std::string b = std::max({std::ref(i), std::ref(j), std::ref(k)});
Options
=======

.. option:: IncludeStyle

A string specifying which include-style is used, `llvm` or `google`. Default
is `llvm`.

.. option:: IgnoreNonTrivialTypes

A boolean specifying whether to ignore non-trivial types. Default is `true`.

.. option:: IgnoreTrivialTypesOfSizeAbove

An integer specifying the size (in bytes) above which trivial types are
ignored. Default is `32`.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ modernize-use-nullptr
=====================

The check converts the usage of null pointer constants (e.g. ``NULL``, ``0``)
to use the new C++11 ``nullptr`` keyword.
to use the new C++11 and C23 ``nullptr`` keyword.

Example
-------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,70 +6,83 @@ readability-enum-initial-value
Enforces consistent style for enumerators' initialization, covering three
styles: none, first only, or all initialized explicitly.

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

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

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

.. code-block:: c++

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

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

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

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

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

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

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

Options
-------

.. option:: AllowExplicitZeroFirstInitialValue

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

.. code-block:: c++

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


.. option:: AllowExplicitSequentialInitialValues

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

.. code-block:: c++

enum A {
e0 = 1, // not allowed if AllowExplicitSequentialInitialValues is false
e1 = 2,
e2 = 3,
};
enum G {
g0 = 1, // Not allowed if AllowExplicitSequentialInitialValues is false.
g1 = 2,
g2 = 3,
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.. title:: clang-tidy - readability-math-missing-parentheses

readability-math-missing-parentheses
====================================

Check for missing parentheses in mathematical expressions that involve operators
of different priorities.

Parentheses in mathematical expressions clarify the order
of operations, especially with different-priority operators. Lengthy or multiline
expressions can obscure this order, leading to coding errors. IDEs can aid clarity
by highlighting parentheses. Explicitly using parentheses also clarifies what the
developer had in mind when writing the expression. Ensuring their presence reduces
ambiguity and errors, promoting clearer and more maintainable code.

Before:

.. code-block:: c++

int x = 1 + 2 * 3 - 4 / 5;


After:

.. code-block:: c++

int x = 1 + (2 * 3) - (4 / 5);
45 changes: 21 additions & 24 deletions clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,30 @@ foreach(dep ${LLVM_UTILS_DEPS})
endif()
endforeach()

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

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

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

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

namespace PR87069 {
void castconstVoidToVoid() {
const void* ptr = nullptr;
int* numberPtr = static_cast<int*>(const_cast<void*>(ptr));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ struct HeapArray { // Ok, since destruc

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
// RUN: %check_clang_tidy %s modernize-min-max-use-initializer-list %t

// CHECK-FIXES: #include <algorithm>
namespace utils {
template <typename T>
T max(T a, T b) {
return (a < b) ? b : a;
}
} // namespace utils

namespace std {
template< class T >
struct initializer_list {
initializer_list()=default;
initializer_list(T*,int){}
const T* begin() const {return nullptr;}
const T* end() const {return nullptr;}
};

template<class ForwardIt>
ForwardIt min_element(ForwardIt first, ForwardIt last)
{
if (first == last)
return last;

ForwardIt smallest = first;

while (++first != last)
if (*first < *smallest)
smallest = first;

return smallest;
}

template<class ForwardIt, class Compare>
ForwardIt min_element(ForwardIt first, ForwardIt last, Compare comp)
{
if (first == last)
return last;

ForwardIt smallest = first;

while (++first != last)
if (comp(*first, *smallest))
smallest = first;

return smallest;
}

template<class ForwardIt>
ForwardIt max_element(ForwardIt first, ForwardIt last)
{
if (first == last)
return last;

ForwardIt largest = first;

while (++first != last)
if (*largest < *first)
largest = first;

return largest;
}

template<class ForwardIt, class Compare>
ForwardIt max_element(ForwardIt first, ForwardIt last, Compare comp)
{
if (first == last)
return last;

ForwardIt largest = first;

while(++first != last)
if (comp(*largest, *first))
largest = first;

return largest;
}

template< class T >
const T& max( const T& a, const T& b ) {
return (a < b) ? b : a;
};

template< class T >
T max(std::initializer_list<T> ilist)
{
return *std::max_element(ilist.begin(), ilist.end());
}

template< class T, class Compare >
const T& max( const T& a, const T& b, Compare comp ) {
return (comp(a, b)) ? b : a;
};

template< class T, class Compare >
T max(std::initializer_list<T> ilist, Compare comp) {
return *std::max_element(ilist.begin(), ilist.end(), comp);
};

template< class T >
const T& min( const T& a, const T& b ) {
return (b < a) ? b : a;
};

template< class T >
T min(std::initializer_list<T> ilist)
{
return *std::min_element(ilist.begin(), ilist.end());
}


template< class T, class Compare >
const T& min( const T& a, const T& b, Compare comp ) {
return (comp(b, a)) ? b : a;
};

template< class T, class Compare >
T min(std::initializer_list<T> ilist, Compare comp) {
return *std::min_element(ilist.begin(), ilist.end(), comp);
};

} // namespace std

using namespace std;

namespace {
bool fless_than(int a, int b) {
return a < b;
}

bool fgreater_than(int a, int b) {
return a > b;
}
auto less_than = [](int a, int b) { return a < b; };
auto greater_than = [](int a, int b) { return a > b; };

int max1 = std::max(1, std::max(2, 3));
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max1 = std::max({1, 2, 3});

int min1 = std::min(1, std::min(2, 3));
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int min1 = std::min({1, 2, 3});

int max2 = std::max(1, std::max(2, std::max(3, 4)));
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max2 = std::max({1, 2, 3, 4});

int max2b = std::max(std::max(std::max(1, 2), std::max(3, 4)), std::max(std::max(5, 6), std::max(7, 8)));
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max2b = std::max({1, 2, 3, 4, 5, 6, 7, 8});

int max2c = std::max(std::max(1, std::max(2, 3)), 4);
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max2c = std::max({1, 2, 3, 4});

int max2d = std::max(std::max({1, 2, 3}), 4);
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max2d = std::max({1, 2, 3, 4});


int max2e = std::max(1, max(2, max(3, 4)));
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max2e = std::max({1, 2, 3, 4});

int min2 = std::min(1, std::min(2, std::min(3, 4)));
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int min2 = std::min({1, 2, 3, 4});

int max3 = std::max(std::max(4, 5), std::min(2, std::min(3, 1)));
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max3 = std::max({4, 5, std::min({2, 3, 1})});

int min3 = std::min(std::min(4, 5), std::max(2, std::max(3, 1)));
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int min3 = std::min({4, 5, std::max({2, 3, 1})});

int max4 = std::max(1, std::max(2, 3, greater_than), less_than);
// CHECK-FIXES: int max4 = std::max(1, std::max(2, 3, greater_than), less_than);

int min4 = std::min(1, std::min(2, 3, greater_than), less_than);
// CHECK-FIXES: int min4 = std::min(1, std::min(2, 3, greater_than), less_than);

int max5 = std::max(1, std::max(2, 3), less_than);
// CHECK-FIXES: int max5 = std::max(1, std::max(2, 3), less_than);

int min5 = std::min(1, std::min(2, 3), less_than);
// CHECK-FIXES: int min5 = std::min(1, std::min(2, 3), less_than);

int max6 = std::max(1, std::max(2, 3, greater_than), greater_than);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max6 = std::max({1, 2, 3 }, greater_than);

int min6 = std::min(1, std::min(2, 3, greater_than), greater_than);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int min6 = std::min({1, 2, 3 }, greater_than);

int max7 = std::max(1, std::max(2, 3, fless_than), fgreater_than);
// CHECK-FIXES: int max7 = std::max(1, std::max(2, 3, fless_than), fgreater_than);

int min7 = std::min(1, std::min(2, 3, fless_than), fgreater_than);
// CHECK-FIXES: int min7 = std::min(1, std::min(2, 3, fless_than), fgreater_than);

int max8 = std::max(1, std::max(2, 3, fless_than), less_than);
// CHECK-FIXES: int max8 = std::max(1, std::max(2, 3, fless_than), less_than)

int min8 = std::min(1, std::min(2, 3, fless_than), less_than);
// CHECK-FIXES: int min8 = std::min(1, std::min(2, 3, fless_than), less_than);

int max9 = std::max(1, std::max(2, 3, fless_than), fless_than);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max9 = std::max({1, 2, 3 }, fless_than);

int min9 = std::min(1, std::min(2, 3, fless_than), fless_than);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int min9 = std::min({1, 2, 3 }, fless_than);

int min10 = std::min(std::min(4, 5), std::max(2, utils::max(3, 1)));
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int min10 = std::min({4, 5, std::max(2, utils::max(3, 1))});

int max10 = std::max({std::max(1, 2), std::max({5, 6, 1}), 2, std::min({1, 2, 4})});
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int max10 = std::max({1, 2, 5, 6, 1, 2, std::min({1, 2, 4})});

int typecastTest = std::max(std::max<int>(0U, 0.0f), 0);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int typecastTest = std::max({static_cast<int>(0U), static_cast<int>(0.0f), 0});

int typecastTest1 = std::max(std::max<long>(0U, 0.0f), 0L);
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int typecastTest1 = std::max({static_cast<long>(0U), static_cast<long>(0.0f), 0L});

int typecastTest2 = std::max(std::max<int>(10U, 20.0f), 30);
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int typecastTest2 = std::max({static_cast<int>(10U), static_cast<int>(20.0f), 30});

int typecastTest3 = std::max(std::max<int>(0U, std::max<int>(0.0f, 1.0f)), 0);
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int typecastTest3 = std::max({static_cast<int>(0U), static_cast<int>(0.0f), static_cast<int>(1.0f), 0});

#define max3f(a, b, c) std::max(a, std::max(b, c))
// CHECK-FIXES: #define max3f(a, b, c) std::max(a, std::max(b, c))

#define value 4545
int macroVarMax = std::max(value, std::max(1, 2));
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int macroVarMax = std::max({value, 1, 2});

#define value2 45U
int macroVarMax2 = std::max(1, std::max<int>(value2, 2.0f));
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: int macroVarMax2 = std::max({1, static_cast<int>(value2), static_cast<int>(2.0f)});

// True-negative tests
int maxTN1 = std::max(1, 2);
// CHECK-FIXES: int maxTN1 = std::max(1, 2);

int maxTN2 = std::max({1, 2, 3});
// CHECK-FIXES: int maxTN2 = std::max({1, 2, 3});

int maxTN3 = std::max({1, 2, 3}, less_than);
// CHECK-FIXES: int maxTN3 = std::max({1, 2, 3}, less_than);

// non-trivial types
struct A {
int a;
A(int a) : a(a) {}
bool operator<(const A &rhs) const { return a < rhs.a; }
};

A maxNT1 = std::max(A(1), A(2));
// CHECK-FIXES: A maxNT1 = std::max(A(1), A(2));

A maxNT2 = std::max(A(1), std::max(A(2), A(3)));
// CHECK-FIXES: A maxNT2 = std::max(A(1), std::max(A(2), A(3)));

A maxNT3 = std::max(A(1), std::max(A(2), A(3)), [](const A &lhs, const A &rhs) { return lhs.a < rhs.a; });
// CHECK-FIXES: A maxNT3 = std::max(A(1), std::max(A(2), A(3)), [](const A &lhs, const A &rhs) { return lhs.a < rhs.a; });

// Trivial type with size greater than 32
struct B {
// 9*4 = 36 bytes > 32 bytes
int a[9];

bool operator<(const B& rhs) const {
return a[0] < rhs.a[0];
}
};

B maxTT1 = std::max(B(), B());
// CHECK-FIXES: B maxTT1 = std::max(B(), B());

B maxTT2 = std::max(B(), std::max(B(), B()));
// CHECK-FIXES: B maxTT2 = std::max(B(), std::max(B(), B()));

B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; });
// CHECK-FIXES: B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; });


} // namespace

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

Template<int> T1;


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

DT1<int> Dt1;

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

DT2<T> Dt2;

// Default arguments.
Expand Down
139 changes: 139 additions & 0 deletions clang-tools-extra/test/clang-tidy/checkers/modernize/use-nullptr-c23.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// RUN: %check_clang_tidy %s modernize-use-nullptr %t -- -- -std=c23

#define NULL 0

void test_assignment() {
int *p1 = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr [modernize-use-nullptr]
// CHECK-FIXES: int *p1 = nullptr;
p1 = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
// CHECK-FIXES: p1 = nullptr;

int *p2 = NULL;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
// CHECK-FIXES: int *p2 = nullptr;

p2 = p1;
// CHECK-FIXES: p2 = p1;

const int null = 0;
int *p3 = &null;

p3 = NULL;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
// CHECK-FIXES: p3 = nullptr;

int *p4 = p3;

int i1 = 0;

int i2 = NULL;

int i3 = null;

int *p5, *p6, *p7;
p5 = p6 = p7 = NULL;
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
// CHECK-FIXES: p5 = p6 = p7 = nullptr;
}

void test_function(int *p) {}

void test_function_no_ptr_param(int i) {}

void test_function_call() {
test_function(0);
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
// CHECK-FIXES: test_function(nullptr);

test_function(NULL);
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
// CHECK-FIXES: test_function(nullptr);

test_function_no_ptr_param(0);
}

char *test_function_return1() {
return 0;
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
// CHECK-FIXES: return nullptr;
}

void *test_function_return2() {
return NULL;
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
// CHECK-FIXES: return nullptr;
}

int test_function_return4() {
return 0;
}

int test_function_return5() {
return NULL;
}

int *test_function_return_cast1() {
return(int)0;
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
// CHECK-FIXES: return nullptr;
}

int *test_function_return_cast2() {
#define RET return
RET(int)0;
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use nullptr
// CHECK-FIXES: RET nullptr;
#undef RET
}

// Test parentheses expressions resulting in a nullptr.
int *test_parentheses_expression1() {
return(0);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
// CHECK-FIXES: return(nullptr);
}

int *test_parentheses_expression2() {
return((int)(0.0f));
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
// CHECK-FIXES: return(nullptr);
}

int *test_nested_parentheses_expression() {
return((((0))));
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
// CHECK-FIXES: return((((nullptr))));
}

void test_const_pointers() {
const int *const_p1 = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
// CHECK-FIXES: const int *const_p1 = nullptr;
const int *const_p2 = NULL;
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
// CHECK-FIXES: const int *const_p2 = nullptr;
const int *const_p3 = (int)0;
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
// CHECK-FIXES: const int *const_p3 = nullptr;
const int *const_p4 = (int)0.0f;
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
// CHECK-FIXES: const int *const_p4 = nullptr;
}

void test_nested_implicit_cast_expr() {
int func0(void*, void*);
int func1(int, void*, void*);

(double)func1(0, 0, 0);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use nullptr
// CHECK-MESSAGES: :[[@LINE-2]]:23: warning: use nullptr
// CHECK-FIXES: (double)func1(0, nullptr, nullptr);
(double)func1(func0(0, 0), 0, 0);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use nullptr
// CHECK-MESSAGES: :[[@LINE-2]]:26: warning: use nullptr
// CHECK-MESSAGES: :[[@LINE-3]]:30: warning: use nullptr
// CHECK-MESSAGES: :[[@LINE-4]]:33: warning: use nullptr
// CHECK-FIXES: (double)func1(func0(nullptr, nullptr), nullptr, nullptr);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: clang-tidy %s -checks=-*,modernize-use-nullptr -- | count 0
// RUN: clang-tidy %s -checks=-*,modernize-use-nullptr -- -std=c17 | count 0

// Note: this test expects no diagnostics, but FileCheck cannot handle that,
// hence the use of | count 0.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#ifndef READABILITY_DUPLICATE_INCLUDE_H
#define READABILITY_DUPLICATE_INCLUDE_H

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

#endif
#ifndef READABILITY_DUPLICATE_INCLUDE_H
#define READABILITY_DUPLICATE_INCLUDE_H

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

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

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

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

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

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

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

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

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

void test() {
getFirst<Pair<int, int>>();
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++17

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

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

if constexpr (sizeof(int) > 4)
return;
else if constexpr (sizeof(long) > 4)
return;
else
return;
}
// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++17

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

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

if constexpr (sizeof(int) > 4)
return;
else if constexpr (sizeof(long) > 4)
return;
else
return;
}
Loading