14 changes: 10 additions & 4 deletions clang-tools-extra/clang-tidy/GlobList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ static bool consumeNegativeIndicator(StringRef &GlobList) {
return GlobList.consume_front("-");
}

// Converts first glob from the comma-separated list of globs to Regex and
// removes it and the trailing comma from the GlobList.
static llvm::Regex consumeGlob(StringRef &GlobList) {
// Extracts the first glob from the comma-separated list of globs,
// removes it and the trailing comma from the GlobList and
// returns the extracted glob.
static llvm::StringRef extractNextGlob(StringRef &GlobList) {
StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find_first_of(",\n"));
StringRef Glob = UntrimmedGlob.trim();
GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
return Glob;
}

static llvm::Regex createRegexFromGlob(StringRef &Glob) {
SmallString<128> RegexText("^");
StringRef MetaChars("()^$|*+?.[]\\{}");
for (char C : Glob) {
Expand All @@ -43,7 +48,8 @@ GlobList::GlobList(StringRef Globs, bool KeepNegativeGlobs /* =true */) {
do {
GlobListItem Item;
Item.IsPositive = !consumeNegativeIndicator(Globs);
Item.Regex = consumeGlob(Globs);
Item.Text = extractNextGlob(Globs);
Item.Regex = createRegexFromGlob(Item.Text);
if (Item.IsPositive || KeepNegativeGlobs)
Items.push_back(std::move(Item));
} while (!Globs.empty());
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/GlobList.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ class GlobList {
struct GlobListItem {
bool IsPositive;
llvm::Regex Regex;
llvm::StringRef Text;
};
SmallVector<GlobListItem, 0> Items;

public:
const SmallVectorImpl<GlobListItem> &getItems() const { return Items; };
};

/// A \p GlobList that caches search results, so that search is performed only
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "PosixReturnCheck.h"
#include "RedundantBranchConditionCheck.h"
#include "ReservedIdentifierCheck.h"
#include "ReturnConstRefFromParameterCheck.h"
#include "SharedPtrArrayMismatchCheck.h"
#include "SignalHandlerCheck.h"
#include "SignedCharMisuseCheck.h"
Expand Down Expand Up @@ -137,6 +138,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-inaccurate-erase");
CheckFactories.registerCheck<IncorrectEnableIfCheck>(
"bugprone-incorrect-enable-if");
CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>(
"bugprone-return-const-ref-from-parameter");
CheckFactories.registerCheck<SwitchMissingDefaultCaseCheck>(
"bugprone-switch-missing-default-case");
CheckFactories.registerCheck<IncDecInConditionsCheck>(
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_clang_library(clangTidyBugproneModule
ImplicitWideningOfMultiplicationResultCheck.cpp
InaccurateEraseCheck.cpp
IncorrectEnableIfCheck.cpp
ReturnConstRefFromParameterCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
SwitchMissingDefaultCaseCheck.cpp
IncDecInConditionsCheck.cpp
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
14 changes: 11 additions & 3 deletions clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

#include "LambdaFunctionNameCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
Expand Down Expand Up @@ -56,6 +58,8 @@ class MacroExpansionsWithFileAndLine : public PPCallbacks {
LambdaFunctionNameCheck::SourceRangeSet* SuppressMacroExpansions;
};

AST_MATCHER(CXXMethodDecl, isInLambda) { return Node.getParent()->isLambda(); }

} // namespace

LambdaFunctionNameCheck::LambdaFunctionNameCheck(StringRef Name,
Expand All @@ -69,9 +73,13 @@ void LambdaFunctionNameCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}

void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {
// Match on PredefinedExprs inside a lambda.
Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"),
this);
Finder->addMatcher(
cxxMethodDecl(isInLambda(),
hasBody(forEachDescendant(
predefinedExpr(hasAncestor(cxxMethodDecl().bind("fn")))
.bind("E"))),
equalsBoundNode("fn")),
this);
}

void LambdaFunctionNameCheck::registerPPCallbacks(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- ReturnConstRefFromParameterCheck.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 "ReturnConstRefFromParameterCheck.h"
#include "../utils/Matchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"

using namespace clang::ast_matchers;

namespace clang::tidy::bugprone {

void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(hasReturnValue(declRefExpr(to(parmVarDecl(hasType(
hasCanonicalType(matchers::isReferenceToConst())))))))
.bind("ret"),
this);
}

void ReturnConstRefFromParameterCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
diag(R->getRetValue()->getBeginLoc(),
"returning a constant reference parameter may cause a use-after-free "
"when the parameter is constructed from a temporary");
}

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

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_RETURNCONSTREFFROMPARAMETERCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_RETURNCONSTREFFROMPARAMETERCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::bugprone {

/// Detects return statements that return a constant reference parameter as
/// constant reference. This may cause use-after-free errors if the caller uses
/// xvalues as arguments.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/return-const-ref-from-parameter.html
class ReturnConstRefFromParameterCheck : public ClangTidyCheck {
public:
ReturnConstRefFromParameterCheck(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 {
// Use 'AsIs' to make sure the return type is exactly the same as the
// parameter type.
return TK_AsIs;
}
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
};

} // namespace clang::tidy::bugprone

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_RETURNCONSTREFFROMPARAMETERCHECK_H
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
103 changes: 89 additions & 14 deletions clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
callee(cxxMethodDecl(hasName("find")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))));
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));

const auto RFindExpr = cxxMemberCallExpr(
// A method call with a second argument of zero...
Expand All @@ -52,15 +54,68 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))));
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));

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

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

