Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----- NondeterministicPointerIterationOrderCheck.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 "NondeterministicPointerIterationOrderCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

namespace clang::tidy::bugprone {

void NondeterministicPointerIterationOrderCheck::registerMatchers(
MatchFinder *Finder) {

auto LoopVariable = varDecl(hasType(
qualType(hasCanonicalType(anyOf(referenceType(), pointerType())))));

auto RangeInit = declRefExpr(to(varDecl(
hasType(recordDecl(hasAnyName("std::unordered_set", "std::unordered_map",
"std::unordered_multiset",
"std::unordered_multimap"))
.bind("recorddecl")))));

Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVariable),
hasRangeInit(RangeInit.bind("rangeinit")))
.bind("cxxForRangeStmt"),
this);

auto SortFuncM = callee(functionDecl(hasAnyName(
"std::is_sorted", "std::nth_element", "std::sort", "std::partial_sort",
"std::partition", "std::stable_partition", "std::stable_sort")));

auto IteratesPointerEltsM = hasArgument(
0,
cxxMemberCallExpr(on(hasType(cxxRecordDecl(has(fieldDecl(hasType(qualType(
hasCanonicalType(pointsTo(hasCanonicalType(pointerType()))))))))))));

Finder->addMatcher(
callExpr(allOf(SortFuncM, IteratesPointerEltsM)).bind("sortsemantic"),
this);
}

void NondeterministicPointerIterationOrderCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *ForRangePointers =
Result.Nodes.getNodeAs<CXXForRangeStmt>("cxxForRangeStmt");

if ((ForRangePointers) && !(ForRangePointers->getBeginLoc().isMacroID())) {
const auto *RangeInit = Result.Nodes.getNodeAs<Stmt>("rangeinit");
if (const auto *ClassTemplate =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(
"recorddecl")) {
const TemplateArgumentList &TemplateArgs =
ClassTemplate->getTemplateArgs();
const bool IsAlgoArgPointer =
TemplateArgs[0].getAsType()->isPointerType();

if (IsAlgoArgPointer) {
SourceRange R = RangeInit->getSourceRange();
diag(R.getBegin(), "iteration of pointers is nondeterministic") << R;
}
}
return;
}
const auto *SortPointers = Result.Nodes.getNodeAs<Stmt>("sortsemantic");

if ((SortPointers) && !(SortPointers->getBeginLoc().isMacroID())) {
SourceRange R = SortPointers->getSourceRange();
diag(R.getBegin(), "sorting pointers is nondeterministic") << R;
}
}

} // namespace clang::tidy::bugprone
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//=== NondeterministicPointerIterationOrderCheck.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_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::bugprone {

/// Finds nondeterministic usages of pointers in unordered containers. The
/// check also finds calls to sorting-like algorithms on a container of
/// pointers.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.html
class NondeterministicPointerIterationOrderCheck : public ClangTidyCheck {
public:
NondeterministicPointerIterationOrderCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
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::bugprone

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ void ProTypeUnionAccessCheck::registerMatchers(MatchFinder *Finder) {

void ProTypeUnionAccessCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Matched = Result.Nodes.getNodeAs<MemberExpr>("expr");
diag(Matched->getMemberLoc(),
"do not access members of unions; use (boost::)variant instead");
SourceLocation Loc = Matched->getMemberLoc();
if (Loc.isInvalid())
Loc = Matched->getBeginLoc();
diag(Loc, "do not access members of unions; consider using (boost::)variant "
"instead");
}

} // namespace clang::tidy::cppcoreguidelines
10 changes: 5 additions & 5 deletions clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,25 +172,25 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {

using namespace utils::fixit;
if (VC == VariableCategory::Value && TransformValues) {
Diag << addQualifierToVarDecl(*Variable, *Result.Context,
DeclSpec::TQ_const, QualifierTarget::Value,
Diag << addQualifierToVarDecl(*Variable, *Result.Context, Qualifiers::Const,
QualifierTarget::Value,
QualifierPolicy::Right);
// FIXME: Add '{}' for default initialization if no user-defined default
// constructor exists and there is no initializer.
return;
}

if (VC == VariableCategory::Reference && TransformReferences) {
Diag << addQualifierToVarDecl(*Variable, *Result.Context,
DeclSpec::TQ_const, QualifierTarget::Value,
Diag << addQualifierToVarDecl(*Variable, *Result.Context, Qualifiers::Const,
QualifierTarget::Value,
QualifierPolicy::Right);
return;
}

if (VC == VariableCategory::Pointer) {
if (WarnPointersAsValues && TransformPointersAsValues) {
Diag << addQualifierToVarDecl(*Variable, *Result.Context,
DeclSpec::TQ_const, QualifierTarget::Value,
Qualifiers::Const, QualifierTarget::Value,
QualifierPolicy::Right);
}
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,13 @@ unsigned getNumberOfDesignated(const InitListExpr *SyntacticInitList) {
});
}

AST_MATCHER(CXXRecordDecl, isAggregate) { return Node.isAggregate(); }
AST_MATCHER(CXXRecordDecl, isAggregate) {
return Node.hasDefinition() && Node.isAggregate();
}

AST_MATCHER(CXXRecordDecl, isPOD) { return Node.isPOD(); }
AST_MATCHER(CXXRecordDecl, isPOD) {
return Node.hasDefinition() && Node.isPOD();
}

AST_MATCHER(InitListExpr, isFullyDesignated) {
if (const InitListExpr *SyntacticForm =
Expand Down
82 changes: 38 additions & 44 deletions clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
#include "UseStartsEndsWithCheck.h"

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

#include <string>
Expand Down Expand Up @@ -82,60 +83,53 @@ UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
const auto ZeroLiteral = integerLiteral(equals(0));

const auto HasStartsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
.bind("starts_with_fun"));
const auto ClassTypeWithMethod = [](const StringRef MethodBoundName,
const auto... Methods) {
return cxxRecordDecl(anyOf(
hasMethod(cxxMethodDecl(isConst(), parameterCountIs(1),
returns(booleanType()), hasAnyName(Methods))
.bind(MethodBoundName))...));
};
const auto HasStartsWithMethod =
anyOf(HasStartsWithMethodWithName("starts_with"),
HasStartsWithMethodWithName("startsWith"),
HasStartsWithMethodWithName("startswith"));

const auto OnClassWithStartsWithFunction =
on(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasStartsWithMethod,
hasAnyBase(hasType(hasCanonicalType(
hasDeclaration(cxxRecordDecl(HasStartsWithMethod)))))))))));

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

const auto OnClassWithEndsWithFunction = ClassTypeWithMethod(
"ends_with_fun", "ends_with", "endsWith", "endswith", "EndsWith");

// Case 1: X.find(Y) [!=]= 0 -> starts_with.
const auto FindExpr = cxxMemberCallExpr(
anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
callee(cxxMethodDecl(hasName("find")).bind("find_fun")),
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));
callee(
cxxMethodDecl(hasName("find"), ofClass(OnClassWithStartsWithFunction))
.bind("find_fun")),
hasArgument(0, expr().bind("needle")));

// Case 2: X.rfind(Y, 0) [!=]= 0 -> starts_with.
const auto RFindExpr = cxxMemberCallExpr(
hasArgument(1, ZeroLiteral),
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));
callee(cxxMethodDecl(hasName("rfind"),
ofClass(OnClassWithStartsWithFunction))
.bind("find_fun")),
hasArgument(0, expr().bind("needle")));

// Case 3: X.compare(0, LEN(Y), Y) [!=]= 0 -> starts_with.
const auto CompareExpr = cxxMemberCallExpr(
argumentCountIs(3), hasArgument(0, ZeroLiteral),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
OnClassWithStartsWithFunction, hasArgument(2, expr().bind("needle")),
callee(cxxMethodDecl(hasName("compare"),
ofClass(OnClassWithStartsWithFunction))
.bind("find_fun")),
hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")));

// Case 4: X.compare(LEN(X) - LEN(Y), LEN(Y), Y) [!=]= 0 -> ends_with.
const auto CompareEndsWithExpr = cxxMemberCallExpr(
argumentCountIs(3),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
OnClassWithEndsWithFunction, hasArgument(2, expr().bind("needle")),
callee(cxxMethodDecl(hasName("compare"),
ofClass(OnClassWithEndsWithFunction))
.bind("find_fun")),
on(expr().bind("haystack")), hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")),
hasArgument(0,
binaryOperator(hasOperatorName("-"),
Expand All @@ -145,7 +139,7 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
// All cases comparing to 0.
Finder->addMatcher(
binaryOperator(
hasAnyOperatorName("==", "!="),
matchers::isEqualityOperator(),
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
CompareEndsWithExpr))
.bind("find_expr"),
Expand All @@ -156,7 +150,7 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
// Case 5: X.rfind(Y) [!=]= LEN(X) - LEN(Y) -> ends_with.
Finder->addMatcher(
binaryOperator(
hasAnyOperatorName("==", "!="),
matchers::isEqualityOperator(),
hasOperands(
cxxMemberCallExpr(
anyOf(
Expand All @@ -166,8 +160,10 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
1,
anyOf(declRefExpr(to(varDecl(hasName("npos")))),
memberExpr(member(hasName("npos"))))))),
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
OnClassWithEndsWithFunction,
callee(cxxMethodDecl(hasName("rfind"),
ofClass(OnClassWithEndsWithFunction))
.bind("find_fun")),
on(expr().bind("haystack")),
hasArgument(0, expr().bind("needle")))
.bind("find_expr"),
binaryOperator(hasOperatorName("-"),
Expand All @@ -190,9 +186,8 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const CXXMethodDecl *ReplacementFunction =
StartsWithFunction ? StartsWithFunction : EndsWithFunction;

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

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

Expand Down Expand Up @@ -220,9 +215,8 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
(ReplacementFunction->getName() + "(").str());

// Add possible negation '!'.
if (Neg) {
if (Neg)
Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(), "!");
}
}

} // namespace clang::tidy::modernize
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
<< utils::fixit::changeVarDeclToReference(LoopVar, Context);
if (!LoopVar.getType().isConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
LoopVar, Context, DeclSpec::TQ::TQ_const))
LoopVar, Context, Qualifiers::Const))
Diagnostic << *Fix;
}
return true;
Expand Down Expand Up @@ -129,7 +129,7 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
"making it a const reference");

