Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@
//===----------------------------------------------------------------------===//

#include "AvoidReturnWithVoidValueCheck.h"
#include "clang/AST/Stmt.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "../utils/BracesAroundStatement.h"
#include "../utils/LexerUtils.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

static constexpr auto IgnoreMacrosName = "IgnoreMacros";
static constexpr auto IgnoreMacrosDefault = true;
static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
static const bool IgnoreMacrosDefault = true;

static constexpr auto StrictModeName = "StrictMode";
static constexpr auto StrictModeDefault = true;
static constexpr char StrictModeName[] = "StrictMode";
static const bool StrictModeDefault = true;

AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck(
StringRef Name, ClangTidyContext *Context)
Expand All @@ -32,7 +31,10 @@ void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(
hasReturnValue(allOf(hasType(voidType()), unless(initListExpr()))),
optionally(hasParent(compoundStmt().bind("compound_parent"))))
optionally(hasParent(
compoundStmt(
optionally(hasParent(functionDecl().bind("function_parent"))))
.bind("compound_parent"))))
.bind("void_return"),
this);
}
Expand All @@ -42,10 +44,30 @@ void AvoidReturnWithVoidValueCheck::check(
const auto *VoidReturn = Result.Nodes.getNodeAs<ReturnStmt>("void_return");
if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID())
return;
if (!StrictMode && !Result.Nodes.getNodeAs<CompoundStmt>("compound_parent"))
const auto *SurroundingBlock =
Result.Nodes.getNodeAs<CompoundStmt>("compound_parent");
if (!StrictMode && !SurroundingBlock)
return;
diag(VoidReturn->getBeginLoc(), "return statement within a void function "
"should not have a specified return value");
DiagnosticBuilder Diag = diag(VoidReturn->getBeginLoc(),
"return statement within a void function "
"should not have a specified return value");
const SourceLocation SemicolonPos = utils::lexer::findNextTerminator(
VoidReturn->getEndLoc(), *Result.SourceManager, getLangOpts());
if (SemicolonPos.isInvalid())
return;
if (!SurroundingBlock) {
const auto BraceInsertionHints = utils::getBraceInsertionsHints(
VoidReturn, getLangOpts(), *Result.SourceManager,
VoidReturn->getBeginLoc());
if (BraceInsertionHints)
Diag << BraceInsertionHints.openingBraceFixIt()
<< BraceInsertionHints.closingBraceFixIt();
}
Diag << FixItHint::CreateRemoval(VoidReturn->getReturnLoc());
if (!Result.Nodes.getNodeAs<FunctionDecl>("function_parent") ||
SurroundingBlock->body_back() != VoidReturn)
Diag << FixItHint::CreateInsertion(SemicolonPos.getLocWithOffset(1),
" return;", true);
}

void AvoidReturnWithVoidValueCheck::storeOptions(
Expand Down
157 changes: 25 additions & 132 deletions clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "BracesAroundStatementsCheck.h"
#include "../utils/BracesAroundStatement.h"
#include "../utils/LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
Expand All @@ -17,12 +18,10 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {

static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
const ASTContext *Context) {
const LangOptions &LangOpts) {
Token Tok;
SourceLocation Beginning =
Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
const bool Invalid =
Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
assert(!Invalid && "Expected a valid token.");

if (Invalid)
Expand All @@ -33,64 +32,21 @@ static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,

static SourceLocation
forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM,
const ASTContext *Context) {
const LangOptions &LangOpts) {
assert(Loc.isValid());
for (;;) {
while (isWhitespace(*SM.getCharacterData(Loc)))
Loc = Loc.getLocWithOffset(1);

tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);
if (TokKind != tok::comment)
return Loc;

// Fast-forward current token.
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
}
}

static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
const ASTContext *Context) {
SourceLocation Loc =
utils::lexer::getUnifiedEndLoc(S, SM, Context->getLangOpts());
if (!Loc.isValid())
return Loc;

// Start searching right after S.
Loc = Loc.getLocWithOffset(1);

for (;;) {
assert(Loc.isValid());
while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
Loc = Loc.getLocWithOffset(1);
}

if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
// EOL, insert brace before.
break;
}
tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
if (TokKind != tok::comment) {
// Non-comment token, insert brace before.
break;
}

SourceLocation TokEndLoc =
Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
SourceRange TokRange(Loc, TokEndLoc);
StringRef Comment = Lexer::getSourceText(
CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
if (Comment.starts_with("/*") && Comment.contains('\n')) {
// Multi-line block comment, insert brace before.
break;
}
// else: Trailing comment, insert brace after the newline.

// Fast-forward current token.
Loc = TokEndLoc;
}
return Loc;
}

