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

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H

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

namespace clang::tidy::modernize {

/// Converts calls to absl::StrFormat, or other functions via configuration
/// options, to C++20's std::format, or another function via a configuration
/// option, modifying the format string appropriately and removing
/// now-unnecessary calls to std::string::c_str() and std::string::data().
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-format.html
class UseStdFormatCheck : public ClangTidyCheck {
public:
UseStdFormatCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
if (ReplacementFormatFunction == "std::format")
return LangOpts.CPlusPlus20;
return LangOpts.CPlusPlus;
}
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
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:
bool StrictMode;
std::vector<StringRef> StrFormatLikeFunctions;
StringRef ReplacementFormatFunction;
utils::IncludeInserter IncludeInserter;
std::optional<StringRef> MaybeHeaderToInclude;
};

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H
5 changes: 4 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,11 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
FormatArgOffset = 1;
}

utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
ConverterConfig.AllowTrailingNewlineRemoval = true;
utils::FormatStringConverter Converter(
Result.Context, Printf, FormatArgOffset, StrictMode, getLangOpts());
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts());
const Expr *PrintfCall = Printf->getCallee();
const StringRef ReplacementFunction = Converter.usePrintNewlineFunction()
? ReplacementPrintlnFunction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ static bool containsDeclInScope(const Stmt *Node) {
}