if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
LoopVar, Context, DeclSpec::TQ::TQ_const))
LoopVar, Context, Qualifiers::Const))
Diag << *Fix << utils::fixit::changeVarDeclToReference(LoopVar, Context);

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void recordFixes(const VarDecl &Var, ASTContext &Context,
Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
if (!Var.getType().isLocalConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
Var, Context, DeclSpec::TQ::TQ_const))
Var, Context, Qualifiers::Const))
Diagnostic << *Fix;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ void UnnecessaryValueParamCheck::handleConstRefFix(const FunctionDecl &Function,
// declaration.
if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
CurrentParam, Context, DeclSpec::TQ::TQ_const))
CurrentParam, Context, Qualifiers::Const))
Diag << *Fix;
}
}
Expand Down
40 changes: 20 additions & 20 deletions clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,44 +62,44 @@ void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
.bind("positiveComparison"),
this);
AddSimpleMatcher(
binaryOperator(hasOperatorName("!="), hasOperands(CountCall, Literal0))
binaryOperation(hasOperatorName("!="), hasOperands(CountCall, Literal0))
.bind("positiveComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
binaryOperation(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
.bind("positiveComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
.bind("positiveComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(CountCall), hasOperatorName(">="), hasRHS(Literal1))
.bind("positiveComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(Literal1), hasOperatorName("<="), hasRHS(CountCall))
binaryOperation(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
.bind("positiveComparison"));
AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName(">="),
hasRHS(Literal1))
.bind("positiveComparison"));
AddSimpleMatcher(binaryOperation(hasLHS(Literal1), hasOperatorName("<="),
hasRHS(CountCall))
.bind("positiveComparison"));

// Find inverted membership tests which use `count()`.
AddSimpleMatcher(
binaryOperator(hasOperatorName("=="), hasOperands(CountCall, Literal0))
.bind("negativeComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(CountCall), hasOperatorName("<="), hasRHS(Literal0))
.bind("negativeComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(Literal0), hasOperatorName(">="), hasRHS(CountCall))
binaryOperation(hasOperatorName("=="), hasOperands(CountCall, Literal0))
.bind("negativeComparison"));
AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName("<="),
hasRHS(Literal0))
.bind("negativeComparison"));
AddSimpleMatcher(binaryOperation(hasLHS(Literal0), hasOperatorName(">="),
hasRHS(CountCall))
.bind("negativeComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
binaryOperation(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
.bind("negativeComparison"));
AddSimpleMatcher(
binaryOperator(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
binaryOperation(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
.bind("negativeComparison"));

// Find membership tests based on `find() == end()`.
AddSimpleMatcher(
binaryOperator(hasOperatorName("!="), hasOperands(FindCall, EndCall))
binaryOperation(hasOperatorName("!="), hasOperands(FindCall, EndCall))
.bind("positiveComparison"));
AddSimpleMatcher(
binaryOperator(hasOperatorName("=="), hasOperands(FindCall, EndCall))
binaryOperation(hasOperatorName("=="), hasOperands(FindCall, EndCall))
.bind("negativeComparison"));
}

Expand Down
24 changes: 16 additions & 8 deletions clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ AST_MATCHER(EnumDecl, hasSequentialInitialValues) {
return !AllEnumeratorsArePowersOfTwo;
}

std::string getName(const EnumDecl *Decl) {
if (!Decl->getDeclName())
return "<unnamed>";

return Decl->getQualifiedNameAsString();
}

} // namespace

EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name,
Expand Down Expand Up @@ -160,10 +167,11 @@ void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {
void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("inconsistent")) {
DiagnosticBuilder Diag =
diag(Enum->getBeginLoc(),
"initial values in enum %0 are not consistent, consider explicit "
"initialization of all, none or only the first enumerator")
<< Enum;
diag(
Enum->getBeginLoc(),
"initial values in enum '%0' are not consistent, consider explicit "
"initialization of all, none or only the first enumerator")
<< getName(Enum);
for (const EnumConstantDecl *ECD : Enum->enumerators())
if (ECD->getInitExpr() == nullptr) {
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Expand All @@ -183,16 +191,16 @@ void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (Loc.isInvalid() || Loc.isMacroID())
return;
DiagnosticBuilder Diag = diag(Loc, "zero initial value for the first "
"enumerator in %0 can be disregarded")
<< Enum;
"enumerator in '%0' can be disregarded")
<< getName(Enum);
cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());
return;
}
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("sequential")) {
DiagnosticBuilder Diag =
diag(Enum->getBeginLoc(),
"sequential initial value in %0 can be ignored")
<< Enum;
"sequential initial value in '%0' can be ignored")
<< getName(Enum);
for (const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))
cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../utils/FixItHintUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/FixIt.h"
#include <queue>
Expand All @@ -26,6 +27,8 @@ AST_MATCHER(Stmt, isMacroExpansion) {
return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
}

AST_MATCHER(Stmt, isC23) { return Finder->getASTContext().getLangOpts().C23; }

bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
SourceManager &SM = Context.getSourceManager();
const LangOptions &LO = Context.getLangOpts();
Expand Down Expand Up @@ -298,6 +301,11 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
hasCastKind(CK_FloatingToBoolean),
hasCastKind(CK_PointerToBoolean),
hasCastKind(CK_MemberPointerToBoolean)),
// Exclude cases of C23 comparison result.
unless(allOf(isC23(),
hasSourceExpression(ignoringParens(
binaryOperator(hasAnyOperatorName(
">", ">=", "==", "!=", "<", "<=")))))),
// Exclude case of using if or while statements with variable
// declaration, e.g.:
// if (int var = functionCall()) {}
Expand Down
19 changes: 11 additions & 8 deletions clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Tooling/FixIt.h"
#include <optional>

Expand Down Expand Up @@ -71,15 +72,17 @@ static std::optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,

// Build a string that can be emitted as FixIt with either a space in before
// or after the qualifier, either ' const' or 'const '.
static std::string buildQualifier(DeclSpec::TQ Qualifier,
static std::string buildQualifier(Qualifiers::TQ Qualifier,
bool WhitespaceBefore = false) {
if (WhitespaceBefore)
return (llvm::Twine(' ') + DeclSpec::getSpecifierName(Qualifier)).str();
return (llvm::Twine(DeclSpec::getSpecifierName(Qualifier)) + " ").str();
return (llvm::Twine(' ') + Qualifiers::fromCVRMask(Qualifier).getAsString())
.str();
return (llvm::Twine(Qualifiers::fromCVRMask(Qualifier).getAsString()) + " ")
.str();
}

static std::optional<FixItHint> changeValue(const VarDecl &Var,
DeclSpec::TQ Qualifier,
Qualifiers::TQ Qualifier,
QualifierTarget QualTarget,
QualifierPolicy QualPolicy,
const ASTContext &Context) {
Expand All @@ -99,7 +102,7 @@ static std::optional<FixItHint> changeValue(const VarDecl &Var,
}

static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
DeclSpec::TQ Qualifier,
Qualifiers::TQ Qualifier,
const ASTContext &Context) {
if (locDangerous(Var.getLocation()))
return std::nullopt;
Expand All @@ -112,7 +115,7 @@ static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
}

static std::optional<FixItHint>
changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
changePointer(const VarDecl &Var, Qualifiers::TQ Qualifier, const Type *Pointee,
QualifierTarget QualTarget, QualifierPolicy QualPolicy,
const ASTContext &Context) {
// The pointer itself shall be marked as `const`. This is always to the right
Expand Down Expand Up @@ -163,7 +166,7 @@ changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
}

static std::optional<FixItHint>
changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
changeReferencee(const VarDecl &Var, Qualifiers::TQ Qualifier, QualType Pointee,
QualifierTarget QualTarget, QualifierPolicy QualPolicy,
const ASTContext &Context) {
if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))
Expand All @@ -183,7 +186,7 @@ changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,

std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
const ASTContext &Context,
DeclSpec::TQ Qualifier,
Qualifiers::TQ Qualifier,
QualifierTarget QualTarget,
QualifierPolicy QualPolicy) {
assert((QualPolicy == QualifierPolicy::Left ||
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/AST/Type.h"
#include <optional>

namespace clang::tidy::utils::fixit {
Expand Down Expand Up @@ -41,7 +41,7 @@ enum class QualifierTarget {
/// Requires that `Var` is isolated in written code like in `int foo = 42;`.
std::optional<FixItHint>
addQualifierToVarDecl(const VarDecl &Var, const ASTContext &Context,
DeclSpec::TQ Qualifier,
Qualifiers::TQ Qualifier,
QualifierTarget QualTarget = QualifierTarget::Pointee,
QualifierPolicy QualPolicy = QualifierPolicy::Left);

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ struct Config {
bool DeducedTypes = true;
bool Designators = true;
bool BlockEnd = false;
bool DefaultArguments = false;
// Limit the length of type names in inlay hints. (0 means no limit)
uint32_t TypeNameLimit = 32;
} InlayHints;
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/clangd/ConfigCompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
#include "llvm/Support/Regex.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
Expand Down Expand Up @@ -669,6 +668,11 @@ struct FragmentCompiler {
Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config &C) {
C.InlayHints.BlockEnd = Value;
});
if (F.DefaultArguments)
Out.Apply.push_back(
[Value(**F.DefaultArguments)](const Params &, Config &C) {
C.InlayHints.DefaultArguments = Value;
});
if (F.TypeNameLimit)
Out.Apply.push_back(
[Value(**F.TypeNameLimit)](const Params &, Config &C) {
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ struct Fragment {
std::optional<Located<bool>> Designators;
/// Show defined symbol names at the end of a definition block.
std::optional<Located<bool>> BlockEnd;
/// Show parameter names and default values of default arguments after all
/// of the explicit arguments.
std::optional<Located<bool>> DefaultArguments;
/// Limit the length of type name hints. (0 means no limit)
std::optional<Located<uint32_t>> TypeNameLimit;
};
Expand Down
5 changes: 4 additions & 1 deletion clang-tools-extra/clangd/ConfigYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "llvm/Support/YAMLParser.h"
#include <optional>
#include <string>
#include <system_error>

namespace clang {
namespace clangd {
Expand Down Expand Up @@ -268,6 +267,10 @@ class Parser {
if (auto Value = boolValue(N, "BlockEnd"))
F.BlockEnd = *Value;
});
Dict.handle("DefaultArguments", [&](Node &N) {
if (auto Value = boolValue(N, "DefaultArguments"))
F.DefaultArguments = *Value;
});
Dict.handle("TypeNameLimit", [&](Node &N) {
if (auto Value = uint32Value(N, "TypeNameLimit"))
F.TypeNameLimit = *Value;
Expand Down
78 changes: 70 additions & 8 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
#include "Config.h"
#include "HeuristicResolver.h"
#include "ParsedAST.h"
#include "Protocol.h"
#include "SourceCode.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
Expand All @@ -23,15 +25,22 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <iterator>
#include <optional>
#include <string>

Expand Down Expand Up @@ -372,6 +381,23 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
return Params;
}

template <typename R>
std::string joinAndTruncate(const R &Range, size_t MaxLength) {
std::string Out;
llvm::raw_string_ostream OS(Out);
llvm::ListSeparator Sep(", ");
for (auto &&Element : Range) {
OS << Sep;
if (Out.size() + Element.size() >= MaxLength) {
OS << "...";
break;
}
OS << Element;
}
OS.flush();
return Out;
}

struct Callee {
// Only one of Decl or Loc is set.
// Loc is for calls through function pointers.
Expand Down Expand Up @@ -422,7 +448,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
Callee.Decl = E->getConstructor();
if (!Callee.Decl)
return true;
processCall(Callee, {E->getArgs(), E->getNumArgs()});
processCall(Callee, E->getParenOrBraceRange().getEnd(),
{E->getArgs(), E->getNumArgs()});
return true;
}

Expand Down Expand Up @@ -495,7 +522,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
dyn_cast_or_null<CXXMethodDecl>(Callee.Decl))
if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter())
Args = Args.drop_front(1);
processCall(Callee, Args);
processCall(Callee, E->getRParenLoc(), Args);
return true;
}

Expand Down Expand Up @@ -709,10 +736,12 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
private:
using NameVec = SmallVector<StringRef, 8>;

void processCall(Callee Callee, llvm::ArrayRef<const Expr *> Args) {
void processCall(Callee Callee, SourceLocation RParenOrBraceLoc,
llvm::ArrayRef<const Expr *> Args) {
assert(Callee.Decl || Callee.Loc);

if (!Cfg.InlayHints.Parameters || Args.size() == 0)
if ((!Cfg.InlayHints.Parameters && !Cfg.InlayHints.DefaultArguments) ||
Args.size() == 0)
return;

// The parameter name of a move or copy constructor is not very interesting.
Expand All @@ -721,6 +750,9 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
if (Ctor->isCopyOrMoveConstructor())
return;

SmallVector<std::string> FormattedDefaultArgs;
bool HasNonDefaultArgs = false;

ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
// Resolve parameter packs to their forwarded parameter
SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
Expand Down Expand Up @@ -752,15 +784,44 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
}

StringRef Name = ParameterNames[I];
bool NameHint = shouldHintName(Args[I], Name);
bool ReferenceHint = shouldHintReference(Params[I], ForwardedParams[I]);

if (NameHint || ReferenceHint) {
const bool NameHint =
shouldHintName(Args[I], Name) && Cfg.InlayHints.Parameters;
const bool ReferenceHint =
shouldHintReference(Params[I], ForwardedParams[I]) &&
Cfg.InlayHints.Parameters;

const bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]);
HasNonDefaultArgs |= !IsDefault;
if (IsDefault) {
if (Cfg.InlayHints.DefaultArguments) {
const auto SourceText = Lexer::getSourceText(
CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()),
AST.getSourceManager(), AST.getLangOpts());
const auto Abbrev =
(SourceText.size() > Cfg.InlayHints.TypeNameLimit ||
SourceText.contains("\n"))
? "..."
: SourceText;
if (NameHint)
FormattedDefaultArgs.emplace_back(
llvm::formatv("{0}: {1}", Name, Abbrev));
else
FormattedDefaultArgs.emplace_back(llvm::formatv("{0}", Abbrev));
}
} else if (NameHint || ReferenceHint) {
addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
InlayHintKind::Parameter, ReferenceHint ? "&" : "",
NameHint ? Name : "", ": ");
}
}

if (!FormattedDefaultArgs.empty()) {
std::string Hint =
joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit);
addInlayHint(SourceRange{RParenOrBraceLoc}, HintSide::Left,
InlayHintKind::DefaultArgument,
HasNonDefaultArgs ? ", " : "", Hint, "");
}
}

static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) {
Expand Down Expand Up @@ -968,6 +1029,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
CHECK_KIND(Type, DeducedTypes);
CHECK_KIND(Designator, Designators);
CHECK_KIND(BlockEnd, BlockEnd);
CHECK_KIND(DefaultArgument, DefaultArguments);
#undef CHECK_KIND
}

Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,7 @@ llvm::json::Value toJSON(const InlayHintKind &Kind) {
return 2;
case InlayHintKind::Designator:
case InlayHintKind::BlockEnd:
case InlayHintKind::DefaultArgument:
// This is an extension, don't serialize.
return nullptr;
}
Expand Down Expand Up @@ -1517,6 +1518,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
return "designator";
case InlayHintKind::BlockEnd:
return "block-end";
case InlayHintKind::DefaultArgument:
return "default-argument";
}
llvm_unreachable("Unknown clang.clangd.InlayHintKind");
};
Expand Down
9 changes: 9 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,15 @@ enum class InlayHintKind {
/// This is a clangd extension.
BlockEnd = 4,

/// An inlay hint that is for a default argument.
///
/// An example of a parameter hint for a default argument:
/// void foo(bool A = true);
/// foo(^);
/// Adds an inlay hint "A: true".
/// This is a clangd extension.
DefaultArgument = 6,

/// Other ideas for hints that are not currently implemented:
///
/// * Chaining hints, showing the types of intermediate expressions
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/URI.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,8 @@ typedef llvm::Registry<URIScheme> URISchemeRegistry;
} // namespace clangd
} // namespace clang