BracesAroundStatementsCheck::BracesAroundStatementsCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Expand Down Expand Up @@ -124,7 +80,7 @@ void BracesAroundStatementsCheck::check(
} else if (const auto *S = Result.Nodes.getNodeAs<DoStmt>("do")) {
checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
} else if (const auto *S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
SourceLocation StartLoc = findRParenLoc(S, SM, Context);
SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
if (StartLoc.isInvalid())
return;
checkStmt(Result, S->getBody(), StartLoc);
Expand All @@ -133,7 +89,7 @@ void BracesAroundStatementsCheck::check(
if (S->isConsteval())
return;

SourceLocation StartLoc = findRParenLoc(S, SM, Context);
SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
if (StartLoc.isInvalid())
return;
if (ForceBracesStmts.erase(S))
Expand All @@ -156,7 +112,7 @@ template <typename IfOrWhileStmt>
SourceLocation
BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
const SourceManager &SM,
const ASTContext *Context) {
const LangOptions &LangOpts) {
// Skip macros.
if (S->getBeginLoc().isMacroID())
return {};
Expand All @@ -170,14 +126,14 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
}

SourceLocation PastCondEndLoc =
Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, LangOpts);
if (PastCondEndLoc.isInvalid())
return {};
SourceLocation RParenLoc =
forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, LangOpts);
if (RParenLoc.isInvalid())
return {};
tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, LangOpts);
if (TokKind != tok::r_paren)
return {};
return RParenLoc;
Expand All @@ -188,86 +144,23 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
bool BracesAroundStatementsCheck::checkStmt(
const MatchFinder::MatchResult &Result, const Stmt *S,
SourceLocation StartLoc, SourceLocation EndLocHint) {

while (const auto *AS = dyn_cast<AttributedStmt>(S))
S = AS->getSubStmt();

const SourceManager &SM = *Result.SourceManager;
const ASTContext *Context = Result.Context;

// 1) If there's a corresponding "else" or "while", the check inserts "} "
// right before that token.
// 2) If there's a multi-line block comment starting on the same line after
// the location we're inserting the closing brace at, or there's a non-comment
// token, the check inserts "\n}" right before that token.
// 3) Otherwise the check finds the end of line (possibly after some block or
// line comments) and inserts "\n}" right before that EOL.
if (!S || isa<CompoundStmt>(S)) {
// Already inside braces.
return false;
}

// When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
// This NullStmt can be detected according to beginning token.
const SourceLocation StmtBeginLoc = S->getBeginLoc();
if (isa<NullStmt>(S) && StmtBeginLoc.isValid() &&
getTokenKind(StmtBeginLoc, SM, Context) == tok::l_brace)
return false;

if (StartLoc.isInvalid())
return false;

// Convert StartLoc to file location, if it's on the same macro expansion
// level as the start of the statement. We also need file locations for
// Lexer::getLocForEndOfToken working properly.
StartLoc = Lexer::makeFileCharRange(
CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,
Context->getLangOpts())
.getBegin();
if (StartLoc.isInvalid())
return false;
StartLoc =
Lexer::getLocForEndOfToken(StartLoc, 0, SM, Context->getLangOpts());

// StartLoc points at the location of the opening brace to be inserted.
SourceLocation EndLoc;
std::string ClosingInsertion;
if (EndLocHint.isValid()) {
EndLoc = EndLocHint;
ClosingInsertion = "} ";
} else {
EndLoc = findEndLocation(*S, SM, Context);
ClosingInsertion = "\n}";
}

assert(StartLoc.isValid());

// Don't require braces for statements spanning less than certain number of
// lines.
if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
if (EndLine - StartLine < ShortStatementLines)
const auto BraceInsertionHints = utils::getBraceInsertionsHints(
S, Result.Context->getLangOpts(), *Result.SourceManager, StartLoc,
EndLocHint);
if (BraceInsertionHints) {
if (ShortStatementLines && !ForceBracesStmts.erase(S) &&
BraceInsertionHints.resultingCompoundLineExtent(*Result.SourceManager) <
ShortStatementLines)
return false;
auto Diag = diag(BraceInsertionHints.DiagnosticPos,
"statement should be inside braces");
if (BraceInsertionHints.offersFixIts())
Diag << BraceInsertionHints.openingBraceFixIt()
<< BraceInsertionHints.closingBraceFixIt();
}

auto Diag = diag(StartLoc, "statement should be inside braces");

// Change only if StartLoc and EndLoc are on the same macro expansion level.
// This will also catch invalid EndLoc.
// Example: LLVM_DEBUG( for(...) do_something() );
// In this case fix-it cannot be provided as the semicolon which is not
// visible here is part of the macro. Adding braces here would require adding
// another semicolon.
if (Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(SourceRange(
SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
SM, Context->getLangOpts())
.isInvalid())
return false;

Diag << FixItHint::CreateInsertion(StartLoc, " {")
<< FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class BracesAroundStatementsCheck : public ClangTidyCheck {
SourceLocation EndLocHint = SourceLocation());
template <typename IfOrWhileStmt>
SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
const ASTContext *Context);
const LangOptions &LangOpts);
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_clang_library(clangTidyReadabilityModule
DeleteNullPointerCheck.cpp
DuplicateIncludeCheck.cpp
ElseAfterReturnCheck.cpp
EnumInitialValueCheck.cpp
FunctionCognitiveComplexityCheck.cpp
FunctionSizeCheck.cpp
IdentifierLengthCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ void DuplicateIncludeCallbacks::InclusionDirective(
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
// Skip includes behind macros
if (FilenameRange.getBegin().isMacroID() ||
FilenameRange.getEnd().isMacroID())
return;
if (llvm::is_contained(Files.back(), FileName)) {
// We want to delete the entire line, so make sure that [Start,End] covers
// everything.
Expand Down
200 changes: 200 additions & 0 deletions clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
//===--- EnumInitialValueCheck.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 "EnumInitialValueCheck.h"
#include "../utils/LexerUtils.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

static bool isNoneEnumeratorsInitialized(const EnumDecl &Node) {
return llvm::all_of(Node.enumerators(), [](const EnumConstantDecl *ECD) {
return ECD->getInitExpr() == nullptr;
});
}

static bool isOnlyFirstEnumeratorInitialized(const EnumDecl &Node) {
bool IsFirst = true;
for (const EnumConstantDecl *ECD : Node.enumerators()) {
if ((IsFirst && ECD->getInitExpr() == nullptr) ||
(!IsFirst && ECD->getInitExpr() != nullptr))
return false;
IsFirst = false;
}
return !IsFirst;
}

static bool areAllEnumeratorsInitialized(const EnumDecl &Node) {
return llvm::all_of(Node.enumerators(), [](const EnumConstantDecl *ECD) {
return ECD->getInitExpr() != nullptr;
});
}

/// Check if \p Enumerator is initialized with a (potentially negated) \c
/// IntegerLiteral.
static bool isInitializedByLiteral(const EnumConstantDecl *Enumerator) {
const Expr *const Init = Enumerator->getInitExpr();
if (!Init)
return false;
return Init->isIntegerConstantExpr(Enumerator->getASTContext());
}

static void cleanInitialValue(DiagnosticBuilder &Diag,
const EnumConstantDecl *ECD,
const SourceManager &SM,
const LangOptions &LangOpts) {
const SourceRange InitExprRange = ECD->getInitExpr()->getSourceRange();
if (InitExprRange.isInvalid() || InitExprRange.getBegin().isMacroID() ||
InitExprRange.getEnd().isMacroID())
return;
std::optional<Token> EqualToken = utils::lexer::findNextTokenSkippingComments(
ECD->getLocation(), SM, LangOpts);
if (!EqualToken.has_value() ||
EqualToken.value().getKind() != tok::TokenKind::equal)
return;
const SourceLocation EqualLoc{EqualToken->getLocation()};
if (EqualLoc.isInvalid() || EqualLoc.isMacroID())
return;
Diag << FixItHint::CreateRemoval(EqualLoc)
<< FixItHint::CreateRemoval(InitExprRange);
return;
}

namespace {

AST_MATCHER(EnumDecl, isMacro) {
SourceLocation Loc = Node.getBeginLoc();
return Loc.isMacroID();
}

AST_MATCHER(EnumDecl, hasConsistentInitialValues) {
return isNoneEnumeratorsInitialized(Node) ||
isOnlyFirstEnumeratorInitialized(Node) ||
areAllEnumeratorsInitialized(Node);
}

AST_MATCHER(EnumDecl, hasZeroInitialValueForFirstEnumerator) {
const EnumDecl::enumerator_range Enumerators = Node.enumerators();
if (Enumerators.empty())
return false;
const EnumConstantDecl *ECD = *Enumerators.begin();
return isOnlyFirstEnumeratorInitialized(Node) &&
isInitializedByLiteral(ECD) && ECD->getInitVal().isZero();
}

/// Excludes bitfields because enumerators initialized with the result of a
/// bitwise operator on enumeration values or any other expr that is not a
/// potentially negative integer literal.
/// Enumerations where it is not directly clear if they are used with
/// bitmask, evident when enumerators are only initialized with (potentially
/// negative) integer literals, are ignored. This is also the case when all
/// enumerators are powers of two (e.g., 0, 1, 2).
AST_MATCHER(EnumDecl, hasSequentialInitialValues) {
const EnumDecl::enumerator_range Enumerators = Node.enumerators();
if (Enumerators.empty())
return false;
const EnumConstantDecl *const FirstEnumerator = *Node.enumerator_begin();
llvm::APSInt PrevValue = FirstEnumerator->getInitVal();
if (!isInitializedByLiteral(FirstEnumerator))
return false;
bool AllEnumeratorsArePowersOfTwo = true;
for (const EnumConstantDecl *Enumerator : llvm::drop_begin(Enumerators)) {
const llvm::APSInt NewValue = Enumerator->getInitVal();
if (NewValue != ++PrevValue)
return false;
if (!isInitializedByLiteral(Enumerator))
return false;
PrevValue = NewValue;
AllEnumeratorsArePowersOfTwo &= NewValue.isPowerOf2();
}
return !AllEnumeratorsArePowersOfTwo;
}

} // namespace

EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
AllowExplicitZeroFirstInitialValue(
Options.get("AllowExplicitZeroFirstInitialValue", true)),
AllowExplicitSequentialInitialValues(
Options.get("AllowExplicitSequentialInitialValues", true)) {}

void EnumInitialValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AllowExplicitZeroFirstInitialValue",
AllowExplicitZeroFirstInitialValue);
Options.store(Opts, "AllowExplicitSequentialInitialValues",
AllowExplicitSequentialInitialValues);
}

void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
enumDecl(unless(isMacro()), unless(hasConsistentInitialValues()))
.bind("inconsistent"),
this);
if (!AllowExplicitZeroFirstInitialValue)
Finder->addMatcher(
enumDecl(hasZeroInitialValueForFirstEnumerator()).bind("zero_first"),
this);
if (!AllowExplicitSequentialInitialValues)
Finder->addMatcher(enumDecl(unless(isMacro()), hasSequentialInitialValues())
.bind("sequential"),
this);
}

void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("inconsistent")) {
DiagnosticBuilder Diag =
diag(Enum->getBeginLoc(),
"inital values in enum %0 are not consistent, consider explicit "
"initialization of all, none or only the first enumerator")
<< Enum;
for (const EnumConstantDecl *ECD : Enum->enumerators())
if (ECD->getInitExpr() == nullptr) {
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
ECD->getLocation(), 0, *Result.SourceManager, getLangOpts());
if (EndLoc.isMacroID())
continue;
llvm::SmallString<8> Str{" = "};
ECD->getInitVal().toString(Str);
Diag << FixItHint::CreateInsertion(EndLoc, Str);
}
return;
}

if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("zero_first")) {
const EnumConstantDecl *ECD = *Enum->enumerator_begin();
const SourceLocation Loc = ECD->getLocation();
if (Loc.isInvalid() || Loc.isMacroID())
return;
DiagnosticBuilder Diag = diag(Loc, "zero initial value for the first "
"enumerator in %0 can be disregarded")
<< 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;
for (const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))
cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());
return;
}
}

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

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ENUMINITIALVALUECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ENUMINITIALVALUECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// Enforces consistent style for enumerators' initialization, covering three
/// styles: none, first only, or all initialized explicitly.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/enum-initial-value.html
class EnumInitialValueCheck : public ClangTidyCheck {
public:
EnumInitialValueCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
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;
}

private:
const bool AllowExplicitZeroFirstInitialValue;
const bool AllowExplicitSequentialInitialValues;
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ENUMINITIALVALUECHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "DeleteNullPointerCheck.h"
#include "DuplicateIncludeCheck.h"
#include "ElseAfterReturnCheck.h"
#include "EnumInitialValueCheck.h"
#include "FunctionCognitiveComplexityCheck.h"
#include "FunctionSizeCheck.h"
#include "IdentifierLengthCheck.h"
Expand Down Expand Up @@ -92,6 +93,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-duplicate-include");
CheckFactories.registerCheck<ElseAfterReturnCheck>(
"readability-else-after-return");
CheckFactories.registerCheck<EnumInitialValueCheck>(
"readability-enum-initial-value");
CheckFactories.registerCheck<FunctionCognitiveComplexityCheck>(
"readability-function-cognitive-complexity");
CheckFactories.registerCheck<FunctionSizeCheck>(
Expand Down
168 changes: 168 additions & 0 deletions clang-tools-extra/clang-tidy/utils/BracesAroundStatement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//===--- BracesAroundStatement.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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file provides utilities to put braces around a statement.
///
//===----------------------------------------------------------------------===//

#include "BracesAroundStatement.h"
#include "../utils/LexerUtils.h"
#include "LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/Lexer.h"

namespace clang::tidy::utils {

BraceInsertionHints::operator bool() const { return DiagnosticPos.isValid(); }

bool BraceInsertionHints::offersFixIts() const {
return OpeningBracePos.isValid() && ClosingBracePos.isValid();
}

unsigned BraceInsertionHints::resultingCompoundLineExtent(
const SourceManager &SourceMgr) const {
return SourceMgr.getSpellingLineNumber(ClosingBracePos) -
SourceMgr.getSpellingLineNumber(OpeningBracePos);
}

FixItHint BraceInsertionHints::openingBraceFixIt() const {
return OpeningBracePos.isValid()
? FixItHint::CreateInsertion(OpeningBracePos, " {")
: FixItHint();
}

FixItHint BraceInsertionHints::closingBraceFixIt() const {
return ClosingBracePos.isValid()
? FixItHint::CreateInsertion(ClosingBracePos, ClosingBrace)
: FixItHint();
}

static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
const LangOptions &LangOpts) {
Token Tok;
SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
assert(!Invalid && "Expected a valid token.");

if (Invalid)
return tok::NUM_TOKENS;

return Tok.getKind();
}

static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
const LangOptions &LangOpts) {
SourceLocation Loc = lexer::getUnifiedEndLoc(S, SM, LangOpts);
if (!Loc.isValid())
return Loc;

// Start searching right after S.
Loc = Loc.getLocWithOffset(1);

for (;;) {
assert(Loc.isValid());
while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
Loc = Loc.getLocWithOffset(1);
}

if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
// EOL, insert brace before.
break;
}
tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);
if (TokKind != tok::comment) {
// Non-comment token, insert brace before.
break;
}