const auto FindOrRFindExpr =
cxxMemberCallExpr(anyOf(FindExpr, RFindExpr)).bind("find_expr");
// Match either one of the two cases above.
const auto HasStringAndLengthArgs =
[HasStringLiteralAndLengthArgs, HasStringVariableAndSizeCallArgs](
const auto StringArgIndex, const auto LengthArgIndex) {
return anyOf(
HasStringLiteralAndLengthArgs(StringArgIndex, LengthArgIndex),
HasStringVariableAndSizeCallArgs(StringArgIndex, LengthArgIndex));
};

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

Finder->addMatcher(
// Match [=!]= with a zero on one side and a string.(r?)find on the other.
binaryOperator(hasAnyOperatorName("==", "!="),
hasOperands(FindOrRFindExpr, ZeroLiteral))
// Match [=!]= with a zero on one side and (r?)find|compare on the other.
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr))
.bind("find_expr"),
ZeroLiteral))
.bind("expr"),
this);
}
Expand All @@ -69,23 +124,42 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>("find_expr");
const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("find_fun");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("search_expr");
const auto *StartsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("starts_with_fun");

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

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

if (StringLiteralArg && StrlenArg &&
StringLiteralArg->getLength() != StrlenArg->getLength()) {
return;
}

if (ComparisonExpr->getBeginLoc().isMacroID()) {
return;
}

const bool Neg = ComparisonExpr->getOpcode() == BO_NE;

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

// Remove possible zero second argument and ' [!=]= 0' suffix.
// Remove possible arguments after search expression and ' [!=]= 0' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(FindExpr->getArg(0)->getEndLoc(), 0,
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
*Result.SourceManager, getLangOpts()),
ComparisonExpr->getEndLoc()),
")");
Expand All @@ -94,11 +168,12 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));

// Replace '(r?)find' with 'starts_with'.
// Replace method name by 'starts_with'.
// Remove possible arguments before search expression.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(FindExpr->getExprLoc(),
FindExpr->getExprLoc()),
StartsWithFunction->getName());
CharSourceRange::getCharRange(FindExpr->getExprLoc(),
SearchExpr->getBeginLoc()),
(StartsWithFunction->getName() + "(").str());

// Add possible negation '!'.
if (Neg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@

namespace clang::tidy::modernize {

/// Checks whether a ``find`` or ``rfind`` result is compared with 0 and
/// suggests replacing with ``starts_with`` when the method exists in the class.
/// Notably, this will work with ``std::string`` and ``std::string_view``.
/// Checks for common roundabout ways to express ``starts_with`` and
/// ``ends_with`` and suggests replacing with ``starts_with`` when the method is
/// available. Notably, this will work with ``std::string`` and
/// ``std::string_view``.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-starts-ends-with.html
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===--- 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);
if (EndLoc.isInvalid())
return;

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())
<< FixItHint::CreateInsertion(StartLoc, "(")
<< FixItHint::CreateInsertion(EndLoc, ")")
<< SourceRange(StartLoc, 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
57 changes: 16 additions & 41 deletions clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,52 +454,27 @@ static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";

static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob,
StringRef Source) {
llvm::StringRef Cur, Rest;
GlobList Globs(CheckGlob);
bool AnyInvalid = false;
for (std::tie(Cur, Rest) = CheckGlob.split(',');
!(Cur.empty() && Rest.empty()); std::tie(Cur, Rest) = Rest.split(',')) {
Cur = Cur.trim();
if (Cur.empty())
for (const auto &Item : Globs.getItems()) {
if (Item.Text.starts_with("clang-diagnostic"))
continue;
Cur.consume_front("-");
if (Cur.starts_with("clang-diagnostic"))
continue;
if (Cur.contains('*')) {
SmallString<128> RegexText("^");
StringRef MetaChars("()^$|*+?.[]\\{}");
for (char C : Cur) {
if (C == '*')
RegexText.push_back('.');
else if (MetaChars.contains(C))
RegexText.push_back('\\');
RegexText.push_back(C);
}
RegexText.push_back('$');
llvm::Regex Glob(RegexText);
std::string Error;
if (!Glob.isValid(Error)) {
AnyInvalid = true;
llvm::WithColor::error(llvm::errs(), Source)
<< "building check glob '" << Cur << "' " << Error << "'\n";
continue;
}
if (llvm::none_of(AllChecks.keys(),
[&Glob](StringRef S) { return Glob.match(S); })) {
AnyInvalid = true;
if (llvm::none_of(AllChecks.keys(),
[&Item](StringRef S) { return Item.Regex.match(S); })) {
AnyInvalid = true;
if (Item.Text.contains('*'))
llvm::WithColor::warning(llvm::errs(), Source)
<< "check glob '" << Cur << "' doesn't match any known check"
<< "check glob '" << Item.Text << "' doesn't match any known check"
<< VerifyConfigWarningEnd;
else {
llvm::raw_ostream &Output =
llvm::WithColor::warning(llvm::errs(), Source)
<< "unknown check '" << Item.Text << '\'';
llvm::StringRef Closest = closest(Item.Text, AllChecks);
if (!Closest.empty())
Output << "; did you mean '" << Closest << '\'';
Output << VerifyConfigWarningEnd;
}
} else {
if (AllChecks.contains(Cur))
continue;
AnyInvalid = true;
llvm::raw_ostream &Output = llvm::WithColor::warning(llvm::errs(), Source)
<< "unknown check '" << Cur << '\'';
llvm::StringRef Closest = closest(Cur, AllChecks);
if (!Closest.empty())
Output << "; did you mean '" << Closest << '\'';
Output << VerifyConfigWarningEnd;
}
}
return AnyInvalid;
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
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS
support
AllTargetsInfos
FrontendOpenMP
TargetParser
)

if(CLANG_BUILT_STANDALONE)
Expand Down
18 changes: 10 additions & 8 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ TEST_F(TargetDeclTest, Types) {
[[auto]] X = S{};
)cpp";
// FIXME: deduced type missing in AST. https://llvm.org/PR42914
EXPECT_DECLS("AutoTypeLoc");
EXPECT_DECLS("AutoTypeLoc", );

Code = R"cpp(
template <typename... E>
Expand Down Expand Up @@ -727,13 +727,13 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
template <class T, int N>
using make_integer_sequence = [[__make_integer_seq]]<integer_sequence, T, N>;
)cpp";
EXPECT_DECLS("TemplateSpecializationTypeLoc");
EXPECT_DECLS("TemplateSpecializationTypeLoc", );

Code = R"cpp(
template <int N, class... Pack>
using type_pack_element = [[__type_pack_element]]<N, Pack...>;
)cpp";
EXPECT_DECLS("TemplateSpecializationTypeLoc");
EXPECT_DECLS("TemplateSpecializationTypeLoc", );
}