namespace llvm {
extern template class Registry<clang::clangd::URIScheme>;
} // namespace llvm

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_URI_H
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/refactor/Tweak.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,8 @@ prepareTweak(StringRef ID, const Tweak::Selection &S,
} // namespace clangd
} // namespace clang

namespace llvm {
extern template class Registry<clang::clangd::Tweak>;
} // namespace llvm

#endif
3 changes: 0 additions & 3 deletions clang-tools-extra/clangd/test/.gitattributes

This file was deleted.

34 changes: 17 additions & 17 deletions clang-tools-extra/clangd/test/input-mirror.test
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# RUN: clangd -pretty -sync -input-mirror-file %t < %s
# Note that we have to use '-b' as -input-mirror-file does not have a newline at the end of file.
# RUN: diff -b %t %s
# It is absolutely vital that this file has CRLF line endings.
#
Content-Length: 125

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
Content-Length: 172

{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Content-Length: 33

{"jsonrpc":"2.0","method":"exit"}
# RUN: clangd -pretty -sync -input-mirror-file %t < %s
# Note that we have to use '-b' as -input-mirror-file does not have a newline at the end of file.
# RUN: diff -b %t %s
# It is absolutely vital that this file has CRLF line endings.
#
Content-Length: 125
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
Content-Length: 172
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}
Content-Length: 44
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Content-Length: 33
{"jsonrpc":"2.0","method":"exit"}
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/test/log.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: env CLANGD_FLAGS=-compile-commands-dir=no-such-dir not clangd -lit-test </dev/null 2>&1 >/dev/null | FileCheck %s
CHECK: I[{{.*}}]{{.*}} clangd version {{.*}}
CHECK: Working directory: {{.*}}
CHECK: argv[0]: clangd
CHECK: argv[0]: {{.*}}clangd
CHECK: argv[1]: -lit-test
CHECK: CLANGD_FLAGS: -compile-commands-dir=no-such-dir
CHECK: E[{{.*}}] Path specified by --compile-commands-dir does not exist.
Expand Down
226 changes: 113 additions & 113 deletions clang-tools-extra/clangd/test/protocol.test
Original file line number Diff line number Diff line change
@@ -1,113 +1,113 @@
# RUN: not clangd -pretty -sync -enable-test-uri-scheme < %s | FileCheck -strict-whitespace %s
# RUN: not clangd -pretty -sync -enable-test-uri-scheme < %s 2>&1 | FileCheck -check-prefix=STDERR %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
#
# Note that we invert the test because we intent to let clangd exit prematurely.
#
# Test protocol parsing
Content-Length: 125
Content-Type: application/vscode-jsonrpc; charset-utf-8

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
# Test message with Content-Type after Content-Length
#
# CHECK: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK: }
Content-Length: 246

{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n fake f;\n f.\n}\n"}}}

Content-Length: 104