SourceLocation TokEndLoc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
SourceRange TokRange(Loc, TokEndLoc);
StringRef Comment = Lexer::getSourceText(
CharSourceRange::getTokenRange(TokRange), SM, LangOpts);
if (Comment.starts_with("/*") && Comment.contains('\n')) {
// Multi-line block comment, insert brace before.
break;
}
// else: Trailing comment, insert brace after the newline.

// Fast-forward current token.
Loc = TokEndLoc;
}
return Loc;
}

BraceInsertionHints getBraceInsertionsHints(const Stmt *const S,
const LangOptions &LangOpts,
const SourceManager &SM,
SourceLocation StartLoc,
SourceLocation EndLocHint) {
// 1) If there's a corresponding "else" or "while", the check inserts "} "
// right before that token.
// 2) If there's a multi-line block comment starting on the same line after
// the location we're inserting the closing brace at, or there's a non-comment
// token, the check inserts "\n}" right before that token.
// 3) Otherwise the check finds the end of line (possibly after some block or
// line comments) and inserts "\n}" right before that EOL.
if (!S || isa<CompoundStmt>(S)) {
// Already inside braces.
return {};
}

// When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
// This NullStmt can be detected according to beginning token.
const SourceLocation StmtBeginLoc = S->getBeginLoc();
if (isa<NullStmt>(S) && StmtBeginLoc.isValid() &&
getTokenKind(StmtBeginLoc, SM, LangOpts) == tok::l_brace)
return {};

if (StartLoc.isInvalid())
return {};

// Convert StartLoc to file location, if it's on the same macro expansion
// level as the start of the statement. We also need file locations for
// Lexer::getLocForEndOfToken working properly.
StartLoc = Lexer::makeFileCharRange(
CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,
LangOpts)
.getBegin();
if (StartLoc.isInvalid())
return {};
StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOpts);

// StartLoc points at the location of the opening brace to be inserted.
SourceLocation EndLoc;
std::string ClosingInsertion;
if (EndLocHint.isValid()) {
EndLoc = EndLocHint;
ClosingInsertion = "} ";
} else {
EndLoc = findEndLocation(*S, SM, LangOpts);
ClosingInsertion = "\n}";
}

assert(StartLoc.isValid());

// Change only if StartLoc and EndLoc are on the same macro expansion level.
// This will also catch invalid EndLoc.
// Example: LLVM_DEBUG( for(...) do_something() );
// In this case fix-it cannot be provided as the semicolon which is not
// visible here is part of the macro. Adding braces here would require adding
// another semicolon.
if (Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(SourceRange(
SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
SM, LangOpts)
.isInvalid())
return {StartLoc};
return {StartLoc, EndLoc, ClosingInsertion};
}

} // namespace clang::tidy::utils
75 changes: 75 additions & 0 deletions clang-tools-extra/clang-tidy/utils/BracesAroundStatement.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===--- BracesAroundStatement.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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file provides utilities to put braces around a statement.
///
//===----------------------------------------------------------------------===//

#include "clang/AST/Stmt.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"

namespace clang::tidy::utils {

/// A provider of fix-it hints to insert opening and closing braces. An instance
/// of this type is the result of calling `getBraceInsertionsHints` below.
struct BraceInsertionHints {
/// The position of a potential diagnostic. It coincides with the position of
/// the opening brace to insert, but can also just be the place to show a
/// diagnostic in case braces cannot be inserted automatically.
SourceLocation DiagnosticPos;

/// Constructor for a no-hint.
BraceInsertionHints() = default;

/// Constructor for a valid hint that cannot insert braces automatically.
BraceInsertionHints(SourceLocation DiagnosticPos)
: DiagnosticPos(DiagnosticPos) {}

/// Constructor for a hint offering fix-its for brace insertion. Both
/// positions must be valid.
BraceInsertionHints(SourceLocation OpeningBracePos,
SourceLocation ClosingBracePos, std::string ClosingBrace)
: DiagnosticPos(OpeningBracePos), OpeningBracePos(OpeningBracePos),
ClosingBracePos(ClosingBracePos), ClosingBrace(ClosingBrace) {
assert(offersFixIts());
}

/// Indicates whether the hint provides at least the position of a diagnostic.
operator bool() const;

/// Indicates whether the hint provides fix-its to insert braces.
bool offersFixIts() const;

/// The number of lines between the inserted opening brace and its closing
/// counterpart.
unsigned resultingCompoundLineExtent(const SourceManager &SourceMgr) const;

/// Fix-it to insert an opening brace.
FixItHint openingBraceFixIt() const;

/// Fix-it to insert a closing brace.
FixItHint closingBraceFixIt() const;

private:
SourceLocation OpeningBracePos;
SourceLocation ClosingBracePos;
std::string ClosingBrace;
};

/// Create fix-it hints for braces that wrap the given statement when applied.
/// The algorithm computing them respects comment before and after the statement
/// and adds line breaks before the braces accordingly.
BraceInsertionHints
getBraceInsertionsHints(const Stmt *const S, const LangOptions &LangOpts,
const SourceManager &SM, SourceLocation StartLoc,
SourceLocation EndLocHint = SourceLocation());

} // namespace clang::tidy::utils
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangTidyUtils
Aliasing.cpp
ASTUtils.cpp
BracesAroundStatement.cpp
DeclRefExprUtils.cpp
DesignatedInitializers.cpp
ExceptionAnalyzer.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ int compareHeaders(StringRef LHS, StringRef RHS,
IncludeSorter::IncludeStyle Style) {
if (Style == IncludeSorter::IncludeStyle::IS_Google_ObjC) {
const std::pair<const char *, const char *> &Mismatch =
std::mismatch(LHS.begin(), LHS.end(), RHS.begin());
std::mismatch(LHS.begin(), LHS.end(), RHS.begin(), RHS.end());
if ((Mismatch.first != LHS.end()) && (Mismatch.second != RHS.end())) {
if ((*Mismatch.first == '.') && (*Mismatch.second == '+')) {
return -1;
Expand Down
68 changes: 45 additions & 23 deletions clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,12 @@ struct DenseMapInfo<clang::tidy::RenamerClangTidyCheck::NamingCheckId> {
using NamingCheckId = clang::tidy::RenamerClangTidyCheck::NamingCheckId;

static inline NamingCheckId getEmptyKey() {
return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(),
"EMPTY"};
return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(), "EMPTY"};
}

static inline NamingCheckId getTombstoneKey() {
return {DenseMapInfo<clang::SourceLocation>::getTombstoneKey(),
"TOMBSTONE"};
"TOMBSTONE"};
}

static unsigned getHashValue(NamingCheckId Val) {
Expand Down Expand Up @@ -170,14 +169,14 @@ class RenamerClangTidyCheckPPCallbacks : public PPCallbacks {
return;
if (SM.isWrittenInCommandLineFile(MacroNameTok.getLocation()))
return;
Check->checkMacro(SM, MacroNameTok, Info);
Check->checkMacro(MacroNameTok, Info, SM);
}

/// MacroExpands calls expandMacro for macros in the main file
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange /*Range*/,
const MacroArgs * /*Args*/) override {
Check->expandMacro(MacroNameTok, MD.getMacroInfo());
Check->expandMacro(MacroNameTok, MD.getMacroInfo(), SM);
}

private:
Expand All @@ -188,7 +187,7 @@ class RenamerClangTidyCheckPPCallbacks : public PPCallbacks {
class RenamerClangTidyVisitor
: public RecursiveASTVisitor<RenamerClangTidyVisitor> {
public:
RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager *SM,
RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager &SM,
bool AggressiveDependentMemberLookup)
: Check(Check), SM(SM),
AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {}
Expand Down Expand Up @@ -259,7 +258,7 @@ class RenamerClangTidyVisitor
// Fix overridden methods
if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) {
Check->addUsage(Overridden, Method->getLocation());
Check->addUsage(Overridden, Method->getLocation(), SM);
return true; // Don't try to add the actual decl as a Failure.
}
}
Expand All @@ -269,7 +268,7 @@ class RenamerClangTidyVisitor
if (isa<ClassTemplateSpecializationDecl>(Decl))
return true;

Check->checkNamedDecl(Decl, *SM);
Check->checkNamedDecl(Decl, SM);
return true;
}

Expand Down Expand Up @@ -367,9 +366,26 @@ class RenamerClangTidyVisitor
return true;
}

bool VisitDesignatedInitExpr(DesignatedInitExpr *Expr) {
for (const DesignatedInitExpr::Designator &D : Expr->designators()) {
if (!D.isFieldDesignator())
continue;
const FieldDecl *FD = D.getFieldDecl();
if (!FD)
continue;
const IdentifierInfo *II = FD->getIdentifier();
if (!II)
continue;
SourceRange FixLocation{D.getFieldLoc(), D.getFieldLoc()};
Check->addUsage(FD, FixLocation, SM);
}

return true;
}

private:
RenamerClangTidyCheck *Check;
const SourceManager *SM;
const SourceManager &SM;
const bool AggressiveDependentMemberLookup;
};