TEST_F(TargetDeclTest, MemberOfTemplate) {
Expand Down Expand Up @@ -839,7 +839,9 @@ TEST_F(TargetDeclTest, OverloadExpr) {
[[delete]] x;
}
)cpp";
EXPECT_DECLS("CXXDeleteExpr", "void operator delete(void *) noexcept");
// Sized deallocation is enabled by default in C++14 onwards.
EXPECT_DECLS("CXXDeleteExpr",
"void operator delete(void *, unsigned long) noexcept");
}

TEST_F(TargetDeclTest, DependentExprs) {
Expand Down Expand Up @@ -1018,7 +1020,7 @@ TEST_F(TargetDeclTest, DependentTypes) {
typedef typename waldo<N - 1>::type::[[next]] type;
};
)cpp";
EXPECT_DECLS("DependentNameTypeLoc");
EXPECT_DECLS("DependentNameTypeLoc", );

// Similar to above but using mutually recursive templates.
Code = R"cpp(
Expand All @@ -1035,7 +1037,7 @@ TEST_F(TargetDeclTest, DependentTypes) {
using type = typename even<N - 1>::type::[[next]];
};
)cpp";
EXPECT_DECLS("DependentNameTypeLoc");
EXPECT_DECLS("DependentNameTypeLoc", );
}

TEST_F(TargetDeclTest, TypedefCascade) {
Expand Down Expand Up @@ -1263,14 +1265,14 @@ TEST_F(TargetDeclTest, ObjC) {
+ ([[id]])sharedInstance;
@end
)cpp";
EXPECT_DECLS("TypedefTypeLoc");
EXPECT_DECLS("TypedefTypeLoc", );

Code = R"cpp(
@interface Foo
+ ([[instancetype]])sharedInstance;
@end
)cpp";
EXPECT_DECLS("TypedefTypeLoc");
EXPECT_DECLS("TypedefTypeLoc", );
}