{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///main.cpp"}}}

Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 146

{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with Content-Type before Content-Length
#
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "isIncomplete": false,
# CHECK-NEXT: "items": [
# CHECK: "filterText": "a",
# CHECK-NEXT: "insertText": "a",
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }

X-Test: Testing
Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 146
Content-Type: application/vscode-jsonrpc; charset-utf-8
X-Testing: Test

{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}

Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 10
Content-Length: 146

{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with duplicate Content-Length headers
#
# CHECK: "id": 3,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "isIncomplete": false,
# CHECK-NEXT: "items": [
# CHECK: "filterText": "a",
# CHECK-NEXT: "insertText": "a",
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
# STDERR: Warning: Duplicate Content-Length header received. The previous value for this message (10) was ignored.

Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 10

{"jsonrpc":"2.0","id":4,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with malformed Content-Length
#
# STDERR: JSON parse error
# Ensure we recover by sending another (valid) message

Content-Length: 146

{"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with Content-Type before Content-Length
#
# CHECK: "id": 5,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "isIncomplete": false,
# CHECK-NEXT: "items": [
# CHECK: "filterText": "a",
# CHECK-NEXT: "insertText": "a",
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
Content-Length: 1024

{"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message which reads beyond the end of the stream.
#
# Ensure this is the last test in the file!
# STDERR: Input was aborted. Read only {{[0-9]+}} bytes of expected {{[0-9]+}}.

# RUN: not clangd -pretty -sync -enable-test-uri-scheme < %s | FileCheck -strict-whitespace %s
# RUN: not clangd -pretty -sync -enable-test-uri-scheme < %s 2>&1 | FileCheck -check-prefix=STDERR %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
#
# Note that we invert the test because we intent to let clangd exit prematurely.
#
# Test protocol parsing
Content-Length: 125
Content-Type: application/vscode-jsonrpc; charset-utf-8
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
# Test message with Content-Type after Content-Length
#
# CHECK: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK: }
Content-Length: 246
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n fake f;\n f.\n}\n"}}}
Content-Length: 104
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///main.cpp"}}}
Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 146
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with Content-Type before Content-Length
#
# CHECK: "id": 1,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "isIncomplete": false,
# CHECK-NEXT: "items": [
# CHECK: "filterText": "a",
# CHECK-NEXT: "insertText": "a",
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
X-Test: Testing
Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 146
Content-Type: application/vscode-jsonrpc; charset-utf-8
X-Testing: Test
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 10
Content-Length: 146
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with duplicate Content-Length headers
#
# CHECK: "id": 3,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "isIncomplete": false,
# CHECK-NEXT: "items": [
# CHECK: "filterText": "a",
# CHECK-NEXT: "insertText": "a",
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
# STDERR: Warning: Duplicate Content-Length header received. The previous value for this message (10) was ignored.
Content-Type: application/vscode-jsonrpc; charset-utf-8
Content-Length: 10
{"jsonrpc":"2.0","id":4,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with malformed Content-Length
#
# STDERR: JSON parse error
# Ensure we recover by sending another (valid) message
Content-Length: 146
{"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message with Content-Type before Content-Length
#
# CHECK: "id": 5,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": {
# CHECK-NEXT: "isIncomplete": false,
# CHECK-NEXT: "items": [
# CHECK: "filterText": "a",
# CHECK-NEXT: "insertText": "a",
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
Content-Length: 1024
{"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:/main.cpp"},"position":{"line":3,"character":5}}}
# Test message which reads beyond the end of the stream.
#
# Ensure this is the last test in the file!
# STDERR: Input was aborted. Read only {{[0-9]+}} bytes of expected {{[0-9]+}}.
14 changes: 7 additions & 7 deletions clang-tools-extra/clangd/test/too_large.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: not clangd -sync < %s 2>&1 | FileCheck -check-prefix=STDERR %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
#
Content-Length: 2147483648

# STDERR: Refusing to read message
# RUN: not clangd -sync < %s 2>&1 | FileCheck -check-prefix=STDERR %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
#
Content-Length: 2147483648
# STDERR: Refusing to read message
73 changes: 73 additions & 0 deletions clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
#include "support/Context.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
#include <string>
#include <utility>
#include <vector>

namespace clang {
Expand Down Expand Up @@ -81,6 +84,7 @@ Config noHintsConfig() {
C.InlayHints.DeducedTypes = false;
C.InlayHints.Designators = false;
C.InlayHints.BlockEnd = false;
C.InlayHints.DefaultArguments = false;
return C;
}

Expand Down Expand Up @@ -1465,6 +1469,75 @@ TEST(TypeHints, DefaultTemplateArgs) {
ExpectedHint{": A<float>", "binding"});
}

TEST(DefaultArguments, Smoke) {
Config Cfg;
Cfg.InlayHints.Parameters =
true; // To test interplay of parameters and default parameters
Cfg.InlayHints.DeducedTypes = false;
Cfg.InlayHints.Designators = false;
Cfg.InlayHints.BlockEnd = false;

Cfg.InlayHints.DefaultArguments = true;
WithContextValue WithCfg(Config::Key, std::move(Cfg));

const auto *Code = R"cpp(
int foo(int A = 4) { return A; }
int bar(int A, int B = 1, bool C = foo($default1[[)]]) { return A; }
int A = bar($explicit[[2]]$default2[[)]];
void baz(int = 5) { if (false) baz($unnamed[[)]]; };
)cpp";

assertHints(InlayHintKind::DefaultArgument, Code,
ExpectedHint{"A: 4", "default1", Left},
ExpectedHint{", B: 1, C: foo()", "default2", Left},
ExpectedHint{"5", "unnamed", Left});

assertHints(InlayHintKind::Parameter, Code,
ExpectedHint{"A: ", "explicit", Left});
}

TEST(DefaultArguments, WithoutParameterNames) {
Config Cfg;
Cfg.InlayHints.Parameters = false; // To test just default args this time
Cfg.InlayHints.DeducedTypes = false;
Cfg.InlayHints.Designators = false;
Cfg.InlayHints.BlockEnd = false;

Cfg.InlayHints.DefaultArguments = true;
WithContextValue WithCfg(Config::Key, std::move(Cfg));

const auto *Code = R"cpp(
struct Baz {
Baz(float a = 3 //
+ 2);
};
struct Foo {
Foo(int, Baz baz = //
Baz{$abbreviated[[}]]
//
) {}
};
int main() {
Foo foo1(1$paren[[)]];
Foo foo2{2$brace1[[}]];
Foo foo3 = {3$brace2[[}]];
auto foo4 = Foo{4$brace3[[}]];
}
)cpp";

assertHints(InlayHintKind::DefaultArgument, Code,
ExpectedHint{"...", "abbreviated", Left},
ExpectedHint{", Baz{}", "paren", Left},
ExpectedHint{", Baz{}", "brace1", Left},
ExpectedHint{", Baz{}", "brace2", Left},
ExpectedHint{", Baz{}", "brace3", Left});

assertHints(InlayHintKind::Parameter, Code);
}

TEST(TypeHints, Deduplication) {
assertTypeHints(R"cpp(
template <typename T>
Expand Down
25 changes: 22 additions & 3 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Improvements to clangd
Inlay hints
^^^^^^^^^^^

- Added `DefaultArguments` Inlay Hints option.

Diagnostics
^^^^^^^^^^^

Expand Down Expand Up @@ -117,6 +119,12 @@ New checks
Warns about code that tries to cast between pointers by means of
``std::bit_cast`` or ``memcpy``.

- New :doc:`bugprone-nondeterministic-pointer-iteration-order
<clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order>`
check.

Finds nondeterministic usages of pointers in unordered containers.

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

Expand Down Expand Up @@ -178,6 +186,10 @@ Changes in existing checks
avoid false positive when member initialization depends on a structured
binding variable.

- Fixed :doc:`cppcoreguidelines-pro-type-union-access
<clang-tidy/checks/cppcoreguidelines/pro-type-union-access>` check to
report a location even when the member location is not valid.

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by rewording the
diagnostic note that suggests adding ``inline``.
Expand All @@ -204,6 +216,10 @@ Changes in existing checks
a false positive when only an implicit conversion happened inside an
initializer list.

- Improved :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check to fix a
crash when a class is declared but not defined.

- Improved :doc:`modernize-use-nullptr
<clang-tidy/checks/modernize/use-nullptr>` check to also recognize
``NULL``/``__null`` (but not ``0``) when used with a templated type.
Expand Down Expand Up @@ -232,17 +248,20 @@ Changes in existing checks

- Improved :doc:`readability-container-contains
<clang-tidy/checks/readability/container-contains>` check to let it work on
any class that has a ``contains`` method.
any class that has a ``contains`` method. Fix some false negatives in the
``find()`` case.

- Improved :doc:`readability-enum-initial-value
<clang-tidy/checks/readability/enum-initial-value>` check by only issuing
diagnostics for the definition of an ``enum``, and by fixing a typo in the
diagnostics for the definition of an ``enum``, by not emitting a redundant
file path for anonymous enums in the diagnostic, and by fixing a typo in the
diagnostic.

- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check
by adding the option `UseUpperCaseLiteralSuffix` to select the
case of the literal suffix in fixes.
case of the literal suffix in fixes and fixing false positive for implicit
conversion of comparison result in C23.

- Improved :doc:`readability-redundant-smartptr-get
<clang-tidy/checks/readability/redundant-smartptr-get>` check to
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.. title:: clang-tidy - bugprone-nondeterministic-pointer-iteration-order

bugprone-nondeterministic-pointer-iteration-order
=================================================

Finds nondeterministic usages of pointers in unordered containers.

One canonical example is iteration across a container of pointers.

.. code-block:: c++

{
int a = 1, b = 2;
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
for (auto i : UnorderedPtrSet)
f(i);
}
Another such example is sorting a container of pointers.

.. code-block:: c++

{
int a = 1, b = 2;
std::vector<int *> VectorOfPtr = {&a, &b};
std::sort(VectorOfPtr.begin(), VectorOfPtr.end());
}
Iteration of a containers of pointers may present the order of different
pointers differently across different runs of a program. In some cases this
may be acceptable behavior, in others this may be unexpected behavior. This
check is advisory for this reason.

This check only detects range-based for loops over unordered sets and maps. It
also detects calls sorting-like algorithms on containers holding pointers.
Other similar usages will not be found and are false negatives.

Limitations:

* This check currently does not check if a nondeterministic iteration order is
likely to be a mistake, and instead marks all such iterations as bugprone.
* std::reference_wrapper is not considered yet.
* Only for loops are considered, other iterators can be included in
improvements.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ For example:
}
}
Exception: accessor methods
```````````````````````````

The check assumes *accessor* methods of a class are stable, with a heuristic to
determine which methods are accessors. Specifically, parameter-free ``const``
methods are treated as accessors. Note that this is not guaranteed to be safe
-- but, it is widely used (safely) in practice, and so we have chosen to treat
it as generally safe. Calls to non ``const`` methods are assumed to modify
the state of the object and affect the stability of earlier accessor calls.

Rely on invariants of uncommon APIs
-----------------------------------

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Clang-Tidy Checks
:doc:`bugprone-multiple-new-in-one-expression <bugprone/multiple-new-in-one-expression>`,
:doc:`bugprone-multiple-statement-macro <bugprone/multiple-statement-macro>`,
:doc:`bugprone-no-escape <bugprone/no-escape>`,
:doc:`bugprone-nondeterministic-pointer-iteration-order <bugprone/nondeterministic-pointer-iteration-order>`,
:doc:`bugprone-non-zero-enum-to-bool-conversion <bugprone/non-zero-enum-to-bool-conversion>`,
:doc:`bugprone-not-null-terminated-result <bugprone/not-null-terminated-result>`, "Yes"
:doc:`bugprone-optional-value-conversion <bugprone/optional-value-conversion>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ using IncludeSpellingStrategy = llvm::Registry<IncludeSpeller>;
std::string spellHeader(const IncludeSpeller::Input &Input);
} // namespace clang::include_cleaner

namespace llvm {
extern template class Registry<clang::include_cleaner::IncludeSpeller>;
} // namespace llvm

#endif
7 changes: 3 additions & 4 deletions clang-tools-extra/modularize/CoverageChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,9 @@ bool CoverageChecker::collectModuleHeaders(const Module &Mod) {
return false;
}

for (auto &HeaderKind : Mod.Headers)
for (auto &Header : HeaderKind)
ModuleMapHeadersSet.insert(
ModularizeUtilities::getCanonicalPath(Header.Entry.getName()));
for (const auto &Header : Mod.getAllHeaders())
ModuleMapHeadersSet.insert(
ModularizeUtilities::getCanonicalPath(Header.Entry.getName()));

for (auto *Submodule : Mod.submodules())
collectModuleHeaders(*Submodule);
Expand Down
14 changes: 3 additions & 11 deletions clang-tools-extra/modularize/ModularizeUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) {
} else if (std::optional<clang::Module::DirectoryName> UmbrellaDir =
Mod.getUmbrellaDirAsWritten()) {
// If there normal headers, assume these are umbrellas and skip collection.
if (Mod.Headers->size() == 0) {
if (Mod.getHeaders(Module::HK_Normal).empty()) {
// Collect headers in umbrella directory.
if (!collectUmbrellaHeaders(UmbrellaDir->Entry.getName(),
UmbrellaDependents))
Expand All @@ -371,16 +371,8 @@ bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) {
// modules or because they are meant to be included by another header,
// and thus should be ignored by modularize.

int NormalHeaderCount = Mod.Headers[clang::Module::HK_Normal].size();

for (int Index = 0; Index < NormalHeaderCount; ++Index) {
DependentsVector NormalDependents;
// Collect normal header.
const clang::Module::Header &Header(
Mod.Headers[clang::Module::HK_Normal][Index]);
std::string HeaderPath = getCanonicalPath(Header.Entry.getName());
HeaderFileNames.push_back(HeaderPath);
}
for (const auto &Header : Mod.getHeaders(clang::Module::HK_Normal))
HeaderFileNames.push_back(getCanonicalPath(Header.Entry.getName()));

int MissingCountThisModule = Mod.MissingHeaders.size();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: not clang-query --invalid-arg 2>&1 | FileCheck %s

// CHECK: error: clang-query{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-query{{(\.exe)?}} --help'
// CHECK: error: clang-query{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: '{{.*}}clang-query{{(\.exe)?}} --help'
// CHECK-NEXT: clang-query{{(\.exe)?}}: Did you mean '--extra-arg'?
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef _SIM_ALGORITHM
#define _SIM_ALGORITHM

#pragma clang system_header

namespace std {

template<class ForwardIt>
bool is_sorted(ForwardIt first, ForwardIt last);

template <class RandomIt>
void nth_element(RandomIt first, RandomIt nth, RandomIt last);

template<class RandomIt>
void partial_sort(RandomIt first, RandomIt middle, RandomIt last);

template<class RandomIt>
void sort (RandomIt first, RandomIt last);

template<class RandomIt>
void stable_sort(RandomIt first, RandomIt last);

template<class BidirIt, class UnaryPredicate>
BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p);

template<class BidirIt, class UnaryPredicate>
BidirIt stable_partition(BidirIt first, BidirIt last, UnaryPredicate p);

} // namespace std

#endif // _SIM_ALGORITHM
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef _SIM_CPP_CONFIG_H
#define _SIM_CPP_CONFIG_H

#pragma clang system_header

typedef unsigned char uint8_t;

typedef __typeof__(sizeof(int)) size_t;
typedef __typeof__((char*)0-(char*)0) ptrdiff_t;

#endif // _SIM_CPP_CONFIG_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef _INITIALIZER_LIST
#define _INITIALIZER_LIST

#pragma clang system_header
#
#include "sim_c++config.h" // size_t

namespace std {

template <class _E>
class initializer_list {
const _E* __begin_;
size_t __size_;

initializer_list(const _E* __b, size_t __s)
: __begin_(__b),
__size_(__s)
{}

public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;

typedef const _E* iterator;
typedef const _E* const_iterator;

initializer_list() : __begin_(0), __size_(0) {}

size_t size() const {return __size_;}
const _E* begin() const {return __begin_;}
const _E* end() const {return __begin_ + __size_;}

}; // class initializer_list

} // namespace std

#endif // _INITIALIZER_LIST
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef _SIM_ITERATOR_BASE
#define _SIM_ITERATOR_BASE

namespace std {

struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };

template <typename Iterator> struct iterator_traits {
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::iterator_category iterator_category;
};

} // namespace std

#endif // _SIM_ITERATOR_BASE
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

#ifndef _SIM_MAP
#define _SIM_MAP

#pragma clang system_header
#include "sim_stl_pair"

namespace std {

template <typename Key, typename Value>
class map {
public:
using value_type = pair<Key, Value>;
map();
map(initializer_list<pair<Key, Value>> initList);
value_type& operator[](const Key& key);
value_type& operator[](Key&& key);
class iterator {
public:
iterator(Key *key): ptr(key) {}
iterator& operator++() { ++ptr; return *this; }
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
const Key &operator*() const { return *ptr; }
private:
Key *ptr;
};
Key *val;
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + 1); }
};

} // namespace std