Expand Down Expand Up @@ -399,7 +415,7 @@ void RenamerClangTidyCheck::registerPPCallbacks(

void RenamerClangTidyCheck::addUsage(
const RenamerClangTidyCheck::NamingCheckId &Decl, SourceRange Range,
const SourceManager *SourceMgr) {
const SourceManager &SourceMgr) {
// Do nothing if the provided range is invalid.
if (Range.isInvalid())
return;
Expand All @@ -409,8 +425,7 @@ void RenamerClangTidyCheck::addUsage(
// spelling location to different source locations, and we only want to fix
// the token once, before it is expanded by the macro.
SourceLocation FixLocation = Range.getBegin();
if (SourceMgr)
FixLocation = SourceMgr->getSpellingLoc(FixLocation);
FixLocation = SourceMgr.getSpellingLoc(FixLocation);
if (FixLocation.isInvalid())
return;

Expand All @@ -424,15 +439,15 @@ void RenamerClangTidyCheck::addUsage(
if (!Failure.shouldFix())
return;

if (SourceMgr && SourceMgr->isWrittenInScratchSpace(FixLocation))
if (SourceMgr.isWrittenInScratchSpace(FixLocation))
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;

if (!utils::rangeCanBeFixed(Range, SourceMgr))
if (!utils::rangeCanBeFixed(Range, &SourceMgr))
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;
}

void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, SourceRange Range,
const SourceManager *SourceMgr) {
const SourceManager &SourceMgr) {
// Don't keep track for non-identifier names.
auto *II = Decl->getIdentifier();
if (!II)
Expand Down Expand Up @@ -473,18 +488,24 @@ void RenamerClangTidyCheck::checkNamedDecl(const NamedDecl *Decl,
}

Failure.Info = std::move(Info);
addUsage(Decl, Range);
addUsage(Decl, Range, SourceMgr);
}

void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) {
RenamerClangTidyVisitor Visitor(this, Result.SourceManager,
if (!Result.SourceManager) {
// In principle SourceManager is not null but going only by the definition
// of MatchResult it must be handled. Cannot rename anything without a
// SourceManager.
return;
}
RenamerClangTidyVisitor Visitor(this, *Result.SourceManager,
AggressiveDependentMemberLookup);
Visitor.TraverseAST(*Result.Context);
}

void RenamerClangTidyCheck::checkMacro(const SourceManager &SourceMgr,
const Token &MacroNameTok,
const MacroInfo *MI) {
void RenamerClangTidyCheck::checkMacro(const Token &MacroNameTok,
const MacroInfo *MI,
const SourceManager &SourceMgr) {
std::optional<FailureInfo> MaybeFailure =
getMacroFailureInfo(MacroNameTok, SourceMgr);
if (!MaybeFailure)
Expand All @@ -499,11 +520,12 @@ void RenamerClangTidyCheck::checkMacro(const SourceManager &SourceMgr,
Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier;

Failure.Info = std::move(Info);
addUsage(ID, Range);
addUsage(ID, Range, SourceMgr);
}

void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok,
const MacroInfo *MI) {
const MacroInfo *MI,
const SourceManager &SourceMgr) {
StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
NamingCheckId ID(MI->getDefinitionLoc(), Name);

Expand All @@ -512,7 +534,7 @@ void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok,
return;

SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
addUsage(ID, Range);
addUsage(ID, Range, SourceMgr);
}

static std::string
Expand Down
11 changes: 6 additions & 5 deletions clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,19 @@ class RenamerClangTidyCheck : public ClangTidyCheck {
llvm::DenseMap<NamingCheckId, NamingCheckFailure>;

/// Check Macros for style violations.
void checkMacro(const SourceManager &SourceMgr, const Token &MacroNameTok,
const MacroInfo *MI);
void checkMacro(const Token &MacroNameTok, const MacroInfo *MI,
const SourceManager &SourceMgr);

/// Add a usage of a macro if it already has a violation.
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI,
const SourceManager &SourceMgr);

void addUsage(const RenamerClangTidyCheck::NamingCheckId &Decl,
SourceRange Range, const SourceManager *SourceMgr = nullptr);
SourceRange Range, const SourceManager &SourceMgr);

/// Convenience method when the usage to be added is a NamedDecl.
void addUsage(const NamedDecl *Decl, SourceRange Range,
const SourceManager *SourceMgr = nullptr);
const SourceManager &SourceMgr);

void checkNamedDecl(const NamedDecl *Decl, const SourceManager &SourceMgr);

Expand Down
16 changes: 13 additions & 3 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ const CodeCompleteOptions::CodeCompletionRankingModel

namespace {

CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
// Note: changes to this function should also be reflected in the
// CodeCompletionResult overload where appropriate.
CompletionItemKind
toCompletionItemKind(index::SymbolKind Kind,
const llvm::StringRef *Signature = nullptr) {
using SK = index::SymbolKind;
switch (Kind) {
case SK::Unknown:
Expand All @@ -99,7 +103,10 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
case SK::NamespaceAlias:
return CompletionItemKind::Module;
case SK::Macro:
return CompletionItemKind::Text;
// Use macro signature (if provided) to tell apart function-like and
// object-like macros.
return Signature && Signature->contains('(') ? CompletionItemKind::Function
: CompletionItemKind::Constant;
case SK::Enum:
return CompletionItemKind::Enum;
case SK::Struct:
Expand Down Expand Up @@ -150,6 +157,8 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
llvm_unreachable("Unhandled clang::index::SymbolKind.");
}

// Note: changes to this function should also be reflected in the
// index::SymbolKind overload where appropriate.
CompletionItemKind toCompletionItemKind(const CodeCompletionResult &Res,
CodeCompletionContext::Kind CtxKind) {
if (Res.Declaration)
Expand Down Expand Up @@ -379,7 +388,8 @@ struct CodeCompletionBuilder {
if (Completion.Scope.empty())
Completion.Scope = std::string(C.IndexResult->Scope);
if (Completion.Kind == CompletionItemKind::Missing)
Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind);
Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind,
&C.IndexResult->Signature);
if (Completion.Name.empty())
Completion.Name = std::string(C.IndexResult->Name);
if (Completion.FilterText.empty())
Expand Down
6 changes: 4 additions & 2 deletions clang-tools-extra/clangd/CompileCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,8 @@ llvm::ArrayRef<ArgStripper::Rule> ArgStripper::rulesFor(llvm::StringRef Arg) {
static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELP, METAVAR, VALUES) \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
METAVAR, VALUES) \
Prefixes[DriverID::OPT_##ID] = PREFIX;
#include "clang/Driver/Options.inc"
#undef OPTION
Expand All @@ -478,7 +479,8 @@ llvm::ArrayRef<ArgStripper::Rule> ArgStripper::rulesFor(llvm::StringRef Arg) {
const void *AliasArgs;
} AliasTable[] = {
#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELP, METAVAR, VALUES) \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
METAVAR, VALUES) \
{DriverID::OPT_##ID, DriverID::OPT_##ALIAS, ALIASARGS},
#include "clang/Driver/Options.inc"
#undef OPTION
Expand Down
9 changes: 0 additions & 9 deletions clang-tools-extra/clangd/IncludeCleaner.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,6 @@ issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code,
const ThreadsafeFS &TFS,
HeaderFilter IgnoreHeader = {});

/// Affects whether standard library includes should be considered for
/// removal. This is off by default for now due to implementation limitations:
/// - macros are not tracked
/// - symbol names without a unique associated header are not tracked
/// - references to std-namespaced C types are not properly tracked:
/// instead of std::size_t -> <cstddef> we see ::size_t -> <stddef.h>
/// FIXME: remove this hack once the implementation is good enough.
void setIncludeCleanerAnalyzesStdlib(bool B);

/// Converts the clangd include representation to include-cleaner
/// include representation.
include_cleaner::Includes convertIncludes(const ParsedAST &);
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/SymbolCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ class SymbolCollector::HeaderFileURICache {
// Framework headers are spelled as <FrameworkName/Foo.h>, not
// "path/FrameworkName.framework/Headers/Foo.h".
auto &HS = PP->getHeaderSearchInfo();
if (const auto *HFI = HS.getExistingFileInfo(*FE, /*WantExternal*/ false))
if (const auto *HFI = HS.getExistingFileInfo(*FE))
if (!HFI->Framework.empty())
if (auto Spelling =
getFrameworkHeaderIncludeSpelling(*FE, HFI->Framework, HS))
Expand Down
7 changes: 5 additions & 2 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,8 @@ TEST(CompletionTest, Kinds) {
#define MACRO 10
int X = ^
)cpp",
{func("indexFunction"), var("indexVariable"), cls("indexClass")});
{func("indexFunction"), var("indexVariable"), cls("indexClass"),
macro("indexObjMacro"), macro("indexFuncMacro", "(x, y)")});
EXPECT_THAT(Results.Completions,
AllOf(has("function", CompletionItemKind::Function),
has("variable", CompletionItemKind::Variable),
Expand All @@ -680,7 +681,9 @@ TEST(CompletionTest, Kinds) {
has("MACRO", CompletionItemKind::Constant),
has("indexFunction", CompletionItemKind::Function),
has("indexVariable", CompletionItemKind::Variable),
has("indexClass", CompletionItemKind::Class)));
has("indexClass", CompletionItemKind::Class),
has("indexObjMacro", CompletionItemKind::Constant),
has("indexFuncMacro", CompletionItemKind::Function)));

Results = completions("nam^");
EXPECT_THAT(Results.Completions,
Expand Down
8 changes: 6 additions & 2 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1983,10 +1983,14 @@ TEST(Hover, All) {
HI.Kind = index::SymbolKind::Macro;
HI.Definition =
R"cpp(#define MACRO \
{ return 0; }
{ \
return 0; \
}
// Expands to
{ return 0; })cpp";
{
return 0;
})cpp";
}},
{
R"cpp(// Forward class declaration
Expand Down
7 changes: 6 additions & 1 deletion clang-tools-extra/clangd/unittests/TestIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static std::string replace(llvm::StringRef Haystack, llvm::StringRef Needle,
// Helpers to produce fake index symbols for memIndex() or completions().
// USRFormat is a regex replacement string for the unqualified part of the USR.
Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
llvm::StringRef USRFormat) {
llvm::StringRef USRFormat, llvm::StringRef Signature) {
Symbol Sym;
std::string USR = "c:"; // We synthesize a few simple cases of USRs by hand!
size_t Pos = QName.rfind("::");
Expand All @@ -55,6 +55,7 @@ Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
Sym.SymInfo.Kind = Kind;
Sym.Flags |= Symbol::IndexedForCodeCompletion;
Sym.Origin = SymbolOrigin::Static;
Sym.Signature = Signature;
return Sym;
}

Expand Down Expand Up @@ -86,6 +87,10 @@ Symbol conceptSym(llvm::StringRef Name) {
return sym(Name, index::SymbolKind::Concept, "@CT@\\0");
}

Symbol macro(llvm::StringRef Name, llvm::StringRef ArgList) {
return sym(Name, index::SymbolKind::Macro, "@macro@\\0", ArgList);
}

Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind,
llvm::StringRef USRPrefix) {
Symbol Sym;
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/unittests/TestIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Symbol symbol(llvm::StringRef QName);
// Helpers to produce fake index symbols with proper SymbolID.
// USRFormat is a regex replacement string for the unqualified part of the USR.
Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
llvm::StringRef USRFormat);
llvm::StringRef USRFormat, llvm::StringRef Signature = {});
// Creats a function symbol assuming no function arg.
Symbol func(llvm::StringRef Name);
// Creates a class symbol.
Expand All @@ -35,6 +35,8 @@ Symbol var(llvm::StringRef Name);
Symbol ns(llvm::StringRef Name);
// Create a C++20 concept symbol.
Symbol conceptSym(llvm::StringRef Name);
// Create a macro symbol.
Symbol macro(llvm::StringRef Name, llvm::StringRef ArgList = {});