static void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context,
const Stmt *Else, SourceLocation ElseLoc) {
const Stmt *Else, SourceLocation ElseLoc) {
auto Remap = [&](SourceLocation Loc) {
return Context.getSourceManager().getExpansionLoc(Loc);
};
Expand Down Expand Up @@ -172,7 +172,7 @@ void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
breakStmt().bind(InterruptingStr), cxxThrowExpr().bind(InterruptingStr)));
Finder->addMatcher(
compoundStmt(
forEach(ifStmt(unless(isConstexpr()),
forEach(ifStmt(unless(isConstexpr()), unless(isConsteval()),
hasThen(stmt(
anyOf(InterruptsControlFlow,
compoundStmt(has(InterruptsControlFlow))))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1358,7 +1358,7 @@ IdentifierNamingCheck::getFailureInfo(
std::replace(KindName.begin(), KindName.end(), '_', ' ');

std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND);
if (StringRef(Fixup).equals(Name)) {
if (StringRef(Fixup) == Name) {
if (!IgnoreFailedSplit) {
LLVM_DEBUG(Location.print(llvm::dbgs(), SM);
llvm::dbgs()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "SimplifyBooleanExprCheck.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/SaveAndRestore.h"
Expand Down Expand Up @@ -280,9 +281,8 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
if (!S) {
return true;
}
if (Check->IgnoreMacros && S->getBeginLoc().isMacroID()) {
if (Check->canBeBypassed(S))
return false;
}
if (!shouldIgnore(S))
StmtStack.push_back(S);
return true;
Expand Down Expand Up @@ -513,17 +513,23 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
return true;
}

static bool isUnaryLNot(const Expr *E) {
return isa<UnaryOperator>(E) &&
bool isExpectedUnaryLNot(const Expr *E) {
return !Check->canBeBypassed(E) && isa<UnaryOperator>(E) &&
cast<UnaryOperator>(E)->getOpcode() == UO_LNot;
}

bool isExpectedBinaryOp(const Expr *E) {
const auto *BinaryOp = dyn_cast<BinaryOperator>(E);
return !Check->canBeBypassed(E) && BinaryOp && BinaryOp->isLogicalOp() &&
BinaryOp->getType()->isBooleanType();
}

template <typename Functor>
static bool checkEitherSide(const BinaryOperator *BO, Functor Func) {
return Func(BO->getLHS()) || Func(BO->getRHS());
}

static bool nestedDemorgan(const Expr *E, unsigned NestingLevel) {
bool nestedDemorgan(const Expr *E, unsigned NestingLevel) {
const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreUnlessSpelledInSource());
if (!BO)
return false;
Expand All @@ -539,15 +545,13 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
return true;
case BO_LAnd:
case BO_LOr:
if (checkEitherSide(BO, isUnaryLNot))
return true;
if (NestingLevel) {
if (checkEitherSide(BO, [NestingLevel](const Expr *E) {
return nestedDemorgan(E, NestingLevel - 1);
}))
return true;
}
return false;
return checkEitherSide(
BO,
[this](const Expr *E) { return isExpectedUnaryLNot(E); }) ||
(NestingLevel &&
checkEitherSide(BO, [this, NestingLevel](const Expr *E) {
return nestedDemorgan(E, NestingLevel - 1);
}));
default:
return false;
}
Expand All @@ -556,19 +560,19 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
bool TraverseUnaryOperator(UnaryOperator *Op) {
if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot)
return Base::TraverseUnaryOperator(Op);
Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
auto *Parens = dyn_cast<ParenExpr>(SubImp);
auto *BinaryOp =
Parens
? dyn_cast<BinaryOperator>(Parens->getSubExpr()->IgnoreImplicit())
: dyn_cast<BinaryOperator>(SubImp);
if (!BinaryOp || !BinaryOp->isLogicalOp() ||
!BinaryOp->getType()->isBooleanType())
const Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
const auto *Parens = dyn_cast<ParenExpr>(SubImp);
const Expr *SubExpr =
Parens ? Parens->getSubExpr()->IgnoreImplicit() : SubImp;
if (!isExpectedBinaryOp(SubExpr))
return Base::TraverseUnaryOperator(Op);
const auto *BinaryOp = cast<BinaryOperator>(SubExpr);
if (Check->SimplifyDeMorganRelaxed ||
checkEitherSide(BinaryOp, isUnaryLNot) ||
checkEitherSide(BinaryOp,
[](const Expr *E) { return nestedDemorgan(E, 1); })) {
checkEitherSide(
BinaryOp,
[this](const Expr *E) { return isExpectedUnaryLNot(E); }) ||
checkEitherSide(
BinaryOp, [this](const Expr *E) { return nestedDemorgan(E, 1); })) {
if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(),
Parens) &&
!Check->areDiagsSelfContained()) {
Expand Down Expand Up @@ -694,6 +698,10 @@ void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
Visitor(this, *Result.Context).traverse();
}

bool SimplifyBooleanExprCheck::canBeBypassed(const Stmt *S) const {
return IgnoreMacros && S->getBeginLoc().isMacroID();
}

void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context,
SourceLocation Loc,
StringRef Description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class SimplifyBooleanExprCheck : public ClangTidyCheck {
StringRef Description, SourceRange ReplacementRange,
StringRef Replacement);

bool canBeBypassed(const Stmt *S) const;

const bool IgnoreMacros;
const bool ChainedConditionalReturn;
const bool ChainedConditionalAssignment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,11 @@ static bool applyAbbreviationHeuristic(
const llvm::StringMap<std::string> &AbbreviationDictionary, StringRef Arg,
StringRef Param) {
if (AbbreviationDictionary.contains(Arg) &&
Param.equals(AbbreviationDictionary.lookup(Arg)))
Param == AbbreviationDictionary.lookup(Arg))
return true;

if (AbbreviationDictionary.contains(Param) &&
Arg.equals(AbbreviationDictionary.lookup(Param)))
Arg == AbbreviationDictionary.lookup(Param))
return true;

return false;
Expand Down
18 changes: 18 additions & 0 deletions clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Configuration files:
Checks - Same as '--checks'. Additionally, the list of
globs can be specified as a list instead of a
string.
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
ExtraArgs - Same as '--extra-args'.
ExtraArgsBefore - Same as '--extra-args-before'.
FormatStyle - Same as '--format-style'.
Expand Down Expand Up @@ -132,6 +133,20 @@ option in .clang-tidy file, if any.
cl::init(""),
cl::cat(ClangTidyCategory));

static cl::opt<std::string> ExcludeHeaderFilter("exclude-header-filter",
desc(R"(
Regular expression matching the names of the
headers to exclude diagnostics from. Diagnostics
from the main file of each translation unit are
always displayed.
Must be used together with --header-filter.
Can be used together with -line-filter.
This option overrides the 'ExcludeHeaderFilterRegex'
option in .clang-tidy file, if any.
)"),
cl::init(""),
cl::cat(ClangTidyCategory));

static cl::opt<bool> SystemHeaders("system-headers", desc(R"(
Display the errors from system headers.
This option overrides the 'SystemHeaders' option
Expand Down Expand Up @@ -353,6 +368,7 @@ static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider(
DefaultOptions.Checks = DefaultChecks;
DefaultOptions.WarningsAsErrors = "";
DefaultOptions.HeaderFilterRegex = HeaderFilter;
DefaultOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;
DefaultOptions.SystemHeaders = SystemHeaders;
DefaultOptions.FormatStyle = FormatStyle;
DefaultOptions.User = llvm::sys::Process::GetEnv("USER");
Expand All @@ -367,6 +383,8 @@ static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider(
OverrideOptions.WarningsAsErrors = WarningsAsErrors;
if (HeaderFilter.getNumOccurrences() > 0)
OverrideOptions.HeaderFilterRegex = HeaderFilter;
if (ExcludeHeaderFilter.getNumOccurrences() > 0)
OverrideOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;
if (SystemHeaders.getNumOccurrences() > 0)
OverrideOptions.SystemHeaders = SystemHeaders;
if (FormatStyle.getNumOccurrences() > 0)
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,14 @@ def get_tidy_invocation(
use_color,
plugins,
warnings_as_errors,
exclude_header_filter,
):
"""Gets a command line for clang-tidy."""
start = [clang_tidy_binary]
if allow_enabling_alpha_checkers:
start.append("-allow-enabling-analyzer-alpha-checkers")
if exclude_header_filter is not None:
start.append("--exclude-header-filter=" + exclude_header_filter)
if header_filter is not None:
start.append("-header-filter=" + header_filter)
if line_filter is not None:
Expand Down Expand Up @@ -228,6 +231,7 @@ def run_tidy(args, clang_tidy_binary, tmpdir, build_path, queue, lock, failed_fi
args.use_color,
args.plugins,
args.warnings_as_errors,
args.exclude_header_filter,
)

proc = subprocess.Popen(
Expand Down Expand Up @@ -292,6 +296,14 @@ def main():
"-config option after reading specified config file. "
"Use either -config-file or -config, not both.",
)
parser.add_argument(
"-exclude-header-filter",
default=None,
help="Regular expression matching the names of the "
"headers to exclude diagnostics from. Diagnostics from "
"the main file of each translation unit are always "
"displayed.",
)
parser.add_argument(
"-header-filter",
default=None,
Expand Down Expand Up @@ -450,6 +462,7 @@ def main():
args.use_color,
args.plugins,
args.warnings_as_errors,
args.exclude_header_filter,
)
invocation.append("-list-checks")
invocation.append("-")
Expand Down
16 changes: 10 additions & 6 deletions clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,11 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {
FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
bool StrictMode,
const Configuration ConfigIn,
const LangOptions &LO)
: Context(ContextIn),
CastMismatchedIntegerTypes(castMismatchedIntegerTypes(Call, StrictMode)),
: Context(ContextIn), Config(ConfigIn),
CastMismatchedIntegerTypes(
castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)),
Args(Call->getArgs()), NumArgs(Call->getNumArgs()),
ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {
assert(ArgsOffset <= NumArgs);
Expand Down Expand Up @@ -627,9 +628,12 @@ void FormatStringConverter::finalizeFormatText() {

// It's clearer to convert printf("Hello\r\n"); to std::print("Hello\r\n")
// than to std::println("Hello\r");
if (StringRef(StandardFormatString).ends_with("\\n") &&
!StringRef(StandardFormatString).ends_with("\\\\n") &&
!StringRef(StandardFormatString).ends_with("\\r\\n")) {
// Use StringRef until C++20 std::string::ends_with() is available.
const auto StandardFormatStringRef = StringRef(StandardFormatString);
if (Config.AllowTrailingNewlineRemoval &&
StandardFormatStringRef.ends_with("\\n") &&
!StandardFormatStringRef.ends_with("\\\\n") &&
!StandardFormatStringRef.ends_with("\\r\\n")) {
UsePrintNewlineFunction = true;
FormatStringNeededRewriting = true;
StandardFormatString.erase(StandardFormatString.end() - 2,
Expand Down
9 changes: 8 additions & 1 deletion clang-tools-extra/clang-tidy/utils/FormatStringConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ class FormatStringConverter
public:
using ConversionSpecifier = clang::analyze_format_string::ConversionSpecifier;
using PrintfSpecifier = analyze_printf::PrintfSpecifier;

struct Configuration {
bool StrictMode = false;
bool AllowTrailingNewlineRemoval = false;
};

FormatStringConverter(ASTContext *Context, const CallExpr *Call,
unsigned FormatArgOffset, bool StrictMode,
unsigned FormatArgOffset, Configuration Config,
const LangOptions &LO);

bool canApply() const { return ConversionNotPossibleReason.empty(); }
Expand All @@ -45,6 +51,7 @@ class FormatStringConverter

private:
ASTContext *Context;
const Configuration Config;
const bool CastMismatchedIntegerTypes;
const Expr *const *Args;
const unsigned NumArgs;
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ determineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
if (FileCopy.consume_front(Parts.first) &&
FileCopy.consume_back(Parts.second)) {
// Determine the kind of this inclusion.
if (FileCopy.equals("/internal/") ||
FileCopy.equals("/proto/")) {
if (FileCopy == "/internal/" || FileCopy == "/proto/") {
return IncludeSorter::IK_MainTUInclude;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ static const NamedDecl *findDecl(const RecordDecl &RecDecl,
StringRef DeclName) {
for (const Decl *D : RecDecl.decls()) {
if (const auto *ND = dyn_cast<NamedDecl>(D)) {
if (ND->getDeclName().isIdentifier() && ND->getName().equals(DeclName))
if (ND->getDeclName().isIdentifier() && ND->getName() == DeclName)
return ND;
}
}
Expand Down
37 changes: 11 additions & 26 deletions clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,11 @@ getTemplateSpecializationArgLocs(const NamedDecl &ND) {
if (const ASTTemplateArgumentListInfo *Args =
Func->getTemplateSpecializationArgsAsWritten())
return Args->arguments();
} else if (auto *Cls =
llvm::dyn_cast<ClassTemplatePartialSpecializationDecl>(&ND)) {
} else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND)) {
if (auto *Args = Cls->getTemplateArgsAsWritten())
return Args->arguments();
} else if (auto *Var =
llvm::dyn_cast<VarTemplatePartialSpecializationDecl>(&ND)) {
if (auto *Args = Var->getTemplateArgsAsWritten())
return Args->arguments();
} else if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&ND)) {
if (auto *Args = Var->getTemplateArgsInfo())
if (auto *Args = Var->getTemplateArgsAsWritten())
return Args->arguments();
}
// We return std::nullopt for ClassTemplateSpecializationDecls because it does
Expand Down Expand Up @@ -270,22 +265,10 @@ std::string printTemplateSpecializationArgs(const NamedDecl &ND) {
getTemplateSpecializationArgLocs(ND)) {
printTemplateArgumentList(OS, *Args, Policy);
} else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND)) {
if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) {
// ClassTemplateSpecializationDecls do not contain
// TemplateArgumentTypeLocs, they only have TemplateArgumentTypes. So we
// create a new argument location list from TypeSourceInfo.
auto STL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
llvm::SmallVector<TemplateArgumentLoc> ArgLocs;
ArgLocs.reserve(STL.getNumArgs());
for (unsigned I = 0; I < STL.getNumArgs(); ++I)
ArgLocs.push_back(STL.getArgLoc(I));
printTemplateArgumentList(OS, ArgLocs, Policy);
} else {
// FIXME: Fix cases when getTypeAsWritten returns null inside clang AST,
// e.g. friend decls. Currently we fallback to Template Arguments without
// location information.
printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy);
}
// FIXME: Fix cases when getTypeAsWritten returns null inside clang AST,
// e.g. friend decls. Currently we fallback to Template Arguments without
// location information.
printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy);
}
OS.flush();
return TemplateArgs;
Expand Down Expand Up @@ -453,10 +436,12 @@ bool hasReservedScope(const DeclContext &DC) {
}

QualType declaredType(const TypeDecl *D) {
ASTContext &Context = D->getASTContext();
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D))
if (const auto *TSI = CTSD->getTypeAsWritten())
return TSI->getType();
return D->getASTContext().getTypeDeclType(D);
if (const auto *Args = CTSD->getTemplateArgsAsWritten())
return Context.getTemplateSpecializationType(
TemplateName(CTSD->getSpecializedTemplate()), Args->arguments());
return Context.getTypeDeclType(D);
}

namespace {
Expand Down
13 changes: 8 additions & 5 deletions clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,17 +693,22 @@ class CollectExtraHighlightings
return true;
}

bool
VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D) {
if (auto *Args = D->getTemplateArgsAsWritten())
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
return true;
}

bool VisitClassTemplatePartialSpecializationDecl(
ClassTemplatePartialSpecializationDecl *D) {
if (auto *TPL = D->getTemplateParameters())
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
if (auto *Args = D->getTemplateArgsAsWritten())
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
return true;
}

bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *D) {
if (auto *Args = D->getTemplateArgsInfo())
if (auto *Args = D->getTemplateArgsAsWritten())
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
return true;
}
Expand All @@ -712,8 +717,6 @@ class CollectExtraHighlightings
VarTemplatePartialSpecializationDecl *D) {
if (auto *TPL = D->getTemplateParameters())
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
if (auto *Args = D->getTemplateArgsAsWritten())
H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc());
return true;
}

Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clangd/refactor/Rename.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,11 +1090,10 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
return MainFileRenameEdit.takeError();

llvm::DenseSet<Range> RenamedRanges;
if (const auto *MD = dyn_cast<ObjCMethodDecl>(&RenameDecl)) {
if (!isa<ObjCMethodDecl>(RenameDecl)) {
// TODO: Insert the ranges from the ObjCMethodDecl/ObjCMessageExpr selector
// pieces which are being renamed. This will require us to make changes to
// locateDeclAt to preserve this AST node.
} else {
RenamedRanges.insert(CurrentIdentifier);
}

Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/unittests/SelectionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,12 @@ TEST(SelectionTest, CommonAncestor) {
auto x = [[ns::^C<int>]];
)cpp",
"ConceptReference"},
{R"cpp(
template <typename T, typename K>
concept D = true;
template <typename T> void g(D<[[^T]]> auto abc) {}
)cpp",
"TemplateTypeParmTypeLoc"},
};

for (const Case &C : Cases) {
Expand Down
40 changes: 39 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ Improvements to clang-doc
Improvements to clang-query
---------------------------

The improvements are...
- Added the `file` command to dynamically load a list of commands and matchers
from an external file, allowing the cost of reading the compilation database
and building the AST to be imposed just once for faster prototyping.

- Removed support for ``enable output srcloc``. Fixes #GH82591

Improvements to clang-rename
----------------------------
Expand All @@ -113,6 +117,9 @@ Improvements to clang-tidy
- Fixed `--verify-config` option not properly parsing checks when using the
literal operator in the `.clang-tidy` config.

- Added argument `--exclude-header-filter` and config option `ExcludeHeaderFilterRegex`
to exclude headers from analysis via a RegEx.

New checks
^^^^^^^^^^

Expand Down Expand Up @@ -148,6 +155,15 @@ New checks
Finds initializer lists for aggregate types that could be
written as designated initializers instead.

- New :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check.

Converts calls to ``absl::StrFormat``, or other functions via
configuration options, to C++20's ``std::format``, or another function
via a configuration option, modifying the format string appropriately and
removing now-unnecessary calls to ``std::string::c_str()`` and
``std::string::data()``.

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

Expand Down Expand Up @@ -202,6 +218,10 @@ Changes in existing checks
eliminating false positives resulting from direct usage of bitwise operators
within parentheses.

- Improved :doc:`bugprone-optional-value-conversion
<clang-tidy/checks/bugprone/optional-value-conversion>` check by eliminating
false positives resulting from use of optionals in unevaluated context.

- Improved :doc:`bugprone-suspicious-include
<clang-tidy/checks/bugprone/suspicious-include>` check by replacing the local
options `HeaderFileExtensions` and `ImplementationFileExtensions` by the
Expand Down Expand Up @@ -244,6 +264,12 @@ Changes in existing checks
<clang-tidy/checks/cppcoreguidelines/use-default-member-init>`. Fixed
incorrect hints when using list-initialization.

- Improved :doc:`cppcoreguidelines-special-member-functions
<clang-tidy/checks/cppcoreguidelines/special-member-functions>` check with a
new option `AllowImplicitlyDeletedCopyOrMove`, which removes the requirement
for explicit copy or move special member functions when they are already
implicitly deleted.

- Improved :doc:`google-build-namespaces
<clang-tidy/checks/google/build-namespaces>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.
Expand Down Expand Up @@ -304,6 +330,10 @@ Changes in existing checks
don't remove parentheses used in ``sizeof`` calls when they have array index
accesses as arguments.

- Improved :doc:`modernize-use-constraints
<clang-tidy/checks/modernize/use-constraints>` check by fixing a crash that
occurred in some scenarios and excluding system headers from analysis.

- Improved :doc:`modernize-use-nullptr
<clang-tidy/checks/modernize/use-nullptr>` check to include support for C23,
which also has introduced the ``nullptr`` keyword.
Expand Down Expand Up @@ -337,6 +367,10 @@ Changes in existing checks
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.

- Improved :doc:`readability-else-after-return
<clang-tidy/checks/readability/else-after-return>` check to ignore
`if consteval` statements, for which the `else` branch must not be removed.

- 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
Expand All @@ -357,6 +391,10 @@ Changes in existing checks
support calls to overloaded operators as base expression and provide fixes to
expressions with side-effects.

- Improved :doc:`readability-simplify-boolean-expr
<clang-tidy/checks/readability/simplify-boolean-expr>` check to avoid to emit
warning for macro when IgnoreMacro option is enabled.

- Improved :doc:`readability-static-definition-in-anonymous-namespace
<clang-tidy/checks/readability/static-definition-in-anonymous-namespace>`
check by resolving fix-it overlaps in template code by disregarding implicit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ Options

.. option:: AllowMissingMoveFunctions

When set to `true` (default is `false`), this check doesn't flag classes which define no move
operations at all. It still flags classes which define only one of either
move constructor or move assignment operator. With this option enabled, the following class won't be flagged:
When set to `true` (default is `false`), this check doesn't flag classes
which define no move operations at all. It still flags classes which define
only one of either move constructor or move assignment operator. With this
option enabled, the following class won't be flagged:

.. code-block:: c++

Expand All @@ -59,10 +60,11 @@ Options

.. option:: AllowMissingMoveFunctionsWhenCopyIsDeleted

When set to `true` (default is `false`), this check doesn't flag classes which define deleted copy
operations but don't define move operations. This flag is related to Google C++ Style Guide
https://google.github.io/styleguide/cppguide.html#Copyable_Movable_Types. With this option enabled, the
following class won't be flagged:
When set to `true` (default is `false`), this check doesn't flag classes
which define deleted copy operations but don't define move operations. This
flag is related to Google C++ Style Guide `Copyable and Movable Types
<https://google.github.io/styleguide/cppguide.html#Copyable_Movable_Types>`_.
With this option enabled, the following class won't be flagged:

.. code-block:: c++

Expand All @@ -71,3 +73,15 @@ Options
A& operator=(const A&) = delete;
~A();
};

.. option:: AllowImplicitlyDeletedCopyOrMove

When set to `true` (default is `false`), this check doesn't flag classes
which implicitly delete copy or move operations.
With this option enabled, the following class won't be flagged:

.. code-block:: c++

struct A : boost::noncopyable {
~A() { std::cout << "dtor\n"; }
};
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ Clang-Tidy Checks
:doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
:doc:`modernize-use-override <modernize/use-override>`, "Yes"
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
:doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
:doc:`modernize-use-trailing-return-type <modernize/use-trailing-return-type>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ The tool will replace the above code with,
// The tool will not emit a diagnostic or attempt to replace the code.
template <typename T, std::enable_if_t<T::some_trait, int> = 0>
struct my_class {};

.. note::

System headers are not analyzed by this check.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.. title:: clang-tidy - modernize-use-std-format

modernize-use-std-format
========================

Converts calls to ``absl::StrFormat``, or other functions via
configuration options, to C++20's ``std::format``, or another function
via a configuration option, modifying the format string appropriately and
removing now-unnecessary calls to ``std::string::c_str()`` and
``std::string::data()``.

For example, it turns lines like

.. code-block:: c++

return absl::StrFormat("The %s is %3d", description.c_str(), value);

into:

.. code-block:: c++

return std::format("The {} is {:3}", description, value);

The check uses the same format-string-conversion algorithm as
`modernize-use-std-print <../modernize/use-std-print.html>`_ and its
shortcomings are described in the documentation for that check.

Options
-------

.. option:: StrictMode

When `true`, the check will add casts when converting from variadic
functions and printing signed or unsigned integer types (including
fixed-width integer types from ``<cstdint>``, ``ptrdiff_t``, ``size_t``
and ``ssize_t``) as the opposite signedness to ensure that the output
would matches that of a simple wrapper for ``std::sprintf`` that
accepted a C-style variable argument list. For example, with
`StrictMode` enabled,

.. code-block:: c++

extern std::string strprintf(const char *format, ...);
int i = -42;
unsigned int u = 0xffffffff;
return strprintf("%d %u\n", i, u);
would be converted to

.. code-block:: c++

return std::format("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));

to ensure that the output will continue to be the unsigned representation
of -42 and the signed representation of 0xffffffff (often 4294967254
and -1 respectively). When `false` (which is the default), these casts
will not be added which may cause a change in the output. Note that this
option makes no difference for the default value of
`StrFormatLikeFunctions` since ``absl::StrFormat`` takes a function
parameter pack and is not a variadic function.

.. option:: StrFormatLikeFunctions

A semicolon-separated list of (fully qualified) function names to
replace, with the requirement that the first parameter contains the
printf-style format string and the arguments to be formatted follow
immediately afterwards. The default value for this option is
`absl::StrFormat`.

.. option:: ReplacementFormatFunction

The function that will be used to replace the function set by the
`StrFormatLikeFunctions` option rather than the default
`std::format`. It is expected that the function provides an interface
that is compatible with ``std::format``. A suitable candidate would be
`fmt::format`.

.. option:: FormatHeader

The header that must be included for the declaration of
`ReplacementFormatFunction` so that a ``#include`` directive can be added if
required. If `ReplacementFormatFunction` is `std::format` then this option will
default to ``<format>``, otherwise this option will default to nothing
and no ``#include`` directive will be added.
235 changes: 122 additions & 113 deletions clang-tools-extra/docs/clang-tidy/index.rst

Large diffs are not rendered by default.

13 changes: 8 additions & 5 deletions clang-tools-extra/include-cleaner/lib/WalkAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,18 +267,21 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
return true;
}

// Report a reference from explicit specializations to the specialized
// template. Implicit ones are filtered out by RAV and explicit instantiations
// are already traversed through typelocs.
// Report a reference from explicit specializations/instantiations to the
// specialized template. Implicit ones are filtered out by RAV.
bool
VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) {
if (CTSD->isExplicitSpecialization())
// if (CTSD->isExplicitSpecialization())
if (clang::isTemplateExplicitInstantiationOrSpecialization(
CTSD->getTemplateSpecializationKind()))
report(CTSD->getLocation(),
CTSD->getSpecializedTemplate()->getTemplatedDecl());
return true;
}
bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) {
if (VTSD->isExplicitSpecialization())
// if (VTSD->isExplicitSpecialization())
if (clang::isTemplateExplicitInstantiationOrSpecialization(
VTSD->getTemplateSpecializationKind()))
report(VTSD->getLocation(),
VTSD->getSpecializedTemplate()->getTemplatedDecl());
return true;
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/test/clang-query/Inputs/empty.script
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file intentionally has no queries
1 change: 1 addition & 0 deletions clang-tools-extra/test/clang-query/Inputs/file.script
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f DIRECTORY/runtime_file.script
5 changes: 5 additions & 0 deletions clang-tools-extra/test/clang-query/Inputs/runtime_file.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set bind-root false

l func functionDecl(hasName("bar"))
m func.bind("f")
m varDecl().bind("v")
2 changes: 2 additions & 0 deletions clang-tools-extra/test/clang-query/errors.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// RUN: not clang-query -c foo -c bar %s -- | FileCheck %s
// RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s
// RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s
// RUN: not clang-query -c 'file %S/Inputs/nonexistent.script' %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT-FILEQUERY %s
// RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s

// CHECK: unknown command: foo
// CHECK-NOT: unknown command: bar

// CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script
// CHECK-NONEXISTENT-FILEQUERY: cannot open {{.*}}nonexistent.script
// CHECK-BOTH: cannot specify both -c and -f
2 changes: 2 additions & 0 deletions clang-tools-extra/test/clang-query/file-empty.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// RUN: clang-query -c 'file %S/Inputs/empty.script' %s --
// COM: no output expected; nothing to CHECK
14 changes: 14 additions & 0 deletions clang-tools-extra/test/clang-query/file-query.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: rm -rf %/t
// RUN: mkdir %/t
// RUN: cp %/S/Inputs/file.script %/t/file.script
// RUN: cp %/S/Inputs/runtime_file.script %/t/runtime_file.script
// Need to embed the correct temp path in the actual JSON-RPC requests.
// RUN: sed -e "s|DIRECTORY|%/t|" %/t/file.script > %/t/file.script.temp

// RUN: clang-query -c 'file %/t/file.script.temp' %s -- | FileCheck %s

// CHECK: file-query.c:11:1: note: "f" binds here
void bar(void) {}

// CHECK: file-query.c:14:1: note: "v" binds here
int baz{1};
28 changes: 22 additions & 6 deletions clang-tools-extra/test/clang-tidy/check_clang_tidy.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,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.expect_no_diagnosis = False
self.export_fixes = args.export_fixes
self.fixes = MessagePrefix("CHECK-FIXES")
self.messages = MessagePrefix("CHECK-MESSAGES")
Expand Down Expand Up @@ -172,12 +173,21 @@ def get_prefixes(self):
)

if not has_check_fix and not has_check_message and not has_check_note:
sys.exit(
"%s, %s or %s not found in the input"
% (self.fixes.prefix, self.messages.prefix, self.notes.prefix)
)
self.expect_no_diagnosis = True

assert self.has_check_fixes or self.has_check_messages or self.has_check_notes
expect_diagnosis = (
self.has_check_fixes or self.has_check_messages or self.has_check_notes
)
if self.expect_no_diagnosis and expect_diagnosis:
sys.exit(
"%s, %s or %s not found in the input"
% (
self.fixes.prefix,
self.messages.prefix,
self.notes.prefix,
)
)
assert expect_diagnosis or self.expect_no_diagnosis

def prepare_test_inputs(self):
# Remove the contents of the CHECK lines to avoid CHECKs matching on
Expand Down Expand Up @@ -226,6 +236,10 @@ def run_clang_tidy(self):
print("------------------------------------------------------------------")
return clang_tidy_output

def check_no_diagnosis(self, clang_tidy_output):
if clang_tidy_output != "":
sys.exit("No diagnostics were expected, but found the ones above")

def check_fixes(self):
if self.has_check_fixes:
try_run(
Expand Down Expand Up @@ -277,7 +291,9 @@ def run(self):
self.get_prefixes()
self.prepare_test_inputs()
clang_tidy_output = self.run_clang_tidy()
if self.export_fixes is None:
if self.expect_no_diagnosis:
self.check_no_diagnosis(clang_tidy_output)
elif self.export_fixes is None:
self.check_fixes()
self.check_messages(clang_tidy_output)
self.check_notes(clang_tidy_output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,6 @@ void correct(std::optional<int> param)
std::optional<long>* p2 = &p;
takeOptionalValue(p2->value_or(5U));
takeOptionalRef(p2->value_or(5U));

using Type = decltype(takeOptionalValue(*param));
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: {cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions: true, cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true}}" --
// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: {cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions: true, cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true, cppcoreguidelines-special-member-functions.AllowImplicitlyDeletedCopyOrMove: true}}" --

// Don't warn on destructors without definitions, they might be defaulted in another TU.
class DeclaresDestructor {
Expand Down Expand Up @@ -34,12 +34,13 @@ class DefinesCopyAssignment {
class DefinesMoveConstructor {
DefinesMoveConstructor(DefinesMoveConstructor &&);
};
// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions]
// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor or a move assignment operator [cppcoreguidelines-special-member-functions]

class DefinesMoveAssignment {
DefinesMoveAssignment &operator=(DefinesMoveAssignment &&);
};
// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions]
// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor or a move constructor [cppcoreguidelines-special-member-functions]

class DefinesNothing {
};

Expand Down Expand Up @@ -81,3 +82,22 @@ struct TemplateClass {
// This should not cause problems.
TemplateClass<int> InstantiationWithInt;
TemplateClass<double> InstantiationWithDouble;

struct NoCopy
{
NoCopy() = default;
~NoCopy() = default;

NoCopy(const NoCopy&) = delete;
NoCopy(NoCopy&&) = delete;

NoCopy& operator=(const NoCopy&) = delete;
NoCopy& operator=(NoCopy&&) = delete;
};

// CHECK-MESSAGES: [[@LINE+1]]:8: warning: class 'NonCopyable' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
struct NonCopyable : NoCopy
{
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: %check_clang_tidy %s misc-unused-using-decls %t

// Verify that we don't generate the warnings on header files.
namespace foo { class Foo {}; }

using foo::Foo;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -606,11 +606,8 @@ void invoke_template() {
template_fun(foo);
}

void no_fix_for_invalid_new_loc() {
// FIXME: Although the code is valid, the end location of `new struct Base` is
// invalid. Correct it once https://bugs.llvm.org/show_bug.cgi?id=35952 is
// fixed.
void fix_for_c_style_struct() {
auto T = std::unique_ptr<Base>(new struct Base);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_unique instead
// CHECK-FIXES: auto T = std::unique_ptr<Base>(new struct Base);
// CHECK-FIXES: auto T = std::make_unique<Base>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,27 @@ B maxTT2 = std::max(B(), std::max(B(), B()));
B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; });
// CHECK-FIXES: B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; });

struct GH91982 {
int fun0Args();
int fun1Arg(int a);
int fun2Args(int a, int b);
int fun3Args(int a, int b, int c);
int fun4Args(int a, int b, int c, int d);

int foo() {
return std::max(
fun0Args(),
std::max(fun1Arg(0),
std::max(fun2Args(0, 1),
std::max(fun3Args(0, 1, 2), fun4Args(0, 1, 2, 3)))));
// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list]
// CHECK-FIXES: return std::max(
// CHECK-FIXES-NEXT: {fun0Args(),
// CHECK-FIXES-NEXT: fun1Arg(0),
// CHECK-FIXES-NEXT: fun2Args(0, 1),
// CHECK-FIXES-NEXT: fun3Args(0, 1, 2), fun4Args(0, 1, 2, 3)});
}
};

} // namespace

Original file line number Diff line number Diff line change
Expand Up @@ -724,3 +724,35 @@ void not_last_param() {
}

} // namespace enable_if_trailing_type_parameter


// Issue fixes:

namespace PR91872 {

enum expression_template_option { value1, value2 };

template <typename T> struct number_category {
static const int value = 0;
};

constexpr int number_kind_complex = 1;

template <typename T, expression_template_option ExpressionTemplates>
struct number {
using type = T;
};

template <typename T> struct component_type {
using type = T;
};

template <class T, expression_template_option ExpressionTemplates>
inline typename std::enable_if<
number_category<T>::value == number_kind_complex,
component_type<number<T, ExpressionTemplates>>>::type::type
abs(const number<T, ExpressionTemplates> &v) {
return {};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RUN: %check_clang_tidy -check-suffixes=,STRICT \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrictMode: true, \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers

#include <cstdio>
#include <string>
// CHECK-FIXES: #include <fmt/core.h>

std::string strprintf(const char *, ...);

namespace mynamespace {
std::string strprintf2(const char *, ...);
}

std::string strprintf_test(const std::string &name, double value) {
return strprintf("'%s'='%f'\n", name.c_str(), value);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format]
// CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value);

return mynamespace::strprintf2("'%s'='%f'\n", name.c_str(), value);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf2' [modernize-use-std-format]
// CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value);
}

std::string StrFormat_strict_conversion() {
const unsigned char uc = 'A';
return strprintf("Integer %hhd from unsigned char\n", uc);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format]
// CHECK-FIXES-NOTSTRICT: return fmt::format("Integer {} from unsigned char\n", uc);
// CHECK-FIXES-STRICT: return fmt::format("Integer {} from unsigned char\n", static_cast<signed char>(uc));
}

// Ensure that MatchesAnyListedNameMatcher::NameMatcher::match() can cope with a
// NamedDecl that has no name when we're trying to match unqualified_strprintf.
std::string A(const std::string &in)
{
return "_" + in;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %check_clang_tidy %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: StrictMode: true, \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: 'fmt::sprintf', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers

// CHECK-FIXES: #include <fmt/core.h>
#include <string>

namespace fmt
{
// Use const char * for the format since the real type is hard to mock up.
template <typename... Args>
std::string sprintf(const char *format, const Args&... args);
} // namespace fmt

std::string fmt_sprintf_simple() {
return fmt::sprintf("Hello %s %d", "world", 42);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'sprintf' [modernize-use-std-format]
// CHECK-FIXES: fmt::format("Hello {} {}", "world", 42);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// RUN: %check_clang_tidy \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: {StrictMode: true}}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: %check_clang_tidy \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: {StrictMode: false}}" \
// RUN: -- -isystem %clang_tidy_headers
#include <string>
// CHECK-FIXES: #include <format>

namespace absl
{
// Use const char * for the format since the real type is hard to mock up.
template <typename... Args>
std::string StrFormat(const char *format, const Args&... args);
} // namespace absl

template <typename T>
struct iterator {
T *operator->();
T &operator*();
};

std::string StrFormat_simple() {
return absl::StrFormat("Hello");
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("Hello");
}

std::string StrFormat_complex(const char *name, double value) {
return absl::StrFormat("'%s'='%f'", name, value);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("'{}'='{:f}'", name, value);
}

std::string StrFormat_integer_conversions() {
return absl::StrFormat("int:%d int:%d char:%c char:%c", 65, 'A', 66, 'B');
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("int:{} int:{:d} char:{:c} char:{}", 65, 'A', 66, 'B');
}

// FormatConverter is capable of removing newlines from the end of the format
// string. Ensure that isn't incorrectly happening for std::format.
std::string StrFormat_no_newline_removal() {
return absl::StrFormat("a line\n");
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("a line\n");
}

// FormatConverter is capable of removing newlines from the end of the format
// string. Ensure that isn't incorrectly happening for std::format.
std::string StrFormat_cstr_removal(const std::string &s1, const std::string *s2) {
return absl::StrFormat("%s %s %s %s", s1.c_str(), s1.data(), s2->c_str(), s2->data());
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("{} {} {} {}", s1, s1, *s2, *s2);
}

std::string StrFormat_strict_conversion() {
const unsigned char uc = 'A';
return absl::StrFormat("Integer %hhd from unsigned char\n", uc);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("Integer {} from unsigned char\n", uc);
}

std::string StrFormat_field_width_and_precision() {
auto s1 = absl::StrFormat("width only:%*d width and precision:%*.*f precision only:%.*f", 3, 42, 4, 2, 3.14159265358979323846, 5, 2.718);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("width only:{:{}} width and precision:{:{}.{}f} precision only:{:.{}f}", 42, 3, 3.14159265358979323846, 4, 2, 2.718, 5);

auto s2 = absl::StrFormat("width and precision positional:%1$*2$.*3$f after", 3.14159265358979323846, 4, 2);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("width and precision positional:{0:{1}.{2}f} after", 3.14159265358979323846, 4, 2);

const int width = 10, precision = 3;
const unsigned int ui1 = 42, ui2 = 43, ui3 = 44;
auto s3 = absl::StrFormat("casts width only:%*d width and precision:%*.*d precision only:%.*d\n", 3, ui1, 4, 2, ui2, 5, ui3);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES-NOTSTRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", ui1, 3, ui2, 4, 2, ui3, 5);
// CHECK-FIXES-STRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", static_cast<int>(ui1), 3, static_cast<int>(ui2), 4, 2, static_cast<int>(ui3), 5);

auto s4 = absl::StrFormat("c_str removal width only:%*s width and precision:%*.*s precision only:%.*s", 3, s1.c_str(), 4, 2, s2.c_str(), 5, s3.c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("c_str removal width only:{:>{}} width and precision:{:>{}.{}} precision only:{:.{}}", s1, 3, s2, 4, 2, s3, 5);

const std::string *ps1 = &s1, *ps2 = &s2, *ps3 = &s3;
auto s5 = absl::StrFormat("c_str() removal pointer width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, ps1->c_str(), 4, 2, ps2->c_str(), 5, ps3->c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("c_str() removal pointer width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *ps1, 3, *ps2, 4, 2, *ps3, 5);

iterator<std::string> is1, is2, is3;
auto s6 = absl::StrFormat("c_str() removal iterator width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, is1->c_str(), 4, 2, is2->c_str(), 5, is3->c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("c_str() removal iterator width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *is1, 3, *is2, 4, 2, *is3, 5);

return s1 + s2 + s3 + s4 + s5 + s6;
}

std::string StrFormat_macros() {
// The function call is replaced even though it comes from a macro.
#define FORMAT absl::StrFormat
auto s1 = FORMAT("Hello %d", 42);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {}", 42);

// The format string is replaced even though it comes from a macro, this
// behaviour is required so that that <inttypes.h> macros are replaced.
#define FORMAT_STRING "Hello %s"
auto s2 = absl::StrFormat(FORMAT_STRING, 42);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {}", 42);

// Arguments that are macros aren't replaced with their value, even if they are rearranged.
#define VALUE 3.14159265358979323846
#define WIDTH 10
#define PRECISION 4
auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %check_clang_tidy -std=c++23 %s readability-else-after-return %t

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

if consteval {
return;
} else {
return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@
// RUN: --

#define NEGATE(expr) !(expr)
#define NOT_AND_NOT(a, b) (!a && !b)

bool without_macro(bool a, bool b) {
return !(!a && b);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: boolean expression can be simplified by DeMorgan's theorem
// CHECK-FIXES: return a || !b;
}

bool macro(bool a, bool b) {
return NEGATE(!a && b);
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:12: warning: boolean expression can be simplified by DeMorgan's theorem
// CHECK-FIXES: return NEGATE(!a && b);
void macro(bool a, bool b) {
NEGATE(!a && b);
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
// CHECK-FIXES: NEGATE(!a && b);
!NOT_AND_NOT(a, b);
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
// CHECK-FIXES: !NOT_AND_NOT(a, b);
!(NEGATE(a) && b);
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
// CHECK-FIXES: !(NEGATE(a) && b);
!(a && NEGATE(b));
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
// CHECK-FIXES: !(a && NEGATE(b));
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Checks: 'from-parent'
HeaderFilterRegex: 'parent'
ExcludeHeaderFilterRegex: 'exc-parent'
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Checks: 'from-child1'
HeaderFilterRegex: 'child1'
ExcludeHeaderFilterRegex: 'exc-child1'
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
InheritParentConfig: true
Checks: 'from-child3'
HeaderFilterRegex: 'child3'
ExcludeHeaderFilterRegex: 'exc-child3'
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
// RUN: clang-tidy -dump-config %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-BASE
// CHECK-BASE: Checks: {{.*}}from-parent
// CHECK-BASE: HeaderFilterRegex: parent
// CHECK-BASE: ExcludeHeaderFilterRegex: exc-parent
// RUN: clang-tidy -dump-config %S/Inputs/config-files/1/- -- | FileCheck %s -check-prefix=CHECK-CHILD1
// CHECK-CHILD1: Checks: {{.*}}from-child1
// CHECK-CHILD1: HeaderFilterRegex: child1
// CHECK-CHILD1: ExcludeHeaderFilterRegex: exc-child1
// RUN: clang-tidy -dump-config %S/Inputs/config-files/2/- -- | FileCheck %s -check-prefix=CHECK-CHILD2
// CHECK-CHILD2: Checks: {{.*}}from-parent
// CHECK-CHILD2: HeaderFilterRegex: parent
// CHECK-CHILD2: ExcludeHeaderFilterRegex: exc-parent
// RUN: clang-tidy -dump-config %S/Inputs/config-files/3/- -- | FileCheck %s -check-prefix=CHECK-CHILD3
// CHECK-CHILD3: Checks: {{.*}}from-parent,from-child3
// CHECK-CHILD3: HeaderFilterRegex: child3
// RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE
// CHECK-CHILD3: ExcludeHeaderFilterRegex: exc-child3
// RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' -exclude-header-filter='from_command_line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE
// CHECK-COMMAND-LINE: Checks: {{.*}}from-parent,from-command-line
// CHECK-COMMAND-LINE: HeaderFilterRegex: from command line
// CHECK-COMMAND-LINE: ExcludeHeaderFilterRegex: from_command_line

// For this test we have to use names of the real checks because otherwise values are ignored.
// Running with the old key: <Key>, value: <value> CheckOptions
Expand Down Expand Up @@ -68,3 +73,11 @@
// Dumped config does not overflow for unsigned options
// RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- -- | FileCheck %s -check-prefix=CHECK-OVERFLOW
// CHECK-OVERFLOW: misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976'

// RUN: clang-tidy -dump-config -checks='readability-function-size' -header-filter='foo/*' -exclude-header-filter='bar*' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-EXCLUDE-HEADERS
// CHECK-EXCLUDE-HEADERS: HeaderFilterRegex: 'foo/*'
// CHECK-EXCLUDE-HEADERS: ExcludeHeaderFilterRegex: 'bar*'

// RUN: clang-tidy -dump-config -checks='readability-function-size' -header-filter='' -exclude-header-filter='' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=EMPTY-CHECK-EXCLUDE-HEADERS
// EMPTY-CHECK-EXCLUDE-HEADERS: HeaderFilterRegex: ''
// EMPTY-CHECK-EXCLUDE-HEADERS: ExcludeHeaderFilterRegex: ''
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers -quiet %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK4-QUIET %s
// RUN: clang-tidy -checks='-*,cppcoreguidelines-pro-type-cstyle-cast' -header-filter='.*' -system-headers %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK5 %s
// RUN: clang-tidy -checks='-*,cppcoreguidelines-pro-type-cstyle-cast' -header-filter='.*' %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK5-NO-SYSTEM-HEADERS %s
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -exclude-header-filter='header1\.h' %s -- -I %S/Inputs/file-filter/ -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK6 %s

#include "header1.h"
// CHECK-NOT: warning:
Expand All @@ -21,6 +22,7 @@
// CHECK3-QUIET-NOT: warning:
// CHECK4: header1.h:1:12: warning: single-argument constructors
// CHECK4-QUIET: header1.h:1:12: warning: single-argument constructors
// CHECK6-NOT: warning:

#include "header2.h"
// CHECK-NOT: warning:
Expand All @@ -31,6 +33,7 @@
// CHECK3-QUIET: header2.h:1:12: warning: single-argument constructors
// CHECK4: header2.h:1:12: warning: single-argument constructors
// CHECK4-QUIET: header2.h:1:12: warning: single-argument constructors
// CHECK6: header2.h:1:12: warning: single-argument constructors

#include <system-header.h>
// CHECK-NOT: warning:
Expand All @@ -41,6 +44,7 @@
// CHECK3-QUIET-NOT: warning:
// CHECK4: system-header.h:1:12: warning: single-argument constructors
// CHECK4-QUIET: system-header.h:1:12: warning: single-argument constructors
// CHECK6-NOT: warning:

class A { A(int); };
// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors
Expand All @@ -51,6 +55,7 @@ class A { A(int); };
// CHECK3-QUIET: :[[@LINE-6]]:11: warning: single-argument constructors
// CHECK4: :[[@LINE-7]]:11: warning: single-argument constructors
// CHECK4-QUIET: :[[@LINE-8]]:11: warning: single-argument constructors
// CHECK6: :[[@LINE-9]]:11: warning: single-argument constructors

// CHECK-NOT: warning:
// CHECK-QUIET-NOT: warning:
Expand All @@ -73,6 +78,8 @@ class A { A(int); };
// CHECK4-NOT: Suppressed {{.*}} warnings
// CHECK4-NOT: Use -header-filter=.* {{.*}}
// CHECK4-QUIET-NOT: Suppressed
// CHECK6: Suppressed 2 warnings (2 in non-user code)
// CHECK6: Use -header-filter=.* {{.*}}

int x = 123;
auto x_ptr = TO_FLOAT_PTR(&x);
Expand Down
36 changes: 10 additions & 26 deletions clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "clang/Tooling/NodeIntrospection.h"
#include "llvm/LineEditor/LineEditor.h"
#include "gtest/gtest.h"

Expand Down Expand Up @@ -61,7 +60,6 @@ TEST_F(QueryParserTest, Quit) {

TEST_F(QueryParserTest, Set) {

bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport();
QueryRef Q = parse("set");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
Expand All @@ -72,27 +70,17 @@ TEST_F(QueryParserTest, Set) {

Q = parse("set output");
ASSERT_TRUE(isa<InvalidQuery>(Q));
if (HasIntrospection)
EXPECT_EQ(
"expected 'diag', 'print', 'detailed-ast', 'srcloc' or 'dump', got ''",
cast<InvalidQuery>(Q)->ErrStr);
else
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''",
cast<InvalidQuery>(Q)->ErrStr);
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''",
cast<InvalidQuery>(Q)->ErrStr);

Q = parse("set bind-root true foo");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);

Q = parse("set output foo");
ASSERT_TRUE(isa<InvalidQuery>(Q));
if (HasIntrospection)
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast', 'srcloc' or 'dump', "
"got 'foo'",
cast<InvalidQuery>(Q)->ErrStr);
else
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'",
cast<InvalidQuery>(Q)->ErrStr);
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'",
cast<InvalidQuery>(Q)->ErrStr);

Q = parse("set output dump");
ASSERT_TRUE(isa<SetExclusiveOutputQuery >(Q));
Expand Down Expand Up @@ -197,7 +185,7 @@ TEST_F(QueryParserTest, Comment) {
TEST_F(QueryParserTest, Complete) {
std::vector<llvm::LineEditor::Completion> Comps =
QueryParser::complete("", 0, QS);
ASSERT_EQ(8u, Comps.size());
ASSERT_EQ(9u, Comps.size());
EXPECT_EQ("help ", Comps[0].TypedText);
EXPECT_EQ("help", Comps[0].DisplayText);
EXPECT_EQ("let ", Comps[1].TypedText);
Expand All @@ -214,6 +202,8 @@ TEST_F(QueryParserTest, Complete) {
EXPECT_EQ("disable", Comps[6].DisplayText);
EXPECT_EQ("unlet ", Comps[7].TypedText);
EXPECT_EQ("unlet", Comps[7].DisplayText);
EXPECT_EQ("file ", Comps[8].TypedText);
EXPECT_EQ("file", Comps[8].DisplayText);

Comps = QueryParser::complete("set o", 5, QS);
ASSERT_EQ(1u, Comps.size());
Expand All @@ -230,23 +220,17 @@ TEST_F(QueryParserTest, Complete) {
EXPECT_EQ("output ", Comps[0].TypedText);
EXPECT_EQ("output", Comps[0].DisplayText);

bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport();

Comps = QueryParser::complete("enable output ", 14, QS);
ASSERT_EQ(HasIntrospection ? 5u : 4u, Comps.size());
ASSERT_EQ(4u, Comps.size());

EXPECT_EQ("diag ", Comps[0].TypedText);
EXPECT_EQ("diag", Comps[0].DisplayText);
EXPECT_EQ("print ", Comps[1].TypedText);
EXPECT_EQ("print", Comps[1].DisplayText);
EXPECT_EQ("detailed-ast ", Comps[2].TypedText);
EXPECT_EQ("detailed-ast", Comps[2].DisplayText);
if (HasIntrospection) {
EXPECT_EQ("srcloc ", Comps[3].TypedText);
EXPECT_EQ("srcloc", Comps[3].DisplayText);
}
EXPECT_EQ("dump ", Comps[HasIntrospection ? 4 : 3].TypedText);
EXPECT_EQ("dump", Comps[HasIntrospection ? 4 : 3].DisplayText);
EXPECT_EQ("dump ", Comps[3].TypedText);
EXPECT_EQ("dump", Comps[3].DisplayText);

Comps = QueryParser::complete("set traversal ", 14, QS);
ASSERT_EQ(2u, Comps.size());
Expand Down
8 changes: 7 additions & 1 deletion clang/cmake/caches/HLSL.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ set(LLVM_EXPERIMENTAL_TARGETS_TO_BUILD "DirectX;SPIRV" CACHE STRING "")

# HLSL support is currently limted to clang, eventually it will expand to
# clang-tools-extra too.
set(LLVM_ENABLE_PROJECTS "clang" CACHE STRING "")
set(LLVM_ENABLE_PROJECTS "clang;clang-tools-extra" CACHE STRING "")

set(CLANG_ENABLE_HLSL On CACHE BOOL "")

if (NOT CMAKE_CONFIGURATION_TYPES)
set(LLVM_DISTRIBUTION_COMPONENTS
"clang;hlsl-resource-headers;clangd"
CACHE STRING "")
endif()
2 changes: 1 addition & 1 deletion clang/docs/Block-ABI-Apple.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ The following flags bits are in use thusly for a possible ABI.2010.3.16:
In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime -
it had been a transitional marker that did not get deleted after the
transition. This bit is now paired with (1<<30), and represented as the pair
(3<<30), for the following combinations of valid bit settings, and their
(3<<29), for the following combinations of valid bit settings, and their
meanings:

.. code-block:: c
Expand Down
38 changes: 38 additions & 0 deletions clang/docs/ClangLinkerWrapper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ only for the linker wrapper will be forwarded to the wrapped linker job.
-l <libname> Search for library <libname>
--opt-level=<O0, O1, O2, or O3>
Optimization level for LTO
--override-image=<kind=file>
Uses the provided file as if it were the output of the device link step
-o <path> Path to file to write output
--pass-remarks-analysis=<value>
Pass remarks for LTO
Expand Down Expand Up @@ -87,6 +89,42 @@ other. Generally, this requires that the target triple and architecture match.
An exception is made when the architecture is listed as ``generic``, which will
cause it be linked with any other device code with the same target triple.

Debugging
=========

The linker wrapper performs a lot of steps internally, such as input matching,
symbol resolution, and image registration. This makes it difficult to debug in
some scenarios. The behavior of the linker-wrapper is controlled mostly through
metadata, described in `clang documentation
<https://clang.llvm.org/docs/OffloadingDesign.html>`_. Intermediate output can
be obtained from the linker-wrapper using the ``--save-temps`` flag. These files
can then be modified.

.. code-block:: sh
$> clang openmp.c -fopenmp --offload-arch=gfx90a -c
$> clang openmp.o -fopenmp --offload-arch=gfx90a -Wl,--save-temps
$> ; Modify temp files.
$> llvm-objcopy --update-section=.llvm.offloading=out.bc openmp.o
Doing this will allow you to override one of the input files by replacing its
embedded offloading metadata with a user-modified version. However, this will be
more difficult when there are multiple input files. For a very large hammer, the
``--override-image=<kind>=<file>`` flag can be used.

In the following example, we use the ``--save-temps`` to obtain the LLVM-IR just
before running the backend. We then modify it to test altered behavior, and then
compile it to a binary. This can then be passed to the linker-wrapper which will
then ignore all embedded metadata and use the provided image as if it were the
result of the device linking phase.

.. code-block:: sh
$> clang openmp.c -fopenmp --offload-arch=gfx90a -Wl,--save-temps
$> ; Modify temp files.
$> clang --target=amdgcn-amd-amdhsa -mcpu=gfx90a -nogpulib out.bc -o a.out
$> clang openmp.c -fopenmp --offload-arch=gfx90a -Wl,--override-image=openmp=a.out
Example
=======

Expand Down
5 changes: 4 additions & 1 deletion clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1662,8 +1662,11 @@ The following type trait primitives are supported by Clang. Those traits marked
``T`` from ``U`` is ill-formed.
Deprecated, use ``__reference_constructs_from_temporary``.
* ``__reference_constructs_from_temporary(T, U)`` (C++)
Returns true if a reference ``T`` can be constructed from a temporary of type
Returns true if a reference ``T`` can be direct-initialized from a temporary of type
a non-cv-qualified ``U``.
* ``__reference_converts_from_temporary(T, U)`` (C++)
Returns true if a reference ``T`` can be copy-initialized from a temporary of type
a non-cv-qualified ``U``.
* ``__underlying_type`` (C++, GNU, Microsoft)

In addition, the following expression traits are supported:
Expand Down
364 changes: 261 additions & 103 deletions clang/docs/LibASTMatchersReference.html

Large diffs are not rendered by default.

60 changes: 58 additions & 2 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ C++ Specific Potentially Breaking Changes
- The behavior controlled by the `-frelaxed-template-template-args` flag is now
on by default, and the flag is deprecated. Until the flag is finally removed,
it's negative spelling can be used to obtain compatibility with previous
versions of clang.
versions of clang. The deprecation warning for the negative spelling can be
disabled with `-Wno-deprecated-no-relaxed-template-template-args`.

- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).

- Clang now performs semantic analysis for unary operators with dependent operands
that are known to be of non-class non-enumeration type prior to instantiation.

ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
Expand Down Expand Up @@ -114,6 +118,9 @@ Clang Frontend Potentially Breaking Changes
$ clang --target=<your target triple> -print-target-triple
<the normalized target triple>

- The ``hasTypeLoc`` AST matcher will no longer match a ``classTemplateSpecializationDecl``;
existing uses should switch to ``templateArgumentLoc`` or ``hasAnyTemplateArgumentLoc`` instead.

What's New in Clang |release|?
==============================
Some of the major new features and improvements to Clang are listed
Expand Down Expand Up @@ -182,6 +189,9 @@ C++23 Feature Support

- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.

- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -216,6 +226,9 @@ Resolutions to C++ Defect Reports
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).

- Clang now allows attributes on concepts.
(`CWG2428: Deprecating a concept <https://cplusplus.github.io/CWG/issues/2428.html>`_).

- P0522 implementation is enabled by default in all language versions, and
provisional wording for CWG2398 is implemented.

Expand Down Expand Up @@ -304,7 +317,8 @@ New Compiler Flags

- ``-fexperimental-late-parse-attributes`` enables an experimental feature to
allow late parsing certain attributes in specific contexts where they would
not normally be late parsed.
not normally be late parsed. Currently this allows late parsing the
`counted_by` attribute in C. See `Attribute Changes in Clang`_.

- ``-fseparate-named-sections`` uses separate unique sections for global
symbols in named special sections (i.e. symbols annotated with
Expand Down Expand Up @@ -390,6 +404,28 @@ Attribute Changes in Clang
- Clang now warns that the ``exclude_from_explicit_instantiation`` attribute
is ignored when applied to a local class or a member thereof.

- The ``clspv_libclc_builtin`` attribute has been added to allow clspv
(`OpenCL-C to Vulkan SPIR-V compiler <https://github.com/google/clspv>`_) to identify functions coming from libclc
(`OpenCL-C builtin library <https://libclc.llvm.org>`_).
- The ``counted_by`` attribute is now allowed on pointers that are members of a
struct in C.

- The ``counted_by`` attribute can now be late parsed in C when
``-fexperimental-late-parse-attributes`` is passed but only when attribute is
used in the declaration attribute position. This allows using the
attribute on existing code where it previously impossible to do so without
re-ordering struct field declarations would break ABI as shown below.

.. code-block:: c
struct BufferTy {
/* Refering to `count` requires late parsing */
char* buffer __counted_by(count);
/* Swapping `buffer` and `count` to avoid late parsing would break ABI */
size_t count;
};
Improvements to Clang's diagnostics
-----------------------------------
- Clang now applies syntax highlighting to the code snippets it
Expand Down Expand Up @@ -481,9 +517,15 @@ Improvements to Clang's diagnostics
}
};