#endif // _SIM_MAP
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

#ifndef _SIM_SET
#define _SIM_SET

#pragma clang system_header
#include "sim_initializer_list"

namespace std {

template< class T = void >
struct less;

template< class T >
struct allocator;

template< class Key >
struct hash;

template<
class Key,
class Compare = std::less<Key>,
class Alloc = std::allocator<Key>
> class set {
public:
set(initializer_list<Key> __list) {}

class iterator {
public:
iterator(Key *key): ptr(key) {}
iterator& operator++() { ++ptr; return *this; }
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
const Key &operator*() const { return *ptr; }
private:
Key *ptr;
};

Key *val;
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + 1); }
};

} // namespace std

#endif // _SIM_SET
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef _SIM_STL_PAIR
#define _SIM_STL_PAIR

#pragma clang system_header

#include "sim_type_traits"

namespace std {

template <class T1, class T2>
struct pair {
T1 first;
T2 second;

pair() : first(), second() {}
pair(const T1 &a, const T2 &b) : first(a), second(b) {}

template<class U1, class U2>
pair(const pair<U1, U2> &other) : first(other.first),
second(other.second) {}
};

template <typename T1, typename T2>
pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type>
make_pair(T1 &&, T2 &&) {
return {};
};

} // namespace std

#endif // _SIM_STL_PAIR

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

#ifndef _SIM_TYPE_TRAITS
#define _SIM_TYPE_TRAITS

#pragma clang system_header
namespace std {

template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};

template<typename T> typename remove_reference<T>::type&& move(T&& a);

template< class T >
using remove_reference_t = typename remove_reference<T>::type;

} // namespace std

#endif // _SIM_TYPE_TRAITS
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef _SIM_UNORDERED_MAP
#define _SIM_UNORDERED_MAP

#pragma clang system_header
#include "sim_initializer_list"

namespace std {

template <typename Key, typename Value>
class unordered_map {
public:
using value_type = pair<Key, Value>;
unordered_map();
unordered_map(initializer_list<pair<Key, Value>> initList);
value_type& operator[](const Key& key);
value_type& operator[](Key&& key);
class iterator {
public:
iterator(Key *key): ptr(key) {}
iterator& operator++() { ++ptr; return *this; }
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
const Key &operator*() const { return *ptr; }
private:
Key *ptr;
};
Key *val;
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + 1); }
};

} // namespace std

#endif // _SIM_UNORDERED_MAP
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef _SIM_UNORDERED_SET
#define _SIM_UNORDERED_SET

#pragma clang system_header
#include "sim_initializer_list"

namespace std {

template<
class Key,
class Hash = std::hash<Key>,
class Compare = std::less<Key>,
class Alloc = std::allocator<Key>
> class unordered_set {
public:
unordered_set(initializer_list<Key> __list) {}

class iterator {
public:
iterator(Key *key): ptr(key) {}
iterator& operator++() { ++ptr; return *this; }
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
const Key &operator*() const { return *ptr; }
private:
Key *ptr;
};

Key *val;
iterator begin() const { return iterator(val); }
iterator end() const { return iterator(val + 1); }
};

} // namespace std

#endif // _SIM_UNORDERED_SET
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#ifndef _SIM_VECTOR
#define _SIM_VECTOR

#pragma clang system_header

#include "sim_iterator_base"