// Create an Objective-C symbol.
Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind,
Expand Down
36 changes: 33 additions & 3 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ Improvements to clang-tidy
- Improved :program:`run-clang-tidy.py` script. Added argument `-source-filter`
to filter source files from the compilation database, via a RegEx. In a
similar fashion to what `-header-filter` does for header files.
- Improved :program:`check_clang_tidy.py` script. Added argument `-export-fixes`
to aid in clang-tidy and test development.

New checks
^^^^^^^^^^
Expand All @@ -123,6 +125,12 @@ New checks
Finds initializer lists for aggregate types that could be
written as designated initializers instead.

- New :doc:`readability-enum-initial-value
<clang-tidy/checks/readability/enum-initial-value>` check.

Enforces consistent style for enumerators' initialization, covering three
styles: none, first only, or all initialized explicitly.

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

Expand Down Expand Up @@ -173,8 +181,9 @@ Changes in existing checks

- Improved :doc:`cppcoreguidelines-missing-std-forward
<clang-tidy/checks/cppcoreguidelines/missing-std-forward>` check by no longer
giving false positives for deleted functions and fix false negative when some
parameters are forwarded, but other aren't.
giving false positives for deleted functions, by fixing false negatives when only
a few parameters are forwarded and by ignoring parameters without a name (unused
arguments).

- Improved :doc:`cppcoreguidelines-owning-memory
<clang-tidy/checks/cppcoreguidelines/owning-memory>` check to properly handle
Expand Down Expand Up @@ -204,10 +213,18 @@ Changes in existing checks
- Improved :doc:`google-runtime-int <clang-tidy/checks/google/runtime-int>`
check performance through optimizations.

- Improved :doc:`hicpp-ignored-remove-result <clang-tidy/checks/hicpp/ignored-remove-result>`
check by ignoring other functions with same prefixes as the target specific
functions.

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

- Improved :doc:`misc-const-correctness
<clang-tidy/checks/misc/const-correctness>` check by avoiding infinite recursion
for recursive forwarding reference.

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.
Expand Down Expand Up @@ -245,10 +262,19 @@ Changes in existing checks
analyzed, se the check now handles the common patterns
`const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`.

- Improved :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
fix-its.

- Improved :doc:`readability-duplicate-include
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.

- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check in `GetConfigPerFile`
mode by resolving symbolic links to header files. Fixed handling of Hungarian
Prefix when configured to `LowerCase`.
Prefix when configured to `LowerCase`. Added support for renaming designated
initializers. Added support for renaming macro arguments.

- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check to provide
Expand Down Expand Up @@ -277,6 +303,10 @@ Miscellaneous
``--format`` option is specified. Now :program:`clang-apply-replacements`
applies formatting only with the option.

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,6 @@ Options

.. option:: WarnOnSizeOfPointerToAggregate

When `true, the check will warn on an expression like
When `true`, the check will warn on an expression like
``sizeof(expr)`` where the expression is a pointer
to aggregate. Default is `true`.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.. title:: clang-tidy - linuxkernel-must-use-errs
.. title:: clang-tidy - linuxkernel-must-check-errs

linuxkernel-must-use-errs
=========================
linuxkernel-must-check-errs
===========================

Checks Linux kernel code to see if it uses the results from the functions in
``linux/err.h``. Also checks to see if code uses the results from functions that
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ Clang-Tidy Checks
:doc:`hicpp-multiway-paths-covered <hicpp/multiway-paths-covered>`,
:doc:`hicpp-no-assembler <hicpp/no-assembler>`,
:doc:`hicpp-signed-bitwise <hicpp/signed-bitwise>`,
:doc:`linuxkernel-must-use-errs <linuxkernel/must-use-errs>`,
:doc:`linuxkernel-must-check-errs <linuxkernel/must-check-errs>`,
:doc:`llvm-header-guard <llvm/header-guard>`,
:doc:`llvm-include-order <llvm/include-order>`, "Yes"
:doc:`llvm-namespace-comment <llvm/namespace-comment>`,
Expand Down Expand Up @@ -352,6 +352,7 @@ Clang-Tidy Checks
:doc:`readability-delete-null-pointer <readability/delete-null-pointer>`, "Yes"
:doc:`readability-duplicate-include <readability/duplicate-include>`, "Yes"
:doc:`readability-else-after-return <readability/else-after-return>`, "Yes"
:doc:`readability-enum-initial-value <readability/enum-initial-value>`, "Yes"
:doc:`readability-function-cognitive-complexity <readability/function-cognitive-complexity>`,
:doc:`readability-function-size <readability/function-size>`,
:doc:`readability-identifier-length <readability/identifier-length>`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.. title:: clang-tidy - readability-enum-initial-value

readability-enum-initial-value
==============================

Enforces consistent style for enumerators' initialization, covering three
styles: none, first only, or all initialized explicitly.

When adding new enumerations, inconsistent initial value will cause potential
enumeration value conflicts.

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

.. code-block:: c++

// valid, none of enumerators are initialized.
enum A {
e0,
e1,
e2,
};

// valid, the first enumerator is initialized.
enum A {
e0 = 0,
e1,
e2,
};

// valid, all of enumerators are initialized.
enum A {
e0 = 0,
e1 = 1,
e2 = 2,
};

// invalid, e1 is not explicit initialized.
enum A {
e0 = 0,
e1,
e2 = 2,
};

Options
-------

.. option:: AllowExplicitZeroFirstInitialValue

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

.. code-block:: c++

enum A {
e0 = 0, // not allowed if AllowExplicitZeroFirstInitialValue is false
e1,
e2,
};


.. option:: AllowExplicitSequentialInitialValues

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

.. code-block:: c++

enum A {
e0 = 1, // not allowed if AllowExplicitSequentialInitialValues is false
e1 = 2,
e2 = 3,
};
6 changes: 6 additions & 0 deletions clang-tools-extra/include-cleaner/lib/FindHeaders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ llvm::SmallVector<Header> headersForSymbol(const Symbol &S,
// are already ranked in the stdlib mapping.
if (H.kind() == Header::Standard)
continue;
// Don't apply name match hints to exporting headers. As they usually have
// names similar to the original header, e.g. foo_wrapper/foo.h vs
// foo/foo.h, but shouldn't be preferred (unless marked as the public
// interface).
if ((H.Hint & Hints::OriginHeader) == Hints::None)
continue;
if (nameMatch(SymbolName, H))
H.Hint |= Hints::PreferredHeader;
}
Expand Down
19 changes: 19 additions & 0 deletions clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,5 +628,24 @@ TEST_F(HeadersForSymbolTest, StandardHeaders) {
tooling::stdlib::Header::named("<assert.h>")));
}

TEST_F(HeadersForSymbolTest, ExporterNoNameMatch) {
Inputs.Code = R"cpp(
#include "exporter/foo.h"
#include "foo_public.h"
)cpp";
Inputs.ExtraArgs.emplace_back("-I.");
// Deliberately named as foo_public to make sure it doesn't get name-match
// boost and also gets lexicographically bigger order than "exporter/foo.h".
Inputs.ExtraFiles["foo_public.h"] = guard(R"cpp(
struct foo {};
)cpp");
Inputs.ExtraFiles["exporter/foo.h"] = guard(R"cpp(
#include "foo_public.h" // IWYU pragma: export
)cpp");
buildAST();
EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("foo_public.h"),
physicalHeader("exporter/foo.h")));
}

} // namespace
} // namespace clang::include_cleaner
74 changes: 55 additions & 19 deletions clang-tools-extra/test/clang-tidy/check_clang_tidy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,43 @@
#
# ===------------------------------------------------------------------------===#