- Clang emits a ``-Wparentheses`` warning for expressions with consecutive comparisons like ``x < y < z``.
Fixes #GH20456.

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

- Clang now specifies that using ``auto`` in a lambda parameter is a C++14 extension when
appropriate. (`#46059: <https://github.com/llvm/llvm-project/issues/46059>`_).

Bug Fixes in This Version
-------------------------
- Clang's ``-Wundefined-func-template`` no longer warns on pure virtual
Expand Down Expand Up @@ -560,6 +602,9 @@ Bug Fixes in This Version
- Clang will no longer emit a duplicate -Wunused-value warning for an expression
`(A, B)` which evaluates to glvalue `B` that can be converted to non ODR-use. (#GH45783)

- Clang now correctly disallows VLA type compound literals, e.g. ``(int[size]){}``,
as the C standard mandates. (#GH89835)

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -703,11 +748,22 @@ Bug Fixes to C++ Support
within initializers for variables that are usable in constant expressions or are constant
initialized, rather than evaluating them as a part of the larger manifestly constant evaluated
expression.
- Fix a bug in access control checking due to dealyed checking of friend declaration. Fixes (#GH12361).
- Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509).
- When partial ordering alias templates against template template parameters,
allow pack expansions when the alias has a fixed-size parameter list. Fixes (#GH62529).
- Clang now ignores template parameters only used within the exception specification of candidate function
templates during partial ordering when deducing template arguments from a function declaration or when
taking the address of a function template.
- Fix a bug with checking constrained non-type template parameters for equivalence. Fixes (#GH77377).
- Fix a bug where the last argument was not considered when considering the most viable function for
explicit object argument member functions. Fixes (#GH92188).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
- Clang now properly preserves ``FoundDecls`` within a ``ConceptReference``. (#GH82628)
- The presence of the ``typename`` keyword is now stored in ``TemplateTemplateParmDecl``.
- Fixed malformed AST generated for anonymous union access in templates. (#GH90842)

Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 0 additions & 5 deletions clang/docs/tools/clang-formatted-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ clang/include/clang/Tooling/CompilationDatabasePluginRegistry.h
clang/include/clang/Tooling/DiagnosticsYaml.h
clang/include/clang/Tooling/Execution.h
clang/include/clang/Tooling/JSONCompilationDatabase.h
clang/include/clang/Tooling/NodeIntrospection.h
clang/include/clang/Tooling/Refactoring.h
clang/include/clang/Tooling/StandaloneExecution.h
clang/include/clang/Tooling/ToolExecutorPluginRegistry.h
Expand Down Expand Up @@ -562,15 +561,11 @@ clang/lib/Tooling/Execution.cpp
clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp
clang/lib/Tooling/FixIt.cpp
clang/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
clang/lib/Tooling/NodeIntrospection.cpp
clang/lib/Tooling/StandaloneExecution.cpp
clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
clang/lib/Tooling/DumpTool/APIData.h
clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h
clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
clang/lib/Tooling/Inclusions/IncludeStyle.cpp
clang/lib/Tooling/Inclusions/StandardLibrary.cpp
Expand Down
25 changes: 12 additions & 13 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2096,13 +2096,12 @@ class FunctionDecl : public DeclaratorDecl,
///
/// \param PointOfInstantiation point at which the function template
/// specialization was first instantiated.
void setFunctionTemplateSpecialization(ASTContext &C,
FunctionTemplateDecl *Template,
const TemplateArgumentList *TemplateArgs,
void *InsertPos,
TemplateSpecializationKind TSK,
const TemplateArgumentListInfo *TemplateArgsAsWritten,
SourceLocation PointOfInstantiation);
void setFunctionTemplateSpecialization(
ASTContext &C, FunctionTemplateDecl *Template,
TemplateArgumentList *TemplateArgs, void *InsertPos,
TemplateSpecializationKind TSK,
const TemplateArgumentListInfo *TemplateArgsAsWritten,
SourceLocation PointOfInstantiation);

/// Specify that this record is an instantiation of the
/// member function FD.
Expand Down Expand Up @@ -2981,12 +2980,12 @@ class FunctionDecl : public DeclaratorDecl,
///
/// \param PointOfInstantiation point at which the function template
/// specialization was first instantiated.
void setFunctionTemplateSpecialization(FunctionTemplateDecl *Template,
const TemplateArgumentList *TemplateArgs,
void *InsertPos,
TemplateSpecializationKind TSK = TSK_ImplicitInstantiation,
const TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr,
SourceLocation PointOfInstantiation = SourceLocation()) {
void setFunctionTemplateSpecialization(
FunctionTemplateDecl *Template, TemplateArgumentList *TemplateArgs,
void *InsertPos,
TemplateSpecializationKind TSK = TSK_ImplicitInstantiation,
TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr,
SourceLocation PointOfInstantiation = SourceLocation()) {
setFunctionTemplateSpecialization(getASTContext(), Template, TemplateArgs,
InsertPos, TSK, TemplateArgsAsWritten,
PointOfInstantiation);
Expand Down
233 changes: 100 additions & 133 deletions clang/include/clang/AST/DeclTemplate.h

Large diffs are not rendered by default.

130 changes: 129 additions & 1 deletion clang/include/clang/AST/OpenACCClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "clang/AST/StmtIterator.h"
#include "clang/Basic/OpenACCKinds.h"

#include <utility>

namespace clang {
/// This is the base type for all OpenACC Clauses.
class OpenACCClause {
Expand All @@ -36,7 +38,7 @@ class OpenACCClause {
SourceLocation getBeginLoc() const { return Location.getBegin(); }
SourceLocation getEndLoc() const { return Location.getEnd(); }

static bool classof(const OpenACCClause *) { return true; }
static bool classof(const OpenACCClause *) { return false; }

using child_iterator = StmtIterator;
using const_child_iterator = ConstStmtIterator;
Expand All @@ -63,6 +65,8 @@ class OpenACCClauseWithParams : public OpenACCClause {
: OpenACCClause(K, BeginLoc, EndLoc), LParenLoc(LParenLoc) {}

public:
static bool classof(const OpenACCClause *C);

SourceLocation getLParenLoc() const { return LParenLoc; }

child_range children() {
Expand All @@ -73,6 +77,63 @@ class OpenACCClauseWithParams : public OpenACCClause {
}
};

using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;
/// A 'device_type' or 'dtype' clause, takes a list of either an 'asterisk' or
/// an identifier. The 'asterisk' means 'the rest'.
class OpenACCDeviceTypeClause final
: public OpenACCClauseWithParams,
public llvm::TrailingObjects<OpenACCDeviceTypeClause,
DeviceTypeArgument> {
// Data stored in trailing objects as IdentifierInfo* /SourceLocation pairs. A
// nullptr IdentifierInfo* represents an asterisk.
unsigned NumArchs;
OpenACCDeviceTypeClause(OpenACCClauseKind K, SourceLocation BeginLoc,
SourceLocation LParenLoc,
ArrayRef<DeviceTypeArgument> Archs,
SourceLocation EndLoc)
: OpenACCClauseWithParams(K, BeginLoc, LParenLoc, EndLoc),
NumArchs(Archs.size()) {
assert(
(K == OpenACCClauseKind::DeviceType || K == OpenACCClauseKind::DType) &&
"Invalid clause kind for device-type");

assert(!llvm::any_of(Archs, [](const DeviceTypeArgument &Arg) {
return Arg.second.isInvalid();
}) && "Invalid SourceLocation for an argument");

assert(
(Archs.size() == 1 || !llvm::any_of(Archs,
[](const DeviceTypeArgument &Arg) {
return Arg.first == nullptr;
})) &&
"Only a single asterisk version is permitted, and must be the "
"only one");

std::uninitialized_copy(Archs.begin(), Archs.end(),
getTrailingObjects<DeviceTypeArgument>());
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::DType ||
C->getClauseKind() == OpenACCClauseKind::DeviceType;
}
bool hasAsterisk() const {
return getArchitectures().size() > 0 &&
getArchitectures()[0].first == nullptr;
}

ArrayRef<DeviceTypeArgument> getArchitectures() const {
return ArrayRef<DeviceTypeArgument>(
getTrailingObjects<DeviceTypeArgument>(), NumArchs);
}

static OpenACCDeviceTypeClause *
Create(const ASTContext &C, OpenACCClauseKind K, SourceLocation BeginLoc,
SourceLocation LParenLoc, ArrayRef<DeviceTypeArgument> Archs,
SourceLocation EndLoc);
};

/// A 'default' clause, has the optional 'none' or 'present' argument.
class OpenACCDefaultClause : public OpenACCClauseWithParams {
friend class ASTReaderStmt;
Expand All @@ -92,6 +153,9 @@ class OpenACCDefaultClause : public OpenACCClauseWithParams {
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Default;
}
OpenACCDefaultClauseKind getDefaultClauseKind() const {
return DefaultClauseKind;
}
Expand All @@ -116,6 +180,8 @@ class OpenACCClauseWithCondition : public OpenACCClauseWithParams {
ConditionExpr(ConditionExpr) {}

public:
static bool classof(const OpenACCClause *C);

bool hasConditionExpr() const { return ConditionExpr; }
const Expr *getConditionExpr() const { return ConditionExpr; }
Expr *getConditionExpr() { return ConditionExpr; }
Expand Down Expand Up @@ -143,6 +209,9 @@ class OpenACCIfClause : public OpenACCClauseWithCondition {
Expr *ConditionExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::If;
}
static OpenACCIfClause *Create(const ASTContext &C, SourceLocation BeginLoc,
SourceLocation LParenLoc, Expr *ConditionExpr,
SourceLocation EndLoc);
Expand All @@ -154,6 +223,9 @@ class OpenACCSelfClause : public OpenACCClauseWithCondition {
Expr *ConditionExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Self;
}
static OpenACCSelfClause *Create(const ASTContext &C, SourceLocation BeginLoc,
SourceLocation LParenLoc,
Expr *ConditionExpr, SourceLocation EndLoc);
Expand All @@ -180,6 +252,7 @@ class OpenACCClauseWithExprs : public OpenACCClauseWithParams {
llvm::ArrayRef<Expr *> getExprs() const { return Exprs; }

public:
static bool classof(const OpenACCClause *C);
child_range children() {
return child_range(reinterpret_cast<Stmt **>(Exprs.begin()),
reinterpret_cast<Stmt **>(Exprs.end()));
Expand Down Expand Up @@ -214,6 +287,9 @@ class OpenACCWaitClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Wait;
}
static OpenACCWaitClause *Create(const ASTContext &C, SourceLocation BeginLoc,
SourceLocation LParenLoc, Expr *DevNumExpr,
SourceLocation QueuesLoc,
Expand Down Expand Up @@ -246,6 +322,9 @@ class OpenACCNumGangsClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::NumGangs;
}
static OpenACCNumGangsClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc);
Expand Down Expand Up @@ -275,6 +354,7 @@ class OpenACCClauseWithSingleIntExpr : public OpenACCClauseWithExprs {
}

public:
static bool classof(const OpenACCClause *C);
bool hasIntExpr() const { return !getExprs().empty(); }
const Expr *getIntExpr() const {
return hasIntExpr() ? getExprs()[0] : nullptr;
Expand All @@ -288,6 +368,9 @@ class OpenACCNumWorkersClause : public OpenACCClauseWithSingleIntExpr {
Expr *IntExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::NumWorkers;
}
static OpenACCNumWorkersClause *Create(const ASTContext &C,
SourceLocation BeginLoc,
SourceLocation LParenLoc,
Expand All @@ -299,6 +382,9 @@ class OpenACCVectorLengthClause : public OpenACCClauseWithSingleIntExpr {
Expr *IntExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::VectorLength;
}
static OpenACCVectorLengthClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
Expr *IntExpr, SourceLocation EndLoc);
Expand All @@ -309,6 +395,9 @@ class OpenACCAsyncClause : public OpenACCClauseWithSingleIntExpr {
Expr *IntExpr, SourceLocation EndLoc);

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Async;
}
static OpenACCAsyncClause *Create(const ASTContext &C,
SourceLocation BeginLoc,
SourceLocation LParenLoc, Expr *IntExpr,
Expand All @@ -326,6 +415,7 @@ class OpenACCClauseWithVarList : public OpenACCClauseWithExprs {
: OpenACCClauseWithExprs(K, BeginLoc, LParenLoc, EndLoc) {}

public:
static bool classof(const OpenACCClause *C);
ArrayRef<Expr *> getVarList() { return getExprs(); }
ArrayRef<Expr *> getVarList() const { return getExprs(); }
};
Expand All @@ -344,6 +434,9 @@ class OpenACCPrivateClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Private;
}
static OpenACCPrivateClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> VarList, SourceLocation EndLoc);
Expand All @@ -363,6 +456,9 @@ class OpenACCFirstPrivateClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::FirstPrivate;
}
static OpenACCFirstPrivateClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> VarList, SourceLocation EndLoc);
Expand All @@ -382,6 +478,9 @@ class OpenACCDevicePtrClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::DevicePtr;
}
static OpenACCDevicePtrClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> VarList, SourceLocation EndLoc);
Expand All @@ -401,6 +500,9 @@ class OpenACCAttachClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Attach;
}
static OpenACCAttachClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> VarList, SourceLocation EndLoc);
Expand All @@ -420,6 +522,9 @@ class OpenACCNoCreateClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::NoCreate;
}
static OpenACCNoCreateClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> VarList, SourceLocation EndLoc);
Expand All @@ -439,6 +544,9 @@ class OpenACCPresentClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Present;
}
static OpenACCPresentClause *
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<Expr *> VarList, SourceLocation EndLoc);
Expand All @@ -462,6 +570,11 @@ class OpenACCCopyClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Copy ||
C->getClauseKind() == OpenACCClauseKind::PCopy ||
C->getClauseKind() == OpenACCClauseKind::PresentOrCopy;
}
static OpenACCCopyClause *
Create(const ASTContext &C, OpenACCClauseKind Spelling,
SourceLocation BeginLoc, SourceLocation LParenLoc,
Expand All @@ -488,6 +601,11 @@ class OpenACCCopyInClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::CopyIn ||
C->getClauseKind() == OpenACCClauseKind::PCopyIn ||
C->getClauseKind() == OpenACCClauseKind::PresentOrCopyIn;
}
bool isReadOnly() const { return IsReadOnly; }
static OpenACCCopyInClause *
Create(const ASTContext &C, OpenACCClauseKind Spelling,
Expand Down Expand Up @@ -515,6 +633,11 @@ class OpenACCCopyOutClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::CopyOut ||
C->getClauseKind() == OpenACCClauseKind::PCopyOut ||
C->getClauseKind() == OpenACCClauseKind::PresentOrCopyOut;
}
bool isZero() const { return IsZero; }
static OpenACCCopyOutClause *
Create(const ASTContext &C, OpenACCClauseKind Spelling,
Expand Down Expand Up @@ -542,6 +665,11 @@ class OpenACCCreateClause final
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::Create ||
C->getClauseKind() == OpenACCClauseKind::PCreate ||
C->getClauseKind() == OpenACCClauseKind::PresentOrCreate;
}
bool isZero() const { return IsZero; }
static OpenACCCreateClause *
Create(const ASTContext &C, OpenACCClauseKind Spelling,
Expand Down
55 changes: 36 additions & 19 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -736,13 +736,27 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {

// As a syntax visitor, by default we want to ignore declarations for
// implicit declarations (ones not typed explicitly by the user).
if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) {
// For an implicit template type parameter, its type constraints are not
// implicit and are not represented anywhere else. We still need to visit
// them.
if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
return TraverseTemplateTypeParamDeclConstraints(TTPD);
return true;
if (!getDerived().shouldVisitImplicitCode()) {
if (D->isImplicit()) {
// For an implicit template type parameter, its type constraints are not
// implicit and are not represented anywhere else. We still need to visit
// them.
if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
return TraverseTemplateTypeParamDeclConstraints(TTPD);
return true;
}

// Deduction guides for alias templates are always synthesized, so they
// should not be traversed unless shouldVisitImplicitCode() returns true.
//
// It's important to note that checking the implicit bit is not efficient
// for the alias case. For deduction guides synthesized from explicit
// user-defined deduction guides, we must maintain the explicit bit to
// ensure correct overload resolution.
if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
if (llvm::isa_and_present<TypeAliasTemplateDecl>(
FTD->getDeclName().getCXXDeductionGuideTemplate()))
return true;
}

switch (D->getKind()) {
Expand Down Expand Up @@ -2030,6 +2044,15 @@ DEF_TRAVERSE_DECL(RecordDecl, { TRY_TO(TraverseRecordHelper(D)); })

DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })

template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLocsHelper(
const TemplateArgumentLoc *TAL, unsigned Count) {
for (unsigned I = 0; I < Count; ++I) {
TRY_TO(TraverseTemplateArgumentLoc(TAL[I]));
}
return true;
}

#define DEF_TRAVERSE_TMPL_SPEC_DECL(TMPLDECLKIND, DECLKIND) \
DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateSpecializationDecl, { \
/* For implicit instantiations ("set<int> x;"), we don't want to \
Expand All @@ -2039,9 +2062,12 @@ DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })
TemplateSpecializationType). For explicit instantiations \
("template set<int>;"), we do need a callback, since this \
is the only callback that's made for this instantiation. \
We use getTypeAsWritten() to distinguish. */ \
if (TypeSourceInfo *TSI = D->getTypeAsWritten()) \
TRY_TO(TraverseTypeLoc(TSI->getTypeLoc())); \
We use getTemplateArgsAsWritten() to distinguish. */ \
if (const auto *ArgsWritten = D->getTemplateArgsAsWritten()) { \
/* The args that remains unspecialized. */ \
TRY_TO(TraverseTemplateArgumentLocsHelper( \
ArgsWritten->getTemplateArgs(), ArgsWritten->NumTemplateArgs)); \
} \
\
if (getDerived().shouldVisitTemplateInstantiations() || \
D->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { \
Expand All @@ -2061,15 +2087,6 @@ DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })
DEF_TRAVERSE_TMPL_SPEC_DECL(Class, CXXRecord)
DEF_TRAVERSE_TMPL_SPEC_DECL(Var, Var)

template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLocsHelper(
const TemplateArgumentLoc *TAL, unsigned Count) {
for (unsigned I = 0; I < Count; ++I) {
TRY_TO(TraverseTemplateArgumentLoc(TAL[I]));
}
return true;
}

#define DEF_TRAVERSE_TMPL_PART_SPEC_DECL(TMPLDECLKIND, DECLKIND) \
DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplatePartialSpecializationDecl, { \
/* The partial specialization. */ \
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/StmtOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class OpenACCAssociatedStmtConstruct : public OpenACCConstructStmt {
}

public:
static bool classof(const Stmt *T) {
return false;
}

child_range children() {
if (getAssociatedStmt())
return child_range(&AssociatedStmt, &AssociatedStmt + 1);
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2515,6 +2515,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isRecordType() const;
bool isClassType() const;
bool isStructureType() const;
bool isStructureTypeWithFlexibleArrayMember() const;
bool isObjCBoxableRecordType() const;
bool isInterfaceType() const;
bool isStructureOrClassType() const;
Expand Down Expand Up @@ -8044,7 +8045,10 @@ inline bool Type::isUndeducedType() const {
/// Determines whether this is a type for which one can define
/// an overloaded operator.
inline bool Type::isOverloadableType() const {
return isDependentType() || isRecordType() || isEnumeralType();
if (!CanonicalType->isDependentType())
return isRecordType() || isEnumeralType();
return !isArrayType() && !isFunctionType() && !isAnyPointerType() &&
!isMemberPointerType();
}

/// Determines whether this type is written as a typedef-name.
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/AST/VTTBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class VTTBuilder {
using AddressPointsMapTy = llvm::DenseMap<BaseSubobject, uint64_t>;

/// The sub-VTT indices for the bases of the most derived class.
llvm::DenseMap<BaseSubobject, uint64_t> SubVTTIndicies;
llvm::DenseMap<BaseSubobject, uint64_t> SubVTTIndices;

/// The secondary virtual pointer indices of all subobjects of
/// the most derived class.
Expand Down Expand Up @@ -148,8 +148,8 @@ class VTTBuilder {
}

/// Returns a reference to the sub-VTT indices.
const llvm::DenseMap<BaseSubobject, uint64_t> &getSubVTTIndicies() const {
return SubVTTIndicies;
const llvm::DenseMap<BaseSubobject, uint64_t> &getSubVTTIndices() const {
return SubVTTIndices;
}

/// Returns a reference to the secondary virtual pointer indices.
Expand Down
Loading