namespace std {

template <typename T, typename Ptr, typename Ref> struct __vector_iterator {
typedef __vector_iterator<T, T *, T &> iterator;
typedef __vector_iterator<T, const T *, const T &> const_iterator;

typedef ptrdiff_t difference_type;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef std::random_access_iterator_tag iterator_category;

__vector_iterator(const Ptr p = 0) : ptr(p) {}
__vector_iterator(const iterator &rhs): ptr(rhs.base()) {}
__vector_iterator<T, Ptr, Ref>& operator++() { ++ ptr; return *this; }
__vector_iterator<T, Ptr, Ref> operator++(int) {
auto tmp = *this;
++ ptr;
return tmp;
}
__vector_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; }
__vector_iterator<T, Ptr, Ref> operator--(int) {
auto tmp = *this; -- ptr;
return tmp;
}
__vector_iterator<T, Ptr, Ref> operator+(difference_type n) {
return ptr + n;
}
friend __vector_iterator<T, Ptr, Ref> operator+(
difference_type n,
const __vector_iterator<T, Ptr, Ref> &iter) {
return n + iter.ptr;
}
__vector_iterator<T, Ptr, Ref> operator-(difference_type n) {
return ptr - n;
}
__vector_iterator<T, Ptr, Ref> operator+=(difference_type n) {
return ptr += n;
}
__vector_iterator<T, Ptr, Ref> operator-=(difference_type n) {
return ptr -= n;
}

template<typename U, typename Ptr2, typename Ref2>
difference_type operator-(const __vector_iterator<U, Ptr2, Ref2> &rhs);

Ref operator*() const { return *ptr; }
Ptr operator->() const { return ptr; }

Ref operator[](difference_type n) {
return *(ptr+n);
}

bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; }
bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; }

bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; }
bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; }

const Ptr& base() const { return ptr; }

private:
Ptr ptr;
};

template<typename T>
class vector {
T *_start;
T *_finish;
T *_end_of_storage;

public:
typedef T value_type;
typedef size_t size_type;
typedef __vector_iterator<T, T *, T &> iterator;
typedef __vector_iterator<T, const T *, const T &> const_iterator;

vector() : _start(0), _finish(0), _end_of_storage(0) {}
template <typename InputIterator>
vector(InputIterator first, InputIterator last);
vector(const vector &other);
vector(vector &&other);
~vector();

size_t size() const {
return size_t(_finish - _start);
}

vector& operator=(const vector &other);
vector& operator=(vector &&other);
vector& operator=(std::initializer_list<T> ilist);

void assign(size_type count, const T &value);
template <typename InputIterator >
void assign(InputIterator first, InputIterator last);
void assign(std::initializer_list<T> ilist);

void clear();

void push_back(const T &value);
void push_back(T &&value);
template<class... Args>
void emplace_back(Args&&... args);
void pop_back();

iterator insert(const_iterator position, const value_type &val);
iterator insert(const_iterator position, size_type n,
const value_type &val);
template <typename InputIterator>
iterator insert(const_iterator position, InputIterator first,
InputIterator last);
iterator insert(const_iterator position, value_type &&val);
iterator insert(const_iterator position, initializer_list<value_type> il);

template <class... Args>
iterator emplace(const_iterator position, Args&&... args);

iterator erase(const_iterator position);
iterator erase(const_iterator first, const_iterator last);

T &operator[](size_t n) {
return _start[n];
}

const T &operator[](size_t n) const {
return _start[n];
}

iterator begin() { return iterator(_start); }
const_iterator begin() const { return const_iterator(_start); }
const_iterator cbegin() const { return const_iterator(_start); }
iterator end() { return iterator(_finish); }
const_iterator end() const { return const_iterator(_finish); }
const_iterator cend() const { return const_iterator(_finish); }
T& front() { return *begin(); }
const T& front() const { return *begin(); }
T& back() { return *(end() - 1); }
const T& back() const { return *(end() - 1); }
};

} // namespace std

#endif // _SIM_VECTOR
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// RUN: %check_clang_tidy %s bugprone-nondeterministic-pointer-iteration-order %t -- -- -I%S -std=c++!4

#include "Inputs/system-header-simulator/sim_set"
#include "Inputs/system-header-simulator/sim_unordered_set"
#include "Inputs/system-header-simulator/sim_map"
#include "Inputs/system-header-simulator/sim_unordered_map"
#include "Inputs/system-header-simulator/sim_vector"
#include "Inputs/system-header-simulator/sim_algorithm"

template<class T>
void f(T x);

void PointerIteration() {
int a = 1, b = 2;
std::set<int> OrderedIntSet = {a, b};
std::set<int *> OrderedPtrSet = {&a, &b};
std::unordered_set<int> UnorderedIntSet = {a, b};
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
std::map<int, int> IntMap = { std::make_pair(a,a), std::make_pair(b,b) };
std::map<int*, int*> PtrMap = { std::make_pair(&a,&a), std::make_pair(&b,&b) };
std::unordered_map<int, int> IntUnorderedMap = { std::make_pair(a,a), std::make_pair(b,b) };
std::unordered_map<int*, int*> PtrUnorderedMap = { std::make_pair(&a,&a), std::make_pair(&b,&b) };

for (auto i : OrderedIntSet) // no-warning
f(i);

for (auto i : OrderedPtrSet) // no-warning
f(i);

for (auto i : UnorderedIntSet) // no-warning
f(i);

for (auto i : UnorderedPtrSet)
f(i);
// CHECK-MESSAGES: :[[@LINE-2]]:17: warning: iteration of pointers is nondeterministic

for (auto &i : UnorderedPtrSet)
f(i);
// CHECK-MESSAGES: :[[@LINE-2]]:18: warning: iteration of pointers is nondeterministic

for (auto &i : IntMap) // no-warning
f(i);

for (auto &i : PtrMap) // no-warning
f(i);

for (auto &i : IntUnorderedMap) // no-warning
f(i);

for (auto &i : PtrUnorderedMap)
f(i);
// CHECK-MESSAGES: :[[@LINE-2]]:18: warning: iteration of pointers is nondeterministic
}

bool g (int *x) { return true; }
bool h (int x) { return true; }