r"""
"""
ClangTidy Test Helper
=====================
This script runs clang-tidy in fix mode and verify fixes, messages or both.
This script is used to simplify writing, running, and debugging tests compatible
with llvm-lit. By default it runs clang-tidy in fix mode and uses FileCheck to
verify messages and/or fixes.
For debugging, with --export-fixes, the tool simply exports fixes to a provided
file and does not run FileCheck.
Usage:
check_clang_tidy.py [-resource-dir=<resource-dir>] \
[-assume-filename=<file-with-source-extension>] \
[-check-suffix=<comma-separated-file-check-suffixes>] \
[-check-suffixes=<comma-separated-file-check-suffixes>] \
[-std=c++(98|11|14|17|20)[-or-later]] \
<source-file> <check-name> <temp-file> \
-- [optional clang-tidy arguments]
Extra arguments, those after the first -- if any, are passed to either
clang-tidy or clang:
* Arguments between the first -- and second -- are clang-tidy arguments.
* May be only whitespace if there are no clang-tidy arguments.
* clang-tidy's --config would go here.
* Arguments after the second -- are clang arguments
Examples
--------
Example:
// RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs
Notes:
or
// RUN: %check_clang_tidy %s llvm-include-order --export-fixes=fixes.yaml %t -std=c++20
Notes
-----
-std=c++(98|11|14|17|20)-or-later:
This flag will cause multiple runs within the same check_clang_tidy
execution. Make sure you don't have shared state across these runs.
"""

import argparse
import os
import pathlib
import re
import subprocess
import sys
Expand Down Expand Up @@ -88,6 +99,7 @@ def __init__(self, args, extra_args):
self.has_check_fixes = False
self.has_check_messages = False
self.has_check_notes = False
self.export_fixes = args.export_fixes
self.fixes = MessagePrefix("CHECK-FIXES")
self.messages = MessagePrefix("CHECK-MESSAGES")
self.notes = MessagePrefix("CHECK-NOTES")
Expand Down Expand Up @@ -181,7 +193,13 @@ def run_clang_tidy(self):
[
"clang-tidy",
self.temp_file_name,
"-fix",
]
+ [
"-fix"
if self.export_fixes is None
else "--export-fixes=" + self.export_fixes
]
+ [
"--checks=-*," + self.check_name,
]
+ self.clang_tidy_extra_args
Expand Down Expand Up @@ -255,12 +273,14 @@ def check_notes(self, clang_tidy_output):

def run(self):
self.read_input()
self.get_prefixes()
if self.export_fixes is None:
self.get_prefixes()
self.prepare_test_inputs()
clang_tidy_output = self.run_clang_tidy()
self.check_fixes()
self.check_messages(clang_tidy_output)
self.check_notes(clang_tidy_output)
if self.export_fixes is None:
self.check_fixes()
self.check_messages(clang_tidy_output)
self.check_notes(clang_tidy_output)


def expand_std(std):
Expand All @@ -284,7 +304,11 @@ def csv(string):


def parse_arguments():
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(
prog=pathlib.Path(__file__).stem,
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("-expect-clang-tidy-error", action="store_true")
parser.add_argument("-resource-dir")
parser.add_argument("-assume-filename")
Expand All @@ -298,7 +322,19 @@ def parse_arguments():
type=csv,
help="comma-separated list of FileCheck suffixes",
)
parser.add_argument("-std", type=csv, default=["c++11-or-later"])
parser.add_argument(
"-export-fixes",
default=None,
type=str,
metavar="file",
help="A file to export fixes into instead of fixing.",
)
parser.add_argument(
"-std",
type=csv,
default=["c++11-or-later"],
help="Passed to clang. Special -or-later values are expanded.",
)
return parser.parse_known_args()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,16 @@ struct S {
};

} // namespace deleted_functions

namespace unused_arguments {

template<typename F>
void unused_argument1(F&&) {}

template<typename F>
void unused_argument2([[maybe_unused]] F&& f) {}

template<typename F>
void unused_argument3(F&& _) {}

} // namespace unused_arguments
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ ForwardIt unique(ForwardIt, ForwardIt);
template <class InputIt, class T>
InputIt find(InputIt, InputIt, const T&);

struct unique_disposable {
void* release();
};

class error_code {
};

Expand Down Expand Up @@ -63,4 +67,6 @@ void noWarning() {
// bugprone-unused-return-value's checked return types.
errorFunc();
(void) errorFunc();

std::unique_disposable{}.release();
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,18 @@ void concatenate3(Args... args)
(..., (stream << args));
}
} // namespace gh70323

namespace gh60895 {

template <class T> void f1(T &&a);
template <class T> void f2(T &&a);
template <class T> void f1(T &&a) { f2<T>(a); }
template <class T> void f2(T &&a) { f1<T>(a); }
void f() {
int x = 0;
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'x' of type 'int' can be declared 'const'
// CHECK-FIXES: int const x = 0;
f1(x);
}

} // namespace gh60895
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,30 @@ void f2() {
return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: f1();
}

void f3(bool b) {
if (b) return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: if (b) { f1(); return;
// CHECK-NEXT: }
return f2();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: f2();
// CHECK-FIXES-LENIENT: f2();
}

template<class T>
T f4() {}

void f5() {
return f4<void>();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
{ return f4<void>(); }
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:7: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: { f4<void>(); return; }
// CHECK-FIXES-LENIENT: { f4<void>(); return; }
}

void f6() { return; }
Expand All @@ -41,6 +48,8 @@ void f9() {
return (void)f7();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: (void)f7();
// CHECK-FIXES-LENIENT: (void)f7();
}

#define RETURN_VOID return (void)1
Expand All @@ -50,12 +59,12 @@ void f10() {
// CHECK-MESSAGES-INCLUDE-MACROS: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
}

template <typename A>
template <typename A>
struct C {
C(A) {}
};

template <class T>
template <class T>
C<T> f11() { return {}; }

using VOID = void;
Expand All @@ -66,4 +75,36 @@ VOID f13() {
return f12();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: f12(); return;
// CHECK-FIXES-LENIENT: f12(); return;
(void)1;
}

void f14() {
return /* comment */ f1() /* comment */ ;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: /* comment */ f1() /* comment */ ; return;
// CHECK-FIXES-LENIENT: /* comment */ f1() /* comment */ ; return;
(void)1;
}

void f15() {
return/*comment*/f1()/*comment*/;//comment
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: /*comment*/f1()/*comment*/; return;//comment
// CHECK-FIXES-LENIENT: /*comment*/f1()/*comment*/; return;//comment
(void)1;
}

void f16(bool b) {
if (b) return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: if (b) { f1(); return;
// CHECK-NEXT: }
else return f2();
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: else { f2(); return;
// CHECK-NEXT: }
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ int r;
// CHECK-FIXES: {{^int q;$}}
// CHECK-FIXES-NEXT: {{^#include <sys/types.h>$}}
// CHECK-FIXES-NEXT: {{^int r;$}}

namespace Issue_87303 {
#define RESET_INCLUDE_CACHE
// Expect no warnings

#define MACRO_FILENAME "duplicate-include.h"
#include MACRO_FILENAME
#include "duplicate-include.h"

#define MACRO_FILENAME_2 <duplicate-include2.h>
#include <duplicate-include2.h>
#include MACRO_FILENAME_2

#undef RESET_INCLUDE_CACHE
} // Issue_87303
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// RUN: %check_clang_tidy %s readability-enum-initial-value %t
// RUN: %check_clang_tidy -check-suffix=ENABLE %s readability-enum-initial-value %t -- \
// RUN: -config='{CheckOptions: { \
// RUN: readability-enum-initial-value.AllowExplicitZeroFirstInitialValue: false, \
// RUN: readability-enum-initial-value.AllowExplicitSequentialInitialValues: false, \
// RUN: }}'

enum EError {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EError' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: inital values in enum 'EError' are not consistent
EError_a = 1,
EError_b,
// CHECK-FIXES: EError_b = 2,
EError_c = 3,
};

enum ENone {
ENone_a,
ENone_b,
EENone_c,
};

enum EFirst {
EFirst_a = 1,
EFirst_b,
EFirst_c,
};

enum EAll {
EAll_a = 1,
EAll_b = 2,
EAll_c = 4,
};

#define ENUMERATOR_1 EMacro1_b
enum EMacro1 {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EMacro1' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: inital values in enum 'EMacro1' are not consistent
EMacro1_a = 1,
ENUMERATOR_1,
// CHECK-FIXES: ENUMERATOR_1 = 2,
EMacro1_c = 3,
};


#define ENUMERATOR_2 EMacro2_b = 2
enum EMacro2 {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EMacro2' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: inital values in enum 'EMacro2' are not consistent
EMacro2_a = 1,
ENUMERATOR_2,
EMacro2_c,
// CHECK-FIXES: EMacro2_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
// CHECK-FIXES-ENABLE: EnumZeroFirstInitialValue_0 ,
EnumZeroFirstInitialValue_1,
EnumZeroFirstInitialValue_2,
};

enum EnumZeroFirstInitialValueWithComment {
EnumZeroFirstInitialValueWithComment_0 = /* == */ 0,
// CHECK-MESSAGES-ENABLE: :[[@LINE-1]]:3: warning: zero initial value for the first enumerator in 'EnumZeroFirstInitialValueWithComment' can be disregarded
// CHECK-FIXES-ENABLE: EnumZeroFirstInitialValueWithComment_0 /* == */ ,
EnumZeroFirstInitialValueWithComment_1,
EnumZeroFirstInitialValueWithComment_2,
};

enum EnumSequentialInitialValue {
// CHECK-MESSAGES-ENABLE: :[[@LINE-1]]:1: warning: sequential initial value in 'EnumSequentialInitialValue' can be ignored
EnumSequentialInitialValue_0 = 2,
// CHECK-FIXES-ENABLE: EnumSequentialInitialValue_0 = 2,
EnumSequentialInitialValue_1 = 3,
// CHECK-FIXES-ENABLE: EnumSequentialInitialValue_1 ,
EnumSequentialInitialValue_2 = 4,
// CHECK-FIXES-ENABLE: EnumSequentialInitialValue_2 ,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %check_clang_tidy %s readability-enum-initial-value %t

enum class EError {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EError' are not consistent
EError_a = 1,
EError_b,
// CHECK-FIXES: EError_b = 2,
EError_c = 3,
};

enum class ENone {
ENone_a,
ENone_b,
EENone_c,
};

enum class EFirst {
EFirst_a = 1,
EFirst_b,
EFirst_c,
};

enum class EAll {
EAll_a = 1,
EAll_b = 2,
EAll_c = 3,
};
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ USER_NS::object g_s2;
// NO warnings or fixes expected as USER_NS and object are declared in a header file

SYSTEM_MACRO(var1);
// NO warnings or fixes expected as var1 is from macro expansion
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: invalid case style for global variable 'var1' [readability-identifier-naming]
// CHECK-FIXES: {{^}}SYSTEM_MACRO(g_var1);

USER_MACRO(var2);
// NO warnings or fixes expected as var2 is declared in a macro expansion
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: invalid case style for global variable 'var2' [readability-identifier-naming]
// CHECK-FIXES: {{^}}USER_MACRO(g_var2);

#define BLA int FOO_bar
BLA;
Expand Down Expand Up @@ -602,9 +604,20 @@ static void static_Function() {
// CHECK-FIXES: {{^}}#define MY_TEST_MACRO(X) X()

void MY_TEST_Macro(function) {}
// CHECK-FIXES: {{^}}void MY_TEST_MACRO(function) {}
}
}
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: invalid case style for global function 'function' [readability-identifier-naming]
// CHECK-FIXES: {{^}}void MY_TEST_MACRO(Function) {}

#define MY_CAT_IMPL(l, r) l ## r
#define MY_CAT(l, r) MY_CAT_IMPL(l, r)
#define MY_MACRO2(foo) int MY_CAT(awesome_, MY_CAT(foo, __COUNTER__)) = 0
#define MY_MACRO3(foo) int MY_CAT(awesome_, foo) = 0
MY_MACRO2(myglob);
MY_MACRO3(myglob);
// No suggestions should occur even though the resulting decl of awesome_myglob#
// or awesome_myglob are not entirely within a macro argument.

} // namespace InlineNamespace
} // namespace FOO_NS

template <typename t_t> struct a {
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: invalid case style for struct 'a'
Expand Down Expand Up @@ -766,3 +779,13 @@ STATIC_MACRO void someFunc(MyFunPtr, const MyFunPtr****) {}
// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(my_fun_ptr_t, const my_fun_ptr_t****) {}
#undef STATIC_MACRO
}

struct Some_struct {
int SomeMember;
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for public member 'SomeMember' [readability-identifier-naming]
// CHECK-FIXES: {{^}} int some_member;
};
Some_struct g_s1{ .SomeMember = 1 };
// CHECK-FIXES: {{^}}Some_struct g_s1{ .some_member = 1 };
Some_struct g_s2{.SomeMember=1};
// CHECK-FIXES: {{^}}Some_struct g_s2{.some_member=1};
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// RUN: not clang-tidy -checks='-*,modernize-use-override' %T/diagnostics/input.cpp -- -DCOMPILATION_ERROR 2>&1 | FileCheck -check-prefix=CHECK6 -implicit-check-not='{{warning:|error:}}' %s
// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE -std=c++20 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined,clang-diagnostic-literal-conversion' %s -- -DMACRO_FROM_COMMAND_LINE -std=c++20 -Wno-macro-redefined | FileCheck --check-prefix=CHECK7 -implicit-check-not='{{warning:|error:}}' %s
// RUN: not clang-tidy -checks='-*,modernize-use-override' %s -- -std=c++20 -DPR64602 | FileCheck -check-prefix=CHECK8 -implicit-check-not='{{warning:|error:}}' %s
// RUN: clang-tidy -checks='-*,modernize-use-override' %s -- -std=c++20 -DPR64602

// CHECK1: error: no input files [clang-diagnostic-error]
// CHECK1: error: no such file or directory: '{{.*}}nonexistent.cpp' [clang-diagnostic-error]
Expand Down Expand Up @@ -68,6 +68,4 @@ auto S<>::foo(auto)
{
return 1;
}
// CHECK8: error: conflicting types for 'foo' [clang-diagnostic-error]
// CHECK8: note: previous declaration is here
#endif
7 changes: 7 additions & 0 deletions clang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ if(CLANG_ENABLE_LIBXML2)
endif()
endif()

if(CLANG_ENABLE_CIR)
if (NOT "${LLVM_ENABLE_PROJECTS}" MATCHES "MLIR|mlir")
message(FATAL_ERROR
"Cannot build ClangIR without MLIR in LLVM_ENABLE_PROJECTS")
endif()
endif()

include(CheckIncludeFile)
check_include_file(sys/resource.h CLANG_HAVE_RLIMITS)

Expand Down
1 change: 1 addition & 0 deletions clang/cmake/caches/Apple-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
set(LLVM_ENABLE_MODULES ON CACHE BOOL "")
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
set(LLVM_ENABLE_EXPORTED_SYMBOLS_IN_EXECUTABLES OFF CACHE BOOL "")
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
set(CLANG_SPAWN_CC1 ON CACHE BOOL "")
set(BUG_REPORT_URL "http://developer.apple.com/bugreporter/" CACHE STRING "")
Expand Down
42 changes: 32 additions & 10 deletions clang/cmake/caches/CrossWinToARMLinux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
# cmake --build . --target check-cxxabi-<TOOLCHAIN_TARGET_TRIPLE>
# cmake --build . --target check-unwind-<TOOLCHAIN_TARGET_TRIPLE>
# cmake --build . --target check-cxx-<TOOLCHAIN_TARGET_TRIPLE>
# (another way to execute the tests)
# python bin/llvm-lit.py -v --threads=32 runtimes/runtimes-<TOOLCHAIN_TARGET_TRIPLE>bins/libunwind/test 2>&1 | tee libunwind-tests.log
# python bin/llvm-lit.py -v --threads=32 runtimes/runtimes-<TOOLCHAIN_TARGET_TRIPLE>-bins/libcxxabi/test 2>&1 | tee libcxxabi-tests.log
# python bin/llvm-lit.py -v --threads=32 runtimes/runtimes-<TOOLCHAIN_TARGET_TRIPLE>-bins/libcxx/test 2>&1 | tee libcxx-tests.log


# LLVM_PROJECT_DIR is the path to the llvm-project directory.
# The right way to compute it would probably be to use "${CMAKE_SOURCE_DIR}/../",
Expand All @@ -42,9 +47,6 @@ if (NOT DEFINED DEFAULT_SYSROOT)
message(WARNING "DEFAULT_SYSROOT must be specified for the cross toolchain build.")
endif()

if (NOT DEFINED LLVM_TARGETS_TO_BUILD)
set(LLVM_TARGETS_TO_BUILD "ARM" CACHE STRING "")
endif()
if (NOT DEFINED LLVM_ENABLE_ASSERTIONS)
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
endif()
Expand All @@ -56,7 +58,7 @@ if (NOT DEFINED LLVM_ENABLE_RUNTIMES)
endif()

if (NOT DEFINED TOOLCHAIN_TARGET_TRIPLE)
set(TOOLCHAIN_TARGET_TRIPLE "armv7-unknown-linux-gnueabihf")
set(TOOLCHAIN_TARGET_TRIPLE "aarch64-unknown-linux-gnu")
else()
#NOTE: we must normalize specified target triple to a fully specified triple,
# including the vendor part. It is necessary to synchronize the runtime library
Expand All @@ -74,24 +76,38 @@ else()
string(REPLACE ";" "-" TOOLCHAIN_TARGET_TRIPLE "${TOOLCHAIN_TARGET_TRIPLE}")
endif()

message(STATUS "Toolchain target triple: ${TOOLCHAIN_TARGET_TRIPLE}")

if (NOT DEFINED LLVM_TARGETS_TO_BUILD)
if ("${TOOLCHAIN_TARGET_TRIPLE}" MATCHES "^(armv|arm32)+")
set(LLVM_TARGETS_TO_BUILD "ARM" CACHE STRING "")
endif()
if ("${TOOLCHAIN_TARGET_TRIPLE}" MATCHES "^(aarch64|arm64)+")
set(LLVM_TARGETS_TO_BUILD "AArch64" CACHE STRING "")
endif()
endif()

message(STATUS "Toolchain target to build: ${LLVM_TARGETS_TO_BUILD}")

if (NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
endif()

message(STATUS "Toolchain target triple: ${TOOLCHAIN_TARGET_TRIPLE}")

set(CMAKE_CROSSCOMPILING ON CACHE BOOL "")
set(CMAKE_CL_SHOWINCLUDES_PREFIX "Note: including file: " CACHE STRING "")
# Required if COMPILER_RT_DEFAULT_TARGET_ONLY is ON
set(CMAKE_C_COMPILER_TARGET "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")
set(CMAKE_CXX_COMPILER_TARGET "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")

set(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR ON CACHE BOOL "")
set(LLVM_DEFAULT_TARGET_TRIPLE "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")
set(LLVM_TARGET_ARCH "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")
set(LLVM_LIT_ARGS "-vv ${LLVM_LIT_ARGS}" CACHE STRING "" FORCE)

set(CLANG_DEFAULT_CXX_STDLIB "libc++" CACHE STRING "")
set(CLANG_DEFAULT_LINKER "lld" CACHE STRING "")
set(CLANG_DEFAULT_OBJCOPY "llvm-objcopy" CACHE STRING "")
set(CLANG_DEFAULT_RTLIB "compiler-rt" CACHE STRING "")
set(CLANG_DEFAULT_UNWINDLIB "libunwind" CACHE STRING "")

if(WIN32)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded" CACHE STRING "")
Expand All @@ -109,9 +125,10 @@ set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSTEM_NAME
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSROOT "${DEFAULT_SYSROOT}" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_INSTALL_RPATH "${RUNTIMES_INSTALL_RPATH}" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")

set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_LLVM_CMAKE_DIR "${LLVM_PROJECT_DIR}/llvm/cmake/modules" CACHE PATH "")

set(LLVM_RUNTIME_TARGETS "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")
set(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR ON CACHE BOOL "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LLVM_ENABLE_RUNTIMES "${LLVM_ENABLE_RUNTIMES}" CACHE STRING "")

Expand All @@ -125,13 +142,16 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_SANITIZERS
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_XRAY OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_LIBFUZZER OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_PROFILE OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_CRT OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_CRT ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_BUILD_ORC OFF CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_DEFAULT_TARGET_ONLY ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_INCLUDE_TESTS ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_CAN_EXECUTE_TESTS ON CACHE BOOL "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_USE_BUILTINS_LIBRARY ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_CXX_LIBRARY libcxx CACHE STRING "")
# Tell Clang to seach C++ headers alongside with the just-built binaries for the C++ compiler-rt tests.
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_COMPILER_RT_TEST_COMPILER_CFLAGS "--stdlib=libc++" CACHE STRING "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBUNWIND_ENABLE_SHARED OFF CACHE BOOL "")
Expand All @@ -148,8 +168,10 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ABI_VERSION
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI "libcxxabi" CACHE STRING "") #!!!
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "")


# Avoid searching for the python3 interpreter during the runtimes configuration for the cross builds.
# It starts searching the python3 package using the target's sysroot path, that usually is not compatible with the build host.
find_package(Python3 COMPONENTS Interpreter)
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_Python3_EXECUTABLE ${Python3_EXECUTABLE} CACHE PATH "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBUNWIND_TEST_PARAMS_default "${RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_TEST_PARAMS}")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_TEST_PARAMS_default "${RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_TEST_PARAMS}")
Expand Down
2 changes: 2 additions & 0 deletions clang/cmake/caches/Fuchsia.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ set(_FUCHSIA_BOOTSTRAP_PASSTHROUGH
Python3_LIBRARIES
Python3_INCLUDE_DIRS
Python3_RPATH
SWIG_DIR
SWIG_EXECUTABLE
CMAKE_FIND_PACKAGE_PREFER_CONFIG
CMAKE_SYSROOT
CMAKE_MODULE_LINKER_FLAGS
Expand Down
2 changes: 1 addition & 1 deletion clang/cmake/caches/Release.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# General Options
set(LLVM_RELEASE_ENABLE_LTO THIN CACHE STRING "")
set(LLVM_RELEASE_ENABLE_PGO ON CACHE BOOL "")
set(LLVM_RELEASE_ENABLE_PGO OFF CACHE BOOL "")

set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")

Expand Down
2 changes: 1 addition & 1 deletion clang/docs/ClangFormat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
Objective-C: .m .mm
Proto: .proto .protodevel
TableGen: .td
TextProto: .textpb .pb.txt .textproto .asciipb
TextProto: .txtpb .textpb .pb.txt .textproto .asciipb
Verilog: .sv .svh .v .vh
--cursor=<uint> - The position of the cursor when invoking
clang-format from an editor integration
Expand Down
15 changes: 15 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3295,6 +3295,21 @@ the configuration (without a prefix: ``Auto``).



.. _BreakFunctionDefinitionParameters:

**BreakFunctionDefinitionParameters** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ <BreakFunctionDefinitionParameters>`
If ``true``, clang-format will always break before function definition
parameters.

.. code-block:: c++

true:
void functionDefinition(
int A, int B) {}

false:
void functionDefinition(int A, int B) {}

.. _BreakInheritanceList:

**BreakInheritanceList** (``BreakInheritanceListStyle``) :versionbadge:`clang-format 7` :ref:`¶ <BreakInheritanceList>`
Expand Down
14 changes: 14 additions & 0 deletions clang/docs/HIPSupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,20 @@ Host Code Compilation
- These relocatable objects are then linked together.
- Host code within a TU can call host functions and launch kernels from another TU.

Syntax Difference with CUDA
===========================

Clang's front end, used for both CUDA and HIP programming models, shares the same parsing and semantic analysis mechanisms. This includes the resolution of overloads concerning device and host functions. While there exists a comprehensive documentation on the syntax differences between Clang and NVCC for CUDA at `Dialect Differences Between Clang and NVCC <https://llvm.org/docs/CompileCudaWithLLVM.html#dialect-differences-between-clang-and-nvcc>`_, it is important to note that these differences also apply to HIP code compilation.

Predefined Macros for Differentiation
-------------------------------------

To facilitate differentiation between HIP and CUDA code, as well as between device and host compilations within HIP, Clang defines specific macros:

- ``__HIP__`` : This macro is defined only when compiling HIP code. It can be used to conditionally compile code specific to HIP, enabling developers to write portable code that can be compiled for both CUDA and HIP.

- ``__HIP_DEVICE_COMPILE__`` : Defined exclusively during HIP device compilation, this macro allows for conditional compilation of device-specific code. It provides a mechanism to segregate device and host code, ensuring that each can be optimized for their respective execution environments.

Function Pointers Support
=========================

Expand Down
31 changes: 16 additions & 15 deletions clang/docs/HLSL/FunctionCalls.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,22 +157,23 @@ Clang Implementation
of the changes in the prototype implementation are restoring Clang-3.7 code
that was previously modified to its original state.

The implementation in clang depends on two new AST nodes and minor extensions to
Clang's existing support for Objective-C write-back arguments. The goal of this
design is to capture the semantic details of HLSL function calls in the AST, and
minimize the amount of magic that needs to occur during IR generation.

The two new AST nodes are ``HLSLArrayTemporaryExpr`` and ``HLSLOutParamExpr``,
which respectively represent the temporaries used for passing arrays by value
and the temporaries created for function outputs.
The implementation in clang adds a new non-decaying array type, a new AST node
to represent output parameters, and minor extensions to Clang's existing support
for Objective-C write-back arguments. The goal of this design is to capture the
semantic details of HLSL function calls in the AST, and minimize the amount of
magic that needs to occur during IR generation.

Array Temporaries
-----------------

The ``HLSLArrayTemporaryExpr`` represents temporary values for input
constant-sized array arguments. This applies for all constant-sized array
arguments regardless of whether or not the parameter is constant-sized or
unsized.
The new ``ArrayParameterType`` is a sub-class of ``ConstantArrayType``
inheriting all the behaviors and methods of the parent except that it does not
decay to a pointer during overload resolution or template type deduction.

An argument of ``ConstantArrayType`` can be implicitly converted to an
equivalent non-decayed ``ArrayParameterType`` if the underlying canonical
``ConstantArrayType`` is the same. This occurs during overload resolution
instead of array to pointer decay.

.. code-block:: c++

Expand All @@ -193,7 +194,7 @@ In the example above, the following AST is generated for the call to
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(float [4])' <FunctionToPointerDecay>
| `-DeclRefExpr 'void (float [4])' lvalue Function 'SizedArray' 'void (float [4])'
`-HLSLArrayTemporaryExpr 'float [4]'
`-ImplicitCastExpr 'float [4]' <HLSLArrayRValue>
`-DeclRefExpr 'float [4]' lvalue Var 'arr' 'float [4]'
In the example above, the following AST is generated for the call to
Expand All @@ -204,7 +205,7 @@ In the example above, the following AST is generated for the call to
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(float [])' <FunctionToPointerDecay>
| `-DeclRefExpr 'void (float [])' lvalue Function 'UnsizedArray' 'void (float [])'
`-HLSLArrayTemporaryExpr 'float [4]'
`-ImplicitCastExpr 'float [4]' <HLSLArrayRValue>
`-DeclRefExpr 'float [4]' lvalue Var 'arr' 'float [4]'
In both of these cases the argument expression is of known array size so we can
Expand Down Expand Up @@ -236,7 +237,7 @@ An expected AST should be something like:
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(float [])' <FunctionToPointerDecay>
| `-DeclRefExpr 'void (float [])' lvalue Function 'UnsizedArray' 'void (float [])'
`-HLSLArrayTemporaryExpr 'float [4]'
`-ImplicitCastExpr 'float [4]' <HLSLArrayRValue>
`-DeclRefExpr 'float [4]' lvalue Var 'arr' 'float [4]'
Out Parameter Temporaries
Expand Down
50 changes: 50 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+
``if consteval`` __cpp_if_consteval C++23 C++20
``static operator()`` __cpp_static_call_operator C++23 C++03
Attributes on Lambda-Expressions C++23 C++11
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
-------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
Expand Down Expand Up @@ -1610,6 +1611,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_pod`` (C++, GNU, Microsoft, Embarcadero):
Note, the corresponding standard trait was deprecated in C++20.
* ``__is_pointer`` (C++, Embarcadero)
* ``__is_pointer_interconvertible_base_of`` (C++, GNU, Microsoft)
* ``__is_polymorphic`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_reference`` (C++, Embarcadero)
* ``__is_referenceable`` (C++, GNU, Microsoft, Embarcadero):
Expand Down Expand Up @@ -3464,6 +3466,54 @@ Query for this feature with ``__has_builtin(__builtin_trap)``.
``__builtin_arm_trap`` is lowered to the ``llvm.aarch64.break`` builtin, and then to ``brk #payload``.
``__builtin_allow_runtime_check``
---------------------------------
``__builtin_allow_runtime_check`` return true if the check at the current
program location should be executed. It is expected to be used to implement
``assert`` like checks which can be safely removed by optimizer.
**Syntax**:
.. code-block:: c++
bool __builtin_allow_runtime_check(const char* kind)
**Example of use**:
.. code-block:: c++
if (__builtin_allow_runtime_check("mycheck") && !ExpensiveCheck()) {
abort();
}
**Description**
``__builtin_allow_runtime_check`` is lowered to ` ``llvm.allow.runtime.check``
<https://llvm.org/docs/LangRef.html#llvm-allow-runtime-check-intrinsic>`_
builtin.
The ``__builtin_allow_runtime_check()`` is expected to be used with control
flow conditions such as in ``if`` to guard expensive runtime checks. The
specific rules for selecting permitted checks can differ and are controlled by
the compiler options.
Flags to control checks:
* ``-mllvm -lower-allow-check-percentile-cutoff-hot=N`` where N is PGO hotness
cutoff in range ``[0, 999999]`` to disallow checks in hot code.
* ``-mllvm -lower-allow-check-random-rate=P`` where P is number in range
``[0.0, 1.0]`` representation probability of keeping a check.
* If both flags are specified, ``-lower-allow-check-random-rate`` takes
precedence.
* If none is specified, ``__builtin_allow_runtime_check`` is lowered as
``true``, allowing all checks.
Parameter ``kind`` is a string literal representing a user selected kind for
guarded check. It's unused now. It will enable kind-specific lowering in future.
E.g. a higher hotness cutoff can be used for more expensive kind of check.
Query for this feature with ``__has_builtin(__builtin_allow_runtime_check)``.
``__builtin_nondeterministic_value``
------------------------------------
Expand Down
Loading