| 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 |
| 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 |
|---|---|---|
| @@ -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 |
|---|---|---|
| @@ -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 |
|---|---|---|
| @@ -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"} |
| 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"} |
| 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 |
| 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 |
|---|---|---|
| @@ -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 |
|---|---|---|
| @@ -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); |
| 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 |
|---|---|---|
| @@ -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,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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| // This file is intentionally empty. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| // This file is intentionally empty. |
| 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; | ||
| } |