void PointerSorting() {
int a = 1, b = 2, c = 3;
std::vector<int> V1 = {a, b};
std::vector<int *> V2 = {&a, &b};

std::is_sorted(V1.begin(), V1.end()); // no-warning
std::nth_element(V1.begin(), V1.begin() + 1, V1.end()); // no-warning
std::partial_sort(V1.begin(), V1.begin() + 1, V1.end()); // no-warning
std::sort(V1.begin(), V1.end()); // no-warning
std::stable_sort(V1.begin(), V1.end()); // no-warning
std::partition(V1.begin(), V1.end(), h); // no-warning
std::stable_partition(V1.begin(), V1.end(), h); // no-warning
std::is_sorted(V2.begin(), V2.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
std::nth_element(V2.begin(), V2.begin() + 1, V2.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
std::partial_sort(V2.begin(), V2.begin() + 1, V2.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
std::sort(V2.begin(), V2.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
std::stable_sort(V2.begin(), V2.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
std::partition(V2.begin(), V2.end(), g);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
std::stable_partition(V2.begin(), V2.end(), g);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ union U {
char union_member2;
} u;

union W {
template <class TP> operator TP *() const;
};

struct S {
int non_union_member;
union {
Expand All @@ -20,22 +24,25 @@ void f(char);
void f2(U);
void f3(U&);
void f4(U*);
W f5();

void check()
{
u.union_member1 = true;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not access members of unions; use (boost::)variant instead [cppcoreguidelines-pro-type-union-access]
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not access members of unions; consider using (boost::)variant instead [cppcoreguidelines-pro-type-union-access]
auto b = u.union_member2;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not access members of unions; use (boost::)variant instead
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not access members of unions; consider using (boost::)variant instead
auto a = &s.union_member;
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not access members of unions; use (boost::)variant instead
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not access members of unions; consider using (boost::)variant instead
f(s.u.union_member2);
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not access members of unions; use (boost::)variant instead
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not access members of unions; consider using (boost::)variant instead

s.non_union_member = 2; // OK

U u2 = u; // OK
f2(u); // OK
f3(u); // OK
f4(&u); // OK
void *ret = f5();
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not access members of unions; consider using (boost::)variant instead
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,11 @@ DECLARE_S93;
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:1: warning: use designated initializer list to initialize 'S9' [modernize-use-designated-initializers]
// CHECK-MESSAGES-MACROS: :[[@LINE-4]]:28: note: expanded from macro 'DECLARE_S93'
// CHECK-MESSAGES-MACROS: :[[@LINE-71]]:1: note: aggregate type is defined here

// Issue #113652.
struct S14;

struct S15{
S15(S14& d):d{d}{}
S14& d;
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,9 @@ struct prefer_underscore_version_flip {
size_t find(const char *s, size_t pos = 0) const;
};

struct prefer_underscore_version_inherit : public string_like {
bool startsWith(const char *s) const;
};

void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
string_like sl, string_like_camel slc, prefer_underscore_version puv,
prefer_underscore_version_flip puvf,
prefer_underscore_version_inherit puvi) {
prefer_underscore_version_flip puvf) {
s.find("a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find() == 0
// CHECK-FIXES: s.starts_with("a");
Expand Down Expand Up @@ -153,12 +148,6 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: puvf.starts_with("a");

// Here, the subclass has startsWith, the superclass has starts_with.
// We prefer the version from the subclass.
puvi.find("a") == 0;
// 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");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// RUN: %check_clang_tidy -std=c++20-or-later %s readability-container-contains %t
// RUN: %check_clang_tidy -std=c++11-or-later %s readability-container-contains %t

// Some *very* simplified versions of `map` etc.
namespace std {

template <class Key, class T>
struct map {
struct iterator {
bool operator==(const iterator &Other) const;
bool operator!=(const iterator &Other) const;
};

unsigned count(const Key &K) const;
bool contains(const Key &K) const;
void *find(const Key &K);
void *end();
iterator find(const Key &K);
iterator end();
};

template <class Key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ enum EMacro2 {
// CHECK-FIXES: EMacro2_c = 3,
};


enum {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum '<unnamed>' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum '<unnamed>' are not consistent
EAnonymous_a = 1,
EAnonymous_b,
// CHECK-FIXES: EAnonymous_b = 2,
EAnonymous_c = 3,
};


enum EnumZeroFirstInitialValue {
EnumZeroFirstInitialValue_0 = 0,
// CHECK-MESSAGES-ENABLE: :[[@LINE-1]]:3: warning: zero initial value for the first enumerator in 'EnumZeroFirstInitialValue' can be disregarded
Expand Down Expand Up @@ -114,4 +125,3 @@ enum WithFwdDeclSequential : int {
EFS2 = 4,
// CHECK-FIXES-ENABLE: EFS2 ,
};

Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,15 @@ void implicitConversionToBoolFromUnaryMinusAndZeroLiterals() {
// CHECK-FIXES: functionTakingBool((-0.0) != 0.0);
}

void ignoreImplicitCastToBoolForComparisonResult() {
bool boolFromComparison0 = 1 != 0;
bool boolFromComparison1 = 1 == 0;
bool boolFromComparison2 = 1 > 0;
bool boolFromComparison3 = 1 >= 0;
bool boolFromComparison4 = 1 < 0;
bool boolFromComparison5 = 1 <= 0;
}

void ignoreExplicitCastsToBool() {
int integer = 10;
bool boolComingFromInt = (bool)integer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: not clang-tidy --invalid-arg 2>&1 | FileCheck %s

// CHECK: error: clang-tidy{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-tidy{{(\.exe)?}} --help'
// CHECK: error: clang-tidy{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: '{{.*}}clang-tidy{{(\.exe)?}} --help'
// CHECK-NEXT: clang-tidy{{(\.exe)?}}: Did you mean '--extra-arg'?
4 changes: 2 additions & 2 deletions clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class ConstTransform : public ClangTidyCheck {
void check(const MatchFinder::MatchResult &Result) override {
const auto *D = Result.Nodes.getNodeAs<VarDecl>("var");
using utils::fixit::addQualifierToVarDecl;
std::optional<FixItHint> Fix = addQualifierToVarDecl(
*D, *Result.Context, DeclSpec::TQ::TQ_const, CT, CP);
std::optional<FixItHint> Fix =
addQualifierToVarDecl(*D, *Result.Context, Qualifiers::Const, CT, CP);
auto Diag = diag(D->getBeginLoc(), "doing const transformation");
if (Fix)
Diag << *Fix;
Expand Down
45 changes: 45 additions & 0 deletions clang/Maintainers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ AST matchers
| aaron\@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC)

AST Visitors
~~~~~~~~~~~~
| Sirraide
| aeternalmail\@gmail.com (email), Sirraide (GitHub), Ætérnal (Discord), Sirraide (Discourse)

Clang LLVM IR generation
~~~~~~~~~~~~~~~~~~~~~~~~
| John McCall
Expand All @@ -57,6 +63,12 @@ Analysis & CFG
| sgatev\@google.com (email), sgatev (Phabricator), sgatev (GitHub)

Sema
~~~~
| Sirraide
| aeternalmail\@gmail.com (email), Sirraide (GitHub), Ætérnal (Discord), Sirraide (Discourse)

Experimental new constant interpreter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Timm Bäder
Expand All @@ -71,13 +83,22 @@ Modules & serialization
| Michael Spencer
| bigcheesegs\@gmail.com (email), Bigcheese (Phabricator), Bigcheese (GitHub)
| Vassil Vassilev
| Vassil.Vassilev\@cern.ch (email), v.g.vassilev (Phabricator), vgvassilev (GitHub)

Templates
~~~~~~~~~
| Erich Keane
| ekeane\@nvidia.com (email), ErichKeane (Phabricator), erichkeane (GitHub)

Lambdas
~~~~~~~
| Corentin Jabot
| corentin.jabot\@gmail.com (email), cor3ntin (Phabricator), cor3ntin (GitHub)

Debug information
~~~~~~~~~~~~~~~~~
| Adrian Prantl
Expand Down Expand Up @@ -172,6 +193,12 @@ Attributes
| ekeane\@nvidia.com (email), ErichKeane (Phabricator), erichkeane (GitHub)

Plugins
~~~~~~~
| Vassil Vassilev
| Vassil.Vassilev\@cern.ch (email), v.g.vassilev (Phabricator), vgvassilev (GitHub)

Inline assembly
~~~~~~~~~~~~~~~
| Eric Christopher
Expand Down Expand Up @@ -225,6 +252,18 @@ C++ conformance
| Hubert Tong
| hubert.reinterpretcast\@gmail.com (email), hubert.reinterpretcast (Phabricator), hubert-reinterpretcast (GitHub)
| Shafik Yaghmour
| shafik.yaghmour\@intel.com (email), shafik (GitHub), shafik.yaghmour (Discord), shafik (Discourse)
| Vlad Serebrennikov
| serebrennikov.vladislav\@gmail.com (email), Endilll (GitHub), Endill (Discord), Endill (Discourse)

C++ Defect Reports
~~~~~~~~~~~~~~~~~~
| Vlad Serebrennikov
| serebrennikov.vladislav\@gmail.com (email), Endilll (GitHub), Endill (Discord), Endill (Discourse)

Objective-C/C++ conformance
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -244,6 +283,12 @@ OpenCL conformance
| anastasia\@compiler-experts.com (email), Anastasia (Phabricator), AnastasiaStulova (GitHub)

OpenACC
~~~~~~~
| Erich Keane
| ekeane\@nvidia.com (email), ErichKeane (Phabricator), erichkeane (GitHub)

SYCL conformance
~~~~~~~~~~~~~~~~
| Alexey Bader
Expand Down
2 changes: 1 addition & 1 deletion clang/docs/AddressSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Typical slowdown introduced by AddressSanitizer is **2x**.
How to build
============

Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>` and enable
Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>`_ and enable
the ``compiler-rt`` runtime. An example CMake configuration that will allow
for the use/testing of AddressSanitizer:

Expand Down
8 changes: 8 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4663,6 +4663,14 @@ the configuration (without a prefix: ``Auto``).
**KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ <KeepEmptyLinesAtTheStartOfBlocks>`
This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``.

.. _KeepFormFeed:

**KeepFormFeed** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ <KeepFormFeed>`
Keep the form feed character if it's immediately preceded and followed by
a newline. Multiple form feeds and newlines within a whitespace range are
replaced with a single newline and form feed followed by the remaining
newlines.

.. _LambdaBodyIndentation:

**LambdaBodyIndentation** (``LambdaBodyIndentationKind``) :versionbadge:`clang-format 13` :ref:`¶ <LambdaBodyIndentation>`
Expand Down
8,536 changes: 0 additions & 8,536 deletions clang/docs/ClangFormattedStatus.rst

This file was deleted.

535 changes: 535 additions & 0 deletions clang/docs/FunctionEffectAnalysis.rst

Large diffs are not rendered by default.

81 changes: 73 additions & 8 deletions clang/docs/RealtimeSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,22 @@ RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and C+
projects. RTSan can be used to detect real-time violations, i.e. calls to methods
that are not safe for use in functions with deterministic run time requirements.
RTSan considers any function marked with the ``[[clang::nonblocking]]`` attribute
to be a real-time function. If RTSan detects a call to ``malloc``, ``free``,
``pthread_mutex_lock``, or anything else that could have a non-deterministic
execution time in a function marked ``[[clang::nonblocking]]``
to be a real-time function. At run-time, if RTSan detects a call to ``malloc``,
``free``, ``pthread_mutex_lock``, or anything else that could have a
non-deterministic execution time in a function marked ``[[clang::nonblocking]]``
RTSan raises an error.

RTSan performs its analysis at run-time but shares the ``[[clang::nonblocking]]``
attribute with the :doc:`FunctionEffectAnalysis` system, which operates at
compile-time to detect potential real-time safety violations. For comprehensive
detection of real-time safety issues, it is recommended to use both systems together.

The runtime slowdown introduced by RealtimeSanitizer is negligible.

How to build
============

Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>` and enable the
Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>`_ and enable the
``compiler-rt`` runtime. An example CMake configuration that will allow for the
use/testing of RealtimeSanitizer:

Expand Down Expand Up @@ -183,6 +188,10 @@ A **partial** list of flags RealtimeSanitizer respects:
- ``true``
- boolean
- If set, use the symbolizer to turn virtual addresses to file/line locations. If false, can greatly speed up the error reporting.
* - ``suppressions``
- ""
- path
- If set to a valid suppressions file, will suppress issue reporting. See details in "Disabling", below.


Some issues with flags can be debugged using the ``verbosity=$NUM`` flag:
Expand All @@ -194,12 +203,43 @@ Some issues with flags can be debugged using the ``verbosity=$NUM`` flag:
misspelled_flag
...
Disabling
---------
Disabling and suppressing
-------------------------

There are multiple ways to disable error reporting when using RealtimeSanitizer.

In some circumstances, you may want to suppress error reporting in a specific scope.
In general, ``ScopedDisabler`` should be preferred, as it is the most performant.

.. list-table:: Suppression methods
:widths: 30 15 15 10 70
:header-rows: 1

In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope where the ``ScopedDisabler`` object is instantiated, all sanitizer error reports are suppressed. This suppression applies to the current scope as well as all invoked functions, including any functions called transitively.
* - Method
- Specified at?
- Scope
- Run-time cost
- Description
* - ``ScopedDisabler``
- Compile-time
- Stack
- Very low
- Violations are ignored for the lifetime of the ``ScopedDisabler`` object.
* - ``function-name-matches`` suppression
- Run-time
- Single function
- Medium
- Suppresses intercepted and ``[[clang::blocking]]`` function calls by name.
* - ``call-stack-contains`` suppression
- Run-time
- Stack
- High
- Suppresses any stack trace contaning the specified pattern.


``ScopedDisabler``
##################

At compile time, RealtimeSanitizer may be disabled using ``__rtsan::ScopedDisabler``. RTSan ignores any errors originating within the ``ScopedDisabler`` instance variable scope.

.. code-block:: c++

Expand Down Expand Up @@ -233,6 +273,31 @@ In C, you can use the ``__rtsan_disable()`` and ``rtsan_enable()`` functions to

Each call to ``__rtsan_disable()`` must be paired with a subsequent call to ``__rtsan_enable()`` to restore normal sanitizer functionality. If a corresponding ``rtsan_enable()`` call is not made, the behavior is undefined.

Suppression file
################

At run-time, suppressions may be specified using a suppressions file passed in ``RTSAN_OPTIONS``. Run-time suppression may be useful if the source cannot be changed.

.. code-block:: console
> cat suppressions.supp
call-stack-contains:MallocViolation
call-stack-contains:std::*vector
function-name-matches:free
function-name-matches:CustomMarkedBlocking*
> RTSAN_OPTIONS="suppressions=suppressions.supp" ./a.out
...
Suppressions specified in this file are one of two flavors.

``function-name-matches`` suppresses reporting of any intercepted library call, or function marked ``[[clang::blocking]]`` by name. If, for instance, you know that ``malloc`` is real-time safe on your system, you can disable the check for it via ``function-name-matches:malloc``.

``call-stack-contains`` suppresses reporting of errors in any stack that contains a string matching the pattern specified. For example, suppressing error reporting of any non-real-time-safe behavior in ``std::vector`` may be specified ``call-stack-contains:std::*vector``. You must include symbols in your build for this method to be effective, unsymbolicated stack traces cannot be matched. ``call-stack-contains`` has the highest run-time cost of any method of suppression.

Patterns may be exact matches or are "regex-light" patterns, containing special characters such as ``^$*``.

The number of potential errors suppressed via this method may be seen on exit when using the ``print_stats_on_exit`` flag.

Compile-time sanitizer detection
--------------------------------

Expand Down
68 changes: 66 additions & 2 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ code bases.

- The ``clang-rename`` tool has been removed.

- Removed support for RenderScript targets. This technology is
`officially deprecated <https://developer.android.com/guide/topics/renderscript/compute>`_
and users are encouraged to
`migrate to Vulkan <https://developer.android.com/guide/topics/renderscript/migrate>`_
or other options.

C/C++ Language Potentially Breaking Changes
-------------------------------------------

Expand Down Expand Up @@ -133,6 +139,15 @@ C++ Specific Potentially Breaking Changes
// Fixed version:
unsigned operator""_udl_name(unsigned long long);

- Clang will now produce an error diagnostic when [[clang::lifetimebound]] is
applied on a parameter of a function that returns void. This was previously
ignored and had no effect. (#GH107556)

.. code-block:: c++

// Now diagnoses with an error.
void f(int& i [[clang::lifetimebound]]);

ABI Changes in This Version
---------------------------

Expand Down Expand Up @@ -272,6 +287,7 @@ Non-comprehensive list of changes in this release
``__builtin_signbit`` can now be used in constant expressions.
- Plugins can now define custom attributes that apply to statements
as well as declarations.
- ``__builtin_abs`` function can now be used in constant expressions.

New Compiler Flags
------------------
Expand Down Expand Up @@ -301,6 +317,16 @@ Modified Compiler Flags
the ``promoted`` algorithm for complex division when possible rather than the
less basic (limited range) algorithm.

- The ``-fveclib`` option has been updated to enable ``-fno-math-errno`` for
``-fveclib=ArmPL`` and ``-fveclib=SLEEF``. This gives Clang more opportunities
to utilize these vector libraries. The behavior for all other vector function
libraries remains unchanged.

- The ``-Wnontrivial-memaccess`` warning has been updated to also warn about
passing non-trivially-copyable destrination parameter to ``memcpy``,
``memset`` and similar functions for which it is a documented undefined
behavior.

Removed Compiler Flags
-------------------------

Expand Down Expand Up @@ -418,11 +444,13 @@ Improvements to Clang's diagnostics
- The warning for an unsupported type for a named register variable is now phrased ``unsupported type for named register variable``,
instead of ``bad type for named register variable``. This makes it clear that the type is not supported at all, rather than being
suboptimal in some way the error fails to mention (#GH111550).

- Clang now emits a ``-Wdepredcated-literal-operator`` diagnostic, even if the
name was a reserved name, which we improperly allowed to suppress the
diagnostic.

- Clang now diagnoses ``[[deprecated]]`` attribute usage on local variables (#GH90073).

Improvements to Clang's time-trace
----------------------------------

Expand All @@ -438,8 +466,11 @@ Bug Fixes in This Version
- Fixed a crash when trying to transform a dependent address space type. Fixes #GH101685.
- Fixed a crash when diagnosing format strings and encountering an empty
delimited escape sequence (e.g., ``"\o{}"``). #GH102218
- Fixed a crash using ``__array_rank`` on 64-bit targets. (#GH113044).
- The warning emitted for an unsupported register variable type now points to
the unsupported type instead of the ``register`` keyword (#GH109776).
- Fixed a crash when emit ctor for global variant with flexible array init (#GH113187).
- Fixed a crash when GNU statement expression contains invalid statement (#GH113468).

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -506,7 +537,7 @@ Bug Fixes to C++ Support
- Clang no longer tries to capture non-odr used default arguments of template parameters of generic lambdas (#GH107048)
- Fixed a bug where defaulted comparison operators would remove ``const`` from base classes. (#GH102588)
- Fix a crash when using ``source_location`` in the trailing return type of a lambda expression. (#GH67134)
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361), (#GH112352)
- Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
- Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
pack. (#GH63819), (#GH107560)
Expand Down Expand Up @@ -537,6 +568,14 @@ Bug Fixes to C++ Support
certain situations. (#GH47400), (#GH90896)
- Fix erroneous templated array size calculation leading to crashes in generated code. (#GH41441)
- During the lookup for a base class name, non-type names are ignored. (#GH16855)
- Fix a crash when recovering an invalid expression involving an explicit object member conversion operator. (#GH112559)
- Clang incorrectly considered a class with an anonymous union member to not be
const-default-constructible even if a union member has a default member initializer.
(#GH95854).
- Fixed an assertion failure when evaluating an invalid expression in an array initializer. (#GH112140)
- Fixed an assertion failure in range calculations for conditional throw expressions. (#GH111854)
- Clang now correctly ignores previous partial specializations of member templates explicitly specialized for
an implicitly instantiated class template specialization. (#GH51051)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -604,6 +643,10 @@ X86 Support
* Supported MINMAX intrinsics of ``*_(mask(z)))_minmax(ne)_p[s|d|h|bh]`` and
``*_(mask(z)))_minmax_s[s|d|h]``.

- Supported intrinsics for ``SM4 and AVX10.2``.
* Supported SM4 intrinsics of ``_mm512_sm4key4_epi32`` and
``_mm512_sm4rnds4_epi32``.

- All intrinsics in adcintrin.h can now be used in constant expressions.

- All intrinsics in adxintrin.h can now be used in constant expressions.
Expand All @@ -616,6 +659,9 @@ X86 Support

- All intrinsics in tbmintrin.h can now be used in constant expressions.

- Supported intrinsics for ``MOVRS AND AVX10.2``.
* Supported intrinsics of ``_mm(256|512)_(mask(z))_loadrs_epi(8|16|32|64)``.

Arm and AArch64 Support
^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -663,6 +709,15 @@ NetBSD Support
WebAssembly Support
^^^^^^^^^^^^^^^^^^^

The default target CPU, "generic", now enables the `-mnontrapping-fptoint`
and `-mbulk-memory` flags, which correspond to the [Bulk Memory Operations]
and [Non-trapping float-to-int Conversions] language features, which are
[widely implemented in engines].

[Bulk Memory Operations]: https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md
[Non-trapping float-to-int Conversions]: https://github.com/WebAssembly/spec/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md
[widely implemented in engines]: https://webassembly.org/features/

AVR Support
^^^^^^^^^^^

Expand Down Expand Up @@ -690,6 +745,8 @@ AST Matchers

- Fixed a crash when traverse lambda expr with invalid captures. (#GH106444)

- Fixed ``isInstantiated`` and ``isInTemplateInstantiation`` to also match for variable templates. (#GH110666)

- Ensure ``hasName`` matches template specializations across inline namespaces,
making `matchesNodeFullSlow` and `matchesNodeFullFast` consistent.

Expand All @@ -703,6 +760,7 @@ clang-format
multi-line comments without touching their contents, renames ``false`` to
``Never``, and ``true`` to ``Always``.
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.

libclang
--------
Expand Down Expand Up @@ -745,6 +803,12 @@ Moved checkers
To detect too large arguments passed to malloc, consider using the checker
``alpha.taint.TaintedAlloc``.

- The checkers ``alpha.nondeterministic.PointerSorting`` and
``alpha.nondeterministic.PointerIteration`` were moved to a new bugprone
checker named ``bugprone-nondeterministic-pointer-iteration-order``. The
original checkers were implemented only using AST matching and make more
sense as a single clang-tidy check.

.. _release-notes-sanitizers:

Sanitizers
Expand Down
31 changes: 0 additions & 31 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3447,37 +3447,6 @@ Limitations:
More details at the corresponding `GitHub issue <https://github.com/llvm/llvm-project/issues/43459>`_.
.. _alpha-nondeterminism-PointerIteration:
alpha.nondeterminism.PointerIteration (C++)
"""""""""""""""""""""""""""""""""""""""""""
Check for non-determinism caused by iterating unordered containers of pointers.
.. code-block:: c
void test() {
int a = 1, b = 2;
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
for (auto i : UnorderedPtrSet) // warn
f(i);
}
.. _alpha-nondeterminism-PointerSorting:
alpha.nondeterminism.PointerSorting (C++)
"""""""""""""""""""""""""""""""""""""""""
Check for non-determinism caused by sorting of pointers.
.. code-block:: c
void test() {
int a = 1, b = 2;
std::vector<int *> V = {&a, &b};
std::sort(V.begin(), V.end()); // warn
}
alpha.WebKit
^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions clang/docs/analyzer/user-docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Contents:
user-docs/FilingBugs
user-docs/CrossTranslationUnit
user-docs/TaintAnalysisConfiguration
user-docs/FAQ
2 changes: 1 addition & 1 deletion clang/docs/analyzer/user-docs/CommandLineUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Command Line Usage: scan-build and CodeChecker
==============================================

This document provides guidelines for running the static analyzer from the command line on whole projects.
CodeChecker and scan-build are two CLI tools for using CSA on multiple files (tranlation units).
CodeChecker and scan-build are two CLI tools for using CSA on multiple files (translation units).
Both provide a way of driving the analyzer, detecting compilation flags, and generating reports.
CodeChecker is more actively maintained, provides heuristics for working with multiple versions of popular compilers and it also comes with a web-based GUI for viewing, filtering, categorizing and suppressing the results.
Therefore CodeChecker is recommended in case you need any of the above features or just more customizability in general.
Expand Down
Loading