class FindExplicitReferencesTest : public ::testing::Test {
Expand Down
32 changes: 32 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ Improvements to clang-tidy
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``.

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

New checks
^^^^^^^^^^
Expand All @@ -112,13 +117,26 @@ New checks
Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP
can be constructed outside itself and the derived class.

- New :doc:`bugprone-return-const-ref-from-parameter
<clang-tidy/checks/bugprone/return-const-ref-from-parameter>` check.

Detects return statements that return a constant reference parameter as constant
reference. This may cause use-after-free errors if the caller uses xvalues as
arguments.

- New :doc:`bugprone-suspicious-stringview-data-usage
<clang-tidy/checks/bugprone/suspicious-stringview-data-usage>` check.

Identifies suspicious usages of ``std::string_view::data()`` that could lead
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 @@ -131,6 +149,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 @@ -155,6 +179,10 @@ Changes in existing checks
<clang-tidy/checks/bugprone/inc-dec-in-conditions>` check to ignore code
within unevaluated contexts, such as ``decltype``.

- Improved :doc:`bugprone-lambda-function-name<clang-tidy/checks/bugprone/lambda-function-name>`
check by ignoring ``__func__`` macro in lambda captures, initializers of
default parameters and nested function declarations.

- Improved :doc:`bugprone-non-zero-enum-to-bool-conversion
<clang-tidy/checks/bugprone/non-zero-enum-to-bool-conversion>` check by
eliminating false positives resulting from direct usage of bitwise operators
Expand Down Expand Up @@ -257,6 +285,10 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-override>` check to also remove any trailing
whitespace when deleting the ``virtual`` keyword.

- Improved :doc:`modernize-use-starts-ends-with
<clang-tidy/checks/modernize/use-starts-ends-with>` check to also handle
calls to ``compare`` method.

- Improved :doc:`modernize-use-using <clang-tidy/checks/modernize/use-using>`
check by adding support for detection of typedefs declared on function level.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.. title:: clang-tidy - bugprone-return-const-ref-from-parameter

bugprone-return-const-ref-from-parameter
========================================

Detects return statements that return a constant reference parameter as constant
reference. This may cause use-after-free errors if the caller uses xvalues as
arguments.

In C++, constant reference parameters can accept xvalues which will be destructed
after the call. When the function returns such a parameter also as constant reference,
then the returned reference can be used after the object it refers to has been
destroyed.

Example
-------

.. code-block:: c++

struct S {
int v;
S(int);
~S();
};

const S &fn(const S &a) {
return a;
}

const S& s = fn(S{1});
s.v; // use after free
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()>`_.
11 changes: 7 additions & 4 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +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-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 @@ -275,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 @@ -341,9 +343,9 @@ Clang-Tidy Checks
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`,
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
:doc:`readability-avoid-unconditional-preprocessor-if <readability/avoid-unconditional-preprocessor-if>`,
:doc:`readability-braces-around-statements <readability/braces-around-statements>`, "Yes"
:doc:`readability-braces-around-statements <readability/braces-around-statements>`,
:doc:`readability-const-return-type <readability/const-return-type>`, "Yes"
:doc:`readability-container-contains <readability/container-contains>`, "Yes"
:doc:`readability-container-data-pointer <readability/container-data-pointer>`, "Yes"
Expand All @@ -362,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 @@ -529,12 +532,12 @@ Clang-Tidy Checks
:doc:`cppcoreguidelines-non-private-member-variables-in-classes <cppcoreguidelines/non-private-member-variables-in-classes>`, :doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`,
:doc:`cppcoreguidelines-use-default-member-init <cppcoreguidelines/use-default-member-init>`, :doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`fuchsia-header-anon-namespaces <fuchsia/header-anon-namespaces>`, :doc:`google-build-namespaces <google/build-namespaces>`,
:doc:`google-readability-braces-around-statements <google/readability-braces-around-statements>`, :doc:`readability-braces-around-statements <readability/braces-around-statements>`, "Yes"
:doc:`google-readability-braces-around-statements <google/readability-braces-around-statements>`, :doc:`readability-braces-around-statements <readability/braces-around-statements>`,
:doc:`google-readability-function-size <google/readability-function-size>`, :doc:`readability-function-size <readability/function-size>`,
:doc:`google-readability-namespace-comments <google/readability-namespace-comments>`, :doc:`llvm-namespace-comment <llvm/namespace-comment>`,
:doc:`hicpp-avoid-c-arrays <hicpp/avoid-c-arrays>`, :doc:`modernize-avoid-c-arrays <modernize/avoid-c-arrays>`,
:doc:`hicpp-avoid-goto <hicpp/avoid-goto>`, :doc:`cppcoreguidelines-avoid-goto <cppcoreguidelines/avoid-goto>`,
:doc:`hicpp-braces-around-statements <hicpp/braces-around-statements>`, :doc:`readability-braces-around-statements <readability/braces-around-statements>`, "Yes"
:doc:`hicpp-braces-around-statements <hicpp/braces-around-statements>`, :doc:`readability-braces-around-statements <readability/braces-around-statements>`,
:doc:`hicpp-deprecated-headers <hicpp/deprecated-headers>`, :doc:`modernize-deprecated-headers <modernize/deprecated-headers>`, "Yes"
:doc:`hicpp-explicit-conversions <hicpp/explicit-conversions>`, :doc:`google-explicit-constructor <google/explicit-constructor>`, "Yes"
:doc:`hicpp-function-size <hicpp/function-size>`, :doc:`readability-function-size <readability/function-size>`,
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 @@ -3,15 +3,16 @@
modernize-use-starts-ends-with
==============================

Checks whether a ``find`` or ``rfind`` result is compared with 0 and suggests
replacing with ``starts_with`` when the method exists in the class. Notably,
this will work with ``std::string`` and ``std::string_view``.
Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
and suggests replacing with ``starts_with`` when the method is available.
Notably, this will work with ``std::string`` and ``std::string_view``.

.. code-block:: c++

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

Expand All @@ -20,3 +21,4 @@ becomes
std::string s = "...";
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
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
Expand Up @@ -44,6 +44,8 @@ struct basic_string {
int compare(const C* s) const;
int compare(size_type pos, size_type len, const _Type&) const;
int compare(size_type pos, size_type len, const C* s) const;
template<class StringViewLike>
int compare(size_type pos1, size_type count1, const StringViewLike& t) const;

size_type find(const _Type& str, size_type pos = 0) const;
size_type find(const C* s, size_type pos = 0) const;
Expand Down Expand Up @@ -129,6 +131,8 @@ bool operator!=(const char*, const std::string&);
bool operator==(const std::wstring&, const std::wstring&);
bool operator==(const std::wstring&, const wchar_t*);
bool operator==(const wchar_t*, const std::wstring&);

size_t strlen(const char* str);
}

#endif // _STRING_
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
#include "stddef.h"

void *memcpy(void *dest, const void *src, size_t n);
size_t strlen(const char* str);

#endif // _STRING_H_
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// RUN: %check_clang_tidy %s abseil-redundant-strcat-calls %t -- -- -isystem %clang_tidy_headers
#include <string>

int strlen(const char *);

namespace absl {

class string_view {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ void Positives() {
// CHECK-MESSAGES-NO-CONFIG: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [bugprone-lambda-function-name]
[] { EMBED_IN_ANOTHER_MACRO1; }();
// CHECK-MESSAGES-NO-CONFIG: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [bugprone-lambda-function-name]
[] {
__func__;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [bugprone-lambda-function-name]
struct S {
void f() {
__func__;
[] {
__func__;
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [bugprone-lambda-function-name]
}();
__func__;
}
};
__func__;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [bugprone-lambda-function-name]
}();
}

#define FUNC_MACRO_WITH_FILE_AND_LINE Foo(__func__, __FILE__, __LINE__)
Expand All @@ -40,4 +56,7 @@ void Negatives() {
[] { FUNC_MACRO_WITH_FILE_AND_LINE; }();
[] { FUNCTION_MACRO_WITH_FILE_AND_LINE; }();
[] { EMBED_IN_ANOTHER_MACRO2; }();

[] (const char* func = __func__) { func; }();
[func=__func__] { func; }();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t

using T = int;
using TConst = int const;
using TConstRef = int const&;

namespace invalid {

int const &f1(int const &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: returning a constant reference parameter

int const &f2(T const &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: returning a constant reference parameter

int const &f3(TConstRef a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: returning a constant reference parameter

int const &f4(TConst &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter

} // namespace invalid

namespace valid {

int const &f1(int &a) { return a; }

int const &f2(int &&a) { return a; }

int f1(int const &a) { return a; }

} // namespace valid
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@ struct S {
// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
void *operator new(size_t size) noexcept(false);

struct T {
// Sized deallocations are not enabled by default, and so this new/delete pair
// does not match. However, we expect only one warning, for the new, because
// the operator delete is a placement delete and we do not warn on mismatching
// placement operations.
// CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
void *operator new(size_t size) noexcept;
void operator delete(void *ptr, size_t) noexcept; // ok only if sized deallocation is enabled
};

struct U {
void *operator new(size_t size) noexcept;
void operator delete(void *ptr) noexcept;
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
@@ -1,6 +1,7 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-starts-ends-with %t -- \
// RUN: -- -isystem %clang_tidy_headers

#include <string.h>
#include <string>

std::string foo(std::string);
Expand Down Expand Up @@ -158,10 +159,64 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith
// CHECK-FIXES: puvi.startsWith("a");

s.compare(0, 1, "a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare() == 0
// CHECK-FIXES: s.starts_with("a");

s.compare(0, 1, "a") != 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare() != 0
// CHECK-FIXES: !s.starts_with("a");

s.compare(0, strlen("a"), "a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with("a");

s.compare(0, std::strlen("a"), "a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with("a");

s.compare(0, std::strlen(("a")), "a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with("a");

s.compare(0, std::strlen(("a")), (("a"))) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with("a");

s.compare(0, s.size(), s) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with(s);

s.compare(0, s.length(), s) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with(s);

0 != s.compare(0, sv.length(), sv);
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with(sv);

#define LENGTH(x) (x).length()
s.compare(0, LENGTH(s), s) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with(s);

s.compare(ZERO, LENGTH(s), s) == ZERO;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: s.starts_with(s);

s.compare(ZERO, LENGTH(sv), sv) != 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: !s.starts_with(sv);

// Expressions that don't trigger the check are here.
#define EQ(x, y) ((x) == (y))
EQ(s.find("a"), 0);

#define DOTFIND(x, y) (x).find(y)
DOTFIND(s, "a") == 0;

#define STARTS_WITH_COMPARE(x, y) (x).compare(0, (x).size(), (y))
STARTS_WITH_COMPARE(s, s) == 0;

s.compare(0, 1, "ab") == 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// RUN: %check_clang_tidy %s readability-math-missing-parentheses %t

#define MACRO_AND &
#define MACRO_ADD +
#define MACRO_OR |
#define MACRO_MULTIPLY *
#define MACRO_XOR ^
#define MACRO_SUBTRACT -
#define MACRO_DIVIDE /

int foo(){
return 5;
}

int bar(){
return 4;
}

class fun{
public:
int A;
double B;
fun(){
A = 5;
B = 5.4;
}
};

void f(){
//CHECK-MESSAGES: :[[@LINE+2]]:17: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int a = 1 + (2 * 3);
int a = 1 + 2 * 3;

int a_negative = 1 + (2 * 3); // No warning

int b = 1 + 2 + 3; // No warning

int c = 1 * 2 * 3; // No warning

//CHECK-MESSAGES: :[[@LINE+3]]:17: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+2]]:25: warning: '/' has higher precedence than '-'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int d = 1 + (2 * 3) - (4 / 5);
int d = 1 + 2 * 3 - 4 / 5;

int d_negative = 1 + (2 * 3) - (4 / 5); // No warning

//CHECK-MESSAGES: :[[@LINE+4]]:13: warning: '&' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+3]]:17: warning: '+' has higher precedence than '&'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+2]]:25: warning: '*' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int e = (1 & (2 + 3)) | (4 * 5);
int e = 1 & 2 + 3 | 4 * 5;

int e_negative = (1 & (2 + 3)) | (4 * 5); // No warning

//CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int f = (1 * -2) + 4;
int f = 1 * -2 + 4;

int f_negative = (1 * -2) + 4; // No warning

//CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int g = (1 * 2 * 3) + 4 + 5;
int g = 1 * 2 * 3 + 4 + 5;

int g_negative = (1 * 2 * 3) + 4 + 5; // No warning

//CHECK-MESSAGES: :[[@LINE+4]]:13: warning: '&' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+3]]:19: warning: '+' has higher precedence than '&'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+2]]:27: warning: '*' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int h = (120 & (2 + 3)) | (22 * 5);
int h = 120 & 2 + 3 | 22 * 5;

int h_negative = (120 & (2 + 3)) | (22 * 5); // No warning

int i = 1 & 2 & 3; // No warning

int j = 1 | 2 | 3; // No warning

int k = 1 ^ 2 ^ 3; // No warning

//CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '+' has higher precedence than '^'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int l = (1 + 2) ^ 3;
int l = 1 + 2 ^ 3;

int l_negative = (1 + 2) ^ 3; // No warning

//CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int m = (2 * foo()) + bar();
int m = 2 * foo() + bar();

int m_negative = (2 * foo()) + bar(); // No warning

//CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int n = (1.05 * foo()) + double(bar());
int n = 1.05 * foo() + double(bar());

int n_negative = (1.05 * foo()) + double(bar()); // No warning

//CHECK-MESSAGES: :[[@LINE+3]]:17: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int o = 1 + (obj.A * 3) + obj.B;
fun obj;
int o = 1 + obj.A * 3 + obj.B;

int o_negative = 1 + (obj.A * 3) + obj.B; // No warning

//CHECK-MESSAGES: :[[@LINE+2]]:18: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int p = 1U + (2 * 3);
int p = 1U + 2 * 3;

int p_negative = 1U + (2 * 3); // No warning

//CHECK-MESSAGES: :[[@LINE+7]]:13: warning: '+' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+6]]:25: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+5]]:53: warning: '&' has higher precedence than '^'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+4]]:53: warning: '^' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+3]]:77: warning: '-' has higher precedence than '^'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-MESSAGES: :[[@LINE+2]]:94: warning: '/' has higher precedence than '-'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]
//CHECK-FIXES: int q = (1 MACRO_ADD (2 MACRO_MULTIPLY 3)) MACRO_OR ((4 MACRO_AND 5) MACRO_XOR (6 MACRO_SUBTRACT (7 MACRO_DIVIDE 8)));
int q = 1 MACRO_ADD 2 MACRO_MULTIPLY 3 MACRO_OR 4 MACRO_AND 5 MACRO_XOR 6 MACRO_SUBTRACT 7 MACRO_DIVIDE 8; // No warning
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
InheritParentConfig: true
Checks: 'misc-throw-by-value-catch-by-reference'
CheckOptions:
misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976'
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,11 @@

// Validate that check options are printed in alphabetical order:
// RUN: clang-tidy --checks="-*,readability-identifier-naming" --dump-config %S/Inputs/config-files/- -- | grep "readability-identifier-naming\." | sort --check

// Dumped config does not overflow for unsigned options
// RUN: clang-tidy --dump-config \
// RUN: --checks="-*,misc-throw-by-value-catch-by-reference" \
// RUN: -- | grep -v -q "misc-throw-by-value-catch-by-reference.MaxSize: '-1'"

// RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- \
// RUN: -- | grep -q "misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976'"
12 changes: 12 additions & 0 deletions clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@
// CHECK-VERIFY: command-line option '-checks': warning: check glob 'bad*glob' doesn't match any known check [-verify-config]
// CHECK-VERIFY: command-line option '-checks': warning: unknown check 'llvm-includeorder'; did you mean 'llvm-include-order' [-verify-config]
// CHECK-VERIFY: command-line option '-checks': warning: unknown check 'my-made-up-check' [-verify-config]

// RUN: echo -e 'Checks: |\n bugprone-argument-comment\n bugprone-assert-side-effect,\n bugprone-bool-pointer-implicit-conversion\n readability-use-anyof*' > %T/MyClangTidyConfig
// RUN: clang-tidy -verify-config \
// RUN: --config-file=%T/MyClangTidyConfig | FileCheck %s -check-prefix=CHECK-VERIFY-BLOCK-OK
// CHECK-VERIFY-BLOCK-OK: No config errors detected.

// RUN: echo -e 'Checks: |\n bugprone-arguments-*\n bugprone-assert-side-effects\n bugprone-bool-pointer-implicit-conversion' > %T/MyClangTidyConfigBad
// RUN: not clang-tidy -verify-config \
// RUN: --config-file=%T/MyClangTidyConfigBad 2>&1 | FileCheck %s -check-prefix=CHECK-VERIFY-BLOCK-BAD
// CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: check glob 'bugprone-arguments-*' doesn't match any known check [-verify-config]
// CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: unknown check 'bugprone-assert-side-effects'; did you mean 'bugprone-assert-side-effect' [-verify-config]

6 changes: 6 additions & 0 deletions clang-tools-extra/test/pp-trace/pp-trace-pragma-general.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ void foo() {

// CHECK: ---
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "<built-in>:{{.+}}:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "<built-in>:{{.+}}:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:3:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDiagnosticPush
Expand Down
8 changes: 7 additions & 1 deletion clang-tools-extra/test/pp-trace/pp-trace-pragma-ms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@

// CHECK: ---
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "<built-in>:{{.+}}:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "<built-in>:{{.+}}:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:3:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaComment
Expand Down Expand Up @@ -67,7 +73,7 @@
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaMessage
// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:13:9"
// CHECK-NEXT: Namespace:
// CHECK-NEXT: Namespace:
// CHECK-NEXT: Kind: PMK_Message
// CHECK-NEXT: Str: message argument
// CHECK-NEXT: - Callback: PragmaDirective
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/test/pp-trace/pp-trace-pragma-opencl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

// CHECK: ---
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "<built-in>:{{.+}}:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "<built-in>:{{.+}}:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaDirective
// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:1"
// CHECK-NEXT: Introducer: PIK_HashPragma
// CHECK-NEXT: - Callback: PragmaOpenCLExtension
Expand Down
4 changes: 4 additions & 0 deletions clang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ if(CLANG_ENABLE_LIBXML2)
endif()

if(CLANG_ENABLE_CIR)
if (CLANG_BUILT_STANDALONE)
message(FATAL_ERROR
"ClangIR is not yet supported in the standalone build.")
endif()
if (NOT "${LLVM_ENABLE_PROJECTS}" MATCHES "MLIR|mlir")
message(FATAL_ERROR
"Cannot build ClangIR without MLIR in LLVM_ENABLE_PROJECTS")
Expand Down
1 change: 1 addition & 0 deletions clang/cmake/caches/Apple-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
set(LLVM_ENABLE_MODULES ON CACHE BOOL "")
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
set(LLVM_ENABLE_EXPORTED_SYMBOLS_IN_EXECUTABLES OFF CACHE BOOL "")
set(LLVM_PLUGIN_SUPPORT OFF CACHE BOOL "")
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
set(CLANG_SPAWN_CC1 ON CACHE BOOL "")
set(BUG_REPORT_URL "http://developer.apple.com/bugreporter/" CACHE STRING "")
Expand Down
134 changes: 67 additions & 67 deletions clang/cmake/caches/Release.cmake
Original file line number Diff line number Diff line change
@@ -1,93 +1,93 @@
# Plain options configure the first build.
# BOOTSTRAP_* options configure the second build.
# BOOTSTRAP_BOOTSTRAP_* options configure the third build.
# PGO Builds have 3 stages (stage1, stage2-instrumented, stage2)
# non-PGO Builds have 2 stages (stage1, stage2)

# General Options

function (set_final_stage_var name value type)
if (LLVM_RELEASE_ENABLE_PGO)
set(BOOTSTRAP_BOOTSTRAP_${name} ${value} CACHE ${type} "")
else()
set(BOOTSTRAP_${name} ${value} CACHE ${type} "")
endif()
endfunction()

function (set_instrument_and_final_stage_var name value type)
# This sets the varaible for the final stage in non-PGO builds and in
# the stage2-instrumented stage for PGO builds.
set(BOOTSTRAP_${name} ${value} CACHE ${type} "")
if (LLVM_RELEASE_ENABLE_PGO)
# Set the variable in the final stage for PGO builds.
set(BOOTSTRAP_BOOTSTRAP_${name} ${value} CACHE ${type} "")
endif()
endfunction()

# General Options:
# If you want to override any of the LLVM_RELEASE_* variables you can set them
# on the command line via -D, but you need to do this before you pass this
# cache file to CMake via -C. e.g.
#
# cmake -D LLVM_RELEASE_ENABLE_PGO=ON -C Release.cmake
set(LLVM_RELEASE_ENABLE_LTO THIN CACHE STRING "")
set(LLVM_RELEASE_ENABLE_PGO OFF CACHE BOOL "")

set(LLVM_RELEASE_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "")
set(LLVM_RELEASE_ENABLE_PROJECTS "clang;lld;lldb;clang-tools-extra;bolt;polly;mlir;flang" CACHE STRING "")
# Note we don't need to add install here, since it is one of the pre-defined
# steps.
set(LLVM_RELEASE_FINAL_STAGE_TARGETS "clang;package;check-all;check-llvm;check-clang" CACHE STRING "")
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")

# Stage 1 Bootstrap Setup
# Stage 1 Options
set(LLVM_TARGETS_TO_BUILD Native CACHE STRING "")
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")

set(STAGE1_PROJECTS "clang")
set(STAGE1_RUNTIMES "")

if (LLVM_RELEASE_ENABLE_PGO)
list(APPEND STAGE1_PROJECTS "lld")
list(APPEND STAGE1_RUNTIMES "compiler-rt")
set(CLANG_BOOTSTRAP_TARGETS
generate-profdata
stage2
stage2-package
stage2-clang
stage2-distribution
stage2-install
stage2-install-distribution
stage2-install-distribution-toolchain
stage2-check-all
stage2-check-llvm
stage2-check-clang
stage2-test-suite CACHE STRING "")
else()
set(CLANG_BOOTSTRAP_TARGETS
clang
check-all
check-llvm
check-clang
test-suite
stage3
stage3-clang
stage3-check-all
stage3-check-llvm
stage3-check-clang
stage3-install
stage3-test-suite CACHE STRING "")
endif()
stage2-check-clang CACHE STRING "")

# Stage 1 Options
set(STAGE1_PROJECTS "clang")
set(STAGE1_RUNTIMES "")
# Configuration for stage2-instrumented
set(BOOTSTRAP_CLANG_ENABLE_BOOTSTRAP ON CACHE STRING "")
# This enables the build targets for the final stage which is called stage2.
set(BOOTSTRAP_CLANG_BOOTSTRAP_TARGETS ${LLVM_RELEASE_FINAL_STAGE_TARGETS} CACHE STRING "")
set(BOOTSTRAP_LLVM_BUILD_INSTRUMENTED IR CACHE STRING "")
set(BOOTSTRAP_LLVM_ENABLE_RUNTIMES "compiler-rt" CACHE STRING "")
set(BOOTSTRAP_LLVM_ENABLE_PROJECTS "clang;lld" CACHE STRING "")

if (LLVM_RELEASE_ENABLE_PGO)
list(APPEND STAGE1_PROJECTS "lld")
list(APPEND STAGE1_RUNTIMES "compiler-rt")
else()
if (LLVM_RELEASE_ENABLE_LTO)
list(APPEND STAGE1_PROJECTS "lld")
endif()
# Any targets added here will be given the target name stage2-${target}, so
# if you want to run them you can just use:
# ninja -C $BUILDDIR stage2-${target}
set(CLANG_BOOTSTRAP_TARGETS ${LLVM_RELEASE_FINAL_STAGE_TARGETS} CACHE STRING "")
endif()

# Stage 1 Common Config
set(LLVM_ENABLE_RUNTIMES ${STAGE1_RUNTIMES} CACHE STRING "")
set(LLVM_ENABLE_PROJECTS ${STAGE1_PROJECTS} CACHE STRING "")

set(LLVM_TARGETS_TO_BUILD Native CACHE STRING "")

# Stage 2 Bootstrap Setup
set(BOOTSTRAP_CLANG_ENABLE_BOOTSTRAP ON CACHE STRING "")
set(BOOTSTRAP_CLANG_BOOTSTRAP_TARGETS
clang
check-all
check-llvm
check-clang CACHE STRING "")

# Stage 2 Options
set(STAGE2_PROJECTS "clang")
set(STAGE2_RUNTIMES "")

if (LLVM_RELEASE_ENABLE_LTO OR LLVM_RELEASE_ENABLE_PGO)
list(APPEND STAGE2_PROJECTS "lld")
endif()

if (LLVM_RELEASE_ENABLE_PGO)
set(BOOTSTRAP_LLVM_BUILD_INSTRUMENTED IR CACHE STRING "")
list(APPEND STAGE2_RUNTIMES "compiler-rt")
set(BOOTSTRAP_LLVM_ENABLE_LTO ${LLVM_RELEASE_ENABLE_LTO})
if (LLVM_RELEASE_ENABLE_LTO)
set(BOOTSTRAP_LLVM_ENABLE_LLD ON CACHE BOOL "")
endif()
# stage2-instrumented and Final Stage Config:
# Options that need to be set in both the instrumented stage (if we are doing
# a pgo build) and the final stage.
set_instrument_and_final_stage_var(LLVM_ENABLE_LTO "${LLVM_RELEASE_ENABLE_LTO}" STRING)
if (LLVM_RELEASE_ENABLE_LTO)
set_instrument_and_final_stage_var(LLVM_ENABLE_LLD "ON" BOOL)
endif()

set(BOOTSTRAP_LLVM_ENABLE_PROJECTS ${STAGE2_PROJECTS} CACHE STRING "")
set(BOOTSTRAP_LLVM_ENABLE_RUNTIMES ${STAGE2_RUNTIMES} CACHE STRING "")
if (NOT LLVM_RELEASE_ENABLE_PGO)
set(BOOTSTRAP_LLVM_TARGETS_TO_BUILD Native CACHE STRING "")
endif()
# Final Stage Config (stage2)
set_final_stage_var(LLVM_ENABLE_RUNTIMES "${LLVM_RELEASE_ENABLE_RUNTIMES}" STRING)
set_final_stage_var(LLVM_ENABLE_PROJECTS "${LLVM_RELEASE_ENABLE_PROJECTS}" STRING)

# Stage 3 Options
set(BOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "")
set(BOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_PROJECTS "clang;lld;lldb;clang-tools-extra;bolt;polly;mlir;flang" CACHE STRING "")
set(BOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LTO ${LLVM_RELEASE_ENABLE_LTO} CACHE STRING "")
if (LLVM_RELEASE_ENABLE_LTO)
set(BOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LLD ON CACHE BOOL "")
endif()
2 changes: 2 additions & 0 deletions clang/cmake/caches/VectorEngine.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_CRT OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_XRAY OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_LIBFUZZER OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_CTX_PROFILE OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_PROFILE OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_MEMPROF OFF CACHE BOOL "")
set(RUNTIMES_x86_64-unknown-linux-gnu_COMPILER_RT_BUILD_ORC OFF CACHE BOOL "")
Expand All @@ -52,6 +53,7 @@ set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL ""
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_XRAY OFF CACHE BOOL "")
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_LIBFUZZER OFF CACHE BOOL "")
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_PROFILE ON CACHE BOOL "")
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_CTX_PROFILE OFF CACHE BOOL "")
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_MEMPROF OFF CACHE BOOL "")
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_ORC OFF CACHE BOOL "")
set(RUNTIMES_ve-unknown-linux-gnu_COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
Expand Down
5 changes: 4 additions & 1 deletion clang/docs/ClangOffloadBundler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -518,11 +518,14 @@ The compressed offload bundle begins with a header followed by the compressed bi
This is a unique identifier to distinguish compressed offload bundles. The value is the string 'CCOB' (Compressed Clang Offload Bundle).

- **Version Number (16-bit unsigned int)**:
This denotes the version of the compressed offload bundle format. The current version is `1`.
This denotes the version of the compressed offload bundle format. The current version is `2`.

- **Compression Method (16-bit unsigned int)**:
This field indicates the compression method used. The value corresponds to either `zlib` or `zstd`, represented as a 16-bit unsigned integer cast from the LLVM compression enumeration.

- **Total File Size (32-bit unsigned int)**:
This is the total size (in bytes) of the file, including the header. Available in version 2 and above.

- **Uncompressed Binary Size (32-bit unsigned int)**:
This is the size (in bytes) of the binary data before it was compressed.

Expand Down
25 changes: 24 additions & 1 deletion clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1642,7 +1642,8 @@ The following type trait primitives are supported by Clang. Those traits marked
were made trivially relocatable via the ``clang::trivial_abi`` attribute.
* ``__is_trivially_equality_comparable`` (Clang): Returns true if comparing two
objects of the provided type is known to be equivalent to comparing their
value representations.
object representations. Note that types containing padding bytes are never
trivially equality comparable.
* ``__is_unbounded_array`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_union`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_unsigned`` (C++, Embarcadero):
Expand Down Expand Up @@ -5571,3 +5572,25 @@ but the expression has no runtime effects.
Type- and value-dependent expressions are not supported yet.
This facility is designed to aid with testing name lookup machinery.
Predefined Macros
=================
`__GCC_DESTRUCTIVE_SIZE` and `__GCC_CONSTRUCTIVE_SIZE`
------------------------------------------------------
Specify the mimum offset between two objects to avoid false sharing and the
maximum size of contiguous memory to promote true sharing, respectively. These
macros are predefined in all C and C++ language modes, but can be redefined on
the command line with ``-D`` to specify different values as needed or can be
undefined on the command line with ``-U`` to disable support for the feature.
**Note: the values the macros expand to are not guaranteed to be stable. They
are are affected by architectures and CPU tuning flags, can change between
releases of Clang and will not match the values defined by other compilers such
as GCC.**
Compiling different TUs depending on these flags (including use of
``std::hardware_constructive_interference`` or
``std::hardware_destructive_interference``) with different compilers, macro
definitions, or architecture flags will lead to ODR violations and should be
avoided.
4 changes: 3 additions & 1 deletion clang/docs/OpenMPSupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,9 @@ implementation.
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | dispatch construct and function variant argument adjustment | :part:`worked on` | D99537, D99679 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | assume and assumes directives | :part:`worked on` | |
| misc | assumes directives | :part:`worked on` | |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | assume directive | :part:`worked on` | |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | nothing directive | :good:`done` | D123286 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
Expand Down
Loading