aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/readability
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/readability
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/readability')
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.cpp90
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.h37
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp270
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.h67
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.cpp159
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.h36
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.cpp138
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.h36
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.cpp115
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.h41
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp331
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.h41
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp168
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h32
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.cpp74
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.h34
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.cpp112
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.h31
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.cpp325
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.h44
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp563
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h51
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp222
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.h54
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.cpp152
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.h50
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.cpp1453
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.h215
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp392
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.h43
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp351
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h48
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.cpp274
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.h31
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.cpp228
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.h105
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp268
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.h33
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.cpp122
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.h40
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.cpp53
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.h31
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.cpp118
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.h40
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.cpp205
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.h42
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.cpp234
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.h62
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.cpp287
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.h40
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ReadabilityTidyModule.cpp158
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.cpp78
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h38
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.cpp92
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.h50
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.cpp87
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.h33
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp33
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h30
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.cpp70
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.h43
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.cpp105
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.h31
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.cpp173
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.h46
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.cpp201
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.h30
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.cpp163
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.h35
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp958
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.h75
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.cpp67
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.h39
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp95
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h41
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp61
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h31
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.cpp76
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.h34
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp808
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h97
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp82
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h37
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp242
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h43
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp107
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.h36
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/ya.make80
88 files changed, 12493 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.cpp
new file mode 100644
index 0000000000..6906717dd4
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.cpp
@@ -0,0 +1,90 @@
+//===--- AvoidConstParamsInDecls.cpp - clang-tidy--------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidConstParamsInDecls.h"
+#include "../utils/LexerUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+namespace {
+
+SourceRange getTypeRange(const ParmVarDecl &Param) {
+ return SourceRange(Param.getBeginLoc(),
+ Param.getLocation().getLocWithOffset(-1));
+}
+
+} // namespace
+
+void AvoidConstParamsInDecls::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) {
+ const auto ConstParamDecl =
+ parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");
+ Finder->addMatcher(
+ functionDecl(unless(isDefinition()),
+ has(typeLoc(forEach(ConstParamDecl))))
+ .bind("func"),
+ this);
+}
+
+void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+
+ if (!Param->getType().isLocalConstQualified())
+ return;
+
+ if (IgnoreMacros &&
+ (Param->getBeginLoc().isMacroID() || Param->getEndLoc().isMacroID())) {
+ // Suppress the check if macros are involved.
+ return;
+ }
+
+ auto Diag = diag(Param->getBeginLoc(),
+ "parameter %0 is const-qualified in the function "
+ "declaration; const-qualification of parameters only has an "
+ "effect in function definitions");
+ if (Param->getName().empty()) {
+ for (unsigned int I = 0; I < Func->getNumParams(); ++I) {
+ if (Param == Func->getParamDecl(I)) {
+ Diag << (I + 1);
+ break;
+ }
+ }
+ } else {
+ Diag << Param;
+ }
+
+ if (Param->getBeginLoc().isMacroID() != Param->getEndLoc().isMacroID()) {
+ // Do not offer a suggestion if the part of the variable declaration comes
+ // from a macro.
+ return;
+ }
+
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(getTypeRange(*Param)),
+ *Result.SourceManager, getLangOpts());
+
+ if (!FileRange.isValid())
+ return;
+
+ auto Tok = tidy::utils::lexer::getQualifyingToken(
+ tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);
+ if (!Tok)
+ return;
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation()));
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.h
new file mode 100644
index 0000000000..57bac2b39b
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/AvoidConstParamsInDecls.h
@@ -0,0 +1,37 @@
+//===--- AvoidConstParamsInDecls.h - clang-tidy----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+// Detect function declarations that have const value parameters and discourage
+// them.
+class AvoidConstParamsInDecls : public ClangTidyCheck {
+public:
+ AvoidConstParamsInDecls(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const bool IgnoreMacros;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
new file mode 100644
index 0000000000..74bb073848
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
@@ -0,0 +1,270 @@
+//===--- BracesAroundStatementsCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BracesAroundStatementsCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
+ const ASTContext *Context) {
+ Token Tok;
+ SourceLocation Beginning =
+ Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
+ const bool Invalid =
+ Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
+ assert(!Invalid && "Expected a valid token.");
+
+ if (Invalid)
+ return tok::NUM_TOKENS;
+
+ return Tok.getKind();
+}
+
+static SourceLocation
+forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM,
+ const ASTContext *Context) {
+ assert(Loc.isValid());
+ for (;;) {
+ while (isWhitespace(*SM.getCharacterData(Loc)))
+ Loc = Loc.getLocWithOffset(1);
+
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind != tok::comment)
+ return Loc;
+
+ // Fast-forward current token.
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ }
+}
+
+static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
+ const ASTContext *Context) {
+ SourceLocation Loc =
+ utils::lexer::getUnifiedEndLoc(S, SM, Context->getLangOpts());
+ if (!Loc.isValid())
+ return Loc;
+
+ // Start searching right after S.
+ Loc = Loc.getLocWithOffset(1);
+
+ for (;;) {
+ assert(Loc.isValid());
+ while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
+ Loc = Loc.getLocWithOffset(1);
+ }
+
+ if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
+ // EOL, insert brace before.
+ break;
+ }
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind != tok::comment) {
+ // Non-comment token, insert brace before.
+ break;
+ }
+
+ SourceLocation TokEndLoc =
+ Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ SourceRange TokRange(Loc, TokEndLoc);
+ StringRef Comment = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
+ if (Comment.startswith("/*") && Comment.contains('\n')) {
+ // Multi-line block comment, insert brace before.
+ break;
+ }
+ // else: Trailing comment, insert brace after the newline.
+
+ // Fast-forward current token.
+ Loc = TokEndLoc;
+ }
+ return Loc;
+}
+
+BracesAroundStatementsCheck::BracesAroundStatementsCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ // Always add braces by default.
+ ShortStatementLines(Options.get("ShortStatementLines", 0U)) {}
+
+void BracesAroundStatementsCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ShortStatementLines", ShortStatementLines);
+}
+
+void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(ifStmt().bind("if"), this);
+ Finder->addMatcher(whileStmt().bind("while"), this);
+ Finder->addMatcher(doStmt().bind("do"), this);
+ Finder->addMatcher(forStmt().bind("for"), this);
+ Finder->addMatcher(cxxForRangeStmt().bind("for-range"), this);
+}
+
+void BracesAroundStatementsCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const ASTContext *Context = Result.Context;
+
+ // Get location of closing parenthesis or 'do' to insert opening brace.
+ if (const auto *S = Result.Nodes.getNodeAs<ForStmt>("for")) {
+ checkStmt(Result, S->getBody(), S->getRParenLoc());
+ } else if (const auto *S =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>("for-range")) {
+ checkStmt(Result, S->getBody(), S->getRParenLoc());
+ } else if (const auto *S = Result.Nodes.getNodeAs<DoStmt>("do")) {
+ checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
+ } else if (const auto *S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ if (StartLoc.isInvalid())
+ return;
+ checkStmt(Result, S->getBody(), StartLoc);
+ } else if (const auto *S = Result.Nodes.getNodeAs<IfStmt>("if")) {
+ // "if consteval" always has braces.
+ if (S->isConsteval())
+ return;
+
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ if (StartLoc.isInvalid())
+ return;
+ if (ForceBracesStmts.erase(S))
+ ForceBracesStmts.insert(S->getThen());
+ bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
+ const Stmt *Else = S->getElse();
+ if (Else && BracedIf)
+ ForceBracesStmts.insert(Else);
+ if (Else && !isa<IfStmt>(Else)) {
+ // Omit 'else if' statements here, they will be handled directly.
+ checkStmt(Result, Else, S->getElseLoc());
+ }
+ } else {
+ llvm_unreachable("Invalid match");
+ }
+}
+
+/// Find location of right parenthesis closing condition.
+template <typename IfOrWhileStmt>
+SourceLocation
+BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ // Skip macros.
+ if (S->getBeginLoc().isMacroID())
+ return SourceLocation();
+
+ SourceLocation CondEndLoc = S->getCond()->getEndLoc();
+ if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
+ CondEndLoc = CondVar->getEndLoc();
+
+ if (!CondEndLoc.isValid()) {
+ return SourceLocation();
+ }
+
+ SourceLocation PastCondEndLoc =
+ Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
+ if (PastCondEndLoc.isInvalid())
+ return SourceLocation();
+ SourceLocation RParenLoc =
+ forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
+ if (RParenLoc.isInvalid())
+ return SourceLocation();
+ tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
+ if (TokKind != tok::r_paren)
+ return SourceLocation();
+ return RParenLoc;
+}
+
+/// Determine if the statement needs braces around it, and add them if it does.
+/// Returns true if braces where added.
+bool BracesAroundStatementsCheck::checkStmt(
+ const MatchFinder::MatchResult &Result, const Stmt *S,
+ SourceLocation InitialLoc, SourceLocation EndLocHint) {
+
+ while (const auto *AS = dyn_cast<AttributedStmt>(S))
+ S = AS->getSubStmt();
+
+ // 1) If there's a corresponding "else" or "while", the check inserts "} "
+ // right before that token.
+ // 2) If there's a multi-line block comment starting on the same line after
+ // the location we're inserting the closing brace at, or there's a non-comment
+ // token, the check inserts "\n}" right before that token.
+ // 3) Otherwise the check finds the end of line (possibly after some block or
+ // line comments) and inserts "\n}" right before that EOL.
+ if (!S || isa<CompoundStmt>(S)) {
+ // Already inside braces.
+ return false;
+ }
+
+ if (!InitialLoc.isValid())
+ return false;
+ const SourceManager &SM = *Result.SourceManager;
+ const ASTContext *Context = Result.Context;
+
+ // Convert InitialLoc to file location, if it's on the same macro expansion
+ // level as the start of the statement. We also need file locations for
+ // Lexer::getLocForEndOfToken working properly.
+ InitialLoc = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(InitialLoc, S->getBeginLoc()),
+ SM, Context->getLangOpts())
+ .getBegin();
+ if (InitialLoc.isInvalid())
+ return false;
+ SourceLocation StartLoc =
+ Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
+
+ // StartLoc points at the location of the opening brace to be inserted.
+ SourceLocation EndLoc;
+ std::string ClosingInsertion;
+ if (EndLocHint.isValid()) {
+ EndLoc = EndLocHint;
+ ClosingInsertion = "} ";
+ } else {
+ EndLoc = findEndLocation(*S, SM, Context);
+ ClosingInsertion = "\n}";
+ }
+
+ assert(StartLoc.isValid());
+
+ // Don't require braces for statements spanning less than certain number of
+ // lines.
+ if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
+ unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
+ unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
+ if (EndLine - StartLine < ShortStatementLines)
+ return false;
+ }
+
+ auto Diag = diag(StartLoc, "statement should be inside braces");
+
+ // Change only if StartLoc and EndLoc are on the same macro expansion level.
+ // This will also catch invalid EndLoc.
+ // Example: LLVM_DEBUG( for(...) do_something() );
+ // In this case fix-it cannot be provided as the semicolon which is not
+ // visible here is part of the macro. Adding braces here would require adding
+ // another semicolon.
+ if (Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(SourceRange(
+ SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
+ SM, Context->getLangOpts())
+ .isInvalid())
+ return false;
+
+ Diag << FixItHint::CreateInsertion(StartLoc, " {")
+ << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
+ return true;
+}
+
+void BracesAroundStatementsCheck::onEndOfTranslationUnit() {
+ ForceBracesStmts.clear();
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.h
new file mode 100644
index 0000000000..9653dd30f0
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/BracesAroundStatementsCheck.h
@@ -0,0 +1,67 @@
+//===--- BracesAroundStatementsCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks that bodies of `if` statements and loops (`for`, `range-for`,
+/// `do-while`, and `while`) are inside braces
+///
+/// Before:
+///
+/// \code
+/// if (condition)
+/// statement;
+/// \endcode
+///
+/// After:
+///
+/// \code
+/// if (condition) {
+/// statement;
+/// }
+/// \endcode
+///
+/// Additionally, one can define an option `ShortStatementLines` defining the
+/// minimal number of lines that the statement should have in order to trigger
+/// this check.
+///
+/// The number of lines is counted from the end of condition or initial keyword
+/// (`do`/`else`) until the last line of the inner statement. Default value 0
+/// means that braces will be added to all statements (not having them already).
+class BracesAroundStatementsCheck : public ClangTidyCheck {
+public:
+ BracesAroundStatementsCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void onEndOfTranslationUnit() override;
+
+private:
+ bool checkStmt(const ast_matchers::MatchFinder::MatchResult &Result,
+ const Stmt *S, SourceLocation StartLoc,
+ SourceLocation EndLocHint = SourceLocation());
+ template <typename IfOrWhileStmt>
+ SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
+ const ASTContext *Context);
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ std::set<const Stmt *> ForceBracesStmts;
+ const unsigned ShortStatementLines;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.cpp
new file mode 100644
index 0000000000..e92350632b
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.cpp
@@ -0,0 +1,159 @@
+//===--- ConstReturnTypeCheck.cpp - clang-tidy ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ConstReturnTypeCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+// Finds the location of the qualifying `const` token in the `FunctionDecl`'s
+// return type. Returns `std::nullopt` when the return type is not
+// `const`-qualified or `const` does not appear in `Def`'s source, like when the
+// type is an alias or a macro.
+static std::optional<Token>
+findConstToRemove(const FunctionDecl *Def,
+ const MatchFinder::MatchResult &Result) {
+ if (!Def->getReturnType().isLocalConstQualified())
+ return std::nullopt;
+
+ // Get the begin location for the function name, including any qualifiers
+ // written in the source (for out-of-line declarations). A FunctionDecl's
+ // "location" is the start of its name, so, when the name is unqualified, we
+ // use `getLocation()`.
+ SourceLocation NameBeginLoc = Def->getQualifier()
+ ? Def->getQualifierLoc().getBeginLoc()
+ : Def->getLocation();
+ // Since either of the locs can be in a macro, use `makeFileCharRange` to be
+ // sure that we have a consistent `CharSourceRange`, located entirely in the
+ // source file.
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(Def->getBeginLoc(), NameBeginLoc),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (FileRange.isInvalid())
+ return std::nullopt;
+
+ return utils::lexer::getQualifyingToken(
+ tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);
+}
+
+namespace {
+
+AST_MATCHER(QualType, isLocalConstQualified) {
+ return Node.isLocalConstQualified();
+}
+
+AST_MATCHER(QualType, isTypeOfType) {
+ return isa<TypeOfType>(Node.getTypePtr());
+}
+
+AST_MATCHER(QualType, isTypeOfExprType) {
+ return isa<TypeOfExprType>(Node.getTypePtr());
+}
+
+struct CheckResult {
+ // Source range of the relevant `const` token in the definition being checked.
+ CharSourceRange ConstRange;
+
+ // FixItHints associated with the definition being checked.
+ llvm::SmallVector<clang::FixItHint, 4> Hints;
+
+ // Locations of any declarations that could not be fixed.
+ llvm::SmallVector<clang::SourceLocation, 4> DeclLocs;
+};
+
+} // namespace
+
+// Does the actual work of the check.
+static CheckResult checkDef(const clang::FunctionDecl *Def,
+ const MatchFinder::MatchResult &MatchResult) {
+ CheckResult Result;
+ std::optional<Token> Tok = findConstToRemove(Def, MatchResult);
+ if (!Tok)
+ return Result;
+
+ Result.ConstRange =
+ CharSourceRange::getCharRange(Tok->getLocation(), Tok->getEndLoc());
+ Result.Hints.push_back(FixItHint::CreateRemoval(Result.ConstRange));
+
+ // Fix the definition and any visible declarations, but don't warn
+ // separately for each declaration. Instead, associate all fixes with the
+ // single warning at the definition.
+ for (const FunctionDecl *Decl = Def->getPreviousDecl(); Decl != nullptr;
+ Decl = Decl->getPreviousDecl()) {
+ if (std::optional<Token> T = findConstToRemove(Decl, MatchResult))
+ Result.Hints.push_back(FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(T->getLocation(), T->getEndLoc())));
+ else
+ // `getInnerLocStart` gives the start of the return type.
+ Result.DeclLocs.push_back(Decl->getInnerLocStart());
+ }
+ return Result;
+}
+
+void ConstReturnTypeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void ConstReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
+ // Find all function definitions for which the return types are `const`
+ // qualified, ignoring decltype types.
+ auto NonLocalConstType =
+ qualType(unless(isLocalConstQualified()),
+ anyOf(decltypeType(), autoType(), isTypeOfType(),
+ isTypeOfExprType(), substTemplateTypeParmType()));
+ Finder->addMatcher(
+ functionDecl(
+ returns(allOf(isConstQualified(), unless(NonLocalConstType))),
+ anyOf(isDefinition(), cxxMethodDecl(isPure())),
+ // Overridden functions are not actionable.
+ unless(cxxMethodDecl(isOverride())))
+ .bind("func"),
+ this);
+}
+
+void ConstReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Def = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ // Suppress the check if macros are involved.
+ if (IgnoreMacros &&
+ (Def->getBeginLoc().isMacroID() || Def->getEndLoc().isMacroID()))
+ return;
+
+ CheckResult CR = checkDef(Def, Result);
+ {
+ // Clang only supports one in-flight diagnostic at a time. So, delimit the
+ // scope of `Diagnostic` to allow further diagnostics after the scope. We
+ // use `getInnerLocStart` to get the start of the return type.
+ DiagnosticBuilder Diagnostic =
+ diag(Def->getInnerLocStart(),
+ "return type %0 is 'const'-qualified at the top level, which may "
+ "reduce code readability without improving const correctness")
+ << Def->getReturnType();
+ if (CR.ConstRange.isValid())
+ Diagnostic << CR.ConstRange;
+
+ // Do not propose fixes for virtual function.
+ const auto *Method = dyn_cast<CXXMethodDecl>(Def);
+ if (Method && Method->isVirtual())
+ return;
+
+ for (auto &Hint : CR.Hints)
+ Diagnostic << Hint;
+ }
+ for (auto Loc : CR.DeclLocs)
+ diag(Loc, "could not transform this declaration", DiagnosticIDs::Note);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.h
new file mode 100644
index 0000000000..c3d779eafe
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConstReturnTypeCheck.h
@@ -0,0 +1,36 @@
+//===--- ConstReturnTypeCheck.h - clang-tidy --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONSTRETURNTYPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONSTRETURNTYPECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// For any function whose return type is const-qualified, suggests removal of
+/// the `const` qualifier from that return type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/const-return-type.html
+class ConstReturnTypeCheck : public ClangTidyCheck {
+ public:
+ ConstReturnTypeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ private:
+ const bool IgnoreMacros;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONSTRETURNTYPECHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.cpp
new file mode 100644
index 0000000000..970ed8b83e
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.cpp
@@ -0,0 +1,138 @@
+//===--- ContainerContainsCheck.cpp - clang-tidy --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ContainerContainsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
+ const auto SupportedContainers = hasType(
+ hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
+ hasAnyName("::std::set", "::std::unordered_set", "::std::map",
+ "::std::unordered_map", "::std::multiset",
+ "::std::unordered_multiset", "::std::multimap",
+ "::std::unordered_multimap"))))));
+
+ const auto CountCall =
+ cxxMemberCallExpr(on(SupportedContainers),
+ callee(cxxMethodDecl(hasName("count"))),
+ argumentCountIs(1))
+ .bind("call");
+
+ const auto FindCall =
+ cxxMemberCallExpr(on(SupportedContainers),
+ callee(cxxMethodDecl(hasName("find"))),
+ argumentCountIs(1))
+ .bind("call");
+
+ const auto EndCall = cxxMemberCallExpr(on(SupportedContainers),
+ callee(cxxMethodDecl(hasName("end"))),
+ argumentCountIs(0));
+
+ const auto Literal0 = integerLiteral(equals(0));
+ const auto Literal1 = integerLiteral(equals(1));
+
+ auto AddSimpleMatcher = [&](auto Matcher) {
+ Finder->addMatcher(
+ traverse(TK_IgnoreUnlessSpelledInSource, std::move(Matcher)), this);
+ };
+
+ // Find membership tests which use `count()`.
+ Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()),
+ hasSourceExpression(CountCall))
+ .bind("positiveComparison"),
+ this);
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(CountCall), hasOperatorName("!="), hasRHS(Literal0))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(Literal0), hasOperatorName("!="), hasRHS(CountCall))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(CountCall), hasOperatorName(">="), hasRHS(Literal1))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(Literal1), hasOperatorName("<="), hasRHS(CountCall))
+ .bind("positiveComparison"));
+
+ // Find inverted membership tests which use `count()`.
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(CountCall), hasOperatorName("=="), hasRHS(Literal0))
+ .bind("negativeComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(Literal0), hasOperatorName("=="), hasRHS(CountCall))
+ .bind("negativeComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(CountCall), hasOperatorName("<="), hasRHS(Literal0))
+ .bind("negativeComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(Literal0), hasOperatorName(">="), hasRHS(CountCall))
+ .bind("negativeComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
+ .bind("negativeComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
+ .bind("negativeComparison"));
+
+ // Find membership tests based on `find() == end()`.
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(FindCall), hasOperatorName("!="), hasRHS(EndCall))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(
+ binaryOperator(hasLHS(FindCall), hasOperatorName("=="), hasRHS(EndCall))
+ .bind("negativeComparison"));
+}
+
+void ContainerContainsCheck::check(const MatchFinder::MatchResult &Result) {
+ // Extract the information about the match
+ const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
+ const auto *PositiveComparison =
+ Result.Nodes.getNodeAs<Expr>("positiveComparison");
+ const auto *NegativeComparison =
+ Result.Nodes.getNodeAs<Expr>("negativeComparison");
+ assert((!PositiveComparison || !NegativeComparison) &&
+ "only one of PositiveComparison or NegativeComparison should be set");
+ bool Negated = NegativeComparison != nullptr;
+ const auto *Comparison = Negated ? NegativeComparison : PositiveComparison;
+
+ // Diagnose the issue.
+ auto Diag =
+ diag(Call->getExprLoc(), "use 'contains' to check for membership");
+
+ // Don't fix it if it's in a macro invocation. Leave fixing it to the user.
+ SourceLocation FuncCallLoc = Comparison->getEndLoc();
+ if (!FuncCallLoc.isValid() || FuncCallLoc.isMacroID())
+ return;
+
+ // Create the fix it.
+ const auto *Member = cast<MemberExpr>(Call->getCallee());
+ Diag << FixItHint::CreateReplacement(
+ Member->getMemberNameInfo().getSourceRange(), "contains");
+ SourceLocation ComparisonBegin = Comparison->getSourceRange().getBegin();
+ SourceLocation ComparisonEnd = Comparison->getSourceRange().getEnd();
+ SourceLocation CallBegin = Call->getSourceRange().getBegin();
+ SourceLocation CallEnd = Call->getSourceRange().getEnd();
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(ComparisonBegin, CallBegin),
+ Negated ? "!" : "");
+ Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
+ CallEnd.getLocWithOffset(1), ComparisonEnd.getLocWithOffset(1)));
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.h
new file mode 100644
index 0000000000..2e8276d684
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerContainsCheck.h
@@ -0,0 +1,36 @@
+//===--- ContainerContainsCheck.h - clang-tidy ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERCONTAINSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERCONTAINSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds usages of `container.count()` and `find() == end()` which should be
+/// replaced by a call to the `container.contains()` method introduced in C++20.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/container-contains.html
+class ContainerContainsCheck : public ClangTidyCheck {
+public:
+ ContainerContainsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) final;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
+
+protected:
+ bool isLanguageVersionSupported(const LangOptions &LO) const final {
+ return LO.CPlusPlus20;
+ }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERCONTAINSCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.cpp
new file mode 100644
index 0000000000..5a51b2567d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.cpp
@@ -0,0 +1,115 @@
+//===--- ContainerDataPointerCheck.cpp - clang-tidy -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ContainerDataPointerCheck.h"
+
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+constexpr llvm::StringLiteral ContainerExprName = "container-expr";
+constexpr llvm::StringLiteral DerefContainerExprName = "deref-container-expr";
+constexpr llvm::StringLiteral AddrOfContainerExprName =
+ "addr-of-container-expr";
+constexpr llvm::StringLiteral AddressOfName = "address-of";
+
+ContainerDataPointerCheck::ContainerDataPointerCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void ContainerDataPointerCheck::registerMatchers(MatchFinder *Finder) {
+ const auto Record =
+ cxxRecordDecl(
+ isSameOrDerivedFrom(
+ namedDecl(
+ has(cxxMethodDecl(isPublic(), hasName("data")).bind("data")))
+ .bind("container")))
+ .bind("record");
+
+ const auto NonTemplateContainerType =
+ qualType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(Record))));
+ const auto TemplateContainerType =
+ qualType(hasUnqualifiedDesugaredType(templateSpecializationType(
+ hasDeclaration(classTemplateDecl(has(Record))))));
+
+ const auto Container =
+ qualType(anyOf(NonTemplateContainerType, TemplateContainerType));
+
+ const auto ContainerExpr = anyOf(
+ unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(
+ expr(hasType(pointsTo(Container))).bind(DerefContainerExprName)))
+ .bind(ContainerExprName),
+ unaryOperator(hasOperatorName("&"),
+ hasUnaryOperand(expr(anyOf(hasType(Container),
+ hasType(references(Container))))
+ .bind(AddrOfContainerExprName)))
+ .bind(ContainerExprName),
+ expr(anyOf(hasType(Container), hasType(pointsTo(Container)),
+ hasType(references(Container))))
+ .bind(ContainerExprName));
+
+ const auto Zero = integerLiteral(equals(0));
+
+ const auto SubscriptOperator = callee(cxxMethodDecl(hasName("operator[]")));
+
+ Finder->addMatcher(
+ unaryOperator(
+ unless(isExpansionInSystemHeader()), hasOperatorName("&"),
+ hasUnaryOperand(expr(
+ anyOf(cxxOperatorCallExpr(SubscriptOperator, argumentCountIs(2),
+ hasArgument(0, ContainerExpr),
+ hasArgument(1, Zero)),
+ cxxMemberCallExpr(SubscriptOperator, on(ContainerExpr),
+ argumentCountIs(1), hasArgument(0, Zero)),
+ arraySubscriptExpr(hasLHS(ContainerExpr), hasRHS(Zero))))))
+ .bind(AddressOfName),
+ this);
+}
+
+void ContainerDataPointerCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *UO = Result.Nodes.getNodeAs<UnaryOperator>(AddressOfName);
+ const auto *CE = Result.Nodes.getNodeAs<Expr>(ContainerExprName);
+ const auto *DCE = Result.Nodes.getNodeAs<Expr>(DerefContainerExprName);
+ const auto *ACE = Result.Nodes.getNodeAs<Expr>(AddrOfContainerExprName);
+
+ if (!UO || !CE)
+ return;
+
+ if (DCE && !CE->getType()->isPointerType())
+ CE = DCE;
+ else if (ACE)
+ CE = ACE;
+
+ SourceRange SrcRange = CE->getSourceRange();
+
+ std::string ReplacementText{
+ Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange),
+ *Result.SourceManager, getLangOpts())};
+
+ if (!isa<DeclRefExpr, ArraySubscriptExpr, CXXOperatorCallExpr, CallExpr,
+ MemberExpr>(CE))
+ ReplacementText = "(" + ReplacementText + ")";
+
+ if (CE->getType()->isPointerType())
+ ReplacementText += "->data()";
+ else
+ ReplacementText += ".data()";
+
+ FixItHint Hint =
+ FixItHint::CreateReplacement(UO->getSourceRange(), ReplacementText);
+ diag(UO->getBeginLoc(),
+ "'data' should be used for accessing the data pointer instead of taking "
+ "the address of the 0-th element")
+ << Hint;
+}
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.h
new file mode 100644
index 0000000000..65f78b8f6f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerDataPointerCheck.h
@@ -0,0 +1,41 @@
+//===--- ContainerDataPointerCheck.h - clang-tidy ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERDATAPOINTERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERDATAPOINTERCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+/// Checks whether a call to `operator[]` and `&` can be replaced with a call to
+/// `data()`.
+///
+/// This only replaces the case where the offset being accessed through the
+/// subscript operation is a known constant 0. This avoids a potential invalid
+/// memory access when the container is empty. Cases where the constant is not
+/// explicitly zero can be addressed through the clang static analyzer, and
+/// those which cannot be statically identified can be caught using UBSan.
+class ContainerDataPointerCheck : public ClangTidyCheck {
+public:
+ ContainerDataPointerCheck(StringRef Name, ClangTidyContext *Context);
+
+ bool isLanguageVersionSupported(const LangOptions &LO) const override {
+ return LO.CPlusPlus11;
+ }
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERDATAPOINTERCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
new file mode 100644
index 0000000000..a39b72e39c
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
@@ -0,0 +1,331 @@
+//===--- ContainerSizeEmptyCheck.cpp - clang-tidy -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "ContainerSizeEmptyCheck.h"
+#include "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace ast_matchers {
+AST_POLYMORPHIC_MATCHER_P2(hasAnyArgumentWithParam,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
+ CXXConstructExpr),
+ internal::Matcher<Expr>, ArgMatcher,
+ internal::Matcher<ParmVarDecl>, ParamMatcher) {
+ BoundNodesTreeBuilder Result;
+ // The first argument of an overloaded member operator is the implicit object
+ // argument of the method which should not be matched against a parameter, so
+ // we skip over it here.
+ BoundNodesTreeBuilder Matches;
+ unsigned ArgIndex = cxxOperatorCallExpr(callee(cxxMethodDecl()))
+ .matches(Node, Finder, &Matches)
+ ? 1
+ : 0;
+ int ParamIndex = 0;
+ for (; ArgIndex < Node.getNumArgs(); ++ArgIndex) {
+ BoundNodesTreeBuilder ArgMatches(*Builder);
+ if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->IgnoreParenCasts()), Finder,
+ &ArgMatches)) {
+ BoundNodesTreeBuilder ParamMatches(ArgMatches);
+ if (expr(anyOf(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
+ hasParameter(ParamIndex, ParamMatcher)))),
+ callExpr(callee(functionDecl(
+ hasParameter(ParamIndex, ParamMatcher))))))
+ .matches(Node, Finder, &ParamMatches)) {
+ Result.addMatch(ParamMatches);
+ *Builder = std::move(Result);
+ return true;
+ }
+ }
+ ++ParamIndex;
+ }
+ return false;
+}
+
+AST_MATCHER(Expr, usedInBooleanContext) {
+ const char *ExprName = "__booleanContextExpr";
+ auto Result =
+ expr(expr().bind(ExprName),
+ anyOf(hasParent(
+ mapAnyOf(varDecl, fieldDecl).with(hasType(booleanType()))),
+ hasParent(cxxConstructorDecl(
+ hasAnyConstructorInitializer(cxxCtorInitializer(
+ withInitializer(expr(equalsBoundNode(ExprName))),
+ forField(hasType(booleanType())))))),
+ hasParent(stmt(anyOf(
+ explicitCastExpr(hasDestinationType(booleanType())),
+ mapAnyOf(ifStmt, doStmt, whileStmt, forStmt,
+ conditionalOperator)
+ .with(hasCondition(expr(equalsBoundNode(ExprName)))),
+ parenListExpr(hasParent(varDecl(hasType(booleanType())))),
+ parenExpr(hasParent(
+ explicitCastExpr(hasDestinationType(booleanType())))),
+ returnStmt(forFunction(returns(booleanType()))),
+ cxxUnresolvedConstructExpr(hasType(booleanType())),
+ invocation(hasAnyArgumentWithParam(
+ expr(equalsBoundNode(ExprName)),
+ parmVarDecl(hasType(booleanType())))),
+ binaryOperator(hasAnyOperatorName("&&", "||")),
+ unaryOperator(hasOperatorName("!")).bind("NegOnSize"))))))
+ .matches(Node, Finder, Builder);
+ Builder->removeBindings([ExprName](const BoundNodesMap &Nodes) {
+ return Nodes.getNode(ExprName).getNodeKind().isNone();
+ });
+ return Result;
+}
+AST_MATCHER(CXXConstructExpr, isDefaultConstruction) {
+ return Node.getConstructor()->isDefaultConstructor();
+}
+AST_MATCHER(QualType, isIntegralType) {
+ return Node->isIntegralType(Finder->getASTContext());
+}
+} // namespace ast_matchers
+namespace tidy::readability {
+
+using utils::isBinaryOrTernary;
+
+ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ValidContainerRecord = cxxRecordDecl(isSameOrDerivedFrom(
+ namedDecl(
+ has(cxxMethodDecl(
+ isConst(), parameterCountIs(0), isPublic(), hasName("size"),
+ returns(qualType(isIntegralType(), unless(booleanType()))))
+ .bind("size")),
+ has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
+ hasName("empty"), returns(booleanType()))
+ .bind("empty")))
+ .bind("container")));
+
+ const auto ValidContainerNonTemplateType =
+ qualType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(ValidContainerRecord))));
+ const auto ValidContainerTemplateType =
+ qualType(hasUnqualifiedDesugaredType(templateSpecializationType(
+ hasDeclaration(classTemplateDecl(has(ValidContainerRecord))))));
+
+ const auto ValidContainer = qualType(
+ anyOf(ValidContainerNonTemplateType, ValidContainerTemplateType));
+
+ const auto WrongUse =
+ anyOf(hasParent(binaryOperator(
+ isComparisonOperator(),
+ hasEitherOperand(anyOf(integerLiteral(equals(1)),
+ integerLiteral(equals(0)))))
+ .bind("SizeBinaryOp")),
+ usedInBooleanContext());
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
+ hasType(pointsTo(ValidContainer)),
+ hasType(references(ValidContainer))))
+ .bind("MemberCallObject")),
+ callee(cxxMethodDecl(hasName("size"))), WrongUse,
+ unless(hasAncestor(cxxMethodDecl(
+ ofClass(equalsBoundNode("container"))))))
+ .bind("SizeCallExpr"),
+ this);
+
+ Finder->addMatcher(
+ callExpr(has(cxxDependentScopeMemberExpr(
+ hasObjectExpression(
+ expr(anyOf(hasType(ValidContainer),
+ hasType(pointsTo(ValidContainer)),
+ hasType(references(ValidContainer))))
+ .bind("MemberCallObject")),
+ hasMemberName("size"))),
+ WrongUse,
+ unless(hasAncestor(
+ cxxMethodDecl(ofClass(equalsBoundNode("container"))))))
+ .bind("SizeCallExpr"),
+ this);
+
+ // Comparison to empty string or empty constructor.
+ const auto WrongComparend = anyOf(
+ stringLiteral(hasSize(0)), cxxConstructExpr(isDefaultConstruction()),
+ cxxUnresolvedConstructExpr(argumentCountIs(0)));
+ // Match the object being compared.
+ const auto STLArg =
+ anyOf(unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(
+ expr(hasType(pointsTo(ValidContainer))).bind("Pointee"))),
+ expr(hasType(ValidContainer)).bind("STLObject"));
+ Finder->addMatcher(
+ binaryOperation(hasAnyOperatorName("==", "!="),
+ hasOperands(WrongComparend,
+ STLArg),
+ unless(hasAncestor(cxxMethodDecl(
+ ofClass(equalsBoundNode("container"))))))
+ .bind("BinCmp"),
+ this);
+}
+
+void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemberCall = Result.Nodes.getNodeAs<Expr>("SizeCallExpr");
+ const auto *MemberCallObject =
+ Result.Nodes.getNodeAs<Expr>("MemberCallObject");
+ const auto *BinCmp = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("BinCmp");
+ const auto *BinCmpTempl = Result.Nodes.getNodeAs<BinaryOperator>("BinCmp");
+ const auto *BinCmpRewritten =
+ Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>("BinCmp");
+ const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
+ const auto *Pointee = Result.Nodes.getNodeAs<Expr>("Pointee");
+ const auto *E =
+ MemberCallObject
+ ? MemberCallObject
+ : (Pointee ? Pointee : Result.Nodes.getNodeAs<Expr>("STLObject"));
+ FixItHint Hint;
+ std::string ReplacementText = std::string(
+ Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
+ *Result.SourceManager, getLangOpts()));
+ const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E);
+ if (isBinaryOrTernary(E) || isa<UnaryOperator>(E) ||
+ (OpCallExpr && (OpCallExpr->getOperator() == OO_Star))) {
+ ReplacementText = "(" + ReplacementText + ")";
+ }
+ if (OpCallExpr &&
+ OpCallExpr->getOperator() == OverloadedOperatorKind::OO_Arrow) {
+ // This can happen if the object is a smart pointer. Don't add anything
+ // because a '->' is already there (PR#51776), just call the method.
+ ReplacementText += "empty()";
+ } else if (E->getType()->isPointerType())
+ ReplacementText += "->empty()";
+ else
+ ReplacementText += ".empty()";
+
+ if (BinCmp) {
+ if (BinCmp->getOperator() == OO_ExclaimEqual) {
+ ReplacementText = "!" + ReplacementText;
+ }
+ Hint =
+ FixItHint::CreateReplacement(BinCmp->getSourceRange(), ReplacementText);
+ } else if (BinCmpTempl) {
+ if (BinCmpTempl->getOpcode() == BinaryOperatorKind::BO_NE) {
+ ReplacementText = "!" + ReplacementText;
+ }
+ Hint = FixItHint::CreateReplacement(BinCmpTempl->getSourceRange(),
+ ReplacementText);
+ } else if (BinCmpRewritten) {
+ if (BinCmpRewritten->getOpcode() == BinaryOperatorKind::BO_NE) {
+ ReplacementText = "!" + ReplacementText;
+ }
+ Hint = FixItHint::CreateReplacement(BinCmpRewritten->getSourceRange(),
+ ReplacementText);
+ } else if (BinaryOp) { // Determine the correct transformation.
+ const auto *LiteralLHS =
+ llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
+ const auto *LiteralRHS =
+ llvm::dyn_cast<IntegerLiteral>(BinaryOp->getRHS()->IgnoreImpCasts());
+ const bool ContainerIsLHS = !LiteralLHS;
+
+ uint64_t Value = 0;
+ if (LiteralLHS)
+ Value = LiteralLHS->getValue().getLimitedValue();
+ else if (LiteralRHS)
+ Value = LiteralRHS->getValue().getLimitedValue();
+ else
+ return;
+
+ bool Negation = false;
+ const auto OpCode = BinaryOp->getOpcode();
+
+ // Constant that is not handled.
+ if (Value > 1)
+ return;
+
+ if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
+ OpCode == BinaryOperatorKind::BO_NE))
+ return;
+
+ // Always true, no warnings for that.
+ if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
+ (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
+ return;
+
+ // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
+ if (Value == 1) {
+ if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
+ (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
+ return;
+ if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
+ (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
+ return;
+ }
+
+ if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
+ Negation = true;
+ if ((OpCode == BinaryOperatorKind::BO_GT ||
+ OpCode == BinaryOperatorKind::BO_GE) &&
+ ContainerIsLHS)
+ Negation = true;
+ if ((OpCode == BinaryOperatorKind::BO_LT ||
+ OpCode == BinaryOperatorKind::BO_LE) &&
+ !ContainerIsLHS)
+ Negation = true;
+
+ if (Negation)
+ ReplacementText = "!" + ReplacementText;
+ Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
+ ReplacementText);
+
+ } else {
+ // If there is a conversion above the size call to bool, it is safe to just
+ // replace size with empty.
+ if (const auto *UnaryOp =
+ Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize"))
+ Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
+ ReplacementText);
+ else
+ Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
+ "!" + ReplacementText);
+ }
+
+ auto WarnLoc = MemberCall ? MemberCall->getBeginLoc() : SourceLocation{};
+
+ if (WarnLoc.isValid()) {
+ diag(WarnLoc, "the 'empty' method should be used to check "
+ "for emptiness instead of 'size'")
+ << Hint;
+ } else {
+ WarnLoc = BinCmpTempl
+ ? BinCmpTempl->getBeginLoc()
+ : (BinCmp ? BinCmp->getBeginLoc()
+ : (BinCmpRewritten ? BinCmpRewritten->getBeginLoc()
+ : SourceLocation{}));
+ diag(WarnLoc, "the 'empty' method should be used to check "
+ "for emptiness instead of comparing to an empty object")
+ << Hint;
+ }
+
+ const auto *Container = Result.Nodes.getNodeAs<NamedDecl>("container");
+ if (const auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Container)) {
+ // The definition of the empty() method is the same for all implicit
+ // instantiations. In order to avoid duplicate or inconsistent warnings
+ // (depending on how deduplication is done), we use the same class name
+ // for all implicit instantiations of a template.
+ if (CTS->getSpecializationKind() == TSK_ImplicitInstantiation)
+ Container = CTS->getSpecializedTemplate();
+ }
+ const auto *Empty = Result.Nodes.getNodeAs<FunctionDecl>("empty");
+
+ diag(Empty->getLocation(), "method %0::empty() defined here",
+ DiagnosticIDs::Note)
+ << Container;
+}
+
+} // namespace tidy::readability
+} // namespace clang
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.h
new file mode 100644
index 0000000000..90d1839395
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ContainerSizeEmptyCheck.h
@@ -0,0 +1,41 @@
+//===--- ContainerSizeEmptyCheck.h - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks whether a call to the `size()` method can be replaced with a call to
+/// `empty()`.
+///
+/// The emptiness of a container should be checked using the `empty()` method
+/// instead of the `size()` method. It is not guaranteed that `size()` is a
+/// constant-time function, and it is generally more efficient and also shows
+/// clearer intent to use `empty()`. Furthermore some containers may implement
+/// the `empty()` method but not implement the `size()` method. Using `empty()`
+/// whenever possible makes it easier to switch to another container in the
+/// future.
+class ContainerSizeEmptyCheck : public ClangTidyCheck {
+public:
+ ContainerSizeEmptyCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp
new file mode 100644
index 0000000000..0ef7d2955f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp
@@ -0,0 +1,168 @@
+//===--- ConvertMemberFunctionsToStatic.cpp - clang-tidy ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ConvertMemberFunctionsToStatic.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
+
+AST_MATCHER(CXXMethodDecl, hasTrivialBody) { return Node.hasTrivialBody(); }
+
+AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
+ return Node.isOverloadedOperator();
+}
+
+AST_MATCHER(CXXRecordDecl, hasAnyDependentBases) {
+ return Node.hasAnyDependentBases();
+}
+
+AST_MATCHER(CXXMethodDecl, isTemplate) {
+ return Node.getTemplatedKind() != FunctionDecl::TK_NonTemplate;
+}
+
+AST_MATCHER(CXXMethodDecl, isDependentContext) {
+ return Node.isDependentContext();
+}
+
+AST_MATCHER(CXXMethodDecl, isInsideMacroDefinition) {
+ const ASTContext &Ctxt = Finder->getASTContext();
+ return clang::Lexer::makeFileCharRange(
+ clang::CharSourceRange::getCharRange(
+ Node.getTypeSourceInfo()->getTypeLoc().getSourceRange()),
+ Ctxt.getSourceManager(), Ctxt.getLangOpts())
+ .isInvalid();
+}
+
+AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl,
+ ast_matchers::internal::Matcher<CXXMethodDecl>, InnerMatcher) {
+ return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder);
+}
+
+AST_MATCHER(CXXMethodDecl, usesThis) {
+ class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> {
+ public:
+ bool Used = false;
+
+ bool VisitCXXThisExpr(const CXXThisExpr *E) {
+ Used = true;
+ return false; // Stop traversal.
+ }
+ } UsageOfThis;
+
+ // TraverseStmt does not modify its argument.
+ UsageOfThis.TraverseStmt(const_cast<Stmt *>(Node.getBody()));
+
+ return UsageOfThis.Used;
+}
+
+void ConvertMemberFunctionsToStatic::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxMethodDecl(
+ isDefinition(), isUserProvided(),
+ unless(anyOf(
+ isExpansionInSystemHeader(), isVirtual(), isStatic(),
+ hasTrivialBody(), isOverloadedOperator(), cxxConstructorDecl(),
+ cxxDestructorDecl(), cxxConversionDecl(), isTemplate(),
+ isDependentContext(),
+ ofClass(anyOf(
+ isLambda(),
+ hasAnyDependentBases()) // Method might become virtual
+ // depending on template base class.
+ ),
+ isInsideMacroDefinition(),
+ hasCanonicalDecl(isInsideMacroDefinition()), usesThis())))
+ .bind("x"),
+ this);
+}
+
+/// Obtain the original source code text from a SourceRange.
+static StringRef getStringFromRange(SourceManager &SourceMgr,
+ const LangOptions &LangOpts,
+ SourceRange Range) {
+ if (SourceMgr.getFileID(Range.getBegin()) !=
+ SourceMgr.getFileID(Range.getEnd()))
+ return {};
+
+ return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
+ LangOpts);
+}
+
+static SourceRange getLocationOfConst(const TypeSourceInfo *TSI,
+ SourceManager &SourceMgr,
+ const LangOptions &LangOpts) {
+ assert(TSI);
+ const auto FTL = TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
+ assert(FTL);
+
+ SourceRange Range{FTL.getRParenLoc().getLocWithOffset(1),
+ FTL.getLocalRangeEnd()};
+ // Inside Range, there might be other keywords and trailing return types.
+ // Find the exact position of "const".
+ StringRef Text = getStringFromRange(SourceMgr, LangOpts, Range);
+ size_t Offset = Text.find("const");
+ if (Offset == StringRef::npos)
+ return {};
+
+ SourceLocation Start = Range.getBegin().getLocWithOffset(Offset);
+ return {Start, Start.getLocWithOffset(strlen("const") - 1)};
+}
+
+void ConvertMemberFunctionsToStatic::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>("x");
+
+ // TODO: For out-of-line declarations, don't modify the source if the header
+ // is excluded by the -header-filter option.
+ DiagnosticBuilder Diag =
+ diag(Definition->getLocation(), "method %0 can be made static")
+ << Definition;
+
+ // TODO: Would need to remove those in a fix-it.
+ if (Definition->getMethodQualifiers().hasVolatile() ||
+ Definition->getMethodQualifiers().hasRestrict() ||
+ Definition->getRefQualifier() != RQ_None)
+ return;
+
+ const CXXMethodDecl *Declaration = Definition->getCanonicalDecl();
+
+ if (Definition->isConst()) {
+ // Make sure that we either remove 'const' on both declaration and
+ // definition or emit no fix-it at all.
+ SourceRange DefConst = getLocationOfConst(Definition->getTypeSourceInfo(),
+ *Result.SourceManager,
+ Result.Context->getLangOpts());
+
+ if (DefConst.isInvalid())
+ return;
+
+ if (Declaration != Definition) {
+ SourceRange DeclConst = getLocationOfConst(
+ Declaration->getTypeSourceInfo(), *Result.SourceManager,
+ Result.Context->getLangOpts());
+
+ if (DeclConst.isInvalid())
+ return;
+ Diag << FixItHint::CreateRemoval(DeclConst);
+ }
+
+ // Remove existing 'const' from both declaration and definition.
+ Diag << FixItHint::CreateRemoval(DefConst);
+ }
+ Diag << FixItHint::CreateInsertion(Declaration->getBeginLoc(), "static ");
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h
new file mode 100644
index 0000000000..79b0d39bed
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h
@@ -0,0 +1,32 @@
+//===--- ConvertMemberFunctionsToStatic.h - clang-tidy ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONVERTMEMFUNCTOSTATIC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONVERTMEMFUNCTOSTATIC_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// This check finds C++ class methods than can be made static
+/// because they don't use the 'this' pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/
+/// readability-convert-member-functions-to-static.html
+class ConvertMemberFunctionsToStatic : public ClangTidyCheck {
+public:
+ ConvertMemberFunctionsToStatic(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONVERTMEMFUNCTOSTATIC_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.cpp
new file mode 100644
index 0000000000..12131cc078
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.cpp
@@ -0,0 +1,74 @@
+//===--- DeleteNullPointerCheck.cpp - clang-tidy---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeleteNullPointerCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) {
+ const auto DeleteExpr =
+ cxxDeleteExpr(
+ has(declRefExpr(to(decl(equalsBoundNode("deletedPointer"))))))
+ .bind("deleteExpr");
+
+ const auto DeleteMemberExpr =
+ cxxDeleteExpr(has(memberExpr(hasDeclaration(
+ fieldDecl(equalsBoundNode("deletedMemberPointer"))))))
+ .bind("deleteMemberExpr");
+
+ const auto PointerExpr = anyOf(
+ declRefExpr(to(decl().bind("deletedPointer"))),
+ memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer"))));
+
+ const auto BinaryPointerCheckCondition = binaryOperator(hasOperands(
+ anyOf(cxxNullPtrLiteralExpr(), integerLiteral(equals(0))), PointerExpr));
+
+ Finder->addMatcher(
+ ifStmt(hasCondition(anyOf(PointerExpr, BinaryPointerCheckCondition)),
+ hasThen(anyOf(
+ DeleteExpr, DeleteMemberExpr,
+ compoundStmt(anyOf(has(DeleteExpr), has(DeleteMemberExpr)),
+ statementCountIs(1))
+ .bind("compound"))))
+ .bind("ifWithDelete"),
+ this);
+}
+
+void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *IfWithDelete = Result.Nodes.getNodeAs<IfStmt>("ifWithDelete");
+ const auto *Compound = Result.Nodes.getNodeAs<CompoundStmt>("compound");
+
+ auto Diag = diag(
+ IfWithDelete->getBeginLoc(),
+ "'if' statement is unnecessary; deleting null pointer has no effect");
+ if (IfWithDelete->getElse())
+ return;
+ // FIXME: generate fixit for this case.
+
+ Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+ IfWithDelete->getBeginLoc(),
+ utils::lexer::getPreviousToken(IfWithDelete->getThen()->getBeginLoc(),
+ *Result.SourceManager,
+ Result.Context->getLangOpts())
+ .getLocation()));
+
+ if (Compound) {
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Compound->getLBracLoc()));
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Compound->getRBracLoc()));
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.h
new file mode 100644
index 0000000000..e9e7c942d1
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DeleteNullPointerCheck.h
@@ -0,0 +1,34 @@
+//===--- DeleteNullPointerCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Check whether the 'if' statement is unnecessary before calling 'delete' on a
+/// pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/delete-null-pointer.html
+class DeleteNullPointerCheck : public ClangTidyCheck {
+public:
+ DeleteNullPointerCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.cpp
new file mode 100644
index 0000000000..d1f41e0ec7
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.cpp
@@ -0,0 +1,112 @@
+//===--- DuplicateIncludeCheck.cpp - clang-tidy ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DuplicateIncludeCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include <memory>
+
+namespace clang::tidy::readability {
+
+static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM,
+ SourceLocation Start,
+ int Offset) {
+ const FileID Id = SM.getFileID(Start);
+ const unsigned LineNumber = SM.getSpellingLineNumber(Start);
+ while (SM.getFileID(Start) == Id &&
+ SM.getSpellingLineNumber(Start.getLocWithOffset(Offset)) == LineNumber)
+ Start = Start.getLocWithOffset(Offset);
+ return Start;
+}
+
+namespace {
+
+using FileList = SmallVector<StringRef>;
+
+class DuplicateIncludeCallbacks : public PPCallbacks {
+public:
+ DuplicateIncludeCallbacks(DuplicateIncludeCheck &Check,
+ const SourceManager &SM)
+ : Check(Check), SM(SM) {
+ // The main file doesn't participate in the FileChanged notification.
+ Files.emplace_back();
+ }
+
+ void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+ SrcMgr::CharacteristicKind FileType,
+ FileID PrevFID) override;
+
+ void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+ StringRef FileName, bool IsAngled,
+ CharSourceRange FilenameRange,
+ OptionalFileEntryRef File, StringRef SearchPath,
+ StringRef RelativePath, const Module *Imported,
+ SrcMgr::CharacteristicKind FileType) override;
+
+ void MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) override;
+
+ void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
+ const MacroDirective *Undef) override;
+
+private:
+ // A list of included files is kept for each file we enter.
+ SmallVector<FileList> Files;
+ DuplicateIncludeCheck &Check;
+ const SourceManager &SM;
+};
+
+void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,
+ FileChangeReason Reason,
+ SrcMgr::CharacteristicKind FileType,
+ FileID PrevFID) {
+ if (Reason == EnterFile)
+ Files.emplace_back();
+ else if (Reason == ExitFile)
+ Files.pop_back();
+}
+
+void DuplicateIncludeCallbacks::InclusionDirective(
+ SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+ bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
+ StringRef SearchPath, StringRef RelativePath, const Module *Imported,
+ SrcMgr::CharacteristicKind FileType) {
+ if (llvm::is_contained(Files.back(), FileName)) {
+ // We want to delete the entire line, so make sure that [Start,End] covers
+ // everything.
+ SourceLocation Start =
+ advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1);
+ SourceLocation End =
+ advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1);
+ Check.diag(HashLoc, "duplicate include")
+ << FixItHint::CreateRemoval(SourceRange{Start, End});
+ } else
+ Files.back().push_back(FileName);
+}
+
+void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) {
+ Files.back().clear();
+}
+
+void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
+ const MacroDefinition &MD,
+ const MacroDirective *Undef) {
+ Files.back().clear();
+}
+
+} // namespace
+
+void DuplicateIncludeCheck::registerPPCallbacks(
+ const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+ PP->addPPCallbacks(std::make_unique<DuplicateIncludeCallbacks>(*this, SM));
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.h
new file mode 100644
index 0000000000..05395496d8
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/DuplicateIncludeCheck.h
@@ -0,0 +1,31 @@
+//===--- DuplicateIncludeCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DUPLICATE_INCLUDE_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DUPLICATE_INCLUDE_CHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// \brief Find and remove duplicate #include directives.
+///
+/// Only consecutive include directives without any other preprocessor
+/// directives between them are analyzed.
+class DuplicateIncludeCheck : public ClangTidyCheck {
+public:
+ DuplicateIncludeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DUPLICATE_INCLUDE_CHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
new file mode 100644
index 0000000000..1e85caf688
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
@@ -0,0 +1,325 @@
+//===--- ElseAfterReturnCheck.cpp - clang-tidy-----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ElseAfterReturnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/FixIt.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+class PPConditionalCollector : public PPCallbacks {
+public:
+ PPConditionalCollector(
+ ElseAfterReturnCheck::ConditionalBranchMap &Collections,
+ const SourceManager &SM)
+ : Collections(Collections), SM(SM) {}
+ void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
+ if (!SM.isWrittenInSameFile(Loc, IfLoc))
+ return;
+ SmallVectorImpl<SourceRange> &Collection = Collections[SM.getFileID(Loc)];
+ assert(Collection.empty() || Collection.back().getEnd() < Loc);
+ Collection.emplace_back(IfLoc, Loc);
+ }
+
+private:
+ ElseAfterReturnCheck::ConditionalBranchMap &Collections;
+ const SourceManager &SM;
+};
+
+} // namespace
+
+static const char InterruptingStr[] = "interrupting";
+static const char WarningMessage[] = "do not use 'else' after '%0'";
+static const char WarnOnUnfixableStr[] = "WarnOnUnfixable";
+static const char WarnOnConditionVariablesStr[] = "WarnOnConditionVariables";
+
+static const DeclRefExpr *findUsage(const Stmt *Node, int64_t DeclIdentifier) {
+ if (!Node)
+ return nullptr;
+ if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
+ if (DeclRef->getDecl()->getID() == DeclIdentifier)
+ return DeclRef;
+ } else {
+ for (const Stmt *ChildNode : Node->children()) {
+ if (const DeclRefExpr *Result = findUsage(ChildNode, DeclIdentifier))
+ return Result;
+ }
+ }
+ return nullptr;
+}
+
+static const DeclRefExpr *
+findUsageRange(const Stmt *Node,
+ const llvm::ArrayRef<int64_t> &DeclIdentifiers) {
+ if (!Node)
+ return nullptr;
+ if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
+ if (llvm::is_contained(DeclIdentifiers, DeclRef->getDecl()->getID()))
+ return DeclRef;
+ } else {
+ for (const Stmt *ChildNode : Node->children()) {
+ if (const DeclRefExpr *Result =
+ findUsageRange(ChildNode, DeclIdentifiers))
+ return Result;
+ }
+ }
+ return nullptr;
+}
+
+static const DeclRefExpr *checkInitDeclUsageInElse(const IfStmt *If) {
+ const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
+ if (!InitDeclStmt)
+ return nullptr;
+ if (InitDeclStmt->isSingleDecl()) {
+ const Decl *InitDecl = InitDeclStmt->getSingleDecl();
+ assert(isa<VarDecl>(InitDecl) && "SingleDecl must be a VarDecl");
+ return findUsage(If->getElse(), InitDecl->getID());
+ }
+ llvm::SmallVector<int64_t, 4> DeclIdentifiers;
+ for (const Decl *ChildDecl : InitDeclStmt->decls()) {
+ assert(isa<VarDecl>(ChildDecl) && "Init Decls must be a VarDecl");
+ DeclIdentifiers.push_back(ChildDecl->getID());
+ }
+ return findUsageRange(If->getElse(), DeclIdentifiers);
+}
+
+static const DeclRefExpr *checkConditionVarUsageInElse(const IfStmt *If) {
+ if (const VarDecl *CondVar = If->getConditionVariable())
+ return findUsage(If->getElse(), CondVar->getID());
+ return nullptr;
+}
+
+static bool containsDeclInScope(const Stmt *Node) {
+ if (isa<DeclStmt>(Node))
+ return true;
+ if (const auto *Compound = dyn_cast<CompoundStmt>(Node))
+ return llvm::any_of(Compound->body(), [](const Stmt *SubNode) {
+ return isa<DeclStmt>(SubNode);
+ });
+ return false;
+}
+
+static void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context,
+ const Stmt *Else, SourceLocation ElseLoc) {
+ auto Remap = [&](SourceLocation Loc) {
+ return Context.getSourceManager().getExpansionLoc(Loc);
+ };
+ auto TokLen = [&](SourceLocation Loc) {
+ return Lexer::MeasureTokenLength(Loc, Context.getSourceManager(),
+ Context.getLangOpts());
+ };
+
+ if (const auto *CS = dyn_cast<CompoundStmt>(Else)) {
+ Diag << tooling::fixit::createRemoval(ElseLoc);
+ SourceLocation LBrace = CS->getLBracLoc();
+ SourceLocation RBrace = CS->getRBracLoc();
+ SourceLocation RangeStart =
+ Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
+ SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
+
+ llvm::StringRef Repl = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(RangeStart, RangeEnd),
+ Context.getSourceManager(), Context.getLangOpts());
+ Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
+ } else {
+ SourceLocation ElseExpandedLoc = Remap(ElseLoc);
+ SourceLocation EndLoc = Remap(Else->getEndLoc());
+
+ llvm::StringRef Repl = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
+ Context.getSourceManager(), Context.getLangOpts());
+ Diag << tooling::fixit::createReplacement(
+ SourceRange(ElseExpandedLoc, EndLoc), Repl);
+ }
+}
+
+ElseAfterReturnCheck::ElseAfterReturnCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnUnfixable(Options.get(WarnOnUnfixableStr, true)),
+ WarnOnConditionVariables(Options.get(WarnOnConditionVariablesStr, true)) {
+}
+
+void ElseAfterReturnCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, WarnOnUnfixableStr, WarnOnUnfixable);
+ Options.store(Opts, WarnOnConditionVariablesStr, WarnOnConditionVariables);
+}
+
+void ElseAfterReturnCheck::registerPPCallbacks(const SourceManager &SM,
+ Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) {
+ PP->addPPCallbacks(
+ std::make_unique<PPConditionalCollector>(this->PPConditionals, SM));
+}
+
+void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
+ const auto InterruptsControlFlow = stmt(anyOf(
+ returnStmt().bind(InterruptingStr), continueStmt().bind(InterruptingStr),
+ breakStmt().bind(InterruptingStr), cxxThrowExpr().bind(InterruptingStr)));
+ Finder->addMatcher(
+ compoundStmt(
+ forEach(ifStmt(unless(isConstexpr()),
+ hasThen(stmt(
+ anyOf(InterruptsControlFlow,
+ compoundStmt(has(InterruptsControlFlow))))),
+ hasElse(stmt().bind("else")))
+ .bind("if")))
+ .bind("cs"),
+ this);
+}
+
+static bool hasPreprocessorBranchEndBetweenLocations(
+ const ElseAfterReturnCheck::ConditionalBranchMap &ConditionalBranchMap,
+ const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
+
+ SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
+ SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
+ if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
+ return false;
+
+ // StartLoc and EndLoc expand to the same macro.
+ if (ExpandedStartLoc == ExpandedEndLoc)
+ return false;
+
+ assert(ExpandedStartLoc < ExpandedEndLoc);
+
+ auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
+
+ if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
+ return false;
+
+ const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond();
+
+ assert(llvm::is_sorted(ConditionalBranches,
+ [](const SourceRange &LHS, const SourceRange &RHS) {
+ return LHS.getEnd() < RHS.getEnd();
+ }));
+
+ // First conditional block that ends after ExpandedStartLoc.
+ const auto *Begin =
+ llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
+ [](const SourceRange &LHS, const SourceLocation &RHS) {
+ return LHS.getEnd() < RHS;
+ });
+ const auto *End = ConditionalBranches.end();
+ for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
+ if (Begin->getBegin() < ExpandedStartLoc)
+ return true;
+ return false;
+}
+
+static StringRef getControlFlowString(const Stmt &Stmt) {
+ if (isa<ReturnStmt>(Stmt))
+ return "return";
+ if (isa<ContinueStmt>(Stmt))
+ return "continue";
+ if (isa<BreakStmt>(Stmt))
+ return "break";
+ if (isa<CXXThrowExpr>(Stmt))
+ return "throw";
+ llvm_unreachable("Unknown control flow interruptor");
+}
+
+void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+ const auto *Else = Result.Nodes.getNodeAs<Stmt>("else");
+ const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>("cs");
+ const auto *Interrupt = Result.Nodes.getNodeAs<Stmt>(InterruptingStr);
+ SourceLocation ElseLoc = If->getElseLoc();
+
+ if (hasPreprocessorBranchEndBetweenLocations(
+ PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
+ ElseLoc))
+ return;
+
+ bool IsLastInScope = OuterScope->body_back() == If;
+ StringRef ControlFlowInterruptor = getControlFlowString(*Interrupt);
+
+ if (!IsLastInScope && containsDeclInScope(Else)) {
+ if (WarnOnUnfixable) {
+ // Warn, but don't attempt an autofix.
+ diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
+ }
+ return;
+ }
+
+ if (checkConditionVarUsageInElse(If) != nullptr) {
+ if (!WarnOnConditionVariables)
+ return;
+ if (IsLastInScope) {
+ // If the if statement is the last statement of its enclosing statements
+ // scope, we can pull the decl out of the if statement.
+ DiagnosticBuilder Diag = diag(ElseLoc, WarningMessage)
+ << ControlFlowInterruptor
+ << SourceRange(ElseLoc);
+ if (checkInitDeclUsageInElse(If) != nullptr) {
+ Diag << tooling::fixit::createReplacement(
+ SourceRange(If->getIfLoc()),
+ (tooling::fixit::getText(*If->getInit(), *Result.Context) +
+ llvm::StringRef("\n"))
+ .str())
+ << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
+ }
+ const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
+ const VarDecl *VDecl = If->getConditionVariable();
+ std::string Repl =
+ (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
+ llvm::StringRef(";\n") +
+ tooling::fixit::getText(If->getIfLoc(), *Result.Context))
+ .str();
+ Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
+ Repl)
+ << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
+ VDecl->getName());
+ removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
+ } else if (WarnOnUnfixable) {
+ // Warn, but don't attempt an autofix.
+ diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
+ }
+ return;
+ }
+
+ if (checkInitDeclUsageInElse(If) != nullptr) {
+ if (!WarnOnConditionVariables)
+ return;
+ if (IsLastInScope) {
+ // If the if statement is the last statement of its enclosing statements
+ // scope, we can pull the decl out of the if statement.
+ DiagnosticBuilder Diag = diag(ElseLoc, WarningMessage)
+ << ControlFlowInterruptor
+ << SourceRange(ElseLoc);
+ Diag << tooling::fixit::createReplacement(
+ SourceRange(If->getIfLoc()),
+ (tooling::fixit::getText(*If->getInit(), *Result.Context) +
+ "\n" +
+ tooling::fixit::getText(If->getIfLoc(), *Result.Context))
+ .str())
+ << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
+ removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
+ } else if (WarnOnUnfixable) {
+ // Warn, but don't attempt an autofix.
+ diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
+ }
+ return;
+ }
+
+ DiagnosticBuilder Diag = diag(ElseLoc, WarningMessage)
+ << ControlFlowInterruptor << SourceRange(ElseLoc);
+ removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.h
new file mode 100644
index 0000000000..34860c2853
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ElseAfterReturnCheck.h
@@ -0,0 +1,44 @@
+//===--- ElseAfterReturnCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang::tidy::readability {
+
+/// Flags the usages of `else` after `return`.
+///
+/// http://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return
+class ElseAfterReturnCheck : public ClangTidyCheck {
+public:
+ ElseAfterReturnCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) 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;
+ }
+
+ using ConditionalBranchMap =
+ llvm::DenseMap<FileID, SmallVector<SourceRange, 1>>;
+
+private:
+ const bool WarnOnUnfixable;
+ const bool WarnOnConditionVariables;
+ ConditionalBranchMap PPConditionals;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
new file mode 100644
index 0000000000..b5dc09c5f1
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
@@ -0,0 +1,563 @@
+//===--- FunctionCognitiveComplexityCheck.cpp - 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "FunctionCognitiveComplexityCheck.h"
+#include "../ClangTidyDiagnosticConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <array>
+#include <cassert>
+#include <optional>
+#include <stack>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+namespace {
+
+struct CognitiveComplexity final {
+ // Any increment is based on some combination of reasons.
+ // For details you can look at the Specification at
+ // https://www.sonarsource.com/docs/CognitiveComplexity.pdf
+ // or user-facing docs at
+ // http://clang.llvm.org/extra/clang-tidy/checks/readability/function-cognitive-complexity.html
+ // Here are all the possible reasons:
+ enum Criteria : uint8_t {
+ None = 0U,
+
+ // B1, increases cognitive complexity (by 1)
+ // What causes it:
+ // * if, else if, else, ConditionalOperator (not BinaryConditionalOperator)
+ // * SwitchStmt
+ // * ForStmt, CXXForRangeStmt
+ // * WhileStmt, DoStmt
+ // * CXXCatchStmt
+ // * GotoStmt, IndirectGotoStmt (but not BreakStmt, ContinueStmt)
+ // * sequences of binary logical operators (BinOpLAnd, BinOpLOr)
+ // * each method in a recursion cycle (not implemented)
+ Increment = 1U << 0,
+
+ // B2, increases current nesting level (by 1)
+ // What causes it:
+ // * if, else if, else, ConditionalOperator (not BinaryConditionalOperator)
+ // * SwitchStmt
+ // * ForStmt, CXXForRangeStmt
+ // * WhileStmt, DoStmt
+ // * CXXCatchStmt
+ // * nested CXXConstructor, CXXDestructor, CXXMethod (incl. C++11 Lambda)
+ // * GNU Statement Expression
+ // * Apple Block declaration
+ IncrementNesting = 1U << 1,
+
+ // B3, increases cognitive complexity by the current nesting level
+ // Applied before IncrementNesting
+ // What causes it:
+ // * IfStmt, ConditionalOperator (not BinaryConditionalOperator)
+ // * SwitchStmt
+ // * ForStmt, CXXForRangeStmt
+ // * WhileStmt, DoStmt
+ // * CXXCatchStmt
+ PenalizeNesting = 1U << 2,
+
+ All = Increment | PenalizeNesting | IncrementNesting,
+ };
+
+ // The helper struct used to record one increment occurrence, with all the
+ // details necessary.
+ struct Detail {
+ const SourceLocation Loc; // What caused the increment?
+ const unsigned short Nesting; // How deeply nested is Loc located?
+ const Criteria C; // The criteria of the increment
+
+ Detail(SourceLocation SLoc, unsigned short CurrentNesting, Criteria Crit)
+ : Loc(SLoc), Nesting(CurrentNesting), C(Crit) {}
+
+ // To minimize the sizeof(Detail), we only store the minimal info there.
+ // This function is used to convert from the stored info into the usable
+ // information - what message to output, how much of an increment did this
+ // occurrence actually result in.
+ std::pair<unsigned, unsigned short> process() const {
+ assert(C != Criteria::None && "invalid criteria");
+
+ unsigned MsgId; // The id of the message to output.
+ unsigned short Increment; // How much of an increment?
+
+ if (C == Criteria::All) {
+ Increment = 1 + Nesting;
+ MsgId = 0;
+ } else if (C == (Criteria::Increment | Criteria::IncrementNesting)) {
+ Increment = 1;
+ MsgId = 1;
+ } else if (C == Criteria::Increment) {
+ Increment = 1;
+ MsgId = 2;
+ } else if (C == Criteria::IncrementNesting) {
+ Increment = 0; // Unused in this message.
+ MsgId = 3;
+ } else
+ llvm_unreachable("should not get to here.");
+
+ return std::make_pair(MsgId, Increment);
+ }
+ };
+
+ // Limit of 25 is the "upstream"'s default.
+ static constexpr unsigned DefaultLimit = 25U;
+
+ // Based on the publicly-avaliable numbers for some big open-source projects
+ // https://sonarcloud.io/projects?languages=c%2Ccpp&size=5 we can estimate:
+ // value ~20 would result in no allocs for 98% of functions, ~12 for 96%, ~10
+ // for 91%, ~8 for 88%, ~6 for 84%, ~4 for 77%, ~2 for 64%, and ~1 for 37%.
+ static_assert(sizeof(Detail) <= 8,
+ "Since we use SmallVector to minimize the amount of "
+ "allocations, we also need to consider the price we pay for "
+ "that in terms of stack usage. "
+ "Thus, it is good to minimize the size of the Detail struct.");
+ SmallVector<Detail, DefaultLimit> Details; // 25 elements is 200 bytes.
+ // Yes, 25 is a magic number. This is the seemingly-sane default for the
+ // upper limit for function cognitive complexity. Thus it would make sense
+ // to avoid allocations for any function that does not violate the limit.
+
+ // The grand total Cognitive Complexity of the function.
+ unsigned Total = 0;
+
+ // The function used to store new increment, calculate the total complexity.
+ void account(SourceLocation Loc, unsigned short Nesting, Criteria C);
+};
+
+// All the possible messages that can be output. The choice of the message
+// to use is based of the combination of the CognitiveComplexity::Criteria.
+// It would be nice to have it in CognitiveComplexity struct, but then it is
+// not static.
+static const std::array<const StringRef, 4> Msgs = {{
+ // B1 + B2 + B3
+ "+%0, including nesting penalty of %1, nesting level increased to %2",
+
+ // B1 + B2
+ "+%0, nesting level increased to %2",
+
+ // B1
+ "+%0",
+
+ // B2
+ "nesting level increased to %2",
+}};
+
+// Criteria is a bitset, thus a few helpers are needed.
+CognitiveComplexity::Criteria operator|(CognitiveComplexity::Criteria LHS,
+ CognitiveComplexity::Criteria RHS) {
+ return static_cast<CognitiveComplexity::Criteria>(
+ static_cast<std::underlying_type_t<CognitiveComplexity::Criteria>>(LHS) |
+ static_cast<std::underlying_type_t<CognitiveComplexity::Criteria>>(RHS));
+}
+CognitiveComplexity::Criteria operator&(CognitiveComplexity::Criteria LHS,
+ CognitiveComplexity::Criteria RHS) {
+ return static_cast<CognitiveComplexity::Criteria>(
+ static_cast<std::underlying_type_t<CognitiveComplexity::Criteria>>(LHS) &
+ static_cast<std::underlying_type_t<CognitiveComplexity::Criteria>>(RHS));
+}
+CognitiveComplexity::Criteria &operator|=(CognitiveComplexity::Criteria &LHS,
+ CognitiveComplexity::Criteria RHS) {
+ LHS = operator|(LHS, RHS);
+ return LHS;
+}
+CognitiveComplexity::Criteria &operator&=(CognitiveComplexity::Criteria &LHS,
+ CognitiveComplexity::Criteria RHS) {
+ LHS = operator&(LHS, RHS);
+ return LHS;
+}
+
+void CognitiveComplexity::account(SourceLocation Loc, unsigned short Nesting,
+ Criteria C) {
+ C &= Criteria::All;
+ assert(C != Criteria::None && "invalid criteria");
+
+ Details.emplace_back(Loc, Nesting, C);
+ const Detail &D = Details.back();
+
+ unsigned MsgId;
+ unsigned short Increase;
+ std::tie(MsgId, Increase) = D.process();
+
+ Total += Increase;
+}
+
+class FunctionASTVisitor final
+ : public RecursiveASTVisitor<FunctionASTVisitor> {
+ using Base = RecursiveASTVisitor<FunctionASTVisitor>;
+
+ // If set to true, macros are ignored during analysis.
+ const bool IgnoreMacros;
+
+ // The current nesting level (increased by Criteria::IncrementNesting).
+ unsigned short CurrentNestingLevel = 0;
+
+ // Used to efficiently know the last type of the binary sequence operator
+ // that was encountered. It would make sense for the function call to start
+ // the new sequence, thus it is a stack.
+ using OBO = std::optional<BinaryOperator::Opcode>;
+ std::stack<OBO, SmallVector<OBO, 4>> BinaryOperatorsStack;
+
+public:
+ explicit FunctionASTVisitor(const bool IgnoreMacros)
+ : IgnoreMacros(IgnoreMacros) {}
+
+ bool traverseStmtWithIncreasedNestingLevel(Stmt *Node) {
+ ++CurrentNestingLevel;
+ bool ShouldContinue = Base::TraverseStmt(Node);
+ --CurrentNestingLevel;
+ return ShouldContinue;
+ }
+
+ bool traverseDeclWithIncreasedNestingLevel(Decl *Node) {
+ ++CurrentNestingLevel;
+ bool ShouldContinue = Base::TraverseDecl(Node);
+ --CurrentNestingLevel;
+ return ShouldContinue;
+ }
+
+ bool TraverseIfStmt(IfStmt *Node, bool InElseIf = false) {
+ if (!Node)
+ return Base::TraverseIfStmt(Node);
+
+ {
+ CognitiveComplexity::Criteria Reasons;
+
+ Reasons = CognitiveComplexity::Criteria::None;
+
+ // "If" increases cognitive complexity.
+ Reasons |= CognitiveComplexity::Criteria::Increment;
+ // "If" increases nesting level.
+ Reasons |= CognitiveComplexity::Criteria::IncrementNesting;
+
+ if (!InElseIf) {
+ // "If" receives a nesting increment commensurate with it's nested
+ // depth, if it is not part of "else if".
+ Reasons |= CognitiveComplexity::Criteria::PenalizeNesting;
+ }
+
+ CC.account(Node->getIfLoc(), CurrentNestingLevel, Reasons);
+ }
+
+ // If this IfStmt is *NOT* "else if", then only the body (i.e. "Then" and
+ // "Else") is traversed with increased Nesting level.
+ // However if this IfStmt *IS* "else if", then Nesting level is increased
+ // for the whole IfStmt (i.e. for "Init", "Cond", "Then" and "Else").
+
+ if (!InElseIf) {
+ if (!TraverseStmt(Node->getInit()))
+ return false;
+
+ if (!TraverseStmt(Node->getCond()))
+ return false;
+ } else {
+ if (!traverseStmtWithIncreasedNestingLevel(Node->getInit()))
+ return false;
+
+ if (!traverseStmtWithIncreasedNestingLevel(Node->getCond()))
+ return false;
+ }
+
+ // "Then" always increases nesting level.
+ if (!traverseStmtWithIncreasedNestingLevel(Node->getThen()))
+ return false;
+
+ if (!Node->getElse())
+ return true;
+
+ if (auto *E = dyn_cast<IfStmt>(Node->getElse()))
+ return TraverseIfStmt(E, true);
+
+ {
+ CognitiveComplexity::Criteria Reasons;
+
+ Reasons = CognitiveComplexity::Criteria::None;
+
+ // "Else" increases cognitive complexity.
+ Reasons |= CognitiveComplexity::Criteria::Increment;
+ // "Else" increases nesting level.
+ Reasons |= CognitiveComplexity::Criteria::IncrementNesting;
+ // "Else" DOES NOT receive a nesting increment commensurate with it's
+ // nested depth.
+
+ CC.account(Node->getElseLoc(), CurrentNestingLevel, Reasons);
+ }
+
+ // "Else" always increases nesting level.
+ return traverseStmtWithIncreasedNestingLevel(Node->getElse());
+ }
+
+// The currently-being-processed stack entry, which is always the top.
+#define CurrentBinaryOperator BinaryOperatorsStack.top()
+
+ // In a sequence of binary logical operators, if the new operator is different
+ // from the previous one, then the cognitive complexity is increased.
+ bool TraverseBinaryOperator(BinaryOperator *Op) {
+ if (!Op || !Op->isLogicalOp())
+ return Base::TraverseBinaryOperator(Op);
+
+ // Make sure that there is always at least one frame in the stack.
+ if (BinaryOperatorsStack.empty())
+ BinaryOperatorsStack.emplace();
+
+ // If this is the first binary operator that we are processing, or the
+ // previous binary operator was different, there is an increment.
+ if (!CurrentBinaryOperator || Op->getOpcode() != CurrentBinaryOperator)
+ CC.account(Op->getOperatorLoc(), CurrentNestingLevel,
+ CognitiveComplexity::Criteria::Increment);
+
+ // We might encounter a function call, which starts a new sequence, thus
+ // we need to save the current previous binary operator.
+ const std::optional<BinaryOperator::Opcode> BinOpCopy(
+ CurrentBinaryOperator);
+
+ // Record the operator that we are currently processing and traverse it.
+ CurrentBinaryOperator = Op->getOpcode();
+ bool ShouldContinue = Base::TraverseBinaryOperator(Op);
+
+ // And restore the previous binary operator, which might be nonexistent.
+ CurrentBinaryOperator = BinOpCopy;
+
+ return ShouldContinue;
+ }
+
+ // It would make sense for the function call to start the new binary
+ // operator sequence, thus let's make sure that it creates a new stack frame.
+ bool TraverseCallExpr(CallExpr *Node) {
+ // If we are not currently processing any binary operator sequence, then
+ // no Node-handling is needed.
+ if (!Node || BinaryOperatorsStack.empty() || !CurrentBinaryOperator)
+ return Base::TraverseCallExpr(Node);
+
+ // Else, do add [uninitialized] frame to the stack, and traverse call.
+ BinaryOperatorsStack.emplace();
+ bool ShouldContinue = Base::TraverseCallExpr(Node);
+ // And remove the top frame.
+ BinaryOperatorsStack.pop();
+
+ return ShouldContinue;
+ }
+
+#undef CurrentBinaryOperator
+
+ bool TraverseStmt(Stmt *Node) {
+ if (!Node)
+ return Base::TraverseStmt(Node);
+
+ if (IgnoreMacros && Node->getBeginLoc().isMacroID())
+ return true;
+
+ // Three following switch()'es have huge duplication, but it is better to
+ // keep them separate, to simplify comparing them with the Specification.
+
+ CognitiveComplexity::Criteria Reasons = CognitiveComplexity::Criteria::None;
+ SourceLocation Location = Node->getBeginLoc();
+
+ // B1. Increments
+ // There is an increment for each of the following:
+ switch (Node->getStmtClass()) {
+ // if, else if, else are handled in TraverseIfStmt(),
+ // FIXME: "each method in a recursion cycle" Increment is not implemented.
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ case Stmt::GotoStmtClass:
+ case Stmt::IndirectGotoStmtClass:
+ Reasons |= CognitiveComplexity::Criteria::Increment;
+ break;
+ default:
+ // break LABEL, continue LABEL increase cognitive complexity,
+ // but they are not supported in C++ or C.
+ // Regular break/continue do not increase cognitive complexity.
+ break;
+ }
+
+ // B2. Nesting level
+ // The following structures increment the nesting level:
+ switch (Node->getStmtClass()) {
+ // if, else if, else are handled in TraverseIfStmt(),
+ // Nested methods and such are handled in TraverseDecl.
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ case Stmt::LambdaExprClass:
+ case Stmt::StmtExprClass:
+ Reasons |= CognitiveComplexity::Criteria::IncrementNesting;
+ break;
+ default:
+ break;
+ }
+
+ // B3. Nesting increments
+ // The following structures receive a nesting increment
+ // commensurate with their nested depth inside B2 structures:
+ switch (Node->getStmtClass()) {
+ // if, else if, else are handled in TraverseIfStmt().
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ Reasons |= CognitiveComplexity::Criteria::PenalizeNesting;
+ break;
+ default:
+ break;
+ }
+
+ if (Node->getStmtClass() == Stmt::ConditionalOperatorClass) {
+ // A little beautification.
+ // For conditional operator "cond ? true : false" point at the "?"
+ // symbol.
+ Location = cast<ConditionalOperator>(Node)->getQuestionLoc();
+ }
+
+ // If we have found any reasons, let's account it.
+ if (Reasons & CognitiveComplexity::Criteria::All)
+ CC.account(Location, CurrentNestingLevel, Reasons);
+
+ // Did we decide that the nesting level should be increased?
+ if (!(Reasons & CognitiveComplexity::Criteria::IncrementNesting))
+ return Base::TraverseStmt(Node);
+
+ return traverseStmtWithIncreasedNestingLevel(Node);
+ }
+
+ // The parameter MainAnalyzedFunction is needed to differentiate between the
+ // cases where TraverseDecl() is the entry point from
+ // FunctionCognitiveComplexityCheck::check() and the cases where it was called
+ // from the FunctionASTVisitor itself. Explanation: if we get a function
+ // definition (e.g. constructor, destructor, method), the Cognitive Complexity
+ // specification states that the Nesting level shall be increased. But if this
+ // function is the entry point, then the Nesting level should not be
+ // increased. Thus that parameter is there and is used to fall-through
+ // directly to traversing if this is the main function that is being analyzed.
+ bool TraverseDecl(Decl *Node, bool MainAnalyzedFunction = false) {
+ if (!Node || MainAnalyzedFunction)
+ return Base::TraverseDecl(Node);
+
+ // B2. Nesting level
+ // The following structures increment the nesting level:
+ switch (Node->getKind()) {
+ case Decl::Function:
+ case Decl::CXXMethod:
+ case Decl::CXXConstructor:
+ case Decl::CXXDestructor:
+ case Decl::Block:
+ break;
+ default:
+ // If this is something else, we use early return!
+ return Base::TraverseDecl(Node);
+ break;
+ }
+
+ CC.account(Node->getBeginLoc(), CurrentNestingLevel,
+ CognitiveComplexity::Criteria::IncrementNesting);
+
+ return traverseDeclWithIncreasedNestingLevel(Node);
+ }
+
+ CognitiveComplexity CC;
+};
+
+} // namespace
+
+FunctionCognitiveComplexityCheck::FunctionCognitiveComplexityCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ Threshold(Options.get("Threshold", CognitiveComplexity::DefaultLimit)),
+ DescribeBasicIncrements(Options.get("DescribeBasicIncrements", true)),
+ IgnoreMacros(Options.get("IgnoreMacros", false)) {}
+
+void FunctionCognitiveComplexityCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "Threshold", Threshold);
+ Options.store(Opts, "DescribeBasicIncrements", DescribeBasicIncrements);
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void FunctionCognitiveComplexityCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ functionDecl(isDefinition(),
+ unless(anyOf(isDefaulted(), isDeleted(), isWeak())))
+ .bind("func"),
+ this);
+ Finder->addMatcher(lambdaExpr().bind("lambda"), this);
+}
+
+void FunctionCognitiveComplexityCheck::check(
+ const MatchFinder::MatchResult &Result) {
+
+ FunctionASTVisitor Visitor(IgnoreMacros);
+ SourceLocation Loc;
+
+ const auto *TheDecl = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const auto *TheLambdaExpr = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
+ if (TheDecl) {
+ assert(TheDecl->hasBody() &&
+ "The matchers should only match the functions that "
+ "have user-provided body.");
+ Loc = TheDecl->getLocation();
+ Visitor.TraverseDecl(const_cast<FunctionDecl *>(TheDecl), true);
+ } else {
+ Loc = TheLambdaExpr->getBeginLoc();
+ Visitor.TraverseLambdaExpr(const_cast<LambdaExpr *>(TheLambdaExpr));
+ }
+
+ if (Visitor.CC.Total <= Threshold)
+ return;
+
+ if (TheDecl)
+ diag(Loc, "function %0 has cognitive complexity of %1 (threshold %2)")
+ << TheDecl << Visitor.CC.Total << Threshold;
+ else
+ diag(Loc, "lambda has cognitive complexity of %0 (threshold %1)")
+ << Visitor.CC.Total << Threshold;
+
+ if (!DescribeBasicIncrements)
+ return;
+
+ // Output all the basic increments of complexity.
+ for (const auto &Detail : Visitor.CC.Details) {
+ unsigned MsgId; // The id of the message to output.
+ unsigned short Increase; // How much of an increment?
+ std::tie(MsgId, Increase) = Detail.process();
+ assert(MsgId < Msgs.size() && "MsgId should always be valid");
+ // Increase, on the other hand, can be 0.
+
+ diag(Detail.Loc, Msgs[MsgId], DiagnosticIDs::Note)
+ << (unsigned)Increase << (unsigned)Detail.Nesting << 1 + Detail.Nesting;
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h
new file mode 100644
index 0000000000..bdb8550eea
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h
@@ -0,0 +1,51 @@
+//===--- FunctionCognitiveComplexityCheck.h - clang-tidy --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks function Cognitive Complexity metric.
+///
+/// There are the following configuration option:
+///
+/// * `Threshold` - flag functions with Cognitive Complexity exceeding
+/// this number. The default is `25`.
+/// * `DescribeBasicIncrements`- if set to `true`, then for each function
+/// exceeding the complexity threshold the check will issue additional
+/// diagnostics on every piece of code (loop, `if` statement, etc.) which
+/// contributes to that complexity.
+// Default is `true`
+/// * `IgnoreMacros` - if set to `true`, the check will ignore code inside
+/// macros. Default is `false`.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/function-cognitive-complexity.html
+class FunctionCognitiveComplexityCheck : public ClangTidyCheck {
+public:
+ FunctionCognitiveComplexityCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const unsigned Threshold;
+ const bool DescribeBasicIncrements;
+ const bool IgnoreMacros;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp
new file mode 100644
index 0000000000..3c803959ca
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp
@@ -0,0 +1,222 @@
+//===-- FunctionSizeCheck.cpp - clang-tidy --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FunctionSizeCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/BitVector.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+namespace {
+
+class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
+ using Base = RecursiveASTVisitor<FunctionASTVisitor>;
+
+public:
+ bool VisitVarDecl(VarDecl *VD) {
+ // Do not count function params.
+ // Do not count decomposition declarations (C++17's structured bindings).
+ if (StructNesting == 0 &&
+ !(isa<ParmVarDecl>(VD) || isa<DecompositionDecl>(VD)))
+ ++Info.Variables;
+ return true;
+ }
+ bool VisitBindingDecl(BindingDecl *BD) {
+ // Do count each of the bindings (in the decomposition declaration).
+ if (StructNesting == 0)
+ ++Info.Variables;
+ return true;
+ }
+
+ bool TraverseStmt(Stmt *Node) {
+ if (!Node)
+ return Base::TraverseStmt(Node);
+
+ if (TrackedParent.back() && !isa<CompoundStmt>(Node))
+ ++Info.Statements;
+
+ switch (Node->getStmtClass()) {
+ case Stmt::IfStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::SwitchStmtClass:
+ ++Info.Branches;
+ [[fallthrough]];
+ case Stmt::CompoundStmtClass:
+ TrackedParent.push_back(true);
+ break;
+ default:
+ TrackedParent.push_back(false);
+ break;
+ }
+
+ Base::TraverseStmt(Node);
+
+ TrackedParent.pop_back();
+
+ return true;
+ }
+
+ bool TraverseCompoundStmt(CompoundStmt *Node) {
+ // If this new compound statement is located in a compound statement, which
+ // is already nested NestingThreshold levels deep, record the start location
+ // of this new compound statement.
+ if (CurrentNestingLevel == Info.NestingThreshold)
+ Info.NestingThresholders.push_back(Node->getBeginLoc());
+
+ ++CurrentNestingLevel;
+ Base::TraverseCompoundStmt(Node);
+ --CurrentNestingLevel;
+
+ return true;
+ }
+
+ bool TraverseDecl(Decl *Node) {
+ TrackedParent.push_back(false);
+ Base::TraverseDecl(Node);
+ TrackedParent.pop_back();
+ return true;
+ }
+
+ bool TraverseLambdaExpr(LambdaExpr *Node) {
+ ++StructNesting;
+ Base::TraverseLambdaExpr(Node);
+ --StructNesting;
+ return true;
+ }
+
+ bool TraverseCXXRecordDecl(CXXRecordDecl *Node) {
+ ++StructNesting;
+ Base::TraverseCXXRecordDecl(Node);
+ --StructNesting;
+ return true;
+ }
+
+ bool TraverseStmtExpr(StmtExpr *SE) {
+ ++StructNesting;
+ Base::TraverseStmtExpr(SE);
+ --StructNesting;
+ return true;
+ }
+
+ struct FunctionInfo {
+ unsigned Lines = 0;
+ unsigned Statements = 0;
+ unsigned Branches = 0;
+ unsigned NestingThreshold = 0;
+ unsigned Variables = 0;
+ std::vector<SourceLocation> NestingThresholders;
+ };
+ FunctionInfo Info;
+ llvm::BitVector TrackedParent;
+ unsigned StructNesting = 0;
+ unsigned CurrentNestingLevel = 0;
+};
+
+} // namespace
+
+FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ LineThreshold(Options.get("LineThreshold", -1U)),
+ StatementThreshold(Options.get("StatementThreshold", 800U)),
+ BranchThreshold(Options.get("BranchThreshold", -1U)),
+ ParameterThreshold(Options.get("ParameterThreshold", -1U)),
+ NestingThreshold(Options.get("NestingThreshold", -1U)),
+ VariableThreshold(Options.get("VariableThreshold", -1U)) {}
+
+void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "LineThreshold", LineThreshold);
+ Options.store(Opts, "StatementThreshold", StatementThreshold);
+ Options.store(Opts, "BranchThreshold", BranchThreshold);
+ Options.store(Opts, "ParameterThreshold", ParameterThreshold);
+ Options.store(Opts, "NestingThreshold", NestingThreshold);
+ Options.store(Opts, "VariableThreshold", VariableThreshold);
+}
+
+void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
+ // Lambdas ignored - historically considered part of enclosing function.
+ // FIXME: include them instead? Top-level lambdas are currently never counted.
+ Finder->addMatcher(functionDecl(unless(isInstantiated()),
+ unless(cxxMethodDecl(ofClass(isLambda()))))
+ .bind("func"),
+ this);
+}
+
+void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ FunctionASTVisitor Visitor;
+ Visitor.Info.NestingThreshold = NestingThreshold;
+ Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
+ auto &FI = Visitor.Info;
+
+ if (FI.Statements == 0)
+ return;
+
+ // Count the lines including whitespace and comments. Really simple.
+ if (const Stmt *Body = Func->getBody()) {
+ SourceManager *SM = Result.SourceManager;
+ if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getEndLoc())) {
+ FI.Lines = SM->getSpellingLineNumber(Body->getEndLoc()) -
+ SM->getSpellingLineNumber(Body->getBeginLoc());
+ }
+ }
+
+ unsigned ActualNumberParameters = Func->getNumParams();
+
+ if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
+ FI.Branches > BranchThreshold ||
+ ActualNumberParameters > ParameterThreshold ||
+ !FI.NestingThresholders.empty() || FI.Variables > VariableThreshold) {
+ diag(Func->getLocation(),
+ "function %0 exceeds recommended size/complexity thresholds")
+ << Func;
+ }
+
+ if (FI.Lines > LineThreshold) {
+ diag(Func->getLocation(),
+ "%0 lines including whitespace and comments (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Lines << LineThreshold;
+ }
+
+ if (FI.Statements > StatementThreshold) {
+ diag(Func->getLocation(), "%0 statements (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Statements << StatementThreshold;
+ }
+
+ if (FI.Branches > BranchThreshold) {
+ diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
+ << FI.Branches << BranchThreshold;
+ }
+
+ if (ActualNumberParameters > ParameterThreshold) {
+ diag(Func->getLocation(), "%0 parameters (threshold %1)",
+ DiagnosticIDs::Note)
+ << ActualNumberParameters << ParameterThreshold;
+ }
+
+ for (const auto &CSPos : FI.NestingThresholders) {
+ diag(CSPos, "nesting level %0 starts here (threshold %1)",
+ DiagnosticIDs::Note)
+ << NestingThreshold + 1 << NestingThreshold;
+ }
+
+ if (FI.Variables > VariableThreshold) {
+ diag(Func->getLocation(), "%0 variables (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Variables << VariableThreshold;
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.h
new file mode 100644
index 0000000000..5f3fb9a35d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.h
@@ -0,0 +1,54 @@
+//===--- FunctionSizeCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks for large functions based on various metrics.
+///
+/// These options are supported:
+///
+/// * `LineThreshold` - flag functions exceeding this number of lines. The
+/// default is `-1` (ignore the number of lines).
+/// * `StatementThreshold` - flag functions exceeding this number of
+/// statements. This may differ significantly from the number of lines for
+/// macro-heavy code. The default is `800`.
+/// * `BranchThreshold` - flag functions exceeding this number of control
+/// statements. The default is `-1` (ignore the number of branches).
+/// * `ParameterThreshold` - flag functions having a high number of
+/// parameters. The default is `-1` (ignore the number of parameters).
+/// * `NestingThreshold` - flag compound statements which create next nesting
+/// level after `NestingThreshold`. This may differ significantly from the
+/// expected value for macro-heavy code. The default is `-1` (ignore the
+/// nesting level).
+/// * `VariableThreshold` - flag functions having a high number of variable
+/// declarations. The default is `-1` (ignore the number of variables).
+class FunctionSizeCheck : public ClangTidyCheck {
+public:
+ FunctionSizeCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const unsigned LineThreshold;
+ const unsigned StatementThreshold;
+ const unsigned BranchThreshold;
+ const unsigned ParameterThreshold;
+ const unsigned NestingThreshold;
+ const unsigned VariableThreshold;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.cpp
new file mode 100644
index 0000000000..68da28b7f1
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.cpp
@@ -0,0 +1,152 @@
+//===--- IdentifierLengthCheck.cpp - clang-tidy
+//-----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IdentifierLengthCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+const unsigned DefaultMinimumVariableNameLength = 3;
+const unsigned DefaultMinimumLoopCounterNameLength = 2;
+const unsigned DefaultMinimumExceptionNameLength = 2;
+const unsigned DefaultMinimumParameterNameLength = 3;
+const char DefaultIgnoredLoopCounterNames[] = "^[ijk_]$";
+const char DefaultIgnoredVariableNames[] = "";
+const char DefaultIgnoredExceptionVariableNames[] = "^[e]$";
+const char DefaultIgnoredParameterNames[] = "^[n]$";
+
+const char ErrorMessage[] =
+ "%select{variable|exception variable|loop variable|"
+ "parameter}0 name %1 is too short, expected at least %2 characters";
+
+IdentifierLengthCheck::IdentifierLengthCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ MinimumVariableNameLength(Options.get("MinimumVariableNameLength",
+ DefaultMinimumVariableNameLength)),
+ MinimumLoopCounterNameLength(Options.get(
+ "MinimumLoopCounterNameLength", DefaultMinimumLoopCounterNameLength)),
+ MinimumExceptionNameLength(Options.get(
+ "MinimumExceptionNameLength", DefaultMinimumExceptionNameLength)),
+ MinimumParameterNameLength(Options.get(
+ "MinimumParameterNameLength", DefaultMinimumParameterNameLength)),
+ IgnoredVariableNamesInput(
+ Options.get("IgnoredVariableNames", DefaultIgnoredVariableNames)),
+ IgnoredVariableNames(IgnoredVariableNamesInput),
+ IgnoredLoopCounterNamesInput(Options.get("IgnoredLoopCounterNames",
+ DefaultIgnoredLoopCounterNames)),
+ IgnoredLoopCounterNames(IgnoredLoopCounterNamesInput),
+ IgnoredExceptionVariableNamesInput(
+ Options.get("IgnoredExceptionVariableNames",
+ DefaultIgnoredExceptionVariableNames)),
+ IgnoredExceptionVariableNames(IgnoredExceptionVariableNamesInput),
+ IgnoredParameterNamesInput(
+ Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames)),
+ IgnoredParameterNames(IgnoredParameterNamesInput) {}
+
+void IdentifierLengthCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "MinimumVariableNameLength", MinimumVariableNameLength);
+ Options.store(Opts, "MinimumLoopCounterNameLength",
+ MinimumLoopCounterNameLength);
+ Options.store(Opts, "MinimumExceptionNameLength", MinimumExceptionNameLength);
+ Options.store(Opts, "MinimumParameterNameLength", MinimumParameterNameLength);
+ Options.store(Opts, "IgnoredLoopCounterNames", IgnoredLoopCounterNamesInput);
+ Options.store(Opts, "IgnoredVariableNames", IgnoredVariableNamesInput);
+ Options.store(Opts, "IgnoredExceptionVariableNames",
+ IgnoredExceptionVariableNamesInput);
+ Options.store(Opts, "IgnoredParameterNames", IgnoredParameterNamesInput);
+}
+
+void IdentifierLengthCheck::registerMatchers(MatchFinder *Finder) {
+ if (MinimumLoopCounterNameLength > 1)
+ Finder->addMatcher(
+ forStmt(hasLoopInit(declStmt(forEach(varDecl().bind("loopVar"))))),
+ this);
+
+ if (MinimumExceptionNameLength > 1)
+ Finder->addMatcher(varDecl(hasParent(cxxCatchStmt())).bind("exceptionVar"),
+ this);
+
+ if (MinimumParameterNameLength > 1)
+ Finder->addMatcher(parmVarDecl().bind("paramVar"), this);
+
+ if (MinimumVariableNameLength > 1)
+ Finder->addMatcher(
+ varDecl(unless(anyOf(hasParent(declStmt(hasParent(forStmt()))),
+ hasParent(cxxCatchStmt()), parmVarDecl())))
+ .bind("standaloneVar"),
+ this);
+}
+
+void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *StandaloneVar = Result.Nodes.getNodeAs<VarDecl>("standaloneVar");
+ if (StandaloneVar) {
+ if (!StandaloneVar->getIdentifier())
+ return;
+
+ StringRef VarName = StandaloneVar->getName();
+
+ if (VarName.size() >= MinimumVariableNameLength ||
+ IgnoredVariableNames.match(VarName))
+ return;
+
+ diag(StandaloneVar->getLocation(), ErrorMessage)
+ << 0 << StandaloneVar << MinimumVariableNameLength;
+ }
+
+ auto *ExceptionVarName = Result.Nodes.getNodeAs<VarDecl>("exceptionVar");
+ if (ExceptionVarName) {
+ if (!ExceptionVarName->getIdentifier())
+ return;
+
+ StringRef VarName = ExceptionVarName->getName();
+ if (VarName.size() >= MinimumExceptionNameLength ||
+ IgnoredExceptionVariableNames.match(VarName))
+ return;
+
+ diag(ExceptionVarName->getLocation(), ErrorMessage)
+ << 1 << ExceptionVarName << MinimumExceptionNameLength;
+ }
+
+ const auto *LoopVar = Result.Nodes.getNodeAs<VarDecl>("loopVar");
+ if (LoopVar) {
+ if (!LoopVar->getIdentifier())
+ return;
+
+ StringRef VarName = LoopVar->getName();
+
+ if (VarName.size() >= MinimumLoopCounterNameLength ||
+ IgnoredLoopCounterNames.match(VarName))
+ return;
+
+ diag(LoopVar->getLocation(), ErrorMessage)
+ << 2 << LoopVar << MinimumLoopCounterNameLength;
+ }
+
+ const auto *ParamVar = Result.Nodes.getNodeAs<VarDecl>("paramVar");
+ if (ParamVar) {
+ if (!ParamVar->getIdentifier())
+ return;
+
+ StringRef VarName = ParamVar->getName();
+
+ if (VarName.size() >= MinimumParameterNameLength ||
+ IgnoredParameterNames.match(VarName))
+ return;
+
+ diag(ParamVar->getLocation(), ErrorMessage)
+ << 3 << ParamVar << MinimumParameterNameLength;
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.h
new file mode 100644
index 0000000000..2a4b810264
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierLengthCheck.h
@@ -0,0 +1,50 @@
+//===--- IdentifierLengthCheck.h - clang-tidy ---------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERLENGTHCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERLENGTHCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang::tidy::readability {
+
+/// Warns about identifiers names whose length is too short.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-length.html
+class IdentifierLengthCheck : public ClangTidyCheck {
+public:
+ IdentifierLengthCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const unsigned MinimumVariableNameLength;
+ const unsigned MinimumLoopCounterNameLength;
+ const unsigned MinimumExceptionNameLength;
+ const unsigned MinimumParameterNameLength;
+
+ std::string IgnoredVariableNamesInput;
+ llvm::Regex IgnoredVariableNames;
+
+ std::string IgnoredLoopCounterNamesInput;
+ llvm::Regex IgnoredLoopCounterNames;
+
+ std::string IgnoredExceptionVariableNamesInput;
+ llvm::Regex IgnoredExceptionVariableNames;
+
+ std::string IgnoredParameterNamesInput;
+ llvm::Regex IgnoredParameterNames;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERLENGTHCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.cpp
new file mode 100644
index 0000000000..b2b2875731
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.cpp
@@ -0,0 +1,1453 @@
+//===--- IdentifierNamingCheck.cpp - clang-tidy ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IdentifierNamingCheck.h"
+
+#include "../GlobList.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/YAMLParser.h"
+#include <optional>
+
+#define DEBUG_TYPE "clang-tidy"
+
+// FixItHint
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy {
+
+llvm::ArrayRef<
+ std::pair<readability::IdentifierNamingCheck::CaseType, StringRef>>
+OptionEnumMapping<
+ readability::IdentifierNamingCheck::CaseType>::getEnumMapping() {
+ static constexpr std::pair<readability::IdentifierNamingCheck::CaseType,
+ StringRef>
+ Mapping[] = {
+ {readability::IdentifierNamingCheck::CT_AnyCase, "aNy_CasE"},
+ {readability::IdentifierNamingCheck::CT_LowerCase, "lower_case"},
+ {readability::IdentifierNamingCheck::CT_UpperCase, "UPPER_CASE"},
+ {readability::IdentifierNamingCheck::CT_CamelBack, "camelBack"},
+ {readability::IdentifierNamingCheck::CT_CamelCase, "CamelCase"},
+ {readability::IdentifierNamingCheck::CT_CamelSnakeCase,
+ "Camel_Snake_Case"},
+ {readability::IdentifierNamingCheck::CT_CamelSnakeBack,
+ "camel_Snake_Back"}};
+ return llvm::ArrayRef(Mapping);
+}
+
+template <>
+struct OptionEnumMapping<
+ readability::IdentifierNamingCheck::HungarianPrefixType> {
+ using HungarianPrefixType =
+ readability::IdentifierNamingCheck::HungarianPrefixType;
+ static llvm::ArrayRef<std::pair<HungarianPrefixType, StringRef>>
+ getEnumMapping() {
+ static constexpr std::pair<HungarianPrefixType, StringRef> Mapping[] = {
+ {HungarianPrefixType::HPT_Off, "Off"},
+ {HungarianPrefixType::HPT_On, "On"},
+ {HungarianPrefixType::HPT_LowerCase, "LowerCase"},
+ {HungarianPrefixType::HPT_CamelCase, "CamelCase"}};
+ return llvm::ArrayRef(Mapping);
+ }
+};
+
+namespace readability {
+
+// clang-format off
+#define NAMING_KEYS(m) \
+ m(Namespace) \
+ m(InlineNamespace) \
+ m(EnumConstant) \
+ m(ScopedEnumConstant) \
+ m(ConstexprVariable) \
+ m(ConstantMember) \
+ m(PrivateMember) \
+ m(ProtectedMember) \
+ m(PublicMember) \
+ m(Member) \
+ m(ClassConstant) \
+ m(ClassMember) \
+ m(GlobalConstant) \
+ m(GlobalConstantPointer) \
+ m(GlobalPointer) \
+ m(GlobalVariable) \
+ m(LocalConstant) \
+ m(LocalConstantPointer) \
+ m(LocalPointer) \
+ m(LocalVariable) \
+ m(StaticConstant) \
+ m(StaticVariable) \
+ m(Constant) \
+ m(Variable) \
+ m(ConstantParameter) \
+ m(ParameterPack) \
+ m(Parameter) \
+ m(PointerParameter) \
+ m(ConstantPointerParameter) \
+ m(AbstractClass) \
+ m(Struct) \
+ m(Class) \
+ m(Union) \
+ m(Enum) \
+ m(GlobalFunction) \
+ m(ConstexprFunction) \
+ m(Function) \
+ m(ConstexprMethod) \
+ m(VirtualMethod) \
+ m(ClassMethod) \
+ m(PrivateMethod) \
+ m(ProtectedMethod) \
+ m(PublicMethod) \
+ m(Method) \
+ m(Typedef) \
+ m(TypeTemplateParameter) \
+ m(ValueTemplateParameter) \
+ m(TemplateTemplateParameter) \
+ m(TemplateParameter) \
+ m(TypeAlias) \
+ m(MacroDefinition) \
+ m(ObjcIvar) \
+
+enum StyleKind : int {
+#define ENUMERATE(v) SK_ ## v,
+ NAMING_KEYS(ENUMERATE)
+#undef ENUMERATE
+ SK_Count,
+ SK_Invalid
+};
+
+static StringRef const StyleNames[] = {
+#define STRINGIZE(v) #v,
+ NAMING_KEYS(STRINGIZE)
+#undef STRINGIZE
+};
+
+#define HUNGARIAN_NOTATION_PRIMITIVE_TYPES(m) \
+ m(int8_t) \
+ m(int16_t) \
+ m(int32_t) \
+ m(int64_t) \
+ m(uint8_t) \
+ m(uint16_t) \
+ m(uint32_t) \
+ m(uint64_t) \
+ m(char8_t) \
+ m(char16_t) \
+ m(char32_t) \
+ m(float) \
+ m(double) \
+ m(char) \
+ m(bool) \
+ m(_Bool) \
+ m(int) \
+ m(size_t) \
+ m(wchar_t) \
+ m(short-int) \
+ m(short) \
+ m(signed-int) \
+ m(signed-short) \
+ m(signed-short-int) \
+ m(signed-long-long-int) \
+ m(signed-long-long) \
+ m(signed-long-int) \
+ m(signed-long) \
+ m(signed) \
+ m(unsigned-long-long-int) \
+ m(unsigned-long-long) \
+ m(unsigned-long-int) \
+ m(unsigned-long) \
+ m(unsigned-short-int) \
+ m(unsigned-short) \
+ m(unsigned-int) \
+ m(unsigned) \
+ m(long-long-int) \
+ m(long-double) \
+ m(long-long) \
+ m(long-int) \
+ m(long) \
+ m(ptrdiff_t) \
+
+static StringRef const HungarainNotationPrimitiveTypes[] = {
+#define STRINGIZE(v) #v,
+ HUNGARIAN_NOTATION_PRIMITIVE_TYPES(STRINGIZE)
+#undef STRINGIZE
+};
+
+#define HUNGARIAN_NOTATION_USER_DEFINED_TYPES(m) \
+ m(BOOL) \
+ m(BOOLEAN) \
+ m(BYTE) \
+ m(CHAR) \
+ m(UCHAR) \
+ m(SHORT) \
+ m(USHORT) \
+ m(WORD) \
+ m(DWORD) \
+ m(DWORD32) \
+ m(DWORD64) \
+ m(LONG) \
+ m(ULONG) \
+ m(ULONG32) \
+ m(ULONG64) \
+ m(ULONGLONG) \
+ m(HANDLE) \
+ m(INT) \
+ m(INT8) \
+ m(INT16) \
+ m(INT32) \
+ m(INT64) \
+ m(UINT) \
+ m(UINT8) \
+ m(UINT16) \
+ m(UINT32) \
+ m(UINT64) \
+ m(PVOID) \
+
+static StringRef const HungarainNotationUserDefinedTypes[] = {
+#define STRINGIZE(v) #v,
+ HUNGARIAN_NOTATION_USER_DEFINED_TYPES(STRINGIZE)
+#undef STRINGIZE
+};
+
+
+#undef NAMING_KEYS
+// clang-format on
+
+IdentifierNamingCheck::NamingStyle::NamingStyle(
+ std::optional<IdentifierNamingCheck::CaseType> Case,
+ const std::string &Prefix, const std::string &Suffix,
+ const std::string &IgnoredRegexpStr, HungarianPrefixType HPType)
+ : Case(Case), Prefix(Prefix), Suffix(Suffix),
+ IgnoredRegexpStr(IgnoredRegexpStr), HPType(HPType) {
+ if (!IgnoredRegexpStr.empty()) {
+ IgnoredRegexp =
+ llvm::Regex(llvm::SmallString<128>({"^", IgnoredRegexpStr, "$"}));
+ if (!IgnoredRegexp.isValid())
+ llvm::errs() << "Invalid IgnoredRegexp regular expression: "
+ << IgnoredRegexpStr;
+ }
+}
+
+IdentifierNamingCheck::FileStyle IdentifierNamingCheck::getFileStyleFromOptions(
+ const ClangTidyCheck::OptionsView &Options) const {
+ IdentifierNamingCheck::HungarianNotationOption HNOption;
+
+ HungarianNotation.loadDefaultConfig(HNOption);
+ HungarianNotation.loadFileConfig(Options, HNOption);
+
+ SmallVector<std::optional<IdentifierNamingCheck::NamingStyle>, 0> Styles;
+ Styles.resize(SK_Count);
+ SmallString<64> StyleString;
+ for (unsigned I = 0; I < SK_Count; ++I) {
+ size_t StyleSize = StyleNames[I].size();
+ StyleString.assign({StyleNames[I], "HungarianPrefix"});
+
+ auto HPTOpt =
+ Options.get<IdentifierNamingCheck::HungarianPrefixType>(StyleString);
+ if (HPTOpt && !HungarianNotation.checkOptionValid(I))
+ configurationDiag("invalid identifier naming option '%0'") << StyleString;
+
+ memcpy(&StyleString[StyleSize], "IgnoredRegexp", 13);
+ StyleString.truncate(StyleSize + 13);
+ StringRef IgnoredRegexpStr = Options.get(StyleString, "");
+ memcpy(&StyleString[StyleSize], "Prefix", 6);
+ StyleString.truncate(StyleSize + 6);
+ std::string Prefix(Options.get(StyleString, ""));
+ // Fast replacement of [Pre]fix -> [Suf]fix.
+ memcpy(&StyleString[StyleSize], "Suf", 3);
+ std::string Postfix(Options.get(StyleString, ""));
+ memcpy(&StyleString[StyleSize], "Case", 4);
+ StyleString.pop_back_n(2);
+ auto CaseOptional =
+ Options.get<IdentifierNamingCheck::CaseType>(StyleString);
+
+ if (CaseOptional || !Prefix.empty() || !Postfix.empty() ||
+ !IgnoredRegexpStr.empty() || HPTOpt)
+ Styles[I].emplace(std::move(CaseOptional), std::move(Prefix),
+ std::move(Postfix), IgnoredRegexpStr.str(),
+ HPTOpt.value_or(IdentifierNamingCheck::HPT_Off));
+ }
+ bool IgnoreMainLike = Options.get("IgnoreMainLikeFunctions", false);
+ return {std::move(Styles), std::move(HNOption), IgnoreMainLike};
+}
+
+std::string IdentifierNamingCheck::HungarianNotation::getDeclTypeName(
+ const NamedDecl *ND) const {
+ const auto *VD = dyn_cast<ValueDecl>(ND);
+ if (!VD)
+ return {};
+
+ if (isa<FunctionDecl, EnumConstantDecl>(ND))
+ return {};
+
+ // Get type text of variable declarations.
+ auto &SM = VD->getASTContext().getSourceManager();
+ const char *Begin = SM.getCharacterData(VD->getBeginLoc());
+ const char *End = SM.getCharacterData(VD->getEndLoc());
+ intptr_t StrLen = End - Begin;
+
+ // FIXME: Sometimes the value that returns from ValDecl->getEndLoc()
+ // is wrong(out of location of Decl). This causes `StrLen` will be assigned
+ // an unexpected large value. Current workaround to find the terminated
+ // character instead of the `getEndLoc()` function.
+ const char *EOL = strchr(Begin, '\n');
+ if (!EOL)
+ EOL = Begin + strlen(Begin);
+
+ const char *PosList[] = {strchr(Begin, '='), strchr(Begin, ';'),
+ strchr(Begin, ','), strchr(Begin, ')'), EOL};
+ for (const auto &Pos : PosList) {
+ if (Pos > Begin)
+ EOL = std::min(EOL, Pos);
+ }
+
+ StrLen = EOL - Begin;
+ std::string TypeName;
+ if (StrLen > 0) {
+ std::string Type(Begin, StrLen);
+
+ static constexpr StringRef Keywords[] = {
+ // Constexpr specifiers
+ "constexpr", "constinit", "consteval",
+ // Qualifier
+ "const", "volatile", "restrict", "mutable",
+ // Storage class specifiers
+ "register", "static", "extern", "thread_local",
+ // Other keywords
+ "virtual"};
+
+ // Remove keywords
+ for (StringRef Kw : Keywords) {
+ for (size_t Pos = 0;
+ (Pos = Type.find(Kw.data(), Pos)) != std::string::npos;) {
+ Type.replace(Pos, Kw.size(), "");
+ }
+ }
+ TypeName = Type.erase(0, Type.find_first_not_of(" "));
+
+ // Replace spaces with single space.
+ for (size_t Pos = 0; (Pos = Type.find(" ", Pos)) != std::string::npos;
+ Pos += strlen(" ")) {
+ Type.replace(Pos, strlen(" "), " ");
+ }
+
+ // Replace " &" with "&".
+ for (size_t Pos = 0; (Pos = Type.find(" &", Pos)) != std::string::npos;
+ Pos += strlen("&")) {
+ Type.replace(Pos, strlen(" &"), "&");
+ }
+
+ // Replace " *" with "* ".
+ for (size_t Pos = 0; (Pos = Type.find(" *", Pos)) != std::string::npos;
+ Pos += strlen("*")) {
+ Type.replace(Pos, strlen(" *"), "* ");
+ }
+
+ // Remove redundant tailing.
+ static constexpr StringRef TailsOfMultiWordType[] = {
+ " int", " char", " double", " long", " short"};
+ bool RedundantRemoved = false;
+ for (auto Kw : TailsOfMultiWordType) {
+ size_t Pos = Type.rfind(Kw.data());
+ if (Pos != std::string::npos) {
+ Type = Type.substr(0, Pos + Kw.size());
+ RedundantRemoved = true;
+ break;
+ }
+ }
+ TypeName = Type.erase(0, Type.find_first_not_of(" "));
+ if (!RedundantRemoved) {
+ std::size_t FoundSpace = Type.find(" ");
+ if (FoundSpace != std::string::npos)
+ Type = Type.substr(0, FoundSpace);
+ }
+
+ TypeName = Type.erase(0, Type.find_first_not_of(" "));
+
+ QualType QT = VD->getType();
+ if (!QT.isNull() && QT->isArrayType())
+ TypeName.append("[]");
+ }
+
+ return TypeName;
+}
+
+IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : RenamerClangTidyCheck(Name, Context), Context(Context), CheckName(Name),
+ GetConfigPerFile(Options.get("GetConfigPerFile", true)),
+ IgnoreFailedSplit(Options.get("IgnoreFailedSplit", false)) {
+
+ auto IterAndInserted = NamingStylesCache.try_emplace(
+ llvm::sys::path::parent_path(Context->getCurrentFile()),
+ getFileStyleFromOptions(Options));
+ assert(IterAndInserted.second && "Couldn't insert Style");
+ // Holding a reference to the data in the vector is safe as it should never
+ // move.
+ MainFileStyle = &IterAndInserted.first->getValue();
+}
+
+IdentifierNamingCheck::~IdentifierNamingCheck() = default;
+
+bool IdentifierNamingCheck::HungarianNotation::checkOptionValid(
+ int StyleKindIndex) const {
+ if ((StyleKindIndex >= SK_EnumConstant) &&
+ (StyleKindIndex <= SK_ConstantParameter))
+ return true;
+
+ if ((StyleKindIndex >= SK_Parameter) && (StyleKindIndex <= SK_Enum))
+ return true;
+
+ return false;
+}
+
+bool IdentifierNamingCheck::HungarianNotation::isOptionEnabled(
+ StringRef OptionKey, const llvm::StringMap<std::string> &StrMap) const {
+ if (OptionKey.empty())
+ return false;
+
+ auto Iter = StrMap.find(OptionKey);
+ if (Iter == StrMap.end())
+ return false;
+
+ return *llvm::yaml::parseBool(Iter->getValue());
+}
+
+void IdentifierNamingCheck::HungarianNotation::loadFileConfig(
+ const ClangTidyCheck::OptionsView &Options,
+ IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
+
+ static constexpr StringRef HNOpts[] = {"TreatStructAsClass"};
+ static constexpr StringRef HNDerivedTypes[] = {"Array", "Pointer",
+ "FunctionPointer"};
+
+ StringRef Section = "HungarianNotation.";
+
+ SmallString<128> Buffer = {Section, "General."};
+ size_t DefSize = Buffer.size();
+ for (const auto &Opt : HNOpts) {
+ Buffer.truncate(DefSize);
+ Buffer.append(Opt);
+ StringRef Val = Options.get(Buffer, "");
+ if (!Val.empty())
+ HNOption.General[Opt] = Val.str();
+ }
+
+ Buffer = {Section, "DerivedType."};
+ DefSize = Buffer.size();
+ for (const auto &Type : HNDerivedTypes) {
+ Buffer.truncate(DefSize);
+ Buffer.append(Type);
+ StringRef Val = Options.get(Buffer, "");
+ if (!Val.empty())
+ HNOption.DerivedType[Type] = Val.str();
+ }
+
+ static constexpr std::pair<StringRef, StringRef> HNCStrings[] = {
+ {"CharPrinter", "char*"},
+ {"CharArray", "char[]"},
+ {"WideCharPrinter", "wchar_t*"},
+ {"WideCharArray", "wchar_t[]"}};
+
+ Buffer = {Section, "CString."};
+ DefSize = Buffer.size();
+ for (const auto &CStr : HNCStrings) {
+ Buffer.truncate(DefSize);
+ Buffer.append(CStr.first);
+ StringRef Val = Options.get(Buffer, "");
+ if (!Val.empty())
+ HNOption.CString[CStr.first] = Val.str();
+ }
+
+ Buffer = {Section, "PrimitiveType."};
+ DefSize = Buffer.size();
+ for (const auto &PrimType : HungarainNotationPrimitiveTypes) {
+ Buffer.truncate(DefSize);
+ Buffer.append(PrimType);
+ StringRef Val = Options.get(Buffer, "");
+ if (!Val.empty()) {
+ std::string Type = PrimType.str();
+ std::replace(Type.begin(), Type.end(), '-', ' ');
+ HNOption.PrimitiveType[Type] = Val.str();
+ }
+ }
+
+ Buffer = {Section, "UserDefinedType."};
+ DefSize = Buffer.size();
+ for (const auto &Type : HungarainNotationUserDefinedTypes) {
+ Buffer.truncate(DefSize);
+ Buffer.append(Type);
+ StringRef Val = Options.get(Buffer, "");
+ if (!Val.empty())
+ HNOption.UserDefinedType[Type] = Val.str();
+ }
+}
+
+std::string IdentifierNamingCheck::HungarianNotation::getPrefix(
+ const Decl *D,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
+ if (!D)
+ return {};
+ const auto *ND = dyn_cast<NamedDecl>(D);
+ if (!ND)
+ return {};
+
+ std::string Prefix;
+ if (const auto *ECD = dyn_cast<EnumConstantDecl>(ND)) {
+ Prefix = getEnumPrefix(ECD);
+ } else if (const auto *CRD = dyn_cast<CXXRecordDecl>(ND)) {
+ Prefix = getClassPrefix(CRD, HNOption);
+ } else if (isa<VarDecl, FieldDecl, RecordDecl>(ND)) {
+ std::string TypeName = getDeclTypeName(ND);
+ if (!TypeName.empty())
+ Prefix = getDataTypePrefix(TypeName, ND, HNOption);
+ }
+
+ return Prefix;
+}
+
+bool IdentifierNamingCheck::HungarianNotation::removeDuplicatedPrefix(
+ SmallVector<StringRef, 8> &Words,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
+ if (Words.size() <= 1)
+ return true;
+
+ std::string CorrectName = Words[0].str();
+ std::vector<llvm::StringMap<std::string>> MapList = {
+ HNOption.CString, HNOption.DerivedType, HNOption.PrimitiveType,
+ HNOption.UserDefinedType};
+
+ for (const auto &Map : MapList) {
+ for (const auto &Str : Map) {
+ if (Str.getValue() == CorrectName) {
+ Words.erase(Words.begin(), Words.begin() + 1);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+std::string IdentifierNamingCheck::HungarianNotation::getDataTypePrefix(
+ StringRef TypeName, const NamedDecl *ND,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
+ if (!ND || TypeName.empty())
+ return TypeName.str();
+
+ std::string ModifiedTypeName(TypeName);
+
+ // Derived types
+ std::string PrefixStr;
+ if (const auto *TD = dyn_cast<ValueDecl>(ND)) {
+ QualType QT = TD->getType();
+ if (QT->isFunctionPointerType()) {
+ PrefixStr = HNOption.DerivedType.lookup("FunctionPointer");
+ } else if (QT->isPointerType()) {
+ for (const auto &CStr : HNOption.CString) {
+ std::string Key = CStr.getKey().str();
+ if (ModifiedTypeName.find(Key) == 0) {
+ PrefixStr = CStr.getValue();
+ ModifiedTypeName = ModifiedTypeName.substr(
+ Key.size(), ModifiedTypeName.size() - Key.size());
+ break;
+ }
+ }
+ } else if (QT->isArrayType()) {
+ for (const auto &CStr : HNOption.CString) {
+ std::string Key = CStr.getKey().str();
+ if (ModifiedTypeName.find(Key) == 0) {
+ PrefixStr = CStr.getValue();
+ break;
+ }
+ }
+ if (PrefixStr.empty())
+ PrefixStr = HNOption.DerivedType.lookup("Array");
+ } else if (QT->isReferenceType()) {
+ size_t Pos = ModifiedTypeName.find_last_of("&");
+ if (Pos != std::string::npos)
+ ModifiedTypeName = ModifiedTypeName.substr(0, Pos);
+ }
+ }
+
+ // Pointers
+ size_t PtrCount = [&](std::string TypeName) -> size_t {
+ size_t Pos = TypeName.find('*');
+ size_t Count = 0;
+ for (; Pos < TypeName.length(); Pos++, Count++) {
+ if ('*' != TypeName[Pos])
+ break;
+ }
+ return Count;
+ }(ModifiedTypeName);
+ if (PtrCount > 0) {
+ ModifiedTypeName = [&](std::string Str, StringRef From, StringRef To) {
+ size_t StartPos = 0;
+ while ((StartPos = Str.find(From.data(), StartPos)) !=
+ std::string::npos) {
+ Str.replace(StartPos, From.size(), To.data());
+ StartPos += To.size();
+ }
+ return Str;
+ }(ModifiedTypeName, "*", "");
+ }
+
+ // Primitive types
+ if (PrefixStr.empty()) {
+ for (const auto &Type : HNOption.PrimitiveType) {
+ if (ModifiedTypeName == Type.getKey()) {
+ PrefixStr = Type.getValue();
+ break;
+ }
+ }
+ }
+
+ // User-Defined types
+ if (PrefixStr.empty()) {
+ for (const auto &Type : HNOption.UserDefinedType) {
+ if (ModifiedTypeName == Type.getKey()) {
+ PrefixStr = Type.getValue();
+ break;
+ }
+ }
+ }
+
+ for (size_t Idx = 0; Idx < PtrCount; Idx++)
+ PrefixStr.insert(0, HNOption.DerivedType.lookup("Pointer"));
+
+ return PrefixStr;
+}
+
+std::string IdentifierNamingCheck::HungarianNotation::getClassPrefix(
+ const CXXRecordDecl *CRD,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
+
+ if (CRD->isUnion())
+ return {};
+
+ if (CRD->isStruct() &&
+ !isOptionEnabled("TreatStructAsClass", HNOption.General))
+ return {};
+
+ return CRD->isAbstract() ? "I" : "C";
+}
+
+std::string IdentifierNamingCheck::HungarianNotation::getEnumPrefix(
+ const EnumConstantDecl *ECD) const {
+ std::string Name = ECD->getType().getAsString();
+ if (std::string::npos != Name.find("enum")) {
+ Name = Name.substr(strlen("enum"), Name.length() - strlen("enum"));
+ Name = Name.erase(0, Name.find_first_not_of(" "));
+ }
+
+ static llvm::Regex Splitter(
+ "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)");
+
+ StringRef EnumName(Name);
+ SmallVector<StringRef, 8> Substrs;
+ EnumName.split(Substrs, "_", -1, false);
+
+ SmallVector<StringRef, 8> Words;
+ SmallVector<StringRef, 8> Groups;
+ for (auto Substr : Substrs) {
+ while (!Substr.empty()) {
+ Groups.clear();
+ if (!Splitter.match(Substr, &Groups))
+ break;
+
+ if (Groups[2].size() > 0) {
+ Words.push_back(Groups[1]);
+ Substr = Substr.substr(Groups[0].size());
+ } else if (Groups[3].size() > 0) {
+ Words.push_back(Groups[3]);
+ Substr = Substr.substr(Groups[0].size() - Groups[4].size());
+ } else if (Groups[5].size() > 0) {
+ Words.push_back(Groups[5]);
+ Substr = Substr.substr(Groups[0].size() - Groups[6].size());
+ }
+ }
+ }
+
+ std::string Initial;
+ for (StringRef Word : Words)
+ Initial += tolower(Word[0]);
+
+ return Initial;
+}
+
+void IdentifierNamingCheck::HungarianNotation::loadDefaultConfig(
+ IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
+
+ // Options
+ static constexpr std::pair<StringRef, StringRef> General[] = {
+ {"TreatStructAsClass", "false"}};
+ for (const auto &G : General)
+ HNOption.General.try_emplace(G.first, G.second);
+
+ // Derived types
+ static constexpr std::pair<StringRef, StringRef> DerivedTypes[] = {
+ {"Array", "a"}, {"Pointer", "p"}, {"FunctionPointer", "fn"}};
+ for (const auto &DT : DerivedTypes)
+ HNOption.DerivedType.try_emplace(DT.first, DT.second);
+
+ // C strings
+ static constexpr std::pair<StringRef, StringRef> CStrings[] = {
+ {"char*", "sz"},
+ {"char[]", "sz"},
+ {"wchar_t*", "wsz"},
+ {"wchar_t[]", "wsz"}};
+ for (const auto &CStr : CStrings)
+ HNOption.CString.try_emplace(CStr.first, CStr.second);
+
+ // clang-format off
+ static constexpr std::pair<StringRef, StringRef> PrimitiveTypes[] = {
+ {"int8_t", "i8" },
+ {"int16_t", "i16" },
+ {"int32_t", "i32" },
+ {"int64_t", "i64" },
+ {"uint8_t", "u8" },
+ {"uint16_t", "u16" },
+ {"uint32_t", "u32" },
+ {"uint64_t", "u64" },
+ {"char8_t", "c8" },
+ {"char16_t", "c16" },
+ {"char32_t", "c32" },
+ {"float", "f" },
+ {"double", "d" },
+ {"char", "c" },
+ {"bool", "b" },
+ {"_Bool", "b" },
+ {"int", "i" },
+ {"size_t", "n" },
+ {"wchar_t", "wc" },
+ {"short int", "si" },
+ {"short", "s" },
+ {"signed int", "si" },
+ {"signed short", "ss" },
+ {"signed short int", "ssi" },
+ {"signed long long int", "slli"},
+ {"signed long long", "sll" },
+ {"signed long int", "sli" },
+ {"signed long", "sl" },
+ {"signed", "s" },
+ {"unsigned long long int", "ulli"},
+ {"unsigned long long", "ull" },
+ {"unsigned long int", "uli" },
+ {"unsigned long", "ul" },
+ {"unsigned short int", "usi" },
+ {"unsigned short", "us" },
+ {"unsigned int", "ui" },
+ {"unsigned", "u" },
+ {"long long int", "lli" },
+ {"long double", "ld" },
+ {"long long", "ll" },
+ {"long int", "li" },
+ {"long", "l" },
+ {"ptrdiff_t", "p" }};
+ // clang-format on
+ for (const auto &PT : PrimitiveTypes)
+ HNOption.PrimitiveType.try_emplace(PT.first, PT.second);
+
+ // clang-format off
+ static constexpr std::pair<StringRef, StringRef> UserDefinedTypes[] = {
+ // Windows data types
+ {"BOOL", "b" },
+ {"BOOLEAN", "b" },
+ {"BYTE", "by" },
+ {"CHAR", "c" },
+ {"UCHAR", "uc" },
+ {"SHORT", "s" },
+ {"USHORT", "us" },
+ {"WORD", "w" },
+ {"DWORD", "dw" },
+ {"DWORD32", "dw32"},
+ {"DWORD64", "dw64"},
+ {"LONG", "l" },
+ {"ULONG", "ul" },
+ {"ULONG32", "ul32"},
+ {"ULONG64", "ul64"},
+ {"ULONGLONG", "ull" },
+ {"HANDLE", "h" },
+ {"INT", "i" },
+ {"INT8", "i8" },
+ {"INT16", "i16" },
+ {"INT32", "i32" },
+ {"INT64", "i64" },
+ {"UINT", "ui" },
+ {"UINT8", "u8" },
+ {"UINT16", "u16" },
+ {"UINT32", "u32" },
+ {"UINT64", "u64" },
+ {"PVOID", "p" } };
+ // clang-format on
+ for (const auto &UDT : UserDefinedTypes)
+ HNOption.UserDefinedType.try_emplace(UDT.first, UDT.second);
+}
+
+void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ RenamerClangTidyCheck::storeOptions(Opts);
+ SmallString<64> StyleString;
+ ArrayRef<std::optional<NamingStyle>> Styles = MainFileStyle->getStyles();
+ for (size_t I = 0; I < SK_Count; ++I) {
+ if (!Styles[I])
+ continue;
+ size_t StyleSize = StyleNames[I].size();
+ StyleString.assign({StyleNames[I], "HungarianPrefix"});
+
+ Options.store(Opts, StyleString, Styles[I]->HPType);
+
+ memcpy(&StyleString[StyleSize], "IgnoredRegexp", 13);
+ StyleString.truncate(StyleSize + 13);
+ Options.store(Opts, StyleString, Styles[I]->IgnoredRegexpStr);
+ memcpy(&StyleString[StyleSize], "Prefix", 6);
+ StyleString.truncate(StyleSize + 6);
+ Options.store(Opts, StyleString, Styles[I]->Prefix);
+ // Fast replacement of [Pre]fix -> [Suf]fix.
+ memcpy(&StyleString[StyleSize], "Suf", 3);
+ Options.store(Opts, StyleString, Styles[I]->Suffix);
+ if (Styles[I]->Case) {
+ memcpy(&StyleString[StyleSize], "Case", 4);
+ StyleString.pop_back_n(2);
+ Options.store(Opts, StyleString, *Styles[I]->Case);
+ }
+ }
+ Options.store(Opts, "GetConfigPerFile", GetConfigPerFile);
+ Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit);
+ Options.store(Opts, "IgnoreMainLikeFunctions",
+ MainFileStyle->isIgnoringMainLikeFunction());
+}
+
+bool IdentifierNamingCheck::matchesStyle(
+ StringRef Type, StringRef Name,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ const NamedDecl *Decl) const {
+ static llvm::Regex Matchers[] = {
+ llvm::Regex("^.*$"),
+ llvm::Regex("^[a-z][a-z0-9_]*$"),
+ llvm::Regex("^[a-z][a-zA-Z0-9]*$"),
+ llvm::Regex("^[A-Z][A-Z0-9_]*$"),
+ llvm::Regex("^[A-Z][a-zA-Z0-9]*$"),
+ llvm::Regex("^[A-Z]([a-z0-9]*(_[A-Z])?)*"),
+ llvm::Regex("^[a-z]([a-z0-9]*(_[A-Z])?)*"),
+ };
+
+ if (!Name.consume_front(Style.Prefix))
+ return false;
+ if (!Name.consume_back(Style.Suffix))
+ return false;
+ if (IdentifierNamingCheck::HungarianPrefixType::HPT_Off != Style.HPType) {
+ std::string HNPrefix = HungarianNotation.getPrefix(Decl, HNOption);
+ if (!Name.consume_front(HNPrefix))
+ return false;
+ }
+
+ // Ensure the name doesn't have any extra underscores beyond those specified
+ // in the prefix and suffix.
+ if (Name.startswith("_") || Name.endswith("_"))
+ return false;
+
+ if (Style.Case && !Matchers[static_cast<size_t>(*Style.Case)].match(Name))
+ return false;
+
+ return true;
+}
+
+std::string IdentifierNamingCheck::fixupWithCase(
+ StringRef Type, StringRef Name, const Decl *D,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ IdentifierNamingCheck::CaseType Case) const {
+ static llvm::Regex Splitter(
+ "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)");
+
+ SmallVector<StringRef, 8> Substrs;
+ Name.split(Substrs, "_", -1, false);
+
+ SmallVector<StringRef, 8> Words;
+ SmallVector<StringRef, 8> Groups;
+ for (auto Substr : Substrs) {
+ while (!Substr.empty()) {
+ Groups.clear();
+ if (!Splitter.match(Substr, &Groups))
+ break;
+
+ if (Groups[2].size() > 0) {
+ Words.push_back(Groups[1]);
+ Substr = Substr.substr(Groups[0].size());
+ } else if (Groups[3].size() > 0) {
+ Words.push_back(Groups[3]);
+ Substr = Substr.substr(Groups[0].size() - Groups[4].size());
+ } else if (Groups[5].size() > 0) {
+ Words.push_back(Groups[5]);
+ Substr = Substr.substr(Groups[0].size() - Groups[6].size());
+ }
+ }
+ }
+
+ if (Words.empty())
+ return Name.str();
+
+ if (IdentifierNamingCheck::HungarianPrefixType::HPT_Off != Style.HPType) {
+ HungarianNotation.removeDuplicatedPrefix(Words, HNOption);
+ }
+
+ SmallString<128> Fixup;
+ switch (Case) {
+ case IdentifierNamingCheck::CT_AnyCase:
+ return Name.str();
+ break;
+
+ case IdentifierNamingCheck::CT_LowerCase:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front())
+ Fixup += "_";
+ Fixup += Word.lower();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_UpperCase:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front())
+ Fixup += "_";
+ Fixup += Word.upper();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelCase:
+ for (auto const &Word : Words) {
+ Fixup += toupper(Word.front());
+ Fixup += Word.substr(1).lower();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelBack:
+ for (auto const &Word : Words) {
+ if (&Word == &Words.front()) {
+ Fixup += Word.lower();
+ } else {
+ Fixup += toupper(Word.front());
+ Fixup += Word.substr(1).lower();
+ }
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelSnakeCase:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front())
+ Fixup += "_";
+ Fixup += toupper(Word.front());
+ Fixup += Word.substr(1).lower();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelSnakeBack:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front()) {
+ Fixup += "_";
+ Fixup += toupper(Word.front());
+ } else {
+ Fixup += tolower(Word.front());
+ }
+ Fixup += Word.substr(1).lower();
+ }
+ break;
+ }
+
+ return Fixup.str().str();
+}
+
+bool IdentifierNamingCheck::isParamInMainLikeFunction(
+ const ParmVarDecl &ParmDecl, bool IncludeMainLike) const {
+ const auto *FDecl =
+ dyn_cast_or_null<FunctionDecl>(ParmDecl.getParentFunctionOrMethod());
+ if (!FDecl)
+ return false;
+ if (FDecl->isMain())
+ return true;
+ if (!IncludeMainLike)
+ return false;
+ if (FDecl->getAccess() != AS_public && FDecl->getAccess() != AS_none)
+ return false;
+ // If the function doesn't have a name that's an identifier, can occur if the
+ // function is an operator overload, bail out early.
+ if (!FDecl->getDeclName().isIdentifier())
+ return false;
+ enum MainType { None, Main, WMain };
+ auto IsCharPtrPtr = [](QualType QType) -> MainType {
+ if (QType.isNull())
+ return None;
+ if (QType = QType->getPointeeType(), QType.isNull())
+ return None;
+ if (QType = QType->getPointeeType(), QType.isNull())
+ return None;
+ if (QType->isCharType())
+ return Main;
+ if (QType->isWideCharType())
+ return WMain;
+ return None;
+ };
+ auto IsIntType = [](QualType QType) {
+ if (QType.isNull())
+ return false;
+ if (const auto *Builtin =
+ dyn_cast<BuiltinType>(QType->getUnqualifiedDesugaredType())) {
+ return Builtin->getKind() == BuiltinType::Int;
+ }
+ return false;
+ };
+ if (!IsIntType(FDecl->getReturnType()))
+ return false;
+ if (FDecl->getNumParams() < 2 || FDecl->getNumParams() > 3)
+ return false;
+ if (!IsIntType(FDecl->parameters()[0]->getType()))
+ return false;
+ MainType Type = IsCharPtrPtr(FDecl->parameters()[1]->getType());
+ if (Type == None)
+ return false;
+ if (FDecl->getNumParams() == 3 &&
+ IsCharPtrPtr(FDecl->parameters()[2]->getType()) != Type)
+ return false;
+
+ if (Type == Main) {
+ static llvm::Regex Matcher(
+ "(^[Mm]ain([_A-Z]|$))|([a-z0-9_]Main([_A-Z]|$))|(_main(_|$))");
+ assert(Matcher.isValid() && "Invalid Matcher for main like functions.");
+ return Matcher.match(FDecl->getName());
+ }
+ static llvm::Regex Matcher("(^((W[Mm])|(wm))ain([_A-Z]|$))|([a-z0-9_]W[Mm]"
+ "ain([_A-Z]|$))|(_wmain(_|$))");
+ assert(Matcher.isValid() && "Invalid Matcher for wmain like functions.");
+ return Matcher.match(FDecl->getName());
+}
+
+std::string IdentifierNamingCheck::fixupWithStyle(
+ StringRef Type, StringRef Name,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ const Decl *D) const {
+ Name.consume_front(Style.Prefix);
+ Name.consume_back(Style.Suffix);
+ std::string Fixed = fixupWithCase(
+ Type, Name, D, Style, HNOption,
+ Style.Case.value_or(IdentifierNamingCheck::CaseType::CT_AnyCase));
+
+ std::string HungarianPrefix;
+ using HungarianPrefixType = IdentifierNamingCheck::HungarianPrefixType;
+ if (HungarianPrefixType::HPT_Off != Style.HPType) {
+ HungarianPrefix = HungarianNotation.getPrefix(D, HNOption);
+ if (!HungarianPrefix.empty()) {
+ if (Style.HPType == HungarianPrefixType::HPT_LowerCase)
+ HungarianPrefix += "_";
+
+ if (Style.HPType == HungarianPrefixType::HPT_CamelCase)
+ Fixed[0] = toupper(Fixed[0]);
+ }
+ }
+ StringRef Mid = StringRef(Fixed).trim("_");
+ if (Mid.empty())
+ Mid = "_";
+
+ return (Style.Prefix + HungarianPrefix + Mid + Style.Suffix).str();
+}
+
+StyleKind IdentifierNamingCheck::findStyleKind(
+ const NamedDecl *D,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ bool IgnoreMainLikeFunctions) const {
+ assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() &&
+ "Decl must be an explicit identifier with a name.");
+
+ if (isa<ObjCIvarDecl>(D) && NamingStyles[SK_ObjcIvar])
+ return SK_ObjcIvar;
+
+ if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef])
+ return SK_Typedef;
+
+ if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
+ return SK_TypeAlias;
+
+ if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
+ if (Decl->isAnonymousNamespace())
+ return SK_Invalid;
+
+ if (Decl->isInline() && NamingStyles[SK_InlineNamespace])
+ return SK_InlineNamespace;
+
+ if (NamingStyles[SK_Namespace])
+ return SK_Namespace;
+ }
+
+ if (isa<EnumDecl>(D) && NamingStyles[SK_Enum])
+ return SK_Enum;
+
+ if (const auto *EnumConst = dyn_cast<EnumConstantDecl>(D)) {
+ if (cast<EnumDecl>(EnumConst->getDeclContext())->isScoped() &&
+ NamingStyles[SK_ScopedEnumConstant])
+ return SK_ScopedEnumConstant;
+
+ if (NamingStyles[SK_EnumConstant])
+ return SK_EnumConstant;
+
+ if (NamingStyles[SK_Constant])
+ return SK_Constant;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<CXXRecordDecl>(D)) {
+ if (Decl->isAnonymousStructOrUnion())
+ return SK_Invalid;
+
+ if (!Decl->getCanonicalDecl()->isThisDeclarationADefinition())
+ return SK_Invalid;
+
+ if (Decl->hasDefinition() && Decl->isAbstract() &&
+ NamingStyles[SK_AbstractClass])
+ return SK_AbstractClass;
+
+ if (Decl->isStruct() && NamingStyles[SK_Struct])
+ return SK_Struct;
+
+ if (Decl->isStruct() && NamingStyles[SK_Class])
+ return SK_Class;
+
+ if (Decl->isClass() && NamingStyles[SK_Class])
+ return SK_Class;
+
+ if (Decl->isClass() && NamingStyles[SK_Struct])
+ return SK_Struct;
+
+ if (Decl->isUnion() && NamingStyles[SK_Union])
+ return SK_Union;
+
+ if (Decl->isEnum() && NamingStyles[SK_Enum])
+ return SK_Enum;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<FieldDecl>(D)) {
+ QualType Type = Decl->getType();
+
+ if (!Type.isNull() && Type.isConstQualified()) {
+ if (NamingStyles[SK_ConstantMember])
+ return SK_ConstantMember;
+
+ if (NamingStyles[SK_Constant])
+ return SK_Constant;
+ }
+
+ if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember])
+ return SK_PrivateMember;
+
+ if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember])
+ return SK_ProtectedMember;
+
+ if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember])
+ return SK_PublicMember;
+
+ if (NamingStyles[SK_Member])
+ return SK_Member;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) {
+ if (isParamInMainLikeFunction(*Decl, IgnoreMainLikeFunctions))
+ return SK_Invalid;
+ QualType Type = Decl->getType();
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
+ return SK_ConstexprVariable;
+
+ if (!Type.isNull() && Type.isConstQualified()) {
+ if (Type.getTypePtr()->isAnyPointerType() &&
+ NamingStyles[SK_ConstantPointerParameter])
+ return SK_ConstantPointerParameter;
+
+ if (NamingStyles[SK_ConstantParameter])
+ return SK_ConstantParameter;
+
+ if (NamingStyles[SK_Constant])
+ return SK_Constant;
+ }
+
+ if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack])
+ return SK_ParameterPack;
+
+ if (!Type.isNull() && Type.getTypePtr()->isAnyPointerType() &&
+ NamingStyles[SK_PointerParameter])
+ return SK_PointerParameter;
+
+ if (NamingStyles[SK_Parameter])
+ return SK_Parameter;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<VarDecl>(D)) {
+ QualType Type = Decl->getType();
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
+ return SK_ConstexprVariable;
+
+ if (!Type.isNull() && Type.isConstQualified()) {
+ if (Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant])
+ return SK_ClassConstant;
+
+ if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
+ NamingStyles[SK_GlobalConstantPointer])
+ return SK_GlobalConstantPointer;
+
+ if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant])
+ return SK_GlobalConstant;
+
+ if (Decl->isStaticLocal() && NamingStyles[SK_StaticConstant])
+ return SK_StaticConstant;
+
+ if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
+ NamingStyles[SK_LocalConstantPointer])
+ return SK_LocalConstantPointer;
+
+ if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant])
+ return SK_LocalConstant;
+
+ if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant])
+ return SK_LocalConstant;
+
+ if (NamingStyles[SK_Constant])
+ return SK_Constant;
+ }
+
+ if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember])
+ return SK_ClassMember;
+
+ if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
+ NamingStyles[SK_GlobalPointer])
+ return SK_GlobalPointer;
+
+ if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable])
+ return SK_GlobalVariable;
+
+ if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable])
+ return SK_StaticVariable;
+
+ if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
+ NamingStyles[SK_LocalPointer])
+ return SK_LocalPointer;
+
+ if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable])
+ return SK_LocalVariable;
+
+ if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable])
+ return SK_LocalVariable;
+
+ if (NamingStyles[SK_Variable])
+ return SK_Variable;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<CXXMethodDecl>(D)) {
+ if (Decl->isMain() || !Decl->isUserProvided() ||
+ Decl->size_overridden_methods() > 0 || Decl->hasAttr<OverrideAttr>())
+ return SK_Invalid;
+
+ // If this method has the same name as any base method, this is likely
+ // necessary even if it's not an override. e.g. CRTP.
+ for (const CXXBaseSpecifier &Base : Decl->getParent()->bases())
+ if (const auto *RD = Base.getType()->getAsCXXRecordDecl())
+ if (RD->hasMemberName(Decl->getDeclName()))
+ return SK_Invalid;
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod])
+ return SK_ConstexprMethod;
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
+ return SK_ConstexprFunction;
+
+ if (Decl->isStatic() && NamingStyles[SK_ClassMethod])
+ return SK_ClassMethod;
+
+ if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod])
+ return SK_VirtualMethod;
+
+ if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod])
+ return SK_PrivateMethod;
+
+ if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod])
+ return SK_ProtectedMethod;
+
+ if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod])
+ return SK_PublicMethod;
+
+ if (NamingStyles[SK_Method])
+ return SK_Method;
+
+ if (NamingStyles[SK_Function])
+ return SK_Function;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<FunctionDecl>(D)) {
+ if (Decl->isMain())
+ return SK_Invalid;
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
+ return SK_ConstexprFunction;
+
+ if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction])
+ return SK_GlobalFunction;
+
+ if (NamingStyles[SK_Function])
+ return SK_Function;
+ }
+
+ if (isa<TemplateTypeParmDecl>(D)) {
+ if (NamingStyles[SK_TypeTemplateParameter])
+ return SK_TypeTemplateParameter;
+
+ if (NamingStyles[SK_TemplateParameter])
+ return SK_TemplateParameter;
+
+ return SK_Invalid;
+ }
+
+ if (isa<NonTypeTemplateParmDecl>(D)) {
+ if (NamingStyles[SK_ValueTemplateParameter])
+ return SK_ValueTemplateParameter;
+
+ if (NamingStyles[SK_TemplateParameter])
+ return SK_TemplateParameter;
+
+ return SK_Invalid;
+ }
+
+ if (isa<TemplateTemplateParmDecl>(D)) {
+ if (NamingStyles[SK_TemplateTemplateParameter])
+ return SK_TemplateTemplateParameter;
+
+ if (NamingStyles[SK_TemplateParameter])
+ return SK_TemplateParameter;
+
+ return SK_Invalid;
+ }
+
+ return SK_Invalid;
+}
+
+std::optional<RenamerClangTidyCheck::FailureInfo>
+IdentifierNamingCheck::getFailureInfo(
+ StringRef Type, StringRef Name, const NamedDecl *ND,
+ SourceLocation Location,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const {
+ if (SK == SK_Invalid || !NamingStyles[SK])
+ return std::nullopt;
+
+ const IdentifierNamingCheck::NamingStyle &Style = *NamingStyles[SK];
+ if (Style.IgnoredRegexp.isValid() && Style.IgnoredRegexp.match(Name))
+ return std::nullopt;
+
+ if (matchesStyle(Type, Name, Style, HNOption, ND))
+ return std::nullopt;
+
+ std::string KindName =
+ fixupWithCase(Type, StyleNames[SK], ND, Style, HNOption,
+ IdentifierNamingCheck::CT_LowerCase);
+ std::replace(KindName.begin(), KindName.end(), '_', ' ');
+
+ std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND);
+ if (StringRef(Fixup).equals(Name)) {
+ if (!IgnoreFailedSplit) {
+ LLVM_DEBUG(Location.print(llvm::dbgs(), SM);
+ llvm::dbgs()
+ << llvm::formatv(": unable to split words for {0} '{1}'\n",
+ KindName, Name));
+ }
+ return std::nullopt;
+ }
+ return RenamerClangTidyCheck::FailureInfo{std::move(KindName),
+ std::move(Fixup)};
+}
+
+std::optional<RenamerClangTidyCheck::FailureInfo>
+IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl,
+ const SourceManager &SM) const {
+ SourceLocation Loc = Decl->getLocation();
+ const FileStyle &FileStyle = getStyleForFile(SM.getFilename(Loc));
+ if (!FileStyle.isActive())
+ return std::nullopt;
+
+ return getFailureInfo(HungarianNotation.getDeclTypeName(Decl),
+ Decl->getName(), Decl, Loc, FileStyle.getStyles(),
+ FileStyle.getHNOption(),
+ findStyleKind(Decl, FileStyle.getStyles(),
+ FileStyle.isIgnoringMainLikeFunction()),
+ SM, IgnoreFailedSplit);
+}
+
+std::optional<RenamerClangTidyCheck::FailureInfo>
+IdentifierNamingCheck::getMacroFailureInfo(const Token &MacroNameTok,
+ const SourceManager &SM) const {
+ SourceLocation Loc = MacroNameTok.getLocation();
+ const FileStyle &Style = getStyleForFile(SM.getFilename(Loc));
+ if (!Style.isActive())
+ return std::nullopt;
+
+ return getFailureInfo("", MacroNameTok.getIdentifierInfo()->getName(),
+ nullptr, Loc, Style.getStyles(), Style.getHNOption(),
+ SK_MacroDefinition, SM, IgnoreFailedSplit);
+}
+
+RenamerClangTidyCheck::DiagInfo
+IdentifierNamingCheck::getDiagInfo(const NamingCheckId &ID,
+ const NamingCheckFailure &Failure) const {
+ return DiagInfo{"invalid case style for %0 '%1'",
+ [&](DiagnosticBuilder &Diag) {
+ Diag << Failure.Info.KindName << ID.second;
+ }};
+}
+
+const IdentifierNamingCheck::FileStyle &
+IdentifierNamingCheck::getStyleForFile(StringRef FileName) const {
+ if (!GetConfigPerFile)
+ return *MainFileStyle;
+ StringRef Parent = llvm::sys::path::parent_path(FileName);
+ auto Iter = NamingStylesCache.find(Parent);
+ if (Iter != NamingStylesCache.end())
+ return Iter->getValue();
+
+ ClangTidyOptions Options = Context->getOptionsForFile(FileName);
+ if (Options.Checks && GlobList(*Options.Checks).contains(CheckName)) {
+ auto It = NamingStylesCache.try_emplace(
+ Parent,
+ getFileStyleFromOptions({CheckName, Options.CheckOptions, Context}));
+ assert(It.second);
+ return It.first->getValue();
+ }
+ // Default construction gives an empty style.
+ auto It = NamingStylesCache.try_emplace(Parent);
+ assert(It.second);
+ return It.first->getValue();
+}
+
+} // namespace readability
+} // namespace clang::tidy
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.h
new file mode 100644
index 0000000000..cbf0653a28
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IdentifierNamingCheck.h
@@ -0,0 +1,215 @@
+//===--- IdentifierNamingCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
+
+#include "../utils/RenamerClangTidyCheck.h"
+#include <optional>
+namespace clang::tidy {
+namespace readability {
+
+enum StyleKind : int;
+
+/// Checks for identifiers naming style mismatch.
+///
+/// This check will try to enforce coding guidelines on the identifiers naming.
+/// It supports `lower_case`, `UPPER_CASE`, `camelBack` and `CamelCase` casing
+/// and tries to convert from one to another if a mismatch is detected.
+///
+/// It also supports a fixed prefix and suffix that will be prepended or
+/// appended to the identifiers, regardless of the casing.
+///
+/// Many configuration options are available, in order to be able to create
+/// different rules for different kind of identifier. In general, the
+/// rules are falling back to a more generic rule if the specific case is not
+/// configured.
+class IdentifierNamingCheck final : public RenamerClangTidyCheck {
+public:
+ IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context);
+ ~IdentifierNamingCheck();
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+ enum CaseType {
+ CT_AnyCase = 0,
+ CT_LowerCase,
+ CT_CamelBack,
+ CT_UpperCase,
+ CT_CamelCase,
+ CT_CamelSnakeCase,
+ CT_CamelSnakeBack
+ };
+
+ enum HungarianPrefixType {
+ HPT_Off = 0,
+ HPT_On,
+ HPT_LowerCase,
+ HPT_CamelCase,
+ };
+
+ struct HungarianNotationOption {
+ HungarianNotationOption() : HPType(HungarianPrefixType::HPT_Off) {}
+
+ std::optional<CaseType> Case;
+ HungarianPrefixType HPType;
+ llvm::StringMap<std::string> General;
+ llvm::StringMap<std::string> CString;
+ llvm::StringMap<std::string> PrimitiveType;
+ llvm::StringMap<std::string> UserDefinedType;
+ llvm::StringMap<std::string> DerivedType;
+ };
+
+ struct NamingStyle {
+ NamingStyle() = default;
+
+ NamingStyle(std::optional<CaseType> Case, const std::string &Prefix,
+ const std::string &Suffix, const std::string &IgnoredRegexpStr,
+ HungarianPrefixType HPType);
+ NamingStyle(const NamingStyle &O) = delete;
+ NamingStyle &operator=(NamingStyle &&O) = default;
+ NamingStyle(NamingStyle &&O) = default;
+
+ std::optional<CaseType> Case;
+ std::string Prefix;
+ std::string Suffix;
+ // Store both compiled and non-compiled forms so original value can be
+ // serialized
+ llvm::Regex IgnoredRegexp;
+ std::string IgnoredRegexpStr;
+
+ HungarianPrefixType HPType;
+ };
+
+ struct HungarianNotation {
+ public:
+ bool checkOptionValid(int StyleKindIndex) const;
+ bool isOptionEnabled(StringRef OptionKey,
+ const llvm::StringMap<std::string> &StrMap) const;
+ void loadDefaultConfig(
+ IdentifierNamingCheck::HungarianNotationOption &HNOption) const;
+ void loadFileConfig(
+ const ClangTidyCheck::OptionsView &Options,
+ IdentifierNamingCheck::HungarianNotationOption &HNOption) const;
+
+ bool removeDuplicatedPrefix(
+ SmallVector<StringRef, 8> &Words,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const;
+
+ std::string getPrefix(
+ const Decl *D,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const;
+
+ std::string getDataTypePrefix(
+ StringRef TypeName, const NamedDecl *ND,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const;
+
+ std::string getClassPrefix(
+ const CXXRecordDecl *CRD,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption) const;
+
+ std::string getEnumPrefix(const EnumConstantDecl *ECD) const;
+ std::string getDeclTypeName(const NamedDecl *ND) const;
+ };
+
+ struct FileStyle {
+ FileStyle() : IsActive(false), IgnoreMainLikeFunctions(false) {}
+ FileStyle(SmallVectorImpl<std::optional<NamingStyle>> &&Styles,
+ HungarianNotationOption HNOption, bool IgnoreMainLike)
+ : Styles(std::move(Styles)), HNOption(std::move(HNOption)),
+ IsActive(true), IgnoreMainLikeFunctions(IgnoreMainLike) {}
+
+ ArrayRef<std::optional<NamingStyle>> getStyles() const {
+ assert(IsActive);
+ return Styles;
+ }
+
+ const HungarianNotationOption &getHNOption() const {
+ assert(IsActive);
+ return HNOption;
+ }
+
+ bool isActive() const { return IsActive; }
+ bool isIgnoringMainLikeFunction() const { return IgnoreMainLikeFunctions; }
+
+ private:
+ SmallVector<std::optional<NamingStyle>, 0> Styles;
+ HungarianNotationOption HNOption;
+ bool IsActive;
+ bool IgnoreMainLikeFunctions;
+ };
+
+ IdentifierNamingCheck::FileStyle
+ getFileStyleFromOptions(const ClangTidyCheck::OptionsView &Options) const;
+
+ bool
+ matchesStyle(StringRef Type, StringRef Name,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ const NamedDecl *Decl) const;
+
+ std::string
+ fixupWithCase(StringRef Type, StringRef Name, const Decl *D,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ IdentifierNamingCheck::CaseType Case) const;
+
+ std::string
+ fixupWithStyle(StringRef Type, StringRef Name,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ const Decl *D) const;
+
+ StyleKind findStyleKind(
+ const NamedDecl *D,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ bool IgnoreMainLikeFunctions) const;
+
+ std::optional<RenamerClangTidyCheck::FailureInfo> getFailureInfo(
+ StringRef Type, StringRef Name, const NamedDecl *ND,
+ SourceLocation Location,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const;
+
+ bool isParamInMainLikeFunction(const ParmVarDecl &ParmDecl,
+ bool IncludeMainLike) const;
+
+private:
+ std::optional<FailureInfo>
+ getDeclFailureInfo(const NamedDecl *Decl,
+ const SourceManager &SM) const override;
+ std::optional<FailureInfo>
+ getMacroFailureInfo(const Token &MacroNameTok,
+ const SourceManager &SM) const override;
+ DiagInfo getDiagInfo(const NamingCheckId &ID,
+ const NamingCheckFailure &Failure) const override;
+
+ const FileStyle &getStyleForFile(StringRef FileName) const;
+
+ /// Stores the style options as a vector, indexed by the specified \ref
+ /// StyleKind, for a given directory.
+ mutable llvm::StringMap<FileStyle> NamingStylesCache;
+ FileStyle *MainFileStyle;
+ ClangTidyContext *Context;
+ const StringRef CheckName;
+ const bool GetConfigPerFile;
+ const bool IgnoreFailedSplit;
+ HungarianNotation HungarianNotation;
+};
+
+} // namespace readability
+template <>
+struct OptionEnumMapping<readability::IdentifierNamingCheck::CaseType> {
+ static llvm::ArrayRef<
+ std::pair<readability::IdentifierNamingCheck::CaseType, StringRef>>
+ getEnumMapping();
+};
+} // namespace clang::tidy
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
new file mode 100644
index 0000000000..7ec63d0fd9
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
@@ -0,0 +1,392 @@
+//===--- ImplicitBoolConversionCheck.cpp - clang-tidy----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ImplicitBoolConversionCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+#include <queue>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+AST_MATCHER(Stmt, isMacroExpansion) {
+ SourceManager &SM = Finder->getASTContext().getSourceManager();
+ SourceLocation Loc = Node.getBeginLoc();
+ return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
+}
+
+bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
+ SourceManager &SM = Context.getSourceManager();
+ const LangOptions &LO = Context.getLangOpts();
+ SourceLocation Loc = Statement->getBeginLoc();
+ return SM.isMacroBodyExpansion(Loc) &&
+ Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
+}
+
+AST_MATCHER(Stmt, isNULLMacroExpansion) {
+ return isNULLMacroExpansion(&Node, Finder->getASTContext());
+}
+
+StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
+ QualType Type,
+ ASTContext &Context) {
+ switch (CastExprKind) {
+ case CK_IntegralToBoolean:
+ return Type->isUnsignedIntegerType() ? "0u" : "0";
+
+ case CK_FloatingToBoolean:
+ return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
+
+ case CK_PointerToBoolean:
+ case CK_MemberPointerToBoolean: // Fall-through on purpose.
+ return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
+
+ default:
+ llvm_unreachable("Unexpected cast kind");
+ }
+}
+
+bool isUnaryLogicalNotOperator(const Stmt *Statement) {
+ const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
+ return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
+}
+
+bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
+ switch (OperatorKind) {
+ case OO_New:
+ case OO_Delete: // Fall-through on purpose.
+ case OO_Array_New:
+ case OO_Array_Delete:
+ case OO_ArrowStar:
+ case OO_Arrow:
+ case OO_Call:
+ case OO_Subscript:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+bool areParensNeededForStatement(const Stmt *Statement) {
+ if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
+ return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
+ }
+
+ return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
+}
+
+void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
+ const ImplicitCastExpr *Cast, const Stmt *Parent,
+ ASTContext &Context) {
+ // In case of expressions like (! integer), we should remove the redundant not
+ // operator and use inverted comparison (integer == 0).
+ bool InvertComparison =
+ Parent != nullptr && isUnaryLogicalNotOperator(Parent);
+ if (InvertComparison) {
+ SourceLocation ParentStartLoc = Parent->getBeginLoc();
+ SourceLocation ParentEndLoc =
+ cast<UnaryOperator>(Parent)->getSubExpr()->getBeginLoc();
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
+
+ Parent = Context.getParents(*Parent)[0].get<Stmt>();
+ }
+
+ const Expr *SubExpr = Cast->getSubExpr();
+
+ bool NeedInnerParens = areParensNeededForStatement(SubExpr);
+ bool NeedOuterParens =
+ Parent != nullptr && areParensNeededForStatement(Parent);
+
+ std::string StartLocInsertion;
+
+ if (NeedOuterParens) {
+ StartLocInsertion += "(";
+ }
+ if (NeedInnerParens) {
+ StartLocInsertion += "(";
+ }
+
+ if (!StartLocInsertion.empty()) {
+ Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
+ }
+
+ std::string EndLocInsertion;
+
+ if (NeedInnerParens) {
+ EndLocInsertion += ")";
+ }
+
+ if (InvertComparison) {
+ EndLocInsertion += " == ";
+ } else {
+ EndLocInsertion += " != ";
+ }
+
+ EndLocInsertion += getZeroLiteralToCompareWithForType(
+ Cast->getCastKind(), SubExpr->getType(), Context);
+
+ if (NeedOuterParens) {
+ EndLocInsertion += ")";
+ }
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
+ Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
+}
+
+StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
+ ASTContext &Context) {
+ if (isNULLMacroExpansion(Expression, Context)) {
+ return "false";
+ }
+
+ if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
+ return (IntLit->getValue() == 0) ? "false" : "true";
+ }
+
+ if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
+ llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
+ FloatLitAbsValue.clearSign();
+ return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
+ }
+
+ if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
+ return (CharLit->getValue() == 0) ? "false" : "true";
+ }
+
+ if (isa<StringLiteral>(Expression->IgnoreCasts())) {
+ return "true";
+ }
+
+ return StringRef();
+}
+
+void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
+ const ImplicitCastExpr *Cast,
+ ASTContext &Context, StringRef OtherType) {
+ const Expr *SubExpr = Cast->getSubExpr();
+ bool NeedParens = !isa<ParenExpr>(SubExpr);
+
+ Diag << FixItHint::CreateInsertion(
+ Cast->getBeginLoc(),
+ (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
+ .str());
+
+ if (NeedParens) {
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Cast->getEndLoc(), 0, Context.getSourceManager(),
+ Context.getLangOpts());
+
+ Diag << FixItHint::CreateInsertion(EndLoc, ")");
+ }
+}
+
+StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
+ QualType DestType, ASTContext &Context) {
+ // Prior to C++11, false literal could be implicitly converted to pointer.
+ if (!Context.getLangOpts().CPlusPlus11 &&
+ (DestType->isPointerType() || DestType->isMemberPointerType()) &&
+ BoolLiteral->getValue() == false) {
+ return "0";
+ }
+
+ if (DestType->isFloatingType()) {
+ if (Context.hasSameType(DestType, Context.FloatTy)) {
+ return BoolLiteral->getValue() ? "1.0f" : "0.0f";
+ }
+ return BoolLiteral->getValue() ? "1.0" : "0.0";
+ }
+
+ if (DestType->isUnsignedIntegerType()) {
+ return BoolLiteral->getValue() ? "1u" : "0u";
+ }
+ return BoolLiteral->getValue() ? "1" : "0";
+}
+
+bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
+ ASTContext &Context) {
+ std::queue<const Stmt *> Q;
+ Q.push(Cast);
+
+ TraversalKindScope RAII(Context, TK_AsIs);
+
+ while (!Q.empty()) {
+ for (const auto &N : Context.getParents(*Q.front())) {
+ const Stmt *S = N.get<Stmt>();
+ if (!S)
+ return false;
+ if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
+ isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
+ return true;
+ if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
+ isUnaryLogicalNotOperator(S) ||
+ (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
+ Q.push(S);
+ } else {
+ return false;
+ }
+ }
+ Q.pop();
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),
+ AllowPointerConditions(Options.get("AllowPointerConditions", false)) {}
+
+void ImplicitBoolConversionCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);
+ Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);
+}
+
+void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
+ auto ExceptionCases =
+ expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
+ has(ignoringImplicit(
+ memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
+ hasParent(explicitCastExpr())));
+ auto ImplicitCastFromBool = implicitCastExpr(
+ anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
+ // Prior to C++11 cast from bool literal to pointer was allowed.
+ allOf(anyOf(hasCastKind(CK_NullToPointer),
+ hasCastKind(CK_NullToMemberPointer)),
+ hasSourceExpression(cxxBoolLiteral()))),
+ hasSourceExpression(expr(hasType(booleanType()))),
+ unless(ExceptionCases));
+ auto BoolXor =
+ binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool),
+ hasRHS(ImplicitCastFromBool));
+ Finder->addMatcher(
+ traverse(TK_AsIs,
+ implicitCastExpr(
+ anyOf(hasCastKind(CK_IntegralToBoolean),
+ hasCastKind(CK_FloatingToBoolean),
+ hasCastKind(CK_PointerToBoolean),
+ hasCastKind(CK_MemberPointerToBoolean)),
+ // Exclude case of using if or while statements with variable
+ // declaration, e.g.:
+ // if (int var = functionCall()) {}
+ unless(hasParent(
+ stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
+ // Exclude cases common to implicit cast to and from bool.
+ unless(ExceptionCases), unless(has(BoolXor)),
+ // Retrieve also parent statement, to check if we need
+ // additional parens in replacement.
+ anyOf(hasParent(stmt().bind("parentStmt")), anything()),
+ unless(isInTemplateInstantiation()),
+ unless(hasAncestor(functionTemplateDecl())))
+ .bind("implicitCastToBool")),
+ this);
+
+ auto BoolComparison = binaryOperator(hasAnyOperatorName("==", "!="),
+ hasLHS(ImplicitCastFromBool),
+ hasRHS(ImplicitCastFromBool));
+ auto BoolOpAssignment = binaryOperator(hasAnyOperatorName("|=", "&="),
+ hasLHS(expr(hasType(booleanType()))));
+ auto BitfieldAssignment = binaryOperator(
+ hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
+ auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
+ withInitializer(equalsBoundNode("implicitCastFromBool")),
+ forField(hasBitWidth(1)))));
+ Finder->addMatcher(
+ traverse(
+ TK_AsIs,
+ implicitCastExpr(
+ ImplicitCastFromBool,
+ // Exclude comparisons of bools, as they are always cast to
+ // integers in such context:
+ // bool_expr_a == bool_expr_b
+ // bool_expr_a != bool_expr_b
+ unless(hasParent(
+ binaryOperator(anyOf(BoolComparison, BoolXor,
+ BoolOpAssignment, BitfieldAssignment)))),
+ implicitCastExpr().bind("implicitCastFromBool"),
+ unless(hasParent(BitfieldConstruct)),
+ // Check also for nested casts, for example: bool -> int -> float.
+ anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
+ anything()),
+ unless(isInTemplateInstantiation()),
+ unless(hasAncestor(functionTemplateDecl())))),
+ this);
+}
+
+void ImplicitBoolConversionCheck::check(
+ const MatchFinder::MatchResult &Result) {
+
+ if (const auto *CastToBool =
+ Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
+ const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
+ return handleCastToBool(CastToBool, Parent, *Result.Context);
+ }
+
+ if (const auto *CastFromBool =
+ Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
+ const auto *NextImplicitCast =
+ Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
+ return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
+ }
+}
+
+void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
+ const Stmt *Parent,
+ ASTContext &Context) {
+ if (AllowPointerConditions &&
+ (Cast->getCastKind() == CK_PointerToBoolean ||
+ Cast->getCastKind() == CK_MemberPointerToBoolean) &&
+ isCastAllowedInCondition(Cast, Context)) {
+ return;
+ }
+
+ if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
+ isCastAllowedInCondition(Cast, Context)) {
+ return;
+ }
+
+ auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> bool")
+ << Cast->getSubExpr()->getType();
+
+ StringRef EquivalentLiteral =
+ getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
+ if (!EquivalentLiteral.empty()) {
+ Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
+ } else {
+ fixGenericExprCastToBool(Diag, Cast, Parent, Context);
+ }
+}
+
+void ImplicitBoolConversionCheck::handleCastFromBool(
+ const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
+ ASTContext &Context) {
+ QualType DestType =
+ NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
+ auto Diag = diag(Cast->getBeginLoc(), "implicit conversion bool -> %0")
+ << DestType;
+
+ if (const auto *BoolLiteral =
+ dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
+ Diag << tooling::fixit::createReplacement(
+ *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
+ } else {
+ fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.h
new file mode 100644
index 0000000000..67fd3ff5c3
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ImplicitBoolConversionCheck.h
@@ -0,0 +1,43 @@
+//===--- ImplicitBoolConversionCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks for use of implicit bool conversions in expressions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/implicit-bool-conversion.html
+class ImplicitBoolConversionCheck : public ClangTidyCheck {
+public:
+ ImplicitBoolConversionCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.Bool;
+ }
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void handleCastToBool(const ImplicitCastExpr *CastExpression,
+ const Stmt *ParentStatement, ASTContext &Context);
+ void handleCastFromBool(const ImplicitCastExpr *CastExpression,
+ const ImplicitCastExpr *FurtherImplicitCastExpression,
+ ASTContext &Context);
+
+ const bool AllowIntegerConditions;
+ const bool AllowPointerConditions;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp
new file mode 100644
index 0000000000..13031e7cc1
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp
@@ -0,0 +1,351 @@
+//===--- InconsistentDeclarationParameterNameCheck.cpp - clang-tidy-------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InconsistentDeclarationParameterNameCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+
+#include <functional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+AST_MATCHER(FunctionDecl, hasOtherDeclarations) {
+ auto It = Node.redecls_begin();
+ auto EndIt = Node.redecls_end();
+
+ if (It == EndIt)
+ return false;
+
+ ++It;
+ return It != EndIt;
+}
+
+struct DifferingParamInfo {
+ DifferingParamInfo(StringRef SourceName, StringRef OtherName,
+ SourceRange OtherNameRange, bool GenerateFixItHint)
+ : SourceName(SourceName), OtherName(OtherName),
+ OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}
+
+ StringRef SourceName;
+ StringRef OtherName;
+ SourceRange OtherNameRange;
+ bool GenerateFixItHint;
+};
+
+using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
+
+struct InconsistentDeclarationInfo {
+ InconsistentDeclarationInfo(SourceLocation DeclarationLocation,
+ DifferingParamsContainer &&DifferingParams)
+ : DeclarationLocation(DeclarationLocation),
+ DifferingParams(std::move(DifferingParams)) {}
+
+ SourceLocation DeclarationLocation;
+ DifferingParamsContainer DifferingParams;
+};
+
+using InconsistentDeclarationsContainer =
+ llvm::SmallVector<InconsistentDeclarationInfo, 2>;
+
+bool checkIfFixItHintIsApplicable(
+ const FunctionDecl *ParameterSourceDeclaration,
+ const ParmVarDecl *SourceParam, const FunctionDecl *OriginalDeclaration) {
+ // Assumptions with regard to function declarations/definition:
+ // * If both function declaration and definition are seen, assume that
+ // definition is most up-to-date, and use it to generate replacements.
+ // * If only function declarations are seen, there is no easy way to tell
+ // which is up-to-date and which is not, so don't do anything.
+ // TODO: This may be changed later, but for now it seems the reasonable
+ // solution.
+ if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
+ return false;
+
+ // Assumption: if parameter is not referenced in function definition body, it
+ // may indicate that it's outdated, so don't touch it.
+ if (!SourceParam->isReferenced())
+ return false;
+
+ // In case there is the primary template definition and (possibly several)
+ // template specializations (and each with possibly several redeclarations),
+ // it is not at all clear what to change.
+ if (OriginalDeclaration->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization)
+ return false;
+
+ // Other cases seem OK to allow replacements.
+ return true;
+}
+
+bool nameMatch(StringRef L, StringRef R, bool Strict) {
+ if (Strict)
+ return L.empty() || R.empty() || L == R;
+ // We allow two names if one is a prefix/suffix of the other, ignoring case.
+ // Important special case: this is true if either parameter has no name!
+ return L.startswith_insensitive(R) || R.startswith_insensitive(L) ||
+ L.endswith_insensitive(R) || R.endswith_insensitive(L);
+}
+
+DifferingParamsContainer
+findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,
+ const FunctionDecl *OtherDeclaration,
+ const FunctionDecl *OriginalDeclaration,
+ bool Strict) {
+ DifferingParamsContainer DifferingParams;
+
+ const auto *SourceParamIt = ParameterSourceDeclaration->param_begin();
+ const auto *OtherParamIt = OtherDeclaration->param_begin();
+
+ while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
+ OtherParamIt != OtherDeclaration->param_end()) {
+ auto SourceParamName = (*SourceParamIt)->getName();
+ auto OtherParamName = (*OtherParamIt)->getName();
+
+ // FIXME: Provide a way to extract commented out parameter name from comment
+ // next to it.
+ if (!nameMatch(SourceParamName, OtherParamName, Strict)) {
+ SourceRange OtherParamNameRange =
+ DeclarationNameInfo((*OtherParamIt)->getDeclName(),
+ (*OtherParamIt)->getLocation())
+ .getSourceRange();
+
+ bool GenerateFixItHint = checkIfFixItHintIsApplicable(
+ ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
+
+ DifferingParams.emplace_back(SourceParamName, OtherParamName,
+ OtherParamNameRange, GenerateFixItHint);
+ }
+
+ ++SourceParamIt;
+ ++OtherParamIt;
+ }
+
+ return DifferingParams;
+}
+
+InconsistentDeclarationsContainer
+findInconsistentDeclarations(const FunctionDecl *OriginalDeclaration,
+ const FunctionDecl *ParameterSourceDeclaration,
+ SourceManager &SM, bool Strict) {
+ InconsistentDeclarationsContainer InconsistentDeclarations;
+ SourceLocation ParameterSourceLocation =
+ ParameterSourceDeclaration->getLocation();
+
+ for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
+ SourceLocation OtherLocation = OtherDeclaration->getLocation();
+ if (OtherLocation != ParameterSourceLocation) { // Skip self.
+ DifferingParamsContainer DifferingParams =
+ findDifferingParamsInDeclaration(ParameterSourceDeclaration,
+ OtherDeclaration,
+ OriginalDeclaration, Strict);
+ if (!DifferingParams.empty()) {
+ InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
+ std::move(DifferingParams));
+ }
+ }
+ }
+
+ // Sort in order of appearance in translation unit to generate clear
+ // diagnostics.
+ llvm::sort(InconsistentDeclarations,
+ [&SM](const InconsistentDeclarationInfo &Info1,
+ const InconsistentDeclarationInfo &Info2) {
+ return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
+ Info2.DeclarationLocation);
+ });
+ return InconsistentDeclarations;
+}
+
+const FunctionDecl *
+getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
+ const FunctionTemplateDecl *PrimaryTemplate =
+ OriginalDeclaration->getPrimaryTemplate();
+ if (PrimaryTemplate != nullptr) {
+ // In case of template specializations, use primary template declaration as
+ // the source of parameter names.
+ return PrimaryTemplate->getTemplatedDecl();
+ }
+
+ // In other cases, try to change to function definition, if available.
+
+ if (OriginalDeclaration->isThisDeclarationADefinition())
+ return OriginalDeclaration;
+
+ for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
+ if (OtherDeclaration->isThisDeclarationADefinition()) {
+ return OtherDeclaration;
+ }
+ }
+
+ // No definition found, so return original declaration.
+ return OriginalDeclaration;
+}
+
+std::string joinParameterNames(
+ const DifferingParamsContainer &DifferingParams,
+ llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
+ llvm::SmallString<40> Str;
+ bool First = true;
+ for (const DifferingParamInfo &ParamInfo : DifferingParams) {
+ if (First)
+ First = false;
+ else
+ Str += ", ";
+ Str.append({"'", ChooseParamName(ParamInfo), "'"});
+ }
+ return std::string(Str);
+}
+
+void formatDifferingParamsDiagnostic(
+ InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,
+ StringRef OtherDeclarationDescription,
+ const DifferingParamsContainer &DifferingParams) {
+ auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {
+ return ParamInfo.OtherName;
+ };
+ auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {
+ return ParamInfo.SourceName;
+ };
+
+ auto ParamDiag =
+ Check->diag(Location,
+ "differing parameters are named here: (%0), in %1: (%2)",
+ DiagnosticIDs::Level::Note)
+ << joinParameterNames(DifferingParams, ChooseOtherName)
+ << OtherDeclarationDescription
+ << joinParameterNames(DifferingParams, ChooseSourceName);
+
+ for (const DifferingParamInfo &ParamInfo : DifferingParams) {
+ if (ParamInfo.GenerateFixItHint) {
+ ParamDiag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
+ ParamInfo.SourceName);
+ }
+ }
+}
+
+void formatDiagnosticsForDeclarations(
+ InconsistentDeclarationParameterNameCheck *Check,
+ const FunctionDecl *ParameterSourceDeclaration,
+ const FunctionDecl *OriginalDeclaration,
+ const InconsistentDeclarationsContainer &InconsistentDeclarations) {
+ Check->diag(
+ OriginalDeclaration->getLocation(),
+ "function %q0 has %1 other declaration%s1 with different parameter names")
+ << OriginalDeclaration
+ << static_cast<int>(InconsistentDeclarations.size());
+ int Count = 1;
+ for (const InconsistentDeclarationInfo &InconsistentDeclaration :
+ InconsistentDeclarations) {
+ Check->diag(InconsistentDeclaration.DeclarationLocation,
+ "the %ordinal0 inconsistent declaration seen here",
+ DiagnosticIDs::Level::Note)
+ << Count;
+
+ formatDifferingParamsDiagnostic(
+ Check, InconsistentDeclaration.DeclarationLocation,
+ "the other declaration", InconsistentDeclaration.DifferingParams);
+
+ ++Count;
+ }
+}
+
+void formatDiagnostics(
+ InconsistentDeclarationParameterNameCheck *Check,
+ const FunctionDecl *ParameterSourceDeclaration,
+ const FunctionDecl *OriginalDeclaration,
+ const InconsistentDeclarationsContainer &InconsistentDeclarations,
+ StringRef FunctionDescription, StringRef ParameterSourceDescription) {
+ for (const InconsistentDeclarationInfo &InconsistentDeclaration :
+ InconsistentDeclarations) {
+ Check->diag(InconsistentDeclaration.DeclarationLocation,
+ "%0 %q1 has a %2 with different parameter names")
+ << FunctionDescription << OriginalDeclaration
+ << ParameterSourceDescription;
+
+ Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
+ DiagnosticIDs::Level::Note)
+ << ParameterSourceDescription;
+
+ formatDifferingParamsDiagnostic(
+ Check, InconsistentDeclaration.DeclarationLocation,
+ ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
+ }
+}
+
+} // anonymous namespace
+
+void InconsistentDeclarationParameterNameCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+ Options.store(Opts, "Strict", Strict);
+}
+
+void InconsistentDeclarationParameterNameCheck::registerMatchers(
+ MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(hasOtherDeclarations()).bind("functionDecl"),
+ this);
+}
+
+void InconsistentDeclarationParameterNameCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *OriginalDeclaration =
+ Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
+
+ if (VisitedDeclarations.contains(OriginalDeclaration))
+ return; // Avoid multiple warnings.
+
+ const FunctionDecl *ParameterSourceDeclaration =
+ getParameterSourceDeclaration(OriginalDeclaration);
+
+ InconsistentDeclarationsContainer InconsistentDeclarations =
+ findInconsistentDeclarations(OriginalDeclaration,
+ ParameterSourceDeclaration,
+ *Result.SourceManager, Strict);
+ if (InconsistentDeclarations.empty()) {
+ // Avoid unnecessary further visits.
+ markRedeclarationsAsVisited(OriginalDeclaration);
+ return;
+ }
+
+ SourceLocation StartLoc = OriginalDeclaration->getBeginLoc();
+ if (StartLoc.isMacroID() && IgnoreMacros) {
+ markRedeclarationsAsVisited(OriginalDeclaration);
+ return;
+ }
+
+ if (OriginalDeclaration->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization) {
+ formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
+ InconsistentDeclarations,
+ "function template specialization",
+ "primary template declaration");
+ } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
+ formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
+ InconsistentDeclarations, "function", "definition");
+ } else {
+ formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
+ OriginalDeclaration,
+ InconsistentDeclarations);
+ }
+
+ markRedeclarationsAsVisited(OriginalDeclaration);
+}
+
+void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
+ const FunctionDecl *OriginalDeclaration) {
+ for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
+ VisitedDeclarations.insert(Redecl);
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h
new file mode 100644
index 0000000000..11fee1e720
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h
@@ -0,0 +1,48 @@
+//===- InconsistentDeclarationParameterNameCheck.h - clang-tidy-*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
+
+#include "../ClangTidyCheck.h"
+
+#include "llvm/ADT/DenseSet.h"
+
+namespace clang::tidy::readability {
+
+/// Checks for declarations of functions which differ in parameter names.
+///
+/// For detailed documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/inconsistent-declaration-parameter-name.html
+///
+class InconsistentDeclarationParameterNameCheck : public ClangTidyCheck {
+public:
+ InconsistentDeclarationParameterNameCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
+ Strict(Options.getLocalOrGlobal("Strict", false)) {}
+
+ 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:
+ void markRedeclarationsAsVisited(const FunctionDecl *FunctionDeclaration);
+
+ llvm::DenseSet<const FunctionDecl *> VisitedDeclarations;
+ const bool IgnoreMacros;
+ const bool Strict;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.cpp
new file mode 100644
index 0000000000..97abf8d9ec
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.cpp
@@ -0,0 +1,274 @@
+//===--- IsolateDeclarationCheck.cpp - clang-tidy -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IsolateDeclarationCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <optional>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::utils::lexer;
+
+namespace clang::tidy::readability {
+
+namespace {
+AST_MATCHER(DeclStmt, isSingleDecl) { return Node.isSingleDecl(); }
+AST_MATCHER(DeclStmt, onlyDeclaresVariables) {
+ return llvm::all_of(Node.decls(), [](Decl *D) { return isa<VarDecl>(D); });
+}
+} // namespace
+
+void IsolateDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(declStmt(onlyDeclaresVariables(), unless(isSingleDecl()),
+ hasParent(compoundStmt()))
+ .bind("decl_stmt"),
+ this);
+}
+
+static SourceLocation findStartOfIndirection(SourceLocation Start,
+ int Indirections,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ assert(Indirections >= 0 && "Indirections must be non-negative");
+ if (Indirections == 0)
+ return Start;
+
+ // Note that the post-fix decrement is necessary to perform the correct
+ // number of transformations.
+ while (Indirections-- != 0) {
+ Start = findPreviousAnyTokenKind(Start, SM, LangOpts, tok::star, tok::amp);
+ if (Start.isInvalid() || Start.isMacroID())
+ return SourceLocation();
+ }
+ return Start;
+}
+
+static bool isMacroID(SourceRange R) {
+ return R.getBegin().isMacroID() || R.getEnd().isMacroID();
+}
+
+/// This function counts the number of written indirections for the given
+/// Type \p T. It does \b NOT resolve typedefs as it's a helper for lexing
+/// the source code.
+/// \see declRanges
+static int countIndirections(const Type *T, int Indirections = 0) {
+ if (T->isFunctionPointerType()) {
+ const auto *Pointee = T->getPointeeType()->castAs<FunctionType>();
+ return countIndirections(
+ Pointee->getReturnType().IgnoreParens().getTypePtr(), ++Indirections);
+ }
+
+ // Note: Do not increment the 'Indirections' because it is not yet clear
+ // if there is an indirection added in the source code of the array
+ // declaration.
+ if (const auto *AT = dyn_cast<ArrayType>(T))
+ return countIndirections(AT->getElementType().IgnoreParens().getTypePtr(),
+ Indirections);
+
+ if (isa<PointerType>(T) || isa<ReferenceType>(T))
+ return countIndirections(T->getPointeeType().IgnoreParens().getTypePtr(),
+ ++Indirections);
+
+ return Indirections;
+}
+
+static bool typeIsMemberPointer(const Type *T) {
+ if (isa<ArrayType>(T))
+ return typeIsMemberPointer(T->getArrayElementTypeNoTypeQual());
+
+ if ((isa<PointerType>(T) || isa<ReferenceType>(T)) &&
+ isa<PointerType>(T->getPointeeType()))
+ return typeIsMemberPointer(T->getPointeeType().getTypePtr());
+
+ return isa<MemberPointerType>(T);
+}
+
+/// This function tries to extract the SourceRanges that make up all
+/// declarations in this \c DeclStmt.
+///
+/// The resulting vector has the structure {UnderlyingType, Decl1, Decl2, ...}.
+/// Each \c SourceRange is of the form [Begin, End).
+/// If any of the create ranges is invalid or in a macro the result will be
+/// \c None.
+/// If the \c DeclStmt contains only one declaration, the result is \c None.
+/// If the \c DeclStmt contains declarations other than \c VarDecl the result
+/// is \c None.
+///
+/// \code
+/// int * ptr1 = nullptr, value = 42;
+/// // [ ][ ] [ ] - The ranges here are inclusive
+/// \endcode
+/// \todo Generalize this function to take other declarations than \c VarDecl.
+static std::optional<std::vector<SourceRange>>
+declRanges(const DeclStmt *DS, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ std::size_t DeclCount = std::distance(DS->decl_begin(), DS->decl_end());
+ if (DeclCount < 2)
+ return std::nullopt;
+
+ if (rangeContainsExpansionsOrDirectives(DS->getSourceRange(), SM, LangOpts))
+ return std::nullopt;
+
+ // The initial type of the declaration and each declaration has it's own
+ // slice. This is necessary, because pointers and references bind only
+ // to the local variable and not to all variables in the declaration.
+ // Example: 'int *pointer, value = 42;'
+ std::vector<SourceRange> Slices;
+ Slices.reserve(DeclCount + 1);
+
+ // Calculate the first slice, for now only variables are handled but in the
+ // future this should be relaxed and support various kinds of declarations.
+ const auto *FirstDecl = dyn_cast<VarDecl>(*DS->decl_begin());
+
+ if (FirstDecl == nullptr)
+ return std::nullopt;
+
+ // FIXME: Member pointers are not transformed correctly right now, that's
+ // why they are treated as problematic here.
+ if (typeIsMemberPointer(FirstDecl->getType().IgnoreParens().getTypePtr()))
+ return std::nullopt;
+
+ // Consider the following case: 'int * pointer, value = 42;'
+ // Created slices (inclusive) [ ][ ] [ ]
+ // Because 'getBeginLoc' points to the start of the variable *name*, the
+ // location of the pointer must be determined separately.
+ SourceLocation Start = findStartOfIndirection(
+ FirstDecl->getLocation(),
+ countIndirections(FirstDecl->getType().IgnoreParens().getTypePtr()), SM,
+ LangOpts);
+
+ // Fix function-pointer declarations that have a '(' in front of the
+ // pointer.
+ // Example: 'void (*f2)(int), (*g2)(int, float) = gg;'
+ // Slices: [ ][ ] [ ]
+ if (FirstDecl->getType()->isFunctionPointerType())
+ Start = findPreviousTokenKind(Start, SM, LangOpts, tok::l_paren);
+
+ // It is possible that a declarator is wrapped with parens.
+ // Example: 'float (((*f_ptr2)))[42], *f_ptr3, ((f_value2)) = 42.f;'
+ // The slice for the type-part must not contain these parens. Consequently
+ // 'Start' is moved to the most left paren if there are parens.
+ while (true) {
+ if (Start.isInvalid() || Start.isMacroID())
+ break;
+
+ Token T = getPreviousToken(Start, SM, LangOpts);
+ if (T.is(tok::l_paren)) {
+ Start = findPreviousTokenStart(Start, SM, LangOpts);
+ continue;
+ }
+ break;
+ }
+
+ SourceRange DeclRange(DS->getBeginLoc(), Start);
+ if (DeclRange.isInvalid() || isMacroID(DeclRange))
+ return std::nullopt;
+
+ // The first slice, that is prepended to every isolated declaration, is
+ // created.
+ Slices.emplace_back(DeclRange);
+
+ // Create all following slices that each declare a variable.
+ SourceLocation DeclBegin = Start;
+ for (const auto &Decl : DS->decls()) {
+ const auto *CurrentDecl = cast<VarDecl>(Decl);
+
+ // FIXME: Member pointers are not transformed correctly right now, that's
+ // why they are treated as problematic here.
+ if (typeIsMemberPointer(CurrentDecl->getType().IgnoreParens().getTypePtr()))
+ return std::nullopt;
+
+ SourceLocation DeclEnd =
+ CurrentDecl->hasInit()
+ ? findNextTerminator(CurrentDecl->getInit()->getEndLoc(), SM,
+ LangOpts)
+ : findNextTerminator(CurrentDecl->getEndLoc(), SM, LangOpts);
+
+ SourceRange VarNameRange(DeclBegin, DeclEnd);
+ if (VarNameRange.isInvalid() || isMacroID(VarNameRange))
+ return std::nullopt;
+
+ Slices.emplace_back(VarNameRange);
+ DeclBegin = DeclEnd.getLocWithOffset(1);
+ }
+ return Slices;
+}
+
+static std::optional<std::vector<StringRef>>
+collectSourceRanges(llvm::ArrayRef<SourceRange> Ranges, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ std::vector<StringRef> Snippets;
+ Snippets.reserve(Ranges.size());
+
+ for (const auto &Range : Ranges) {
+ CharSourceRange CharRange = Lexer::getAsCharRange(
+ CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()), SM,
+ LangOpts);
+
+ if (CharRange.isInvalid())
+ return std::nullopt;
+
+ bool InvalidText = false;
+ StringRef Snippet =
+ Lexer::getSourceText(CharRange, SM, LangOpts, &InvalidText);
+
+ if (InvalidText)
+ return std::nullopt;
+
+ Snippets.emplace_back(Snippet);
+ }
+
+ return Snippets;
+}
+
+/// Expects a vector {TypeSnippet, Firstdecl, SecondDecl, ...}.
+static std::vector<std::string>
+createIsolatedDecls(llvm::ArrayRef<StringRef> Snippets) {
+ // The first section is the type snippet, which does not make a decl itself.
+ assert(Snippets.size() > 2 && "Not enough snippets to create isolated decls");
+ std::vector<std::string> Decls(Snippets.size() - 1);
+
+ for (std::size_t I = 1; I < Snippets.size(); ++I)
+ Decls[I - 1] = Twine(Snippets[0])
+ .concat(Snippets[0].endswith(" ") ? "" : " ")
+ .concat(Snippets[I].ltrim())
+ .concat(";")
+ .str();
+
+ return Decls;
+}
+
+void IsolateDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *WholeDecl = Result.Nodes.getNodeAs<DeclStmt>("decl_stmt");
+
+ auto Diag =
+ diag(WholeDecl->getBeginLoc(),
+ "multiple declarations in a single statement reduces readability");
+
+ std::optional<std::vector<SourceRange>> PotentialRanges =
+ declRanges(WholeDecl, *Result.SourceManager, getLangOpts());
+ if (!PotentialRanges)
+ return;
+
+ std::optional<std::vector<StringRef>> PotentialSnippets = collectSourceRanges(
+ *PotentialRanges, *Result.SourceManager, getLangOpts());
+
+ if (!PotentialSnippets)
+ return;
+
+ std::vector<std::string> NewDecls = createIsolatedDecls(*PotentialSnippets);
+ std::string Replacement = llvm::join(
+ NewDecls,
+ (Twine("\n") + Lexer::getIndentationForLine(WholeDecl->getBeginLoc(),
+ *Result.SourceManager))
+ .str());
+
+ Diag << FixItHint::CreateReplacement(WholeDecl->getSourceRange(),
+ Replacement);
+}
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.h
new file mode 100644
index 0000000000..63e37a48ca
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/IsolateDeclarationCheck.h
@@ -0,0 +1,31 @@
+//===--- IsolateDeclarationCheck.h - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ISOLATEDECLCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ISOLATEDECLCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// This check diagnoses all DeclStmt's declaring more than one variable and
+/// tries to refactor the code to one statement per declaration.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/isolate-declaration.html
+class IsolateDeclarationCheck : public ClangTidyCheck {
+public:
+ IsolateDeclarationCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ISOLATEDECLCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.cpp
new file mode 100644
index 0000000000..64940ede8e
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.cpp
@@ -0,0 +1,228 @@
+//===--- MagicNumbersCheck.cpp - clang-tidy-------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A checker for magic numbers: integer or floating point literals embedded
+// in the code, outside the definition of a constant or an enumeration.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MagicNumbersCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+
+static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
+ const DynTypedNode &Node) {
+
+ const auto *AsDecl = Node.get<DeclaratorDecl>();
+ if (AsDecl) {
+ if (AsDecl->getType().isConstQualified())
+ return true;
+
+ return AsDecl->isImplicit();
+ }
+
+ if (Node.get<EnumConstantDecl>())
+ return true;
+
+ return llvm::any_of(Result.Context->getParents(Node),
+ [&Result](const DynTypedNode &Parent) {
+ return isUsedToInitializeAConstant(Result, Parent);
+ });
+}
+
+static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result,
+ const DynTypedNode &Node) {
+ const auto *AsFieldDecl = Node.get<FieldDecl>();
+ if (AsFieldDecl && AsFieldDecl->isBitField())
+ return true;
+
+ return llvm::any_of(Result.Context->getParents(Node),
+ [&Result](const DynTypedNode &Parent) {
+ return isUsedToDefineABitField(Result, Parent);
+ });
+}
+
+namespace tidy::readability {
+
+const char DefaultIgnoredIntegerValues[] = "1;2;3;4;";
+const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;";
+
+MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreAllFloatingPointValues(
+ Options.get("IgnoreAllFloatingPointValues", false)),
+ IgnoreBitFieldsWidths(Options.get("IgnoreBitFieldsWidths", true)),
+ IgnorePowersOf2IntegerValues(
+ Options.get("IgnorePowersOf2IntegerValues", false)),
+ RawIgnoredIntegerValues(
+ Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)),
+ RawIgnoredFloatingPointValues(Options.get(
+ "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)) {
+ // Process the set of ignored integer values.
+ const std::vector<StringRef> IgnoredIntegerValuesInput =
+ utils::options::parseStringList(RawIgnoredIntegerValues);
+ IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());
+ llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),
+ [](StringRef Value) {
+ int64_t Res;
+ Value.getAsInteger(10, Res);
+ return Res;
+ });
+ llvm::sort(IgnoredIntegerValues);
+
+ if (!IgnoreAllFloatingPointValues) {
+ // Process the set of ignored floating point values.
+ const std::vector<StringRef> IgnoredFloatingPointValuesInput =
+ utils::options::parseStringList(RawIgnoredFloatingPointValues);
+ IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());
+ IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());
+ for (const auto &InputValue : IgnoredFloatingPointValuesInput) {
+ llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());
+ auto StatusOrErr =
+ FloatValue.convertFromString(InputValue, DefaultRoundingMode);
+ assert(StatusOrErr && "Invalid floating point representation");
+ consumeError(StatusOrErr.takeError());
+ IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());
+
+ llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());
+ StatusOrErr =
+ DoubleValue.convertFromString(InputValue, DefaultRoundingMode);
+ assert(StatusOrErr && "Invalid floating point representation");
+ consumeError(StatusOrErr.takeError());
+ IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());
+ }
+ llvm::sort(IgnoredFloatingPointValues);
+ llvm::sort(IgnoredDoublePointValues);
+ }
+}
+
+void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreAllFloatingPointValues",
+ IgnoreAllFloatingPointValues);
+ Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);
+ Options.store(Opts, "IgnorePowersOf2IntegerValues",
+ IgnorePowersOf2IntegerValues);
+ Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);
+ Options.store(Opts, "IgnoredFloatingPointValues",
+ RawIgnoredFloatingPointValues);
+}
+
+void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(integerLiteral().bind("integer"), this);
+ if (!IgnoreAllFloatingPointValues)
+ Finder->addMatcher(floatLiteral().bind("float"), this);
+}
+
+void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
+
+ TraversalKindScope RAII(*Result.Context, TK_AsIs);
+
+ checkBoundMatch<IntegerLiteral>(Result, "integer");
+ checkBoundMatch<FloatingLiteral>(Result, "float");
+}
+
+bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,
+ const Expr &ExprResult) const {
+ return llvm::any_of(
+ Result.Context->getParents(ExprResult),
+ [&Result](const DynTypedNode &Parent) {
+ if (isUsedToInitializeAConstant(Result, Parent))
+ return true;
+
+ // Ignore this instance, because this matches an
+ // expanded class enumeration value.
+ if (Parent.get<CStyleCastExpr>() &&
+ llvm::any_of(
+ Result.Context->getParents(Parent),
+ [](const DynTypedNode &GrandParent) {
+ return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
+ nullptr;
+ }))
+ return true;
+
+ // Ignore this instance, because this match reports the
+ // location where the template is defined, not where it
+ // is instantiated.
+ if (Parent.get<SubstNonTypeTemplateParmExpr>())
+ return true;
+
+ // Don't warn on string user defined literals:
+ // std::string s = "Hello World"s;
+ if (const auto *UDL = Parent.get<UserDefinedLiteral>())
+ if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)
+ return true;
+
+ return false;
+ });
+}
+
+bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
+ const llvm::APInt IntValue = Literal->getValue();
+ const int64_t Value = IntValue.getZExtValue();
+ if (Value == 0)
+ return true;
+
+ if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
+ return true;
+
+ return std::binary_search(IgnoredIntegerValues.begin(),
+ IgnoredIntegerValues.end(), Value);
+}
+
+bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {
+ const llvm::APFloat FloatValue = Literal->getValue();
+ if (FloatValue.isZero())
+ return true;
+
+ if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {
+ const float Value = FloatValue.convertToFloat();
+ return std::binary_search(IgnoredFloatingPointValues.begin(),
+ IgnoredFloatingPointValues.end(), Value);
+ }
+
+ if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {
+ const double Value = FloatValue.convertToDouble();
+ return std::binary_search(IgnoredDoublePointValues.begin(),
+ IgnoredDoublePointValues.end(), Value);
+ }
+
+ return false;
+}
+
+bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,
+ const IntegerLiteral *Literal) const {
+ const std::pair<FileID, unsigned> FileOffset =
+ SourceManager->getDecomposedLoc(Literal->getLocation());
+ if (FileOffset.first.isInvalid())
+ return false;
+
+ const StringRef BufferIdentifier =
+ SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();
+
+ return BufferIdentifier.empty();
+}
+
+bool MagicNumbersCheck::isBitFieldWidth(
+ const clang::ast_matchers::MatchFinder::MatchResult &Result,
+ const IntegerLiteral &Literal) const {
+ return IgnoreBitFieldsWidths &&
+ llvm::any_of(Result.Context->getParents(Literal),
+ [&Result](const DynTypedNode &Parent) {
+ return isUsedToDefineABitField(Result, Parent);
+ });
+}
+
+} // namespace tidy::readability
+} // namespace clang
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.h
new file mode 100644
index 0000000000..ba39f17f4d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MagicNumbersCheck.h
@@ -0,0 +1,105 @@
+//===--- MagicNumbersCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/Lex/Lexer.h"
+#include <llvm/ADT/APFloat.h>
+#include <llvm/ADT/SmallVector.h>
+
+namespace clang::tidy::readability {
+
+/// Detects magic numbers, integer and floating point literals embedded in code.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/magic-numbers.html
+class MagicNumbersCheck : public ClangTidyCheck {
+public:
+ MagicNumbersCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool isConstant(const clang::ast_matchers::MatchFinder::MatchResult &Result,
+ const clang::Expr &ExprResult) const;
+
+ bool isIgnoredValue(const IntegerLiteral *Literal) const;
+ bool isIgnoredValue(const FloatingLiteral *Literal) const;
+
+ bool isSyntheticValue(const clang::SourceManager *,
+ const FloatingLiteral *) const {
+ return false;
+ }
+ bool isSyntheticValue(const clang::SourceManager *SourceManager,
+ const IntegerLiteral *Literal) const;
+
+ bool isBitFieldWidth(const clang::ast_matchers::MatchFinder::MatchResult &,
+ const FloatingLiteral &) const {
+ return false;
+ }
+
+ bool isBitFieldWidth(const clang::ast_matchers::MatchFinder::MatchResult &Result,
+ const IntegerLiteral &Literal) const;
+
+ template <typename L>
+ void checkBoundMatch(const ast_matchers::MatchFinder::MatchResult &Result,
+ const char *BoundName) {
+ const L *MatchedLiteral = Result.Nodes.getNodeAs<L>(BoundName);
+ if (!MatchedLiteral)
+ return;
+
+ if (Result.SourceManager->isMacroBodyExpansion(
+ MatchedLiteral->getLocation()))
+ return;
+
+ if (isIgnoredValue(MatchedLiteral))
+ return;
+
+ if (isConstant(Result, *MatchedLiteral))
+ return;
+
+ if (isSyntheticValue(Result.SourceManager, MatchedLiteral))
+ return;
+
+ if (isBitFieldWidth(Result, *MatchedLiteral))
+ return;
+
+ const StringRef LiteralSourceText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(MatchedLiteral->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+
+ diag(MatchedLiteral->getLocation(),
+ "%0 is a magic number; consider replacing it with a named constant")
+ << LiteralSourceText;
+ }
+
+ const bool IgnoreAllFloatingPointValues;
+ const bool IgnoreBitFieldsWidths;
+ const bool IgnorePowersOf2IntegerValues;
+ const StringRef RawIgnoredIntegerValues;
+ const StringRef RawIgnoredFloatingPointValues;
+
+ constexpr static unsigned SensibleNumberOfMagicValueExceptions = 16;
+
+ constexpr static llvm::APFloat::roundingMode DefaultRoundingMode =
+ llvm::APFloat::rmNearestTiesToEven;
+
+ llvm::SmallVector<int64_t, SensibleNumberOfMagicValueExceptions>
+ IgnoredIntegerValues;
+ llvm::SmallVector<float, SensibleNumberOfMagicValueExceptions>
+ IgnoredFloatingPointValues;
+ llvm::SmallVector<double, SensibleNumberOfMagicValueExceptions>
+ IgnoredDoublePointValues;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp
new file mode 100644
index 0000000000..2d6a469252
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp
@@ -0,0 +1,268 @@
+//===--- MakeMemberFunctionConstCheck.cpp - clang-tidy --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MakeMemberFunctionConstCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
+
+AST_MATCHER(CXXMethodDecl, hasTrivialBody) { return Node.hasTrivialBody(); }
+
+AST_MATCHER(CXXRecordDecl, hasAnyDependentBases) {
+ return Node.hasAnyDependentBases();
+}
+
+AST_MATCHER(CXXMethodDecl, isTemplate) {
+ return Node.getTemplatedKind() != FunctionDecl::TK_NonTemplate;
+}
+
+AST_MATCHER(CXXMethodDecl, isDependentContext) {
+ return Node.isDependentContext();
+}
+
+AST_MATCHER(CXXMethodDecl, isInsideMacroDefinition) {
+ const ASTContext &Ctxt = Finder->getASTContext();
+ return clang::Lexer::makeFileCharRange(
+ clang::CharSourceRange::getCharRange(
+ Node.getTypeSourceInfo()->getTypeLoc().getSourceRange()),
+ Ctxt.getSourceManager(), Ctxt.getLangOpts())
+ .isInvalid();
+}
+
+AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl,
+ ast_matchers::internal::Matcher<CXXMethodDecl>, InnerMatcher) {
+ return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder);
+}
+
+enum UsageKind { Unused, Const, NonConst };
+
+class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> {
+ ASTContext &Ctxt;
+
+public:
+ FindUsageOfThis(ASTContext &Ctxt) : Ctxt(Ctxt) {}
+ UsageKind Usage = Unused;
+
+ template <class T> const T *getParent(const Expr *E) {
+ DynTypedNodeList Parents = Ctxt.getParents(*E);
+ if (Parents.size() != 1)
+ return nullptr;
+
+ return Parents.begin()->get<T>();
+ }
+
+ const Expr *getParentExprIgnoreParens(const Expr *E) {
+ const Expr *Parent = getParent<Expr>(E);
+ while (isa_and_nonnull<ParenExpr>(Parent))
+ Parent = getParent<Expr>(Parent);
+ return Parent;
+ }
+
+ bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *) {
+ // An UnresolvedMemberExpr might resolve to a non-const non-static
+ // member function.
+ Usage = NonConst;
+ return false; // Stop traversal.
+ }
+
+ bool VisitCXXConstCastExpr(const CXXConstCastExpr *) {
+ // Workaround to support the pattern
+ // class C {
+ // const S *get() const;
+ // S* get() {
+ // return const_cast<S*>(const_cast<const C*>(this)->get());
+ // }
+ // };
+ // Here, we don't want to make the second 'get' const even though
+ // it only calls a const member function on this.
+ Usage = NonConst;
+ return false; // Stop traversal.
+ }
+
+ // Our AST is
+ // `-ImplicitCastExpr
+ // (possibly `-UnaryOperator Deref)
+ // `-CXXThisExpr 'S *' this
+ bool visitUser(const ImplicitCastExpr *Cast) {
+ if (Cast->getCastKind() != CK_NoOp)
+ return false; // Stop traversal.
+
+ // Only allow NoOp cast to 'const S' or 'const S *'.
+ QualType QT = Cast->getType();
+ if (QT->isPointerType())
+ QT = QT->getPointeeType();
+
+ if (!QT.isConstQualified())
+ return false; // Stop traversal.
+
+ const auto *Parent = getParent<Stmt>(Cast);
+ if (!Parent)
+ return false; // Stop traversal.
+
+ if (isa<ReturnStmt>(Parent))
+ return true; // return (const S*)this;
+
+ if (isa<CallExpr>(Parent))
+ return true; // use((const S*)this);
+
+ // ((const S*)this)->Member
+ if (const auto *Member = dyn_cast<MemberExpr>(Parent))
+ return visitUser(Member, /*OnConstObject=*/true);
+
+ return false; // Stop traversal.
+ }
+
+ // If OnConstObject is true, then this is a MemberExpr using
+ // a constant this, i.e. 'const S' or 'const S *'.
+ bool visitUser(const MemberExpr *Member, bool OnConstObject) {
+ if (Member->isBoundMemberFunction(Ctxt)) {
+ if (!OnConstObject || Member->getFoundDecl().getAccess() != AS_public) {
+ // Non-public non-static member functions might not preserve the
+ // logical constness. E.g. in
+ // class C {
+ // int &data() const;
+ // public:
+ // int &get() { return data(); }
+ // };
+ // get() uses a private const method, but must not be made const
+ // itself.
+ return false; // Stop traversal.
+ }
+ // Using a public non-static const member function.
+ return true;
+ }
+
+ const auto *Parent = getParentExprIgnoreParens(Member);
+
+ if (const auto *Cast = dyn_cast_or_null<ImplicitCastExpr>(Parent)) {
+ // A read access to a member is safe when the member either
+ // 1) has builtin type (a 'const int' cannot be modified),
+ // 2) or it's a public member (the pointee of a public 'int * const' can
+ // can be modified by any user of the class).
+ if (Member->getFoundDecl().getAccess() != AS_public &&
+ !Cast->getType()->isBuiltinType())
+ return false;
+
+ if (Cast->getCastKind() == CK_LValueToRValue)
+ return true;
+
+ if (Cast->getCastKind() == CK_NoOp && Cast->getType().isConstQualified())
+ return true;
+ }
+
+ if (const auto *M = dyn_cast_or_null<MemberExpr>(Parent))
+ return visitUser(M, /*OnConstObject=*/false);
+
+ return false; // Stop traversal.
+ }
+
+ bool VisitCXXThisExpr(const CXXThisExpr *E) {
+ Usage = Const;
+
+ const auto *Parent = getParentExprIgnoreParens(E);
+
+ // Look through deref of this.
+ if (const auto *UnOp = dyn_cast_or_null<UnaryOperator>(Parent)) {
+ if (UnOp->getOpcode() == UO_Deref) {
+ Parent = getParentExprIgnoreParens(UnOp);
+ }
+ }
+
+ // It's okay to
+ // return (const S*)this;
+ // use((const S*)this);
+ // ((const S*)this)->f()
+ // when 'f' is a public member function.
+ if (const auto *Cast = dyn_cast_or_null<ImplicitCastExpr>(Parent)) {
+ if (visitUser(Cast))
+ return true;
+
+ // And it's also okay to
+ // (const T)(S->t)
+ // (LValueToRValue)(S->t)
+ // when 't' is either of builtin type or a public member.
+ } else if (const auto *Member = dyn_cast_or_null<MemberExpr>(Parent)) {
+ if (visitUser(Member, /*OnConstObject=*/false))
+ return true;
+ }
+
+ // Unknown user of this.
+ Usage = NonConst;
+ return false; // Stop traversal.
+ }
+};
+
+AST_MATCHER(CXXMethodDecl, usesThisAsConst) {
+ FindUsageOfThis UsageOfThis(Finder->getASTContext());
+
+ // TraverseStmt does not modify its argument.
+ UsageOfThis.TraverseStmt(const_cast<Stmt *>(Node.getBody()));
+
+ return UsageOfThis.Usage == Const;
+}
+
+void MakeMemberFunctionConstCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ traverse(
+ TK_AsIs,
+ cxxMethodDecl(
+ isDefinition(), isUserProvided(),
+ unless(anyOf(
+ isExpansionInSystemHeader(), isVirtual(), isConst(),
+ isStatic(), hasTrivialBody(), cxxConstructorDecl(),
+ cxxDestructorDecl(), isTemplate(), isDependentContext(),
+ ofClass(anyOf(isLambda(),
+ hasAnyDependentBases()) // Method might become
+ // virtual depending on
+ // template base class.
+ ),
+ isInsideMacroDefinition(),
+ hasCanonicalDecl(isInsideMacroDefinition()))),
+ usesThisAsConst())
+ .bind("x")),
+ this);
+}
+
+static SourceLocation getConstInsertionPoint(const CXXMethodDecl *M) {
+ TypeSourceInfo *TSI = M->getTypeSourceInfo();
+ if (!TSI)
+ return {};
+
+ FunctionTypeLoc FTL =
+ TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
+ if (!FTL)
+ return {};
+
+ return FTL.getRParenLoc().getLocWithOffset(1);
+}
+
+void MakeMemberFunctionConstCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>("x");
+
+ const auto *Declaration = Definition->getCanonicalDecl();
+
+ auto Diag = diag(Definition->getLocation(), "method %0 can be made const")
+ << Definition
+ << FixItHint::CreateInsertion(getConstInsertionPoint(Definition),
+ " const");
+ if (Declaration != Definition) {
+ Diag << FixItHint::CreateInsertion(getConstInsertionPoint(Declaration),
+ " const");
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.h
new file mode 100644
index 0000000000..dc8d983327
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MakeMemberFunctionConstCheck.h
@@ -0,0 +1,33 @@
+//===--- MakeMemberFunctionConstCheck.h - clang-tidy ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAKEMEMBERFUNCTIONCONSTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAKEMEMBERFUNCTIONCONSTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds non-static member functions that can be made 'const'.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/make-member-function-const.html
+class MakeMemberFunctionConstCheck : public ClangTidyCheck {
+public:
+ MakeMemberFunctionConstCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAKEMEMBERFUNCTIONCONSTCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.cpp
new file mode 100644
index 0000000000..4c10b6f6ee
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.cpp
@@ -0,0 +1,122 @@
+//===--- MisleadingIndentationCheck.cpp - clang-tidy-----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingIndentationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static const IfStmt *getPrecedingIf(const SourceManager &SM,
+ ASTContext *Context, const IfStmt *If) {
+ auto Parents = Context->getParents(*If);
+ if (Parents.size() != 1)
+ return nullptr;
+ if (const auto *PrecedingIf = Parents[0].get<IfStmt>()) {
+ SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();
+ if (SM.getExpansionLineNumber(PreviousElseLoc) ==
+ SM.getExpansionLineNumber(If->getIfLoc()))
+ return PrecedingIf;
+ }
+ return nullptr;
+}
+
+void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
+ ASTContext *Context,
+ const IfStmt *If) {
+ SourceLocation IfLoc = If->getIfLoc();
+ SourceLocation ElseLoc = If->getElseLoc();
+
+ if (IfLoc.isMacroID() || ElseLoc.isMacroID())
+ return;
+
+ if (SM.getExpansionLineNumber(If->getThen()->getEndLoc()) ==
+ SM.getExpansionLineNumber(ElseLoc))
+ return;
+
+ // Find location of first 'if' in a 'if else if' chain.
+ for (const auto *PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf;
+ PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf))
+ IfLoc = PrecedingIf->getIfLoc();
+
+ if (SM.getExpansionColumnNumber(IfLoc) !=
+ SM.getExpansionColumnNumber(ElseLoc))
+ diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
+}
+
+void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
+ const CompoundStmt *CStmt) {
+ const static StringRef StmtNames[] = {"if", "for", "while"};
+ for (unsigned int I = 0; I < CStmt->size() - 1; I++) {
+ const Stmt *CurrentStmt = CStmt->body_begin()[I];
+ const Stmt *Inner = nullptr;
+ int StmtKind = 0;
+
+ if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
+ StmtKind = 0;
+ Inner =
+ CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
+ } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
+ StmtKind = 1;
+ Inner = CurrentFor->getBody();
+ } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
+ StmtKind = 2;
+ Inner = CurrentWhile->getBody();
+ } else {
+ continue;
+ }
+
+ if (isa<CompoundStmt>(Inner))
+ continue;
+
+ SourceLocation InnerLoc = Inner->getBeginLoc();
+ SourceLocation OuterLoc = CurrentStmt->getBeginLoc();
+
+ if (InnerLoc.isInvalid() || InnerLoc.isMacroID() || OuterLoc.isInvalid() ||
+ OuterLoc.isMacroID())
+ continue;
+
+ if (SM.getExpansionLineNumber(InnerLoc) ==
+ SM.getExpansionLineNumber(OuterLoc))
+ continue;
+
+ const Stmt *NextStmt = CStmt->body_begin()[I + 1];
+ SourceLocation NextLoc = NextStmt->getBeginLoc();
+
+ if (NextLoc.isInvalid() || NextLoc.isMacroID())
+ continue;
+
+ if (SM.getExpansionColumnNumber(InnerLoc) ==
+ SM.getExpansionColumnNumber(NextLoc)) {
+ diag(NextLoc, "misleading indentation: statement is indented too deeply");
+ diag(OuterLoc, "did you mean this line to be inside this '%0'",
+ DiagnosticIDs::Note)
+ << StmtNames[StmtKind];
+ }
+ }
+}
+
+void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this);
+ Finder->addMatcher(
+ compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
+ .bind("compound"),
+ this);
+}
+
+void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))
+ danglingElseCheck(*Result.SourceManager, Result.Context, If);
+
+ if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
+ missingBracesCheck(*Result.SourceManager, CStmt);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.h
new file mode 100644
index 0000000000..c336abbc7c
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisleadingIndentationCheck.h
@@ -0,0 +1,40 @@
+//===--- MisleadingIndentationCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks the code for dangling else, and possible misleading indentations due
+/// to missing braces. Note that this check only works as expected when the tabs
+/// or spaces are used consistently and not mixed.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/misleading-indentation.html
+class MisleadingIndentationCheck : public ClangTidyCheck {
+public:
+ MisleadingIndentationCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ void danglingElseCheck(const SourceManager &SM, ASTContext *Context,
+ const IfStmt *If);
+ void missingBracesCheck(const SourceManager &SM, const CompoundStmt *CStmt);
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.cpp
new file mode 100644
index 0000000000..328d1896ce
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.cpp
@@ -0,0 +1,53 @@
+//===--- MisplacedArrayIndexCheck.cpp - clang-tidy-------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisplacedArrayIndexCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void MisplacedArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ traverse(TK_AsIs, arraySubscriptExpr(hasLHS(hasType(isInteger())),
+ hasRHS(hasType(isAnyPointer())))
+ .bind("expr")),
+ this);
+}
+
+void MisplacedArrayIndexCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *ArraySubscriptE =
+ Result.Nodes.getNodeAs<ArraySubscriptExpr>("expr");
+
+ auto Diag = diag(ArraySubscriptE->getBeginLoc(), "confusing array subscript "
+ "expression, usually the "
+ "index is inside the []");
+
+ // Only try to fixit when LHS and RHS can be swapped directly without changing
+ // the logic.
+ const Expr *RHSE = ArraySubscriptE->getRHS()->IgnoreParenImpCasts();
+ if (!isa<StringLiteral>(RHSE) && !isa<DeclRefExpr>(RHSE) &&
+ !isa<MemberExpr>(RHSE))
+ return;
+
+ const StringRef LText = tooling::fixit::getText(
+ ArraySubscriptE->getLHS()->getSourceRange(), *Result.Context);
+ const StringRef RText = tooling::fixit::getText(
+ ArraySubscriptE->getRHS()->getSourceRange(), *Result.Context);
+
+ Diag << FixItHint::CreateReplacement(
+ ArraySubscriptE->getLHS()->getSourceRange(), RText);
+ Diag << FixItHint::CreateReplacement(
+ ArraySubscriptE->getRHS()->getSourceRange(), LText);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.h
new file mode 100644
index 0000000000..1ccd011b30
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/MisplacedArrayIndexCheck.h
@@ -0,0 +1,31 @@
+//===--- MisplacedArrayIndexCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Warn about unusual array index syntax (`index[array]` instead of
+/// `array[index]`).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/misplaced-array-index.html
+class MisplacedArrayIndexCheck : public ClangTidyCheck {
+public:
+ MisplacedArrayIndexCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.cpp
new file mode 100644
index 0000000000..f621cfe706
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.cpp
@@ -0,0 +1,118 @@
+//===--- NamedParameterCheck.cpp - 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "NamedParameterCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl().bind("decl"), this);
+}
+
+void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+ SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
+
+ // Ignore declarations without a definition if we're not dealing with an
+ // overriden method.
+ const FunctionDecl *Definition = nullptr;
+ if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
+ Function->isDeleted()) &&
+ (!isa<CXXMethodDecl>(Function) ||
+ cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
+ return;
+
+ // TODO: Handle overloads.
+ // TODO: We could check that all redeclarations use the same name for
+ // arguments in the same position.
+ for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
+ const ParmVarDecl *Parm = Function->getParamDecl(I);
+ if (Parm->isImplicit())
+ continue;
+ // Look for unnamed parameters.
+ if (!Parm->getName().empty())
+ continue;
+
+ // Don't warn on the dummy argument on post-inc and post-dec operators.
+ if ((Function->getOverloadedOperator() == OO_PlusPlus ||
+ Function->getOverloadedOperator() == OO_MinusMinus) &&
+ Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
+ continue;
+
+ // Sanity check the source locations.
+ if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
+ !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation()))
+ continue;
+
+ // Skip gmock testing::Unused parameters.
+ if (const auto *Typedef = Parm->getType()->getAs<clang::TypedefType>())
+ if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
+ continue;
+
+ // Skip std::nullptr_t.
+ if (Parm->getType().getCanonicalType()->isNullPtrType())
+ continue;
+
+ // Look for comments. We explicitly want to allow idioms like
+ // void foo(int /*unused*/)
+ const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
+ const char *End = SM.getCharacterData(Parm->getLocation());
+ StringRef Data(Begin, End - Begin);
+ if (Data.contains("/*"))
+ continue;
+
+ UnnamedParams.push_back(std::make_pair(Function, I));
+ }
+
+ // Emit only one warning per function but fixits for all unnamed parameters.
+ if (!UnnamedParams.empty()) {
+ const ParmVarDecl *FirstParm =
+ UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
+ auto D = diag(FirstParm->getLocation(),
+ "all parameters should be named in a function");
+
+ for (auto P : UnnamedParams) {
+ // Fallback to an unused marker.
+ StringRef NewName = "unused";
+
+ // If the method is overridden, try to copy the name from the base method
+ // into the overrider.
+ const auto *M = dyn_cast<CXXMethodDecl>(P.first);
+ if (M && M->size_overridden_methods() > 0) {
+ const ParmVarDecl *OtherParm =
+ (*M->begin_overridden_methods())->getParamDecl(P.second);
+ StringRef Name = OtherParm->getName();
+ if (!Name.empty())
+ NewName = Name;
+ }
+
+ // If the definition has a named parameter use that name.
+ if (Definition) {
+ const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
+ StringRef Name = DefParm->getName();
+ if (!Name.empty())
+ NewName = Name;
+ }
+
+ // Now insert the comment. Note that getLocation() points to the place
+ // where the name would be, this allows us to also get complex cases like
+ // function pointers right.
+ const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
+ D << FixItHint::CreateInsertion(Parm->getLocation(),
+ " /*" + NewName.str() + "*/");
+ }
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.h
new file mode 100644
index 0000000000..812d90ef73
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamedParameterCheck.h
@@ -0,0 +1,40 @@
+//===--- NamedParameterCheck.h - clang-tidy ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Find functions with unnamed arguments.
+///
+/// The check implements the following rule originating in the Google C++ Style
+/// Guide:
+///
+/// https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions
+///
+/// All parameters should be named, with identical names in the declaration and
+/// implementation.
+///
+/// Corresponding cpplint.py check name: 'readability/function'.
+class NamedParameterCheck : public ClangTidyCheck {
+public:
+ NamedParameterCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.cpp
new file mode 100644
index 0000000000..ad2d396311
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.cpp
@@ -0,0 +1,205 @@
+//===--- NamespaceCommentCheck.cpp - clang-tidy ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NamespaceCommentCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringExtras.h"
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
+ "namespace( +([a-zA-Z0-9_:]+))?\\.? *(\\*/)?$",
+ llvm::Regex::IgnoreCase),
+ ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)),
+ SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {}
+
+void NamespaceCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ShortNamespaceLines", ShortNamespaceLines);
+ Options.store(Opts, "SpacesBeforeComments", SpacesBeforeComments);
+}
+
+void NamespaceCommentCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(namespaceDecl().bind("namespace"), this);
+}
+
+static bool locationsInSameFile(const SourceManager &Sources,
+ SourceLocation Loc1, SourceLocation Loc2) {
+ return Loc1.isFileID() && Loc2.isFileID() &&
+ Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
+}
+
+static std::optional<std::string>
+getNamespaceNameAsWritten(SourceLocation &Loc, const SourceManager &Sources,
+ const LangOptions &LangOpts) {
+ // Loc should be at the begin of the namespace decl (usually, `namespace`
+ // token). We skip the first token right away, but in case of `inline
+ // namespace` or `namespace a::inline b` we can see both `inline` and
+ // `namespace` keywords, which we just ignore. Nested parens/squares before
+ // the opening brace can result from attributes.
+ std::string Result;
+ int Nesting = 0;
+ while (std::optional<Token> T = utils::lexer::findNextTokenSkippingComments(
+ Loc, Sources, LangOpts)) {
+ Loc = T->getLocation();
+ if (T->is(tok::l_brace))
+ break;
+
+ if (T->isOneOf(tok::l_square, tok::l_paren)) {
+ ++Nesting;
+ } else if (T->isOneOf(tok::r_square, tok::r_paren)) {
+ --Nesting;
+ } else if (Nesting == 0) {
+ if (T->is(tok::raw_identifier)) {
+ StringRef ID = T->getRawIdentifier();
+ if (ID != "namespace" && ID != "inline")
+ Result.append(std::string(ID));
+ } else if (T->is(tok::coloncolon)) {
+ Result.append("::");
+ } else { // Any other kind of token is unexpected here.
+ return std::nullopt;
+ }
+ }
+ }
+ return Result;
+}
+
+void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
+ const SourceManager &Sources = *Result.SourceManager;
+
+ // Ignore namespaces inside macros and namespaces split across files.
+ if (ND->getBeginLoc().isMacroID() ||
+ !locationsInSameFile(Sources, ND->getBeginLoc(), ND->getRBraceLoc()))
+ return;
+
+ // Don't require closing comments for namespaces spanning less than certain
+ // number of lines.
+ unsigned StartLine = Sources.getSpellingLineNumber(ND->getBeginLoc());
+ unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc());
+ if (EndLine - StartLine + 1 <= ShortNamespaceLines)
+ return;
+
+ // Find next token after the namespace closing brace.
+ SourceLocation AfterRBrace = Lexer::getLocForEndOfToken(
+ ND->getRBraceLoc(), /*Offset=*/0, Sources, getLangOpts());
+ SourceLocation Loc = AfterRBrace;
+ SourceLocation LBraceLoc = ND->getBeginLoc();
+
+ // Currently for nested namespace (n1::n2::...) the AST matcher will match foo
+ // then bar instead of a single match. So if we got a nested namespace we have
+ // to skip the next ones.
+ for (const auto &EndOfNameLocation : Ends) {
+ if (Sources.isBeforeInTranslationUnit(ND->getLocation(), EndOfNameLocation))
+ return;
+ }
+
+ std::optional<std::string> NamespaceNameAsWritten =
+ getNamespaceNameAsWritten(LBraceLoc, Sources, getLangOpts());
+ if (!NamespaceNameAsWritten)
+ return;
+
+ if (NamespaceNameAsWritten->empty() != ND->isAnonymousNamespace()) {
+ // Apparently, we didn't find the correct namespace name. Give up.
+ return;
+ }
+
+ Ends.push_back(LBraceLoc);
+
+ Token Tok;
+ // Skip whitespace until we find the next token.
+ while (Lexer::getRawToken(Loc, Tok, Sources, getLangOpts()) ||
+ Tok.is(tok::semi)) {
+ Loc = Loc.getLocWithOffset(1);
+ }
+
+ if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc))
+ return;
+
+ bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
+ // If we insert a line comment before the token in the same line, we need
+ // to insert a line break.
+ bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
+
+ SourceRange OldCommentRange(AfterRBrace, AfterRBrace);
+ std::string Message = "%0 not terminated with a closing comment";
+
+ // Try to find existing namespace closing comment on the same line.
+ if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
+ StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
+ SmallVector<StringRef, 7> Groups;
+ if (NamespaceCommentPattern.match(Comment, &Groups)) {
+ StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : "";
+ StringRef Anonymous = Groups.size() > 3 ? Groups[3] : "";
+
+ if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) ||
+ (*NamespaceNameAsWritten == NamespaceNameInComment &&
+ Anonymous.empty())) {
+ // Check if the namespace in the comment is the same.
+ // FIXME: Maybe we need a strict mode, where we always fix namespace
+ // comments with different format.
+ return;
+ }
+
+ // Otherwise we need to fix the comment.
+ NeedLineBreak = Comment.startswith("/*");
+ OldCommentRange =
+ SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
+ Message =
+ (llvm::Twine(
+ "%0 ends with a comment that refers to a wrong namespace '") +
+ NamespaceNameInComment + "'")
+ .str();
+ } else if (Comment.startswith("//")) {
+ // Assume that this is an unrecognized form of a namespace closing line
+ // comment. Replace it.
+ NeedLineBreak = false;
+ OldCommentRange =
+ SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
+ Message = "%0 ends with an unrecognized comment";
+ }
+ // If it's a block comment, just move it to the next line, as it can be
+ // multi-line or there may be other tokens behind it.
+ }
+
+ std::string NamespaceNameForDiag =
+ ND->isAnonymousNamespace() ? "anonymous namespace"
+ : ("namespace '" + *NamespaceNameAsWritten + "'");
+
+ std::string Fix(SpacesBeforeComments, ' ');
+ Fix.append("// namespace");
+ if (!ND->isAnonymousNamespace())
+ Fix.append(" ").append(*NamespaceNameAsWritten);
+ if (NeedLineBreak)
+ Fix.append("\n");
+
+ // Place diagnostic at an old comment, or closing brace if we did not have it.
+ SourceLocation DiagLoc =
+ OldCommentRange.getBegin() != OldCommentRange.getEnd()
+ ? OldCommentRange.getBegin()
+ : ND->getRBraceLoc();
+
+ diag(DiagLoc, Message) << NamespaceNameForDiag
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(OldCommentRange),
+ Fix);
+ diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note)
+ << NamespaceNameForDiag;
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.h
new file mode 100644
index 0000000000..fc73d3e06f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NamespaceCommentCheck.h
@@ -0,0 +1,42 @@
+//===--- NamespaceCommentCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang::tidy::readability {
+
+/// Checks that long namespaces have a closing comment.
+///
+/// http://llvm.org/docs/CodingStandards.html#namespace-indentation
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+class NamespaceCommentCheck : public ClangTidyCheck {
+public:
+ NamespaceCommentCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+ llvm::Regex NamespaceCommentPattern;
+ const unsigned ShortNamespaceLines;
+ const unsigned SpacesBeforeComments;
+ llvm::SmallVector<SourceLocation, 4> Ends;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.cpp
new file mode 100644
index 0000000000..974909f3ab
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.cpp
@@ -0,0 +1,234 @@
+//===--- NonConstParameterCheck.cpp - clang-tidy---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonConstParameterCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void NonConstParameterCheck::registerMatchers(MatchFinder *Finder) {
+ // Add parameters to Parameters.
+ Finder->addMatcher(parmVarDecl().bind("Parm"), this);
+
+ // C++ constructor.
+ Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this);
+
+ // Track unused parameters, there is Wunused-parameter about unused
+ // parameters.
+ Finder->addMatcher(declRefExpr().bind("Ref"), this);
+
+ // Analyse parameter usage in function.
+ Finder->addMatcher(stmt(anyOf(unaryOperator(hasAnyOperatorName("++", "--")),
+ binaryOperator(), callExpr(), returnStmt(),
+ cxxConstructExpr()))
+ .bind("Mark"),
+ this);
+ Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
+}
+
+void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>("Parm")) {
+ if (const DeclContext *D = Parm->getParentFunctionOrMethod()) {
+ if (const auto *M = dyn_cast<CXXMethodDecl>(D)) {
+ if (M->isVirtual() || M->size_overridden_methods() != 0)
+ return;
+ }
+ }
+ addParm(Parm);
+ } else if (const auto *Ctor =
+ Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor")) {
+ for (const auto *Parm : Ctor->parameters())
+ addParm(Parm);
+ for (const auto *Init : Ctor->inits())
+ markCanNotBeConst(Init->getInit(), true);
+ } else if (const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("Ref")) {
+ setReferenced(Ref);
+ } else if (const auto *S = Result.Nodes.getNodeAs<Stmt>("Mark")) {
+ if (const auto *B = dyn_cast<BinaryOperator>(S)) {
+ if (B->isAssignmentOp())
+ markCanNotBeConst(B, false);
+ } else if (const auto *CE = dyn_cast<CallExpr>(S)) {
+ // Typically, if a parameter is const then it is fine to make the data
+ // const. But sometimes the data is written even though the parameter
+ // is const. Mark all data passed by address to the function.
+ for (const auto *Arg : CE->arguments()) {
+ markCanNotBeConst(Arg->IgnoreParenCasts(), true);
+ }
+
+ // Data passed by nonconst reference should not be made const.
+ if (const FunctionDecl *FD = CE->getDirectCallee()) {
+ unsigned ArgNr = 0U;
+ for (const auto *Par : FD->parameters()) {
+ if (ArgNr >= CE->getNumArgs())
+ break;
+ const Expr *Arg = CE->getArg(ArgNr++);
+ // Is this a non constant reference parameter?
+ const Type *ParType = Par->getType().getTypePtr();
+ if (!ParType->isReferenceType() || Par->getType().isConstQualified())
+ continue;
+ markCanNotBeConst(Arg->IgnoreParenCasts(), false);
+ }
+ }
+ } else if (const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
+ for (const auto *Arg : CE->arguments()) {
+ markCanNotBeConst(Arg->IgnoreParenCasts(), true);
+ }
+ // Data passed by nonconst reference should not be made const.
+ unsigned ArgNr = 0U;
+ if (const auto *CD = CE->getConstructor()) {
+ for (const auto *Par : CD->parameters()) {
+ if (ArgNr >= CE->getNumArgs())
+ break;
+ const Expr *Arg = CE->getArg(ArgNr++);
+ // Is this a non constant reference parameter?
+ const Type *ParType = Par->getType().getTypePtr();
+ if (!ParType->isReferenceType() || Par->getType().isConstQualified())
+ continue;
+ markCanNotBeConst(Arg->IgnoreParenCasts(), false);
+ }
+ }
+ } else if (const auto *R = dyn_cast<ReturnStmt>(S)) {
+ markCanNotBeConst(R->getRetValue(), true);
+ } else if (const auto *U = dyn_cast<UnaryOperator>(S)) {
+ markCanNotBeConst(U, true);
+ }
+ } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) {
+ const QualType T = VD->getType();
+ if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
+ T->isArrayType())
+ markCanNotBeConst(VD->getInit(), true);
+ else if (T->isLValueReferenceType() &&
+ !T->getPointeeType().isConstQualified())
+ markCanNotBeConst(VD->getInit(), false);
+ }
+}
+
+void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
+ // Only add nonconst integer/float pointer parameters.
+ const QualType T = Parm->getType();
+ if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
+ !(T->getPointeeType()->isIntegerType() ||
+ T->getPointeeType()->isFloatingType()))
+ return;
+
+ if (Parameters.find(Parm) != Parameters.end())
+ return;
+
+ ParmInfo PI;
+ PI.IsReferenced = false;
+ PI.CanBeConst = true;
+ Parameters[Parm] = PI;
+}
+
+void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {
+ auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
+ if (It != Parameters.end())
+ It->second.IsReferenced = true;
+}
+
+void NonConstParameterCheck::onEndOfTranslationUnit() {
+ diagnoseNonConstParameters();
+}
+
+void NonConstParameterCheck::diagnoseNonConstParameters() {
+ for (const auto &It : Parameters) {
+ const ParmVarDecl *Par = It.first;
+ const ParmInfo &ParamInfo = It.second;
+
+ // Unused parameter => there are other warnings about this.
+ if (!ParamInfo.IsReferenced)
+ continue;
+
+ // Parameter can't be const.
+ if (!ParamInfo.CanBeConst)
+ continue;
+
+ SmallVector<FixItHint, 8> Fixes;
+ auto *Function =
+ dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());
+ if (!Function)
+ continue;
+ unsigned Index = Par->getFunctionScopeIndex();
+ for (FunctionDecl *FnDecl : Function->redecls())
+ Fixes.push_back(FixItHint::CreateInsertion(
+ FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));
+
+ diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
+ << Par->getName() << Fixes;
+ }
+}
+
+void NonConstParameterCheck::markCanNotBeConst(const Expr *E,
+ bool CanNotBeConst) {
+ if (!E)
+ return;
+
+ if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
+ // If expression is const then ignore usage.
+ const QualType T = Cast->getType();
+ if (T->isPointerType() && T->getPointeeType().isConstQualified())
+ return;
+ }
+
+ E = E->IgnoreParenCasts();
+
+ if (const auto *B = dyn_cast<BinaryOperator>(E)) {
+ if (B->isAdditiveOp()) {
+ // p + 2
+ markCanNotBeConst(B->getLHS(), CanNotBeConst);
+ markCanNotBeConst(B->getRHS(), CanNotBeConst);
+ } else if (B->isAssignmentOp()) {
+ markCanNotBeConst(B->getLHS(), false);
+
+ // If LHS is not const then RHS can't be const.
+ const QualType T = B->getLHS()->getType();
+ if (T->isPointerType() && !T->getPointeeType().isConstQualified())
+ markCanNotBeConst(B->getRHS(), true);
+ }
+ } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {
+ markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
+ markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
+ } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
+ if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
+ U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
+ if (const auto *SubU =
+ dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
+ markCanNotBeConst(SubU->getSubExpr(), true);
+ markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
+ } else if (U->getOpcode() == UO_Deref) {
+ if (!CanNotBeConst)
+ markCanNotBeConst(U->getSubExpr(), true);
+ } else {
+ markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
+ }
+ } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
+ markCanNotBeConst(A->getBase(), true);
+ } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
+ markCanNotBeConst(CLE->getInitializer(), true);
+ } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
+ for (const auto *Arg : Constr->arguments()) {
+ if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
+ markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);
+ }
+ } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
+ for (unsigned I = 0U; I < ILE->getNumInits(); ++I)
+ markCanNotBeConst(ILE->getInit(I), true);
+ } else if (CanNotBeConst) {
+ // Referencing parameter.
+ if (const auto *D = dyn_cast<DeclRefExpr>(E)) {
+ auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
+ if (It != Parameters.end())
+ It->second.CanBeConst = false;
+ }
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.h
new file mode 100644
index 0000000000..e2598dd01d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/NonConstParameterCheck.h
@@ -0,0 +1,62 @@
+//===--- NonConstParameterCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Warn when a pointer function parameter can be const.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/non-const-parameter.html
+class NonConstParameterCheck : public ClangTidyCheck {
+public:
+ NonConstParameterCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void onEndOfTranslationUnit() override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ /// Parameter info.
+ struct ParmInfo {
+ /// Is function parameter referenced?
+ bool IsReferenced;
+
+ /// Can function parameter be const?
+ bool CanBeConst;
+ };
+
+ /// Track all nonconst integer/float parameters.
+ std::map<const ParmVarDecl *, ParmInfo> Parameters;
+
+ /// Add function parameter.
+ void addParm(const ParmVarDecl *Parm);
+
+ /// Set IsReferenced.
+ void setReferenced(const DeclRefExpr *Ref);
+
+ /// Set CanNotBeConst.
+ /// Visits sub expressions recursively. If a DeclRefExpr is found
+ /// and CanNotBeConst is true the Parameter is marked as not-const.
+ /// The CanNotBeConst is updated as sub expressions are visited.
+ void markCanNotBeConst(const Expr *E, bool CanNotBeConst);
+
+ /// Diagnose non const parameters.
+ void diagnoseNonConstParameters();
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.cpp
new file mode 100644
index 0000000000..e843c593a9
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.cpp
@@ -0,0 +1,287 @@
+//===--- QualifiedAutoCheck.cpp - clang-tidy ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "QualifiedAutoCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/SmallVector.h"
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+// FIXME move to ASTMatchers
+AST_MATCHER_P(QualType, hasUnqualifiedType,
+ ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+ return InnerMatcher.matches(Node.getUnqualifiedType(), Finder, Builder);
+}
+
+enum class Qualifier { Const, Volatile, Restrict };
+
+std::optional<Token> findQualToken(const VarDecl *Decl, Qualifier Qual,
+ const MatchFinder::MatchResult &Result) {
+ // Since either of the locs can be in a macro, use `makeFileCharRange` to be
+ // sure that we have a consistent `CharSourceRange`, located entirely in the
+ // source file.
+
+ assert((Qual == Qualifier::Const || Qual == Qualifier::Volatile ||
+ Qual == Qualifier::Restrict) &&
+ "Invalid Qualifier");
+
+ SourceLocation BeginLoc = Decl->getQualifierLoc().getBeginLoc();
+ if (BeginLoc.isInvalid())
+ BeginLoc = Decl->getBeginLoc();
+ SourceLocation EndLoc = Decl->getLocation();
+
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,
+ Result.Context->getLangOpts());
+
+ if (FileRange.isInvalid())
+ return std::nullopt;
+
+ tok::TokenKind Tok =
+ Qual == Qualifier::Const
+ ? tok::kw_const
+ : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
+
+ return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context,
+ *Result.SourceManager);
+}
+
+std::optional<SourceRange>
+getTypeSpecifierLocation(const VarDecl *Var,
+ const MatchFinder::MatchResult &Result) {
+ SourceRange TypeSpecifier(
+ Var->getTypeSpecStartLoc(),
+ Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength(
+ Var->getTypeSpecEndLoc(), *Result.SourceManager,
+ Result.Context->getLangOpts())));
+
+ if (TypeSpecifier.getBegin().isMacroID() ||
+ TypeSpecifier.getEnd().isMacroID())
+ return std::nullopt;
+ return TypeSpecifier;
+}
+
+std::optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
+ const Token &ConstToken) {
+ if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
+ TypeSpecifier.setBegin(ConstToken.getLocation());
+ return std::nullopt;
+ }
+ if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
+ TypeSpecifier.setEnd(ConstToken.getEndLoc());
+ return std::nullopt;
+ }
+ return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
+}
+
+bool isPointerConst(QualType QType) {
+ QualType Pointee = QType->getPointeeType();
+ assert(!Pointee.isNull() && "can't have a null Pointee");
+ return Pointee.isConstQualified();
+}
+
+bool isAutoPointerConst(QualType QType) {
+ QualType Pointee =
+ cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
+ assert(!Pointee.isNull() && "can't have a null Pointee");
+ return Pointee.isConstQualified();
+}
+
+} // namespace
+
+void QualifiedAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AddConstToQualified", AddConstToQualified);
+}
+
+void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
+ auto ExplicitSingleVarDecl =
+ [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
+ llvm::StringRef ID) {
+ return declStmt(
+ unless(isInTemplateInstantiation()),
+ hasSingleDecl(
+ varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
+ };
+ auto ExplicitSingleVarDeclInTemplate =
+ [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
+ llvm::StringRef ID) {
+ return declStmt(
+ isInTemplateInstantiation(),
+ hasSingleDecl(
+ varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
+ };
+
+ auto IsBoundToType = refersToType(equalsBoundNode("type"));
+ auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));
+ auto IsAutoDeducedToPointer = [](const auto &...InnerMatchers) {
+ return autoType(hasDeducedType(
+ hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...)))));
+ };
+
+ Finder->addMatcher(
+ ExplicitSingleVarDecl(hasType(IsAutoDeducedToPointer(UnlessFunctionType)),
+ "auto"),
+ this);
+
+ Finder->addMatcher(
+ ExplicitSingleVarDeclInTemplate(
+ allOf(hasType(IsAutoDeducedToPointer(
+ hasUnqualifiedType(qualType().bind("type")),
+ UnlessFunctionType)),
+ anyOf(hasAncestor(
+ functionDecl(hasAnyTemplateArgument(IsBoundToType))),
+ hasAncestor(classTemplateSpecializationDecl(
+ hasAnyTemplateArgument(IsBoundToType))))),
+ "auto"),
+ this);
+ if (!AddConstToQualified)
+ return;
+ Finder->addMatcher(ExplicitSingleVarDecl(
+ hasType(pointerType(pointee(autoType()))), "auto_ptr"),
+ this);
+ Finder->addMatcher(
+ ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),
+ "auto_ref"),
+ this);
+}
+
+void QualifiedAutoCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto")) {
+ SourceRange TypeSpecifier;
+ if (std::optional<SourceRange> TypeSpec =
+ getTypeSpecifierLocation(Var, Result)) {
+ TypeSpecifier = *TypeSpec;
+ } else
+ return;
+
+ llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
+ auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) {
+ if (IsPresent) {
+ std::optional<Token> Token = findQualToken(Var, Qual, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return true; // Disregard this VarDecl.
+ if (std::optional<SourceRange> Result =
+ mergeReplacementRange(TypeSpecifier, *Token))
+ RemoveQualifiersRange.push_back(*Result);
+ }
+ return false;
+ };
+
+ bool IsLocalConst = Var->getType().isLocalConstQualified();
+ bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
+ bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
+
+ if (CheckQualifier(IsLocalConst, Qualifier::Const) ||
+ CheckQualifier(IsLocalVolatile, Qualifier::Volatile) ||
+ CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
+ return;
+
+ // Check for bridging the gap between the asterisk and name.
+ if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))
+ TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));
+
+ CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier);
+ if (FixItRange.isInvalid())
+ return;
+
+ SourceLocation FixitLoc = FixItRange.getBegin();
+ for (SourceRange &Range : RemoveQualifiersRange) {
+ if (Range.getBegin() < FixitLoc)
+ FixitLoc = Range.getBegin();
+ }
+
+ std::string ReplStr = [&] {
+ llvm::StringRef PtrConst = isPointerConst(Var->getType()) ? "const " : "";
+ llvm::StringRef LocalConst = IsLocalConst ? "const " : "";
+ llvm::StringRef LocalVol = IsLocalVolatile ? "volatile " : "";
+ llvm::StringRef LocalRestrict = IsLocalRestrict ? "__restrict " : "";
+ return (PtrConst + "auto *" + LocalConst + LocalVol + LocalRestrict)
+ .str();
+ }();
+
+ DiagnosticBuilder Diag =
+ diag(FixitLoc,
+ "'%select{|const }0%select{|volatile }1%select{|__restrict }2auto "
+ "%3' can be declared as '%4%3'")
+ << IsLocalConst << IsLocalVolatile << IsLocalRestrict << Var->getName()
+ << ReplStr;
+
+ for (SourceRange &Range : RemoveQualifiersRange) {
+ Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(Range));
+ }
+
+ Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
+ return;
+ }
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ptr")) {
+ if (!isPointerConst(Var->getType()))
+ return; // Pointer isn't const, no need to add const qualifier.
+ if (!isAutoPointerConst(Var->getType()))
+ return; // Const isn't wrapped in the auto type, so must be declared
+ // explicitly.
+
+ if (Var->getType().isLocalConstQualified()) {
+ std::optional<Token> Token = findQualToken(Var, Qualifier::Const, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return;
+ }
+ if (Var->getType().isLocalVolatileQualified()) {
+ std::optional<Token> Token =
+ findQualToken(Var, Qualifier::Volatile, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return;
+ }
+ if (Var->getType().isLocalRestrictQualified()) {
+ std::optional<Token> Token =
+ findQualToken(Var, Qualifier::Restrict, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return;
+ }
+
+ if (std::optional<SourceRange> TypeSpec =
+ getTypeSpecifierLocation(Var, Result)) {
+ if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
+ TypeSpec->getEnd().isMacroID())
+ return;
+ SourceLocation InsertPos = TypeSpec->getBegin();
+ diag(InsertPos,
+ "'auto *%select{|const }0%select{|volatile }1%2' can be declared as "
+ "'const auto *%select{|const }0%select{|volatile }1%2'")
+ << Var->getType().isLocalConstQualified()
+ << Var->getType().isLocalVolatileQualified() << Var->getName()
+ << FixItHint::CreateInsertion(InsertPos, "const ");
+ }
+ return;
+ }
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ref")) {
+ if (!isPointerConst(Var->getType()))
+ return; // Pointer isn't const, no need to add const qualifier.
+ if (!isAutoPointerConst(Var->getType()))
+ // Const isn't wrapped in the auto type, so must be declared explicitly.
+ return;
+
+ if (std::optional<SourceRange> TypeSpec =
+ getTypeSpecifierLocation(Var, Result)) {
+ if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||
+ TypeSpec->getEnd().isMacroID())
+ return;
+ SourceLocation InsertPos = TypeSpec->getBegin();
+ diag(InsertPos, "'auto &%0' can be declared as 'const auto &%0'")
+ << Var->getName() << FixItHint::CreateInsertion(InsertPos, "const ");
+ }
+ return;
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.h
new file mode 100644
index 0000000000..dae9add48e
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/QualifiedAutoCheck.h
@@ -0,0 +1,40 @@
+//===--- QualifiedAutoCheck.h - clang-tidy ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_QUALIFIEDAUTOCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_QUALIFIEDAUTOCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds variables declared as auto that could be declared as:
+/// 'auto*' or 'const auto *' and reference variables declared as:
+/// 'const auto &'.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/qualified-auto.html
+class QualifiedAutoCheck : public ClangTidyCheck {
+public:
+ QualifiedAutoCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AddConstToQualified(Options.get("AddConstToQualified", true)) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool AddConstToQualified;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_QUALIFIEDAUTOCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ReadabilityTidyModule.cpp
new file mode 100644
index 0000000000..7aa0904242
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -0,0 +1,158 @@
+//===--- ReadabilityTidyModule.cpp - clang-tidy ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "AvoidConstParamsInDecls.h"
+#include "BracesAroundStatementsCheck.h"
+#include "ConstReturnTypeCheck.h"
+#include "ContainerContainsCheck.h"
+#include "ContainerDataPointerCheck.h"
+#include "ContainerSizeEmptyCheck.h"
+#include "ConvertMemberFunctionsToStatic.h"
+#include "DeleteNullPointerCheck.h"
+#include "DuplicateIncludeCheck.h"
+#include "ElseAfterReturnCheck.h"
+#include "FunctionCognitiveComplexityCheck.h"
+#include "FunctionSizeCheck.h"
+#include "IdentifierLengthCheck.h"
+#include "IdentifierNamingCheck.h"
+#include "ImplicitBoolConversionCheck.h"
+#include "InconsistentDeclarationParameterNameCheck.h"
+#include "IsolateDeclarationCheck.h"
+#include "MagicNumbersCheck.h"
+#include "MakeMemberFunctionConstCheck.h"
+#include "MisleadingIndentationCheck.h"
+#include "MisplacedArrayIndexCheck.h"
+#include "NamedParameterCheck.h"
+#include "NonConstParameterCheck.h"
+#include "QualifiedAutoCheck.h"
+#include "RedundantAccessSpecifiersCheck.h"
+#include "RedundantControlFlowCheck.h"
+#include "RedundantDeclarationCheck.h"
+#include "RedundantFunctionPtrDereferenceCheck.h"
+#include "RedundantMemberInitCheck.h"
+#include "RedundantPreprocessorCheck.h"
+#include "RedundantSmartptrGetCheck.h"
+#include "RedundantStringCStrCheck.h"
+#include "RedundantStringInitCheck.h"
+#include "SimplifyBooleanExprCheck.h"
+#include "SimplifySubscriptExprCheck.h"
+#include "StaticAccessedThroughInstanceCheck.h"
+#include "StaticDefinitionInAnonymousNamespaceCheck.h"
+#include "StringCompareCheck.h"
+#include "SuspiciousCallArgumentCheck.h"
+#include "UniqueptrDeleteReleaseCheck.h"
+#include "UppercaseLiteralSuffixCheck.h"
+#include "UseAnyOfAllOfCheck.h"
+
+namespace clang::tidy {
+namespace readability {
+
+class ReadabilityModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<AvoidConstParamsInDecls>(
+ "readability-avoid-const-params-in-decls");
+ CheckFactories.registerCheck<BracesAroundStatementsCheck>(
+ "readability-braces-around-statements");
+ CheckFactories.registerCheck<ConstReturnTypeCheck>(
+ "readability-const-return-type");
+ CheckFactories.registerCheck<ContainerContainsCheck>(
+ "readability-container-contains");
+ CheckFactories.registerCheck<ContainerDataPointerCheck>(
+ "readability-container-data-pointer");
+ CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
+ "readability-container-size-empty");
+ CheckFactories.registerCheck<ConvertMemberFunctionsToStatic>(
+ "readability-convert-member-functions-to-static");
+ CheckFactories.registerCheck<DeleteNullPointerCheck>(
+ "readability-delete-null-pointer");
+ CheckFactories.registerCheck<DuplicateIncludeCheck>(
+ "readability-duplicate-include");
+ CheckFactories.registerCheck<ElseAfterReturnCheck>(
+ "readability-else-after-return");
+ CheckFactories.registerCheck<FunctionCognitiveComplexityCheck>(
+ "readability-function-cognitive-complexity");
+ CheckFactories.registerCheck<FunctionSizeCheck>(
+ "readability-function-size");
+ CheckFactories.registerCheck<IdentifierLengthCheck>(
+ "readability-identifier-length");
+ CheckFactories.registerCheck<IdentifierNamingCheck>(
+ "readability-identifier-naming");
+ CheckFactories.registerCheck<ImplicitBoolConversionCheck>(
+ "readability-implicit-bool-conversion");
+ CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
+ "readability-inconsistent-declaration-parameter-name");
+ CheckFactories.registerCheck<IsolateDeclarationCheck>(
+ "readability-isolate-declaration");
+ CheckFactories.registerCheck<MagicNumbersCheck>(
+ "readability-magic-numbers");
+ CheckFactories.registerCheck<MakeMemberFunctionConstCheck>(
+ "readability-make-member-function-const");
+ CheckFactories.registerCheck<MisleadingIndentationCheck>(
+ "readability-misleading-indentation");
+ CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
+ "readability-misplaced-array-index");
+ CheckFactories.registerCheck<QualifiedAutoCheck>(
+ "readability-qualified-auto");
+ CheckFactories.registerCheck<RedundantAccessSpecifiersCheck>(
+ "readability-redundant-access-specifiers");
+ CheckFactories.registerCheck<RedundantFunctionPtrDereferenceCheck>(
+ "readability-redundant-function-ptr-dereference");
+ CheckFactories.registerCheck<RedundantMemberInitCheck>(
+ "readability-redundant-member-init");
+ CheckFactories.registerCheck<RedundantPreprocessorCheck>(
+ "readability-redundant-preprocessor");
+ CheckFactories.registerCheck<SimplifySubscriptExprCheck>(
+ "readability-simplify-subscript-expr");
+ CheckFactories.registerCheck<StaticAccessedThroughInstanceCheck>(
+ "readability-static-accessed-through-instance");
+ CheckFactories.registerCheck<StaticDefinitionInAnonymousNamespaceCheck>(
+ "readability-static-definition-in-anonymous-namespace");
+ CheckFactories.registerCheck<StringCompareCheck>(
+ "readability-string-compare");
+ CheckFactories.registerCheck<readability::NamedParameterCheck>(
+ "readability-named-parameter");
+ CheckFactories.registerCheck<NonConstParameterCheck>(
+ "readability-non-const-parameter");
+ CheckFactories.registerCheck<RedundantControlFlowCheck>(
+ "readability-redundant-control-flow");
+ CheckFactories.registerCheck<RedundantDeclarationCheck>(
+ "readability-redundant-declaration");
+ CheckFactories.registerCheck<RedundantSmartptrGetCheck>(
+ "readability-redundant-smartptr-get");
+ CheckFactories.registerCheck<RedundantStringCStrCheck>(
+ "readability-redundant-string-cstr");
+ CheckFactories.registerCheck<RedundantStringInitCheck>(
+ "readability-redundant-string-init");
+ CheckFactories.registerCheck<SimplifyBooleanExprCheck>(
+ "readability-simplify-boolean-expr");
+ CheckFactories.registerCheck<SuspiciousCallArgumentCheck>(
+ "readability-suspicious-call-argument");
+ CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>(
+ "readability-uniqueptr-delete-release");
+ CheckFactories.registerCheck<UppercaseLiteralSuffixCheck>(
+ "readability-uppercase-literal-suffix");
+ CheckFactories.registerCheck<UseAnyOfAllOfCheck>(
+ "readability-use-anyofallof");
+ }
+};
+
+// Register the ReadabilityModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<ReadabilityModule>
+ X("readability-module", "Adds readability-related checks.");
+
+} // namespace readability
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the ReadabilityModule.
+volatile int ReadabilityModuleAnchorSource = 0;
+
+} // namespace clang::tidy
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.cpp
new file mode 100644
index 0000000000..c3464b2a83
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.cpp
@@ -0,0 +1,78 @@
+//===--- RedundantAccessSpecifiersCheck.cpp - clang-tidy ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantAccessSpecifiersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void RedundantAccessSpecifiersCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxRecordDecl(has(accessSpecDecl())).bind("redundant-access-specifiers"),
+ this);
+}
+
+void RedundantAccessSpecifiersCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("redundant-access-specifiers");
+
+ const AccessSpecDecl *LastASDecl = nullptr;
+ for (DeclContext::specific_decl_iterator<AccessSpecDecl>
+ AS(MatchedDecl->decls_begin()),
+ ASEnd(MatchedDecl->decls_end());
+ AS != ASEnd; ++AS) {
+ const AccessSpecDecl *ASDecl = *AS;
+
+ // Ignore macro expansions.
+ if (ASDecl->getLocation().isMacroID()) {
+ LastASDecl = ASDecl;
+ continue;
+ }
+
+ if (LastASDecl == nullptr) {
+ // First declaration.
+ LastASDecl = ASDecl;
+
+ if (CheckFirstDeclaration) {
+ AccessSpecifier DefaultSpecifier =
+ MatchedDecl->isClass() ? AS_private : AS_public;
+ if (ASDecl->getAccess() == DefaultSpecifier) {
+ diag(ASDecl->getLocation(),
+ "redundant access specifier has the same accessibility as the "
+ "implicit access specifier")
+ << FixItHint::CreateRemoval(ASDecl->getSourceRange());
+ }
+ }
+
+ continue;
+ }
+
+ if (LastASDecl->getAccess() == ASDecl->getAccess()) {
+ // Ignore macro expansions.
+ if (LastASDecl->getLocation().isMacroID()) {
+ LastASDecl = ASDecl;
+ continue;
+ }
+
+ diag(ASDecl->getLocation(),
+ "redundant access specifier has the same accessibility as the "
+ "previous access specifier")
+ << FixItHint::CreateRemoval(ASDecl->getSourceRange());
+ diag(LastASDecl->getLocation(), "previously declared here",
+ DiagnosticIDs::Note);
+ } else {
+ LastASDecl = ASDecl;
+ }
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h
new file mode 100644
index 0000000000..a5389d063f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h
@@ -0,0 +1,38 @@
+//===--- RedundantAccessSpecifiersCheck.h - clang-tidy ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTACCESSSPECIFIERSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTACCESSSPECIFIERSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Detects redundant access specifiers inside classes, structs, and unions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-access-specifiers.html
+class RedundantAccessSpecifiersCheck : public ClangTidyCheck {
+public:
+ RedundantAccessSpecifiersCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ CheckFirstDeclaration(
+ Options.getLocalOrGlobal("CheckFirstDeclaration", false)) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool CheckFirstDeclaration;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTACCESSSPECIFIERSCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.cpp
new file mode 100644
index 0000000000..ed01d3a2d7
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.cpp
@@ -0,0 +1,92 @@
+//===--- RedundantControlFlowCheck.cpp - clang-tidy------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantControlFlowCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+const char *const RedundantReturnDiag = "redundant return statement at the end "
+ "of a function with a void return type";
+const char *const RedundantContinueDiag = "redundant continue statement at the "
+ "end of loop statement";
+
+bool isLocationInMacroExpansion(const SourceManager &SM, SourceLocation Loc) {
+ return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
+}
+
+} // namespace
+
+void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ functionDecl(isDefinition(), returns(voidType()),
+ hasBody(compoundStmt(hasAnySubstatement(
+ returnStmt(unless(has(expr())))))
+ .bind("return"))),
+ this);
+ Finder->addMatcher(
+ mapAnyOf(forStmt, cxxForRangeStmt, whileStmt, doStmt)
+ .with(hasBody(compoundStmt(hasAnySubstatement(continueStmt()))
+ .bind("continue"))),
+ this);
+}
+
+void RedundantControlFlowCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Return = Result.Nodes.getNodeAs<CompoundStmt>("return"))
+ checkRedundantReturn(Result, Return);
+ else if (const auto *Continue =
+ Result.Nodes.getNodeAs<CompoundStmt>("continue"))
+ checkRedundantContinue(Result, Continue);
+}
+
+void RedundantControlFlowCheck::checkRedundantReturn(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *Block) {
+ CompoundStmt::const_reverse_body_iterator Last = Block->body_rbegin();
+ if (const auto *Return = dyn_cast<ReturnStmt>(*Last))
+ issueDiagnostic(Result, Block, Return->getSourceRange(),
+ RedundantReturnDiag);
+}
+
+void RedundantControlFlowCheck::checkRedundantContinue(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *Block) {
+ CompoundStmt::const_reverse_body_iterator Last = Block->body_rbegin();
+ if (const auto *Continue = dyn_cast<ContinueStmt>(*Last))
+ issueDiagnostic(Result, Block, Continue->getSourceRange(),
+ RedundantContinueDiag);
+}
+
+void RedundantControlFlowCheck::issueDiagnostic(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *const Block,
+ const SourceRange &StmtRange, const char *const Diag) {
+ SourceManager &SM = *Result.SourceManager;
+ if (isLocationInMacroExpansion(SM, StmtRange.getBegin()))
+ return;
+
+ CompoundStmt::const_reverse_body_iterator Previous = ++Block->body_rbegin();
+ SourceLocation Start;
+ if (Previous != Block->body_rend())
+ Start = Lexer::findLocationAfterToken(
+ cast<Stmt>(*Previous)->getEndLoc(), tok::semi, SM, getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true);
+ if (!Start.isValid())
+ Start = StmtRange.getBegin();
+ auto RemovedRange = CharSourceRange::getCharRange(
+ Start, Lexer::findLocationAfterToken(
+ StmtRange.getEnd(), tok::semi, SM, getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true));
+
+ diag(StmtRange.getBegin(), Diag) << FixItHint::CreateRemoval(RemovedRange);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.h
new file mode 100644
index 0000000000..7433005bb7
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantControlFlowCheck.h
@@ -0,0 +1,50 @@
+//===--- RedundantControlFlowCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Eliminates redundant `return` statements at the end of a function that
+/// returns `void`.
+///
+/// Eliminates redundant `continue` statements at the end of a loop body.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-control-flow.html
+class RedundantControlFlowCheck : public ClangTidyCheck {
+public:
+ RedundantControlFlowCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ void
+ checkRedundantReturn(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Block);
+
+ void
+ checkRedundantContinue(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Block);
+
+ void issueDiagnostic(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Block, const SourceRange &StmtRange,
+ const char *Diag);
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.cpp
new file mode 100644
index 0000000000..f21f0a7856
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.cpp
@@ -0,0 +1,87 @@
+//===--- RedundantDeclarationCheck.cpp - clang-tidy------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantDeclarationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+AST_MATCHER(FunctionDecl, doesDeclarationForceExternallyVisibleDefinition) {
+ return Node.doesDeclarationForceExternallyVisibleDefinition();
+}
+
+RedundantDeclarationCheck::RedundantDeclarationCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+void RedundantDeclarationCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ namedDecl(anyOf(varDecl(unless(isDefinition())),
+ functionDecl(unless(anyOf(
+ isDefinition(), isDefaulted(),
+ doesDeclarationForceExternallyVisibleDefinition(),
+ hasAncestor(friendDecl()))))))
+ .bind("Decl"),
+ this);
+}
+
+void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *D = Result.Nodes.getNodeAs<NamedDecl>("Decl");
+ const auto *Prev = D->getPreviousDecl();
+ if (!Prev)
+ return;
+ if (!Prev->getLocation().isValid())
+ return;
+ if (Prev->getLocation() == D->getLocation())
+ return;
+ if (IgnoreMacros &&
+ (D->getLocation().isMacroID() || Prev->getLocation().isMacroID()))
+ return;
+ // Don't complain when the previous declaration is a friend declaration.
+ for (const auto &Parent : Result.Context->getParents(*Prev))
+ if (Parent.get<FriendDecl>())
+ return;
+
+ const SourceManager &SM = *Result.SourceManager;
+
+ const bool DifferentHeaders =
+ !SM.isInMainFile(D->getLocation()) &&
+ !SM.isWrittenInSameFile(Prev->getLocation(), D->getLocation());
+
+ bool MultiVar = false;
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ // Is this a multivariable declaration?
+ for (const auto *Other : VD->getDeclContext()->decls()) {
+ if (Other != D && Other->getBeginLoc() == VD->getBeginLoc()) {
+ MultiVar = true;
+ break;
+ }
+ }
+ }
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ D->getSourceRange().getEnd(), 0, SM, Result.Context->getLangOpts());
+ {
+ auto Diag = diag(D->getLocation(), "redundant %0 declaration") << D;
+ if (!MultiVar && !DifferentHeaders)
+ Diag << FixItHint::CreateRemoval(
+ SourceRange(D->getSourceRange().getBegin(), EndLoc));
+ }
+ diag(Prev->getLocation(), "previously declared here", DiagnosticIDs::Note);
+}
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.h
new file mode 100644
index 0000000000..a14a8aa70f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantDeclarationCheck.h
@@ -0,0 +1,33 @@
+//===--- RedundantDeclarationCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Find redundant variable declarations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-declaration.html
+class RedundantDeclarationCheck : public ClangTidyCheck {
+public:
+ RedundantDeclarationCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool IgnoreMacros;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp
new file mode 100644
index 0000000000..247d287be2
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp
@@ -0,0 +1,33 @@
+//===--- RedundantFunctionPtrDereferenceCheck.cpp - clang-tidy-------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantFunctionPtrDereferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void RedundantFunctionPtrDereferenceCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ traverse(TK_AsIs, unaryOperator(hasOperatorName("*"),
+ has(implicitCastExpr(hasCastKind(
+ CK_FunctionToPointerDecay))))
+ .bind("op")),
+ this);
+}
+
+void RedundantFunctionPtrDereferenceCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Operator = Result.Nodes.getNodeAs<UnaryOperator>("op");
+ diag(Operator->getOperatorLoc(),
+ "redundant repeated dereference of function pointer")
+ << FixItHint::CreateRemoval(Operator->getOperatorLoc());
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h
new file mode 100644
index 0000000000..fdcf821bcf
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h
@@ -0,0 +1,30 @@
+//===--- RedundantFunctionPtrDereferenceCheck.h - clang-tidy-----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Eliminate redundant dereferences of a function pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-function-ptr-dereference.html
+class RedundantFunctionPtrDereferenceCheck : public ClangTidyCheck {
+public:
+ RedundantFunctionPtrDereferenceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.cpp
new file mode 100644
index 0000000000..b5d407773b
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.cpp
@@ -0,0 +1,70 @@
+//===--- RedundantMemberInitCheck.cpp - clang-tidy-------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantMemberInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void RedundantMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreBaseInCopyConstructors",
+ IgnoreBaseInCopyConstructors);
+}
+
+void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ unless(isDelegatingConstructor()), ofClass(unless(isUnion())),
+ forEachConstructorInitializer(
+ cxxCtorInitializer(
+ withInitializer(
+ cxxConstructExpr(
+ hasDeclaration(
+ cxxConstructorDecl(ofClass(cxxRecordDecl(
+ unless(isTriviallyDefaultConstructible()))))))
+ .bind("construct")),
+ unless(forField(hasType(isConstQualified()))),
+ unless(forField(hasParent(recordDecl(isUnion())))))
+ .bind("init")))
+ .bind("constructor"),
+ this);
+}
+
+void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
+ const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
+ const auto *ConstructorDecl =
+ Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor");
+
+ if (IgnoreBaseInCopyConstructors && ConstructorDecl->isCopyConstructor() &&
+ Init->isBaseInitializer())
+ return;
+
+ if (Construct->getNumArgs() == 0 ||
+ Construct->getArg(0)->isDefaultArgument()) {
+ if (Init->isAnyMemberInitializer()) {
+ diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
+ << Init->getAnyMember()
+ << FixItHint::CreateRemoval(Init->getSourceRange());
+ } else {
+ diag(Init->getSourceLocation(),
+ "initializer for base class %0 is redundant")
+ << Construct->getType()
+ << FixItHint::CreateRemoval(Init->getSourceRange());
+ }
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.h
new file mode 100644
index 0000000000..c0e0a6dac0
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantMemberInitCheck.h
@@ -0,0 +1,43 @@
+//===--- RedundantMemberInitCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds member initializations that are unnecessary because the same default
+/// constructor would be called if they were not present.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-member-init.html
+class RedundantMemberInitCheck : public ClangTidyCheck {
+public:
+ RedundantMemberInitCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreBaseInCopyConstructors(
+ Options.get("IgnoreBaseInCopyConstructors", false)) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ 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 IgnoreBaseInCopyConstructors;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.cpp
new file mode 100644
index 0000000000..513687f03d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.cpp
@@ -0,0 +1,105 @@
+//===--- RedundantPreprocessorCheck.cpp - clang-tidy ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantPreprocessorCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang::tidy::readability {
+
+namespace {
+/// Information about an opening preprocessor directive.
+struct PreprocessorEntry {
+ SourceLocation Loc;
+ /// Condition used after the preprocessor directive.
+ std::string Condition;
+};
+
+const char WarningDescription[] =
+ "nested redundant %select{#if|#ifdef|#ifndef}0; consider removing it";
+const char NoteDescription[] = "previous %select{#if|#ifdef|#ifndef}0 was here";
+
+class RedundantPreprocessorCallbacks : public PPCallbacks {
+ enum DirectiveKind { DK_If = 0, DK_Ifdef = 1, DK_Ifndef = 2 };
+
+public:
+ explicit RedundantPreprocessorCallbacks(ClangTidyCheck &Check,
+ Preprocessor &PP)
+ : Check(Check), PP(PP) {}
+
+ void If(SourceLocation Loc, SourceRange ConditionRange,
+ ConditionValueKind ConditionValue) override {
+ StringRef Condition =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
+ PP.getSourceManager(), PP.getLangOpts());
+ checkMacroRedundancy(Loc, Condition, IfStack, DK_If, DK_If, true);
+ }
+
+ void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MacroDefinition) override {
+ std::string MacroName = PP.getSpelling(MacroNameTok);
+ checkMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifdef, DK_Ifdef, true);
+ checkMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifdef, DK_Ifndef,
+ false);
+ }
+
+ void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MacroDefinition) override {
+ std::string MacroName = PP.getSpelling(MacroNameTok);
+ checkMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifndef, DK_Ifndef,
+ true);
+ checkMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifndef, DK_Ifdef,
+ false);
+ }
+
+ void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
+ if (!IfStack.empty() && IfLoc == IfStack.back().Loc)
+ IfStack.pop_back();
+ if (!IfdefStack.empty() && IfLoc == IfdefStack.back().Loc)
+ IfdefStack.pop_back();
+ if (!IfndefStack.empty() && IfLoc == IfndefStack.back().Loc)
+ IfndefStack.pop_back();
+ }
+
+private:
+ void checkMacroRedundancy(SourceLocation Loc, StringRef MacroName,
+ SmallVector<PreprocessorEntry, 4> &Stack,
+ DirectiveKind WarningKind, DirectiveKind NoteKind,
+ bool Store) {
+ if (PP.getSourceManager().isInMainFile(Loc)) {
+ for (const auto &Entry : Stack) {
+ if (Entry.Condition == MacroName) {
+ Check.diag(Loc, WarningDescription) << WarningKind;
+ Check.diag(Entry.Loc, NoteDescription, DiagnosticIDs::Note)
+ << NoteKind;
+ }
+ }
+ }
+
+ if (Store)
+ // This is an actual directive to be remembered.
+ Stack.push_back({Loc, std::string(MacroName)});
+ }
+
+ ClangTidyCheck &Check;
+ Preprocessor &PP;
+ SmallVector<PreprocessorEntry, 4> IfStack;
+ SmallVector<PreprocessorEntry, 4> IfdefStack;
+ SmallVector<PreprocessorEntry, 4> IfndefStack;
+};
+} // namespace
+
+void RedundantPreprocessorCheck::registerPPCallbacks(
+ const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+ PP->addPPCallbacks(
+ ::std::make_unique<RedundantPreprocessorCallbacks>(*this, *PP));
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.h
new file mode 100644
index 0000000000..8a6fb6fd98
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantPreprocessorCheck.h
@@ -0,0 +1,31 @@
+//===--- RedundantPreprocessorCheck.h - clang-tidy --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTPREPROCESSORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTPREPROCESSORCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// This check flags redundant preprocessor directives: nested directives with
+/// the same condition.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-preprocessor.html
+class RedundantPreprocessorCheck : public ClangTidyCheck {
+public:
+ RedundantPreprocessorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTPREPROCESSORCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.cpp
new file mode 100644
index 0000000000..8837ac16e8
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.cpp
@@ -0,0 +1,173 @@
+//===--- RedundantSmartptrGetCheck.cpp - clang-tidy -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantSmartptrGetCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+internal::Matcher<Expr> callToGet(const internal::Matcher<Decl> &OnClass) {
+ return expr(
+ anyOf(cxxMemberCallExpr(
+ on(expr(anyOf(hasType(OnClass),
+ hasType(qualType(pointsTo(
+ decl(OnClass).bind("ptr_to_ptr"))))))
+ .bind("smart_pointer")),
+ unless(callee(
+ memberExpr(hasObjectExpression(cxxThisExpr())))),
+ callee(cxxMethodDecl(hasName("get"),
+ returns(qualType(pointsTo(
+ type().bind("getType"))))))),
+ cxxDependentScopeMemberExpr(
+ hasMemberName("get"),
+ hasObjectExpression(
+ expr(hasType(qualType(hasCanonicalType(
+ templateSpecializationType(hasDeclaration(
+ classTemplateDecl(has(cxxRecordDecl(
+ OnClass,
+ hasMethod(cxxMethodDecl(
+ hasName("get"),
+ returns(qualType(
+ pointsTo(type().bind(
+ "getType")))))))))))))))
+ .bind("smart_pointer")))))
+ .bind("redundant_get");
+}
+
+internal::Matcher<Decl> knownSmartptr() {
+ return recordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));
+}
+
+void registerMatchersForGetArrowStart(MatchFinder *Finder,
+ MatchFinder::MatchCallback *Callback) {
+ const auto QuacksLikeASmartptr = recordDecl(
+ recordDecl().bind("duck_typing"),
+ has(cxxMethodDecl(hasName("operator->"),
+ returns(qualType(pointsTo(type().bind("op->Type")))))),
+ has(cxxMethodDecl(hasName("operator*"), returns(qualType(references(
+ type().bind("op*Type")))))));
+
+ // Make sure we are not missing the known standard types.
+ const auto Smartptr = anyOf(knownSmartptr(), QuacksLikeASmartptr);
+
+ // Catch 'ptr.get()->Foo()'
+ Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(),
+ hasObjectExpression(callToGet(Smartptr))),
+ Callback);
+
+ // Catch '*ptr.get()' or '*ptr->get()'
+ Finder->addMatcher(
+ unaryOperator(hasOperatorName("*"), hasUnaryOperand(callToGet(Smartptr))),
+ Callback);
+
+ // Catch '!ptr.get()'
+ const auto CallToGetAsBool = callToGet(
+ recordDecl(Smartptr, has(cxxConversionDecl(returns(booleanType())))));
+ Finder->addMatcher(
+ unaryOperator(hasOperatorName("!"), hasUnaryOperand(CallToGetAsBool)),
+ Callback);
+
+ // Catch 'if(ptr.get())'
+ Finder->addMatcher(ifStmt(hasCondition(CallToGetAsBool)), Callback);
+
+ // Catch 'ptr.get() ? X : Y'
+ Finder->addMatcher(conditionalOperator(hasCondition(CallToGetAsBool)),
+ Callback);
+
+ Finder->addMatcher(cxxDependentScopeMemberExpr(hasObjectExpression(
+ callExpr(has(callToGet(Smartptr))).bind("obj"))),
+ Callback);
+}
+
+void registerMatchersForGetEquals(MatchFinder *Finder,
+ MatchFinder::MatchCallback *Callback) {
+ // This one is harder to do with duck typing.
+ // The operator==/!= that we are looking for might be member or non-member,
+ // might be on global namespace or found by ADL, might be a template, etc.
+ // For now, lets keep it to the known standard types.
+
+ // Matches against nullptr.
+ Finder->addMatcher(
+ binaryOperator(hasAnyOperatorName("==", "!="),
+ hasOperands(anyOf(cxxNullPtrLiteralExpr(), gnuNullExpr(),
+ integerLiteral(equals(0))),
+ callToGet(knownSmartptr()))),
+ Callback);
+
+ // FIXME: Match and fix if (l.get() == r.get()).
+}
+
+} // namespace
+
+void RedundantSmartptrGetCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {
+ registerMatchersForGetArrowStart(Finder, this);
+ registerMatchersForGetEquals(Finder, this);
+}
+
+namespace {
+bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
+ if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
+ return true;
+ // Verify that the types match.
+ // We can't do this on the matcher because the type nodes can be different,
+ // even though they represent the same type. This difference comes from how
+ // the type is referenced (eg. through a typedef, a type trait, etc).
+ const Type *OpArrowType =
+ Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
+ const Type *OpStarType =
+ Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
+ const Type *GetType =
+ Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
+ return OpArrowType == OpStarType && OpArrowType == GetType;
+}
+} // namespace
+
+void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {
+ if (!allReturnTypesMatch(Result))
+ return;
+
+ bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
+ bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
+ const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
+ if (GetCall->getBeginLoc().isMacroID() && IgnoreMacros)
+ return;
+
+ const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
+
+ if (IsPtrToPtr && IsMemberExpr) {
+ // Ignore this case (eg. Foo->get()->DoSomething());
+ return;
+ }
+
+ auto SR = GetCall->getSourceRange();
+ // CXXDependentScopeMemberExpr source range does not include parens
+ // Extend the source range of the get call to account for them.
+ if (isa<CXXDependentScopeMemberExpr>(GetCall))
+ SR.setEnd(Lexer::getLocForEndOfToken(SR.getEnd(), 0, *Result.SourceManager,
+ getLangOpts())
+ .getLocWithOffset(1));
+
+ StringRef SmartptrText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ // Replace foo->get() with *foo, and foo.get() with foo.
+ std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
+ diag(GetCall->getBeginLoc(), "redundant get() call on smart pointer")
+ << FixItHint::CreateReplacement(SR, Replacement);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.h
new file mode 100644
index 0000000000..ffe8df8857
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantSmartptrGetCheck.h
@@ -0,0 +1,46 @@
+//===--- RedundantSmartptrGetCheck.h - clang-tidy ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Find and remove redundant calls to smart pointer's `.get()` method.
+///
+/// Examples:
+///
+/// \code
+/// ptr.get()->Foo() ==> ptr->Foo()
+/// *ptr.get() ==> *ptr
+/// *ptr->get() ==> **ptr
+/// \endcode
+class RedundantSmartptrGetCheck : public ClangTidyCheck {
+public:
+ RedundantSmartptrGetCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const bool IgnoreMacros;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.cpp
new file mode 100644
index 0000000000..960d5abcc9
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.cpp
@@ -0,0 +1,201 @@
+//===- RedundantStringCStrCheck.cpp - Check for redundant c_str calls -----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a check for redundant calls of c_str() on strings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantStringCStrCheck.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+// Return true if expr needs to be put in parens when it is an argument of a
+// prefix unary operator, e.g. when it is a binary or ternary operator
+// syntactically.
+bool needParensAfterUnaryOperator(const Expr &ExprNode) {
+ if (isa<clang::BinaryOperator>(&ExprNode) ||
+ isa<clang::ConditionalOperator>(&ExprNode)) {
+ return true;
+ }
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
+ return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
+ Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
+ Op->getOperator() != OO_Subscript;
+ }
+ return false;
+}
+
+// Format a pointer to an expression: prefix with '*' but simplify
+// when it already begins with '&'. Return empty string on failure.
+std::string
+formatDereference(const ast_matchers::MatchFinder::MatchResult &Result,
+ const Expr &ExprNode) {
+ if (const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
+ if (Op->getOpcode() == UO_AddrOf) {
+ // Strip leading '&'.
+ return std::string(tooling::fixit::getText(
+ *Op->getSubExpr()->IgnoreParens(), *Result.Context));
+ }
+ }
+ StringRef Text = tooling::fixit::getText(ExprNode, *Result.Context);
+
+ if (Text.empty())
+ return std::string();
+ // Add leading '*'.
+ if (needParensAfterUnaryOperator(ExprNode)) {
+ return (llvm::Twine("*(") + Text + ")").str();
+ }
+ return (llvm::Twine("*") + Text).str();
+}
+
+AST_MATCHER(MaterializeTemporaryExpr, isBoundToLValue) {
+ return Node.isBoundToLvalueReference();
+}
+
+} // end namespace
+
+void RedundantStringCStrCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Match expressions of type 'string' or 'string*'.
+ const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
+ hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))));
+ const auto StringExpr =
+ expr(anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
+
+ // Match string constructor.
+ const auto StringConstructorExpr = expr(anyOf(
+ cxxConstructExpr(argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+ cxxConstructExpr(
+ argumentCountIs(2),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ // If present, the second argument is the alloc object which must not
+ // be present explicitly.
+ hasArgument(1, cxxDefaultArgExpr()))));
+
+ // Match string constructor.
+ const auto StringViewConstructorExpr = cxxConstructExpr(
+ argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string_view"))));
+
+ // Match a call to the string 'c_str()' method.
+ const auto StringCStrCallExpr =
+ cxxMemberCallExpr(on(StringExpr.bind("arg")),
+ callee(memberExpr().bind("member")),
+ callee(cxxMethodDecl(hasAnyName("c_str", "data"))))
+ .bind("call");
+ const auto HasRValueTempParent =
+ hasParent(materializeTemporaryExpr(unless(isBoundToLValue())));
+ // Detect redundant 'c_str()' calls through a string constructor.
+ // If CxxConstructExpr is the part of some CallExpr we need to
+ // check that matched ParamDecl of the ancestor CallExpr is not rvalue.
+ Finder->addMatcher(
+ traverse(
+ TK_AsIs,
+ cxxConstructExpr(
+ anyOf(StringConstructorExpr, StringViewConstructorExpr),
+ hasArgument(0, StringCStrCallExpr),
+ unless(anyOf(HasRValueTempParent, hasParent(cxxBindTemporaryExpr(
+ HasRValueTempParent)))))),
+ this);
+
+ // Detect: 's == str.c_str()' -> 's == str'
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ hasAnyOverloadedOperatorName("<", ">", ">=", "<=", "!=", "==", "+"),
+ anyOf(allOf(hasArgument(0, StringExpr),
+ hasArgument(1, StringCStrCallExpr)),
+ allOf(hasArgument(0, StringCStrCallExpr),
+ hasArgument(1, StringExpr)))),
+ this);
+
+ // Detect: 'dst += str.c_str()' -> 'dst += str'
+ // Detect: 's = str.c_str()' -> 's = str'
+ Finder->addMatcher(
+ cxxOperatorCallExpr(hasAnyOverloadedOperatorName("=", "+="),
+ hasArgument(0, StringExpr),
+ hasArgument(1, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.append(str.c_str())' -> 'dst.append(str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName(
+ "append", "assign", "compare")))),
+ argumentCountIs(1), hasArgument(0, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.compare(p, n, str.c_str())' -> 'dst.compare(p, n, str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr),
+ callee(decl(cxxMethodDecl(hasName("compare")))),
+ argumentCountIs(3), hasArgument(2, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.find(str.c_str())' -> 'dst.find(str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr),
+ callee(decl(cxxMethodDecl(hasAnyName(
+ "find", "find_first_not_of", "find_first_of",
+ "find_last_not_of", "find_last_of", "rfind")))),
+ anyOf(argumentCountIs(1), argumentCountIs(2)),
+ hasArgument(0, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.insert(pos, str.c_str())' -> 'dst.insert(pos, str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr),
+ callee(decl(cxxMethodDecl(hasName("insert")))),
+ argumentCountIs(2), hasArgument(1, StringCStrCallExpr)),
+ this);
+
+ // Detect redundant 'c_str()' calls through a StringRef constructor.
+ Finder->addMatcher(
+ traverse(
+ TK_AsIs,
+ cxxConstructExpr(
+ // Implicit constructors of these classes are overloaded
+ // wrt. string types and they internally make a StringRef
+ // referring to the argument. Passing a string directly to
+ // them is preferred to passing a char pointer.
+ hasDeclaration(cxxMethodDecl(hasAnyName(
+ "::llvm::StringRef::StringRef", "::llvm::Twine::Twine"))),
+ argumentCountIs(1),
+ // The only argument must have the form x.c_str() or p->c_str()
+ // where the method is string::c_str(). StringRef also has
+ // a constructor from string which is more efficient (avoids
+ // strlen), so we can construct StringRef from the string
+ // directly.
+ hasArgument(0, StringCStrCallExpr))),
+ this);
+}
+
+void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
+ const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
+ bool Arrow = Member->isArrow();
+ // Replace the "call" node with the "arg" node, prefixed with '*'
+ // if the call was using '->' rather than '.'.
+ std::string ArgText =
+ Arrow ? formatDereference(Result, *Arg)
+ : tooling::fixit::getText(*Arg, *Result.Context).str();
+ if (ArgText.empty())
+ return;
+
+ diag(Call->getBeginLoc(), "redundant call to %0")
+ << Member->getMemberDecl()
+ << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.h
new file mode 100644
index 0000000000..662fb955fc
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringCStrCheck.h
@@ -0,0 +1,30 @@
+//===--- RedundantStringCStrCheck.h - clang-tidy ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds unnecessary calls to `std::string::c_str()`.
+class RedundantStringCStrCheck : public ClangTidyCheck {
+public:
+ RedundantStringCStrCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.cpp
new file mode 100644
index 0000000000..b579aafe8e
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.cpp
@@ -0,0 +1,163 @@
+//===- RedundantStringInitCheck.cpp - 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantStringInitCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include <optional>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+const char DefaultStringNames[] =
+ "::std::basic_string_view;::std::basic_string";
+
+static std::vector<StringRef> removeNamespaces(ArrayRef<StringRef> Names) {
+ std::vector<StringRef> Result;
+ Result.reserve(Names.size());
+ for (StringRef Name : Names) {
+ StringRef::size_type ColonPos = Name.rfind(':');
+ Result.push_back(
+ Name.drop_front(ColonPos == StringRef::npos ? 0 : ColonPos + 1));
+ }
+ return Result;
+}
+
+static const CXXConstructExpr *
+getConstructExpr(const CXXCtorInitializer &CtorInit) {
+ const Expr *InitExpr = CtorInit.getInit();
+ if (const auto *CleanUpExpr = dyn_cast<ExprWithCleanups>(InitExpr))
+ InitExpr = CleanUpExpr->getSubExpr();
+ return dyn_cast<CXXConstructExpr>(InitExpr);
+}
+
+static std::optional<SourceRange>
+getConstructExprArgRange(const CXXConstructExpr &Construct) {
+ SourceLocation B, E;
+ for (const Expr *Arg : Construct.arguments()) {
+ if (B.isInvalid())
+ B = Arg->getBeginLoc();
+ if (Arg->getEndLoc().isValid())
+ E = Arg->getEndLoc();
+ }
+ if (B.isInvalid() || E.isInvalid())
+ return std::nullopt;
+ return SourceRange(B, E);
+}
+
+RedundantStringInitCheck::RedundantStringInitCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StringNames(utils::options::parseStringList(
+ Options.get("StringNames", DefaultStringNames))) {}
+
+void RedundantStringInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "StringNames", DefaultStringNames);
+}
+
+void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasStringTypeName = hasAnyName(StringNames);
+ const auto HasStringCtorName = hasAnyName(removeNamespaces(StringNames));
+
+ // Match string constructor.
+ const auto StringConstructorExpr = expr(
+ anyOf(cxxConstructExpr(argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(HasStringCtorName))),
+ // If present, the second argument is the alloc object which must
+ // not be present explicitly.
+ cxxConstructExpr(argumentCountIs(2),
+ hasDeclaration(cxxMethodDecl(HasStringCtorName)),
+ hasArgument(1, cxxDefaultArgExpr()))));
+
+ // Match a string constructor expression with an empty string literal.
+ const auto EmptyStringCtorExpr = cxxConstructExpr(
+ StringConstructorExpr,
+ hasArgument(0, ignoringParenImpCasts(stringLiteral(hasSize(0)))));
+
+ const auto EmptyStringCtorExprWithTemporaries =
+ cxxConstructExpr(StringConstructorExpr,
+ hasArgument(0, ignoringImplicit(EmptyStringCtorExpr)));
+
+ const auto StringType = hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(cxxRecordDecl(HasStringTypeName)))));
+ const auto EmptyStringInit = traverse(
+ TK_AsIs, expr(ignoringImplicit(anyOf(
+ EmptyStringCtorExpr, EmptyStringCtorExprWithTemporaries))));
+
+ // Match a variable declaration with an empty string literal as initializer.
+ // Examples:
+ // string foo = "";
+ // string bar("");
+ Finder->addMatcher(
+ traverse(TK_AsIs,
+ namedDecl(varDecl(StringType, hasInitializer(EmptyStringInit))
+ .bind("vardecl"),
+ unless(parmVarDecl()))),
+ this);
+ // Match a field declaration with an empty string literal as initializer.
+ Finder->addMatcher(
+ namedDecl(fieldDecl(StringType, hasInClassInitializer(EmptyStringInit))
+ .bind("fieldDecl")),
+ this);
+ // Matches Constructor Initializers with an empty string literal as
+ // initializer.
+ // Examples:
+ // Foo() : SomeString("") {}
+ Finder->addMatcher(
+ cxxCtorInitializer(
+ isWritten(),
+ forField(allOf(StringType, optionally(hasInClassInitializer(
+ EmptyStringInit.bind("empty_init"))))),
+ withInitializer(EmptyStringInit))
+ .bind("ctorInit"),
+ this);
+}
+
+void RedundantStringInitCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *VDecl = Result.Nodes.getNodeAs<VarDecl>("vardecl")) {
+ // VarDecl's getSourceRange() spans 'string foo = ""' or 'string bar("")'.
+ // So start at getLocation() to span just 'foo = ""' or 'bar("")'.
+ SourceRange ReplaceRange(VDecl->getLocation(), VDecl->getEndLoc());
+ diag(VDecl->getLocation(), "redundant string initialization")
+ << FixItHint::CreateReplacement(ReplaceRange, VDecl->getName());
+ }
+ if (const auto *FDecl = Result.Nodes.getNodeAs<FieldDecl>("fieldDecl")) {
+ // FieldDecl's getSourceRange() spans 'string foo = ""'.
+ // So start at getLocation() to span just 'foo = ""'.
+ SourceRange ReplaceRange(FDecl->getLocation(), FDecl->getEndLoc());
+ diag(FDecl->getLocation(), "redundant string initialization")
+ << FixItHint::CreateReplacement(ReplaceRange, FDecl->getName());
+ }
+ if (const auto *CtorInit =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("ctorInit")) {
+ if (const FieldDecl *Member = CtorInit->getMember()) {
+ if (!Member->hasInClassInitializer() ||
+ Result.Nodes.getNodeAs<Expr>("empty_init")) {
+ // The String isn't declared in the class with an initializer or its
+ // declared with a redundant initializer, which will be removed. Either
+ // way the string will be default initialized, therefore we can remove
+ // the constructor initializer entirely.
+ diag(CtorInit->getMemberLocation(), "redundant string initialization")
+ << FixItHint::CreateRemoval(CtorInit->getSourceRange());
+ return;
+ }
+ }
+ const CXXConstructExpr *Construct = getConstructExpr(*CtorInit);
+ if (!Construct)
+ return;
+ if (std::optional<SourceRange> RemovalRange =
+ getConstructExprArgRange(*Construct))
+ diag(CtorInit->getMemberLocation(), "redundant string initialization")
+ << FixItHint::CreateRemoval(*RemovalRange);
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.h
new file mode 100644
index 0000000000..853ea2fcd0
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/RedundantStringInitCheck.h
@@ -0,0 +1,35 @@
+//===- RedundantStringInitCheck.h - clang-tidy ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
+
+#include "../ClangTidyCheck.h"
+#include <string>
+#include <vector>
+
+namespace clang::tidy::readability {
+
+/// Finds unnecessary string initializations.
+class RedundantStringInitCheck : public ClangTidyCheck {
+public:
+ RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ std::vector<StringRef> StringNames;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
new file mode 100644
index 0000000000..07cb07f053
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
@@ -0,0 +1,958 @@
+//===-- SimplifyBooleanExprCheck.cpp - clang-tidy -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SimplifyBooleanExprCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/Support/SaveAndRestore.h"
+
+#include <optional>
+#include <string>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+StringRef getText(const ASTContext &Context, SourceRange Range) {
+ return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
+ Context.getSourceManager(),
+ Context.getLangOpts());
+}
+
+template <typename T> StringRef getText(const ASTContext &Context, T &Node) {
+ return getText(Context, Node.getSourceRange());
+}
+
+} // namespace
+
+static constexpr char SimplifyOperatorDiagnostic[] =
+ "redundant boolean literal supplied to boolean operator";
+static constexpr char SimplifyConditionDiagnostic[] =
+ "redundant boolean literal in if statement condition";
+static constexpr char SimplifyConditionalReturnDiagnostic[] =
+ "redundant boolean literal in conditional return statement";
+
+static bool needsParensAfterUnaryNegation(const Expr *E) {
+ E = E->IgnoreImpCasts();
+ if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
+ return true;
+
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
+ return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
+ Op->getOperator() != OO_Subscript;
+
+ return false;
+}
+
+static std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
+ {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
+
+static StringRef negatedOperator(const BinaryOperator *BinOp) {
+ const BinaryOperatorKind Opcode = BinOp->getOpcode();
+ for (auto NegatableOp : Opposites) {
+ if (Opcode == NegatableOp.first)
+ return BinOp->getOpcodeStr(NegatableOp.second);
+ if (Opcode == NegatableOp.second)
+ return BinOp->getOpcodeStr(NegatableOp.first);
+ }
+ return {};
+}
+
+static std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
+ {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
+ {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
+
+static StringRef getOperatorName(OverloadedOperatorKind OpKind) {
+ for (auto Name : OperatorNames) {
+ if (Name.first == OpKind)
+ return Name.second;
+ }
+
+ return {};
+}
+
+static std::pair<OverloadedOperatorKind, OverloadedOperatorKind>
+ OppositeOverloads[] = {{OO_EqualEqual, OO_ExclaimEqual},
+ {OO_Less, OO_GreaterEqual},
+ {OO_Greater, OO_LessEqual}};
+
+static StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
+ const OverloadedOperatorKind Opcode = OpCall->getOperator();
+ for (auto NegatableOp : OppositeOverloads) {
+ if (Opcode == NegatableOp.first)
+ return getOperatorName(NegatableOp.second);
+ if (Opcode == NegatableOp.second)
+ return getOperatorName(NegatableOp.first);
+ }
+ return {};
+}
+
+static std::string asBool(StringRef Text, bool NeedsStaticCast) {
+ if (NeedsStaticCast)
+ return ("static_cast<bool>(" + Text + ")").str();
+
+ return std::string(Text);
+}
+
+static bool needsNullPtrComparison(const Expr *E) {
+ if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
+ return ImpCast->getCastKind() == CK_PointerToBoolean ||
+ ImpCast->getCastKind() == CK_MemberPointerToBoolean;
+
+ return false;
+}
+
+static bool needsZeroComparison(const Expr *E) {
+ if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
+ return ImpCast->getCastKind() == CK_IntegralToBoolean;
+
+ return false;
+}
+
+static bool needsStaticCast(const Expr *E) {
+ if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
+ if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
+ ImpCast->getSubExpr()->getType()->isBooleanType()) {
+ if (const auto *MemCall =
+ dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
+ if (const auto *MemDecl =
+ dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
+ if (MemDecl->isExplicit())
+ return true;
+ }
+ }
+ }
+ }
+
+ E = E->IgnoreImpCasts();
+ return !E->getType()->isBooleanType();
+}
+
+static std::string compareExpressionToConstant(const ASTContext &Context,
+ const Expr *E, bool Negated,
+ const char *Constant) {
+ E = E->IgnoreImpCasts();
+ const std::string ExprText =
+ (isa<BinaryOperator>(E) ? ("(" + getText(Context, *E) + ")")
+ : getText(Context, *E))
+ .str();
+ return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
+}
+
+static std::string compareExpressionToNullPtr(const ASTContext &Context,
+ const Expr *E, bool Negated) {
+ const char *NullPtr = Context.getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
+ return compareExpressionToConstant(Context, E, Negated, NullPtr);
+}
+
+static std::string compareExpressionToZero(const ASTContext &Context,
+ const Expr *E, bool Negated) {
+ return compareExpressionToConstant(Context, E, Negated, "0");
+}
+
+static std::string replacementExpression(const ASTContext &Context,
+ bool Negated, const Expr *E) {
+ E = E->IgnoreParenBaseCasts();
+ if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
+ E = EC->getSubExpr();
+
+ const bool NeedsStaticCast = needsStaticCast(E);
+ if (Negated) {
+ if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+ if (UnOp->getOpcode() == UO_LNot) {
+ if (needsNullPtrComparison(UnOp->getSubExpr()))
+ return compareExpressionToNullPtr(Context, UnOp->getSubExpr(), true);
+
+ if (needsZeroComparison(UnOp->getSubExpr()))
+ return compareExpressionToZero(Context, UnOp->getSubExpr(), true);
+
+ return replacementExpression(Context, false, UnOp->getSubExpr());
+ }
+ }
+
+ if (needsNullPtrComparison(E))
+ return compareExpressionToNullPtr(Context, E, false);
+
+ if (needsZeroComparison(E))
+ return compareExpressionToZero(Context, E, false);
+
+ StringRef NegatedOperator;
+ const Expr *LHS = nullptr;
+ const Expr *RHS = nullptr;
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
+ NegatedOperator = negatedOperator(BinOp);
+ LHS = BinOp->getLHS();
+ RHS = BinOp->getRHS();
+ } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
+ if (OpExpr->getNumArgs() == 2) {
+ NegatedOperator = negatedOperator(OpExpr);
+ LHS = OpExpr->getArg(0);
+ RHS = OpExpr->getArg(1);
+ }
+ }
+ if (!NegatedOperator.empty() && LHS && RHS)
+ return (asBool((getText(Context, *LHS) + " " + NegatedOperator + " " +
+ getText(Context, *RHS))
+ .str(),
+ NeedsStaticCast));
+
+ StringRef Text = getText(Context, *E);
+ if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
+ return ("!(" + Text + ")").str();
+
+ if (needsNullPtrComparison(E))
+ return compareExpressionToNullPtr(Context, E, false);
+
+ if (needsZeroComparison(E))
+ return compareExpressionToZero(Context, E, false);
+
+ return ("!" + asBool(Text, NeedsStaticCast));
+ }
+
+ if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+ if (UnOp->getOpcode() == UO_LNot) {
+ if (needsNullPtrComparison(UnOp->getSubExpr()))
+ return compareExpressionToNullPtr(Context, UnOp->getSubExpr(), false);
+
+ if (needsZeroComparison(UnOp->getSubExpr()))
+ return compareExpressionToZero(Context, UnOp->getSubExpr(), false);
+ }
+ }
+
+ if (needsNullPtrComparison(E))
+ return compareExpressionToNullPtr(Context, E, true);
+
+ if (needsZeroComparison(E))
+ return compareExpressionToZero(Context, E, true);
+
+ return asBool(getText(Context, *E), NeedsStaticCast);
+}
+
+static bool containsDiscardedTokens(const ASTContext &Context,
+ CharSourceRange CharRange) {
+ std::string ReplacementText =
+ Lexer::getSourceText(CharRange, Context.getSourceManager(),
+ Context.getLangOpts())
+ .str();
+ Lexer Lex(CharRange.getBegin(), Context.getLangOpts(), ReplacementText.data(),
+ ReplacementText.data(),
+ ReplacementText.data() + ReplacementText.size());
+ Lex.SetCommentRetentionState(true);
+
+ Token Tok;
+ while (!Lex.LexFromRawLexer(Tok)) {
+ if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
+ return true;
+ }
+
+ return false;
+}
+
+class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
+ using Base = RecursiveASTVisitor<Visitor>;
+
+public:
+ Visitor(SimplifyBooleanExprCheck *Check, ASTContext &Context)
+ : Check(Check), Context(Context) {}
+
+ bool traverse() { return TraverseAST(Context); }
+
+ static bool shouldIgnore(Stmt *S) {
+ switch (S->getStmtClass()) {
+ case Stmt::ImplicitCastExprClass:
+ case Stmt::MaterializeTemporaryExprClass:
+ case Stmt::CXXBindTemporaryExprClass:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool dataTraverseStmtPre(Stmt *S) {
+ if (S && !shouldIgnore(S))
+ StmtStack.push_back(S);
+ return true;
+ }
+
+ bool dataTraverseStmtPost(Stmt *S) {
+ if (S && !shouldIgnore(S)) {
+ assert(StmtStack.back() == S);
+ StmtStack.pop_back();
+ }
+ return true;
+ }
+
+ bool VisitBinaryOperator(const BinaryOperator *Op) const {
+ Check->reportBinOp(Context, Op);
+ return true;
+ }
+
+ // Extracts a bool if an expression is (true|false|!true|!false);
+ static std::optional<bool> getAsBoolLiteral(const Expr *E, bool FilterMacro) {
+ if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(E)) {
+ if (FilterMacro && Bool->getBeginLoc().isMacroID())
+ return std::nullopt;
+ return Bool->getValue();
+ }
+ if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) {
+ if (FilterMacro && UnaryOp->getBeginLoc().isMacroID())
+ return std::nullopt;
+ if (UnaryOp->getOpcode() == UO_LNot)
+ if (std::optional<bool> Res = getAsBoolLiteral(
+ UnaryOp->getSubExpr()->IgnoreImplicit(), FilterMacro))
+ return !*Res;
+ }
+ return std::nullopt;
+ }
+
+ template <typename Node> struct NodeAndBool {
+ const Node *Item = nullptr;
+ bool Bool = false;
+
+ operator bool() const { return Item != nullptr; }
+ };
+
+ using ExprAndBool = NodeAndBool<Expr>;
+ using DeclAndBool = NodeAndBool<Decl>;
+
+ /// Detect's return (true|false|!true|!false);
+ static ExprAndBool parseReturnLiteralBool(const Stmt *S) {
+ const auto *RS = dyn_cast<ReturnStmt>(S);
+ if (!RS || !RS->getRetValue())
+ return {};
+ if (std::optional<bool> Ret =
+ getAsBoolLiteral(RS->getRetValue()->IgnoreImplicit(), false)) {
+ return {RS->getRetValue(), *Ret};
+ }
+ return {};
+ }
+
+ /// If \p S is not a \c CompoundStmt, applies F on \p S, otherwise if there is
+ /// only 1 statement in the \c CompoundStmt, applies F on that single
+ /// statement.
+ template <typename Functor>
+ static auto checkSingleStatement(Stmt *S, Functor F) -> decltype(F(S)) {
+ if (auto *CS = dyn_cast<CompoundStmt>(S)) {
+ if (CS->size() == 1)
+ return F(CS->body_front());
+ return {};
+ }
+ return F(S);
+ }
+
+ Stmt *parent() const {
+ return StmtStack.size() < 2 ? nullptr : StmtStack[StmtStack.size() - 2];
+ }
+
+ bool VisitIfStmt(IfStmt *If) {
+ // Skip any if's that have a condition var or an init statement, or are
+ // "if consteval" statements.
+ if (If->hasInitStorage() || If->hasVarStorage() || If->isConsteval())
+ return true;
+ /*
+ * if (true) ThenStmt(); -> ThenStmt();
+ * if (false) ThenStmt(); -> <Empty>;
+ * if (false) ThenStmt(); else ElseStmt() -> ElseStmt();
+ */
+ Expr *Cond = If->getCond()->IgnoreImplicit();
+ if (std::optional<bool> Bool = getAsBoolLiteral(Cond, true)) {
+ if (*Bool)
+ Check->replaceWithThenStatement(Context, If, Cond);
+ else
+ Check->replaceWithElseStatement(Context, If, Cond);
+ }
+
+ if (If->getElse()) {
+ /*
+ * if (Cond) return true; else return false; -> return Cond;
+ * if (Cond) return false; else return true; -> return !Cond;
+ */
+ if (ExprAndBool ThenReturnBool =
+ checkSingleStatement(If->getThen(), parseReturnLiteralBool)) {
+ ExprAndBool ElseReturnBool =
+ checkSingleStatement(If->getElse(), parseReturnLiteralBool);
+ if (ElseReturnBool && ThenReturnBool.Bool != ElseReturnBool.Bool) {
+ if (Check->ChainedConditionalReturn ||
+ !isa_and_nonnull<IfStmt>(parent())) {
+ Check->replaceWithReturnCondition(Context, If, ThenReturnBool.Item,
+ ElseReturnBool.Bool);
+ }
+ }
+ } else {
+ /*
+ * if (Cond) A = true; else A = false; -> A = Cond;
+ * if (Cond) A = false; else A = true; -> A = !Cond;
+ */
+ Expr *Var = nullptr;
+ SourceLocation Loc;
+ auto VarBoolAssignmentMatcher = [&Var,
+ &Loc](const Stmt *S) -> DeclAndBool {
+ const auto *BO = dyn_cast<BinaryOperator>(S);
+ if (!BO || BO->getOpcode() != BO_Assign)
+ return {};
+ std::optional<bool> RightasBool =
+ getAsBoolLiteral(BO->getRHS()->IgnoreImplicit(), false);
+ if (!RightasBool)
+ return {};
+ Expr *IgnImp = BO->getLHS()->IgnoreImplicit();
+ if (!Var) {
+ // We only need to track these for the Then branch.
+ Loc = BO->getRHS()->getBeginLoc();
+ Var = IgnImp;
+ }
+ if (auto *DRE = dyn_cast<DeclRefExpr>(IgnImp))
+ return {DRE->getDecl(), *RightasBool};
+ if (auto *ME = dyn_cast<MemberExpr>(IgnImp))
+ return {ME->getMemberDecl(), *RightasBool};
+ return {};
+ };
+ if (DeclAndBool ThenAssignment =
+ checkSingleStatement(If->getThen(), VarBoolAssignmentMatcher)) {
+ DeclAndBool ElseAssignment =
+ checkSingleStatement(If->getElse(), VarBoolAssignmentMatcher);
+ if (ElseAssignment.Item == ThenAssignment.Item &&
+ ElseAssignment.Bool != ThenAssignment.Bool) {
+ if (Check->ChainedConditionalAssignment ||
+ !isa_and_nonnull<IfStmt>(parent())) {
+ Check->replaceWithAssignment(Context, If, Var, Loc,
+ ElseAssignment.Bool);
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ bool VisitConditionalOperator(ConditionalOperator *Cond) {
+ /*
+ * Condition ? true : false; -> Condition
+ * Condition ? false : true; -> !Condition;
+ */
+ if (std::optional<bool> Then =
+ getAsBoolLiteral(Cond->getTrueExpr()->IgnoreImplicit(), false)) {
+ if (std::optional<bool> Else =
+ getAsBoolLiteral(Cond->getFalseExpr()->IgnoreImplicit(), false)) {
+ if (*Then != *Else)
+ Check->replaceWithCondition(Context, Cond, *Else);
+ }
+ }
+ return true;
+ }
+
+ bool VisitCompoundStmt(CompoundStmt *CS) {
+ if (CS->size() < 2)
+ return true;
+ bool CurIf = false, PrevIf = false;
+ for (auto First = CS->body_begin(), Second = std::next(First),
+ End = CS->body_end();
+ Second != End; ++Second, ++First) {
+ PrevIf = CurIf;
+ CurIf = isa<IfStmt>(*First);
+ ExprAndBool TrailingReturnBool = parseReturnLiteralBool(*Second);
+ if (!TrailingReturnBool)
+ continue;
+
+ if (CurIf) {
+ /*
+ * if (Cond) return true; return false; -> return Cond;
+ * if (Cond) return false; return true; -> return !Cond;
+ */
+ auto *If = cast<IfStmt>(*First);
+ if (!If->hasInitStorage() && !If->hasVarStorage() &&
+ !If->isConsteval()) {
+ ExprAndBool ThenReturnBool =
+ checkSingleStatement(If->getThen(), parseReturnLiteralBool);
+ if (ThenReturnBool &&
+ ThenReturnBool.Bool != TrailingReturnBool.Bool) {
+ if ((Check->ChainedConditionalReturn || !PrevIf) &&
+ If->getElse() == nullptr) {
+ Check->replaceCompoundReturnWithCondition(
+ Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool,
+ If, ThenReturnBool.Item);
+ }
+ }
+ }
+ } else if (isa<LabelStmt, CaseStmt, DefaultStmt>(*First)) {
+ /*
+ * (case X|label_X|default): if (Cond) return BoolLiteral;
+ * return !BoolLiteral
+ */
+ Stmt *SubStmt =
+ isa<LabelStmt>(*First) ? cast<LabelStmt>(*First)->getSubStmt()
+ : isa<CaseStmt>(*First) ? cast<CaseStmt>(*First)->getSubStmt()
+ : cast<DefaultStmt>(*First)->getSubStmt();
+ auto *SubIf = dyn_cast<IfStmt>(SubStmt);
+ if (SubIf && !SubIf->getElse() && !SubIf->hasInitStorage() &&
+ !SubIf->hasVarStorage() && !SubIf->isConsteval()) {
+ ExprAndBool ThenReturnBool =
+ checkSingleStatement(SubIf->getThen(), parseReturnLiteralBool);
+ if (ThenReturnBool &&
+ ThenReturnBool.Bool != TrailingReturnBool.Bool) {
+ Check->replaceCompoundReturnWithCondition(
+ Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool,
+ SubIf, ThenReturnBool.Item);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ static bool isUnaryLNot(const Expr *E) {
+ return isa<UnaryOperator>(E) &&
+ cast<UnaryOperator>(E)->getOpcode() == UO_LNot;
+ }
+
+ 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) {
+ const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreUnlessSpelledInSource());
+ if (!BO)
+ return false;
+ if (!BO->getType()->isBooleanType())
+ return false;
+ switch (BO->getOpcode()) {
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
+ 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;
+ default:
+ return false;
+ }
+ }
+
+ 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())
+ return Base::TraverseUnaryOperator(Op);
+ if (Check->SimplifyDeMorganRelaxed ||
+ checkEitherSide(BinaryOp, isUnaryLNot) ||
+ checkEitherSide(BinaryOp,
+ [](const Expr *E) { return nestedDemorgan(E, 1); })) {
+ if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(),
+ Parens) &&
+ !Check->areDiagsSelfContained()) {
+ llvm::SaveAndRestore RAII(IsProcessing, true);
+ return Base::TraverseUnaryOperator(Op);
+ }
+ }
+ return Base::TraverseUnaryOperator(Op);
+ }
+
+private:
+ bool IsProcessing = false;
+ SimplifyBooleanExprCheck *Check;
+ SmallVector<Stmt *, 32> StmtStack;
+ ASTContext &Context;
+};
+
+SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
+ ChainedConditionalAssignment(
+ Options.get("ChainedConditionalAssignment", false)),
+ SimplifyDeMorgan(Options.get("SimplifyDeMorgan", true)),
+ SimplifyDeMorganRelaxed(Options.get("SimplifyDeMorganRelaxed", false)) {
+ if (SimplifyDeMorganRelaxed && !SimplifyDeMorgan)
+ configurationDiag("%0: 'SimplifyDeMorganRelaxed' cannot be enabled "
+ "without 'SimplifyDeMorgan' enabled")
+ << Name;
+}
+
+static bool containsBoolLiteral(const Expr *E) {
+ if (!E)
+ return false;
+ E = E->IgnoreParenImpCasts();
+ if (isa<CXXBoolLiteralExpr>(E))
+ return true;
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
+ return containsBoolLiteral(BinOp->getLHS()) ||
+ containsBoolLiteral(BinOp->getRHS());
+ if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
+ return containsBoolLiteral(UnaryOp->getSubExpr());
+ return false;
+}
+
+void SimplifyBooleanExprCheck::reportBinOp(const ASTContext &Context,
+ const BinaryOperator *Op) {
+ const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
+ const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
+
+ const CXXBoolLiteralExpr *Bool;
+ const Expr *Other;
+ if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)) != nullptr)
+ Other = RHS;
+ else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)) != nullptr)
+ Other = LHS;
+ else
+ return;
+
+ if (Bool->getBeginLoc().isMacroID())
+ return;
+
+ // FIXME: why do we need this?
+ if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
+ return;
+
+ bool BoolValue = Bool->getValue();
+
+ auto ReplaceWithExpression = [this, &Context, LHS, RHS,
+ Bool](const Expr *ReplaceWith, bool Negated) {
+ std::string Replacement =
+ replacementExpression(Context, Negated, ReplaceWith);
+ SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
+ issueDiag(Context, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
+ Replacement);
+ };
+
+ switch (Op->getOpcode()) {
+ case BO_LAnd:
+ if (BoolValue)
+ // expr && true -> expr
+ ReplaceWithExpression(Other, /*Negated=*/false);
+ else
+ // expr && false -> false
+ ReplaceWithExpression(Bool, /*Negated=*/false);
+ break;
+ case BO_LOr:
+ if (BoolValue)
+ // expr || true -> true
+ ReplaceWithExpression(Bool, /*Negated=*/false);
+ else
+ // expr || false -> expr
+ ReplaceWithExpression(Other, /*Negated=*/false);
+ break;
+ case BO_EQ:
+ // expr == true -> expr, expr == false -> !expr
+ ReplaceWithExpression(Other, /*Negated=*/!BoolValue);
+ break;
+ case BO_NE:
+ // expr != true -> !expr, expr != false -> expr
+ ReplaceWithExpression(Other, /*Negated=*/BoolValue);
+ break;
+ default:
+ break;
+ }
+}
+
+void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
+ Options.store(Opts, "ChainedConditionalAssignment",
+ ChainedConditionalAssignment);
+ Options.store(Opts, "SimplifyDeMorgan", SimplifyDeMorgan);
+ Options.store(Opts, "SimplifyDeMorganRelaxed", SimplifyDeMorganRelaxed);
+}
+
+void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(translationUnitDecl(), this);
+}
+
+void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
+ Visitor(this, *Result.Context).traverse();
+}
+
+void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context,
+ SourceLocation Loc,
+ StringRef Description,
+ SourceRange ReplacementRange,
+ StringRef Replacement) {
+ CharSourceRange CharRange =
+ Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
+ Context.getSourceManager(), getLangOpts());
+
+ DiagnosticBuilder Diag = diag(Loc, Description);
+ if (!containsDiscardedTokens(Context, CharRange))
+ Diag << FixItHint::CreateReplacement(CharRange, Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithThenStatement(
+ const ASTContext &Context, const IfStmt *IfStatement,
+ const Expr *BoolLiteral) {
+ issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic,
+ IfStatement->getSourceRange(),
+ getText(Context, *IfStatement->getThen()));
+}
+
+void SimplifyBooleanExprCheck::replaceWithElseStatement(
+ const ASTContext &Context, const IfStmt *IfStatement,
+ const Expr *BoolLiteral) {
+ const Stmt *ElseStatement = IfStatement->getElse();
+ issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic,
+ IfStatement->getSourceRange(),
+ ElseStatement ? getText(Context, *ElseStatement) : "");
+}
+
+void SimplifyBooleanExprCheck::replaceWithCondition(
+ const ASTContext &Context, const ConditionalOperator *Ternary,
+ bool Negated) {
+ std::string Replacement =
+ replacementExpression(Context, Negated, Ternary->getCond());
+ issueDiag(Context, Ternary->getTrueExpr()->getBeginLoc(),
+ "redundant boolean literal in ternary expression result",
+ Ternary->getSourceRange(), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithReturnCondition(
+ const ASTContext &Context, const IfStmt *If, const Expr *BoolLiteral,
+ bool Negated) {
+ StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
+ std::string Condition =
+ replacementExpression(Context, Negated, If->getCond());
+ std::string Replacement = ("return " + Condition + Terminator).str();
+ SourceLocation Start = BoolLiteral->getBeginLoc();
+ issueDiag(Context, Start, SimplifyConditionalReturnDiagnostic,
+ If->getSourceRange(), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
+ const ASTContext &Context, const ReturnStmt *Ret, bool Negated,
+ const IfStmt *If, const Expr *ThenReturn) {
+ const std::string Replacement =
+ "return " + replacementExpression(Context, Negated, If->getCond());
+ issueDiag(Context, ThenReturn->getBeginLoc(),
+ SimplifyConditionalReturnDiagnostic,
+ SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context,
+ const IfStmt *IfAssign,
+ const Expr *Var,
+ SourceLocation Loc,
+ bool Negated) {
+ SourceRange Range = IfAssign->getSourceRange();
+ StringRef VariableName = getText(Context, *Var);
+ StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
+ std::string Condition =
+ replacementExpression(Context, Negated, IfAssign->getCond());
+ std::string Replacement =
+ (VariableName + " = " + Condition + Terminator).str();
+ issueDiag(Context, Loc, "redundant boolean literal in conditional assignment",
+ Range, Replacement);
+}
+
+/// Swaps a \c BinaryOperator opcode from `&&` to `||` or vice-versa.
+static bool flipDemorganOperator(llvm::SmallVectorImpl<FixItHint> &Output,
+ const BinaryOperator *BO) {
+ assert(BO->isLogicalOp());
+ if (BO->getOperatorLoc().isMacroID())
+ return true;
+ Output.push_back(FixItHint::CreateReplacement(
+ BO->getOperatorLoc(), BO->getOpcode() == BO_LAnd ? "||" : "&&"));
+ return false;
+}
+
+static BinaryOperatorKind getDemorganFlippedOperator(BinaryOperatorKind BO) {
+ assert(BinaryOperator::isLogicalOp(BO));
+ return BO == BO_LAnd ? BO_LOr : BO_LAnd;
+}
+
+static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx, const Expr *E,
+ std::optional<BinaryOperatorKind> OuterBO);
+
+/// Inverts \p BinOp, Removing \p Parens if they exist and are safe to remove.
+/// returns \c true if there is any issue building the Fixes, \c false
+/// otherwise.
+static bool
+flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx, const BinaryOperator *BinOp,
+ std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ switch (BinOp->getOpcode()) {
+ case BO_LAnd:
+ case BO_LOr: {
+ // if we have 'a && b' or 'a || b', use demorgan to flip it to '!a || !b'
+ // or '!a && !b'.
+ if (flipDemorganOperator(Fixes, BinOp))
+ return true;
+ auto NewOp = getDemorganFlippedOperator(BinOp->getOpcode());
+ if (OuterBO) {
+ // The inner parens are technically needed in a fix for
+ // `!(!A1 && !(A2 || A3)) -> (A1 || (A2 && A3))`,
+ // however this would trip the LogicalOpParentheses warning.
+ // FIXME: Make this user configurable or detect if that warning is
+ // enabled.
+ constexpr bool LogicalOpParentheses = true;
+ if (((*OuterBO == NewOp) || (!LogicalOpParentheses &&
+ (*OuterBO == BO_LOr && NewOp == BO_LAnd))) &&
+ Parens) {
+ if (!Parens->getLParen().isMacroID() &&
+ !Parens->getRParen().isMacroID()) {
+ Fixes.push_back(FixItHint::CreateRemoval(Parens->getLParen()));
+ Fixes.push_back(FixItHint::CreateRemoval(Parens->getRParen()));
+ }
+ }
+ if (*OuterBO == BO_LAnd && NewOp == BO_LOr && !Parens) {
+ Fixes.push_back(FixItHint::CreateInsertion(BinOp->getBeginLoc(), "("));
+ Fixes.push_back(FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
+ Ctx.getSourceManager(),
+ Ctx.getLangOpts()),
+ ")"));
+ }
+ }
+ if (flipDemorganSide(Fixes, Ctx, BinOp->getLHS(), NewOp) ||
+ flipDemorganSide(Fixes, Ctx, BinOp->getRHS(), NewOp))
+ return true;
+ return false;
+ };
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
+ // For comparison operators, just negate the comparison.
+ if (BinOp->getOperatorLoc().isMacroID())
+ return true;
+ Fixes.push_back(FixItHint::CreateReplacement(
+ BinOp->getOperatorLoc(),
+ BinaryOperator::getOpcodeStr(
+ BinaryOperator::negateComparisonOp(BinOp->getOpcode()))));
+ return false;
+ default:
+ // for any other binary operator, just use logical not and wrap in
+ // parens.
+ if (Parens) {
+ if (Parens->getBeginLoc().isMacroID())
+ return true;
+ Fixes.push_back(FixItHint::CreateInsertion(Parens->getBeginLoc(), "!"));
+ } else {
+ if (BinOp->getBeginLoc().isMacroID() || BinOp->getEndLoc().isMacroID())
+ return true;
+ Fixes.append({FixItHint::CreateInsertion(BinOp->getBeginLoc(), "!("),
+ FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
+ Ctx.getSourceManager(),
+ Ctx.getLangOpts()),
+ ")")});
+ }
+ break;
+ }
+ return false;
+}
+
+static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx, const Expr *E,
+ std::optional<BinaryOperatorKind> OuterBO) {
+ if (isa<UnaryOperator>(E) && cast<UnaryOperator>(E)->getOpcode() == UO_LNot) {
+ // if we have a not operator, '!a', just remove the '!'.
+ if (cast<UnaryOperator>(E)->getOperatorLoc().isMacroID())
+ return true;
+ Fixes.push_back(
+ FixItHint::CreateRemoval(cast<UnaryOperator>(E)->getOperatorLoc()));
+ return false;
+ }
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
+ return flipDemorganBinaryOperator(Fixes, Ctx, BinOp, OuterBO);
+ }
+ if (const auto *Paren = dyn_cast<ParenExpr>(E)) {
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(Paren->getSubExpr())) {
+ return flipDemorganBinaryOperator(Fixes, Ctx, BinOp, OuterBO, Paren);
+ }
+ }
+ // Fallback case just insert a logical not operator.
+ if (E->getBeginLoc().isMacroID())
+ return true;
+ Fixes.push_back(FixItHint::CreateInsertion(E->getBeginLoc(), "!"));
+ return false;
+}
+
+static bool shouldRemoveParens(const Stmt *Parent,
+ BinaryOperatorKind NewOuterBinary,
+ const ParenExpr *Parens) {
+ if (!Parens)
+ return false;
+ if (!Parent)
+ return true;
+ switch (Parent->getStmtClass()) {
+ case Stmt::BinaryOperatorClass: {
+ const auto *BO = cast<BinaryOperator>(Parent);
+ if (BO->isAssignmentOp())
+ return true;
+ if (BO->isCommaOp())
+ return true;
+ if (BO->getOpcode() == NewOuterBinary)
+ return true;
+ return false;
+ }
+ case Stmt::UnaryOperatorClass:
+ case Stmt::CXXRewrittenBinaryOperatorClass:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool SimplifyBooleanExprCheck::reportDeMorgan(const ASTContext &Context,
+ const UnaryOperator *Outer,
+ const BinaryOperator *Inner,
+ bool TryOfferFix,
+ const Stmt *Parent,
+ const ParenExpr *Parens) {
+ assert(Outer);
+ assert(Inner);
+ assert(Inner->isLogicalOp());
+
+ auto Diag =
+ diag(Outer->getBeginLoc(),
+ "boolean expression can be simplified by DeMorgan's theorem");
+ Diag << Outer->getSourceRange();
+ // If we have already fixed this with a previous fix, don't attempt any fixes
+ if (!TryOfferFix)
+ return false;
+ if (Outer->getOperatorLoc().isMacroID())
+ return false;
+ SmallVector<FixItHint> Fixes;
+ auto NewOpcode = getDemorganFlippedOperator(Inner->getOpcode());
+ if (shouldRemoveParens(Parent, NewOpcode, Parens)) {
+ Fixes.push_back(FixItHint::CreateRemoval(
+ SourceRange(Outer->getOperatorLoc(), Parens->getLParen())));
+ Fixes.push_back(FixItHint::CreateRemoval(Parens->getRParen()));
+ } else {
+ Fixes.push_back(FixItHint::CreateRemoval(Outer->getOperatorLoc()));
+ }
+ if (flipDemorganOperator(Fixes, Inner))
+ return false;
+ if (flipDemorganSide(Fixes, Context, Inner->getLHS(), NewOpcode) ||
+ flipDemorganSide(Fixes, Context, Inner->getRHS(), NewOpcode))
+ return false;
+ Diag << Fixes;
+ return true;
+}
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.h
new file mode 100644
index 0000000000..f9ed331b7b
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifyBooleanExprCheck.h
@@ -0,0 +1,75 @@
+//===--- SimplifyBooleanExpr.h clang-tidy -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Looks for boolean expressions involving boolean constants and simplifies
+/// them to use the appropriate boolean expression directly.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/simplify-boolean-expr.html
+class SimplifyBooleanExprCheck : public ClangTidyCheck {
+public:
+ SimplifyBooleanExprCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Options) 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:
+ class Visitor;
+
+ void reportBinOp(const ASTContext &Context, const BinaryOperator *Op);
+
+ void replaceWithThenStatement(const ASTContext &Context,
+ const IfStmt *IfStatement,
+ const Expr *BoolLiteral);
+
+ void replaceWithElseStatement(const ASTContext &Context,
+ const IfStmt *IfStatement,
+ const Expr *BoolLiteral);
+
+ void replaceWithCondition(const ASTContext &Context,
+ const ConditionalOperator *Ternary, bool Negated);
+
+ void replaceWithReturnCondition(const ASTContext &Context, const IfStmt *If,
+ const Expr *BoolLiteral, bool Negated);
+
+ void replaceWithAssignment(const ASTContext &Context, const IfStmt *If,
+ const Expr *Var, SourceLocation Loc, bool Negated);
+
+ void replaceCompoundReturnWithCondition(const ASTContext &Context,
+ const ReturnStmt *Ret, bool Negated,
+ const IfStmt *If,
+ const Expr *ThenReturn);
+
+ bool reportDeMorgan(const ASTContext &Context, const UnaryOperator *Outer,
+ const BinaryOperator *Inner, bool TryOfferFix,
+ const Stmt *Parent, const ParenExpr *Parens);
+
+ void issueDiag(const ASTContext &Result, SourceLocation Loc,
+ StringRef Description, SourceRange ReplacementRange,
+ StringRef Replacement);
+
+ const bool ChainedConditionalReturn;
+ const bool ChainedConditionalAssignment;
+ const bool SimplifyDeMorgan;
+ const bool SimplifyDeMorganRelaxed;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.cpp
new file mode 100644
index 0000000000..d274abcbfa
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.cpp
@@ -0,0 +1,67 @@
+//===--- SimplifySubscriptExprCheck.cpp - clang-tidy-----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SimplifySubscriptExprCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static const char KDefaultTypes[] =
+ "::std::basic_string;::std::basic_string_view;::std::vector;::std::array";
+
+SimplifySubscriptExprCheck::SimplifySubscriptExprCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), Types(utils::options::parseStringList(
+ Options.get("Types", KDefaultTypes))) {
+}
+
+void SimplifySubscriptExprCheck::registerMatchers(MatchFinder *Finder) {
+ const auto TypesMatcher = hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(cxxRecordDecl(hasAnyName(Types)))));
+
+ Finder->addMatcher(
+ arraySubscriptExpr(hasBase(
+ cxxMemberCallExpr(
+ has(memberExpr().bind("member")),
+ on(hasType(qualType(
+ unless(anyOf(substTemplateTypeParmType(),
+ hasDescendant(substTemplateTypeParmType()))),
+ anyOf(TypesMatcher, pointerType(pointee(TypesMatcher)))))),
+ callee(namedDecl(hasName("data"))))
+ .bind("call"))),
+ this);
+}
+
+void SimplifySubscriptExprCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
+ if (Result.Context->getSourceManager().isMacroBodyExpansion(
+ Call->getExprLoc()))
+ return;
+
+ const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
+ auto DiagBuilder =
+ diag(Member->getMemberLoc(),
+ "accessing an element of the container does not require a call to "
+ "'data()'; did you mean to use 'operator[]'?");
+ if (Member->isArrow())
+ DiagBuilder << FixItHint::CreateInsertion(Member->getBeginLoc(), "(*")
+ << FixItHint::CreateInsertion(Member->getOperatorLoc(), ")");
+ DiagBuilder << FixItHint::CreateRemoval(
+ {Member->getOperatorLoc(), Call->getEndLoc()});
+}
+
+void SimplifySubscriptExprCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "Types", utils::options::serializeStringList(Types));
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.h
new file mode 100644
index 0000000000..83bab16b2f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SimplifySubscriptExprCheck.h
@@ -0,0 +1,39 @@
+//===--- SimplifySubscriptExprCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFYSUBSCRIPTEXPRCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFYSUBSCRIPTEXPRCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Simplifies subscript expressions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/simplify-subscript-expr.html
+class SimplifySubscriptExprCheck : public ClangTidyCheck {
+public:
+ SimplifySubscriptExprCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap& Opts) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const std::vector<StringRef> Types;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFYSUBSCRIPTEXPRCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp
new file mode 100644
index 0000000000..b23b67e626
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp
@@ -0,0 +1,95 @@
+//===--- StaticAccessedThroughInstanceCheck.cpp - clang-tidy---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StaticAccessedThroughInstanceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static unsigned getNameSpecifierNestingLevel(const QualType &QType) {
+ if (const ElaboratedType *ElType = QType->getAs<ElaboratedType>()) {
+ if (const NestedNameSpecifier *NestedSpecifiers = ElType->getQualifier()) {
+ unsigned NameSpecifierNestingLevel = 1;
+ do {
+ NameSpecifierNestingLevel++;
+ NestedSpecifiers = NestedSpecifiers->getPrefix();
+ } while (NestedSpecifiers);
+
+ return NameSpecifierNestingLevel;
+ }
+ }
+ return 0;
+}
+
+void StaticAccessedThroughInstanceCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "NameSpecifierNestingThreshold",
+ NameSpecifierNestingThreshold);
+}
+
+void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStaticStorageClass()),
+ varDecl(hasStaticStorageDuration()))))
+ .bind("memberExpression"),
+ this);
+}
+
+void StaticAccessedThroughInstanceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MemberExpression =
+ Result.Nodes.getNodeAs<MemberExpr>("memberExpression");
+
+ if (MemberExpression->getBeginLoc().isMacroID())
+ return;
+
+ const Expr *BaseExpr = MemberExpression->getBase();
+
+ // Do not warn for overloaded -> operators.
+ if (isa<CXXOperatorCallExpr>(BaseExpr))
+ return;
+
+ QualType BaseType =
+ BaseExpr->getType()->isPointerType()
+ ? BaseExpr->getType()->getPointeeType().getUnqualifiedType()
+ : BaseExpr->getType().getUnqualifiedType();
+
+ const ASTContext *AstContext = Result.Context;
+ PrintingPolicy PrintingPolicyWithSupressedTag(AstContext->getLangOpts());
+ PrintingPolicyWithSupressedTag.SuppressTagKeyword = true;
+ PrintingPolicyWithSupressedTag.SuppressUnwrittenScope = true;
+
+ PrintingPolicyWithSupressedTag.PrintCanonicalTypes =
+ !BaseExpr->getType()->isTypedefNameType();
+
+ std::string BaseTypeName =
+ BaseType.getAsString(PrintingPolicyWithSupressedTag);
+
+ // Do not warn for CUDA built-in variables.
+ if (StringRef(BaseTypeName).startswith("__cuda_builtin_"))
+ return;
+
+ SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc();
+ auto Diag =
+ diag(MemberExprStartLoc, "static member accessed through instance");
+
+ if (BaseExpr->HasSideEffects(*AstContext) ||
+ getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold)
+ return;
+
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(MemberExprStartLoc,
+ MemberExpression->getMemberLoc()),
+ BaseTypeName + "::");
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h
new file mode 100644
index 0000000000..4044f13769
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h
@@ -0,0 +1,41 @@
+//===--- StaticAccessedThroughInstanceCheck.h - clang-tidy-------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_ACCESSED_THROUGH_INSTANCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_ACCESSED_THROUGH_INSTANCE_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks for member expressions that access static members through
+/// instances and replaces them with uses of the appropriate qualified-id.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/static-accessed-through-instance.html
+class StaticAccessedThroughInstanceCheck : public ClangTidyCheck {
+public:
+ StaticAccessedThroughInstanceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ NameSpecifierNestingThreshold(
+ Options.get("NameSpecifierNestingThreshold", 3U)) {}
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const unsigned NameSpecifierNestingThreshold;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_ACCESSED_THROUGH_INSTANCE_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp
new file mode 100644
index 0000000000..3e6bb01b55
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp
@@ -0,0 +1,61 @@
+//===--- StaticDefinitionInAnonymousNamespaceCheck.cpp - clang-tidy--------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StaticDefinitionInAnonymousNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void StaticDefinitionInAnonymousNamespaceCheck::registerMatchers(
+ MatchFinder *Finder) {
+ Finder->addMatcher(
+ namedDecl(anyOf(functionDecl(isDefinition(), isStaticStorageClass()),
+ varDecl(isDefinition(), isStaticStorageClass())),
+ isInAnonymousNamespace())
+ .bind("static-def"),
+ this);
+}
+
+void StaticDefinitionInAnonymousNamespaceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Def = Result.Nodes.getNodeAs<NamedDecl>("static-def");
+ // Skips all static definitions defined in Macro.
+ if (Def->getLocation().isMacroID())
+ return;
+
+ // Skips all static definitions in function scope.
+ const DeclContext *DC = Def->getDeclContext();
+ if (DC->getDeclKind() != Decl::Namespace)
+ return;
+
+ auto Diag =
+ diag(Def->getLocation(), "%0 is a static definition in "
+ "anonymous namespace; static is redundant here")
+ << Def;
+ Token Tok;
+ SourceLocation Loc = Def->getSourceRange().getBegin();
+ while (Loc < Def->getSourceRange().getEnd() &&
+ !Lexer::getRawToken(Loc, Tok, *Result.SourceManager, getLangOpts(),
+ true)) {
+ SourceRange TokenRange(Tok.getLocation(), Tok.getEndLoc());
+ StringRef SourceText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(TokenRange),
+ *Result.SourceManager, getLangOpts());
+ if (SourceText == "static") {
+ Diag << FixItHint::CreateRemoval(TokenRange);
+ break;
+ }
+ Loc = Tok.getEndLoc();
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h
new file mode 100644
index 0000000000..c28087e07e
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h
@@ -0,0 +1,31 @@
+//===--- StaticDefinitionInAnonymousNamespaceCheck.h - clang-tidy*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds static function and variable definitions in anonymous namespace.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/static-definition-in-anonymous-namespace.html
+class StaticDefinitionInAnonymousNamespaceCheck : public ClangTidyCheck {
+public:
+ StaticDefinitionInAnonymousNamespaceCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.cpp
new file mode 100644
index 0000000000..3b5d89c8c6
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.cpp
@@ -0,0 +1,76 @@
+//===-- StringCompareCheck.cpp - clang-tidy--------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringCompareCheck.h"
+#include "../utils/FixItHintUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static const StringRef CompareMessage = "do not use 'compare' to test equality "
+ "of strings; use the string equality "
+ "operator instead";
+
+void StringCompareCheck::registerMatchers(MatchFinder *Finder) {
+ const auto StrCompare = cxxMemberCallExpr(
+ callee(cxxMethodDecl(hasName("compare"),
+ ofClass(classTemplateSpecializationDecl(
+ hasName("::std::basic_string"))))),
+ hasArgument(0, expr().bind("str2")), argumentCountIs(1),
+ callee(memberExpr().bind("str1")));
+
+ // First and second case: cast str.compare(str) to boolean.
+ Finder->addMatcher(
+ traverse(TK_AsIs,
+ implicitCastExpr(hasImplicitDestinationType(booleanType()),
+ has(StrCompare))
+ .bind("match1")),
+ this);
+
+ // Third and fourth case: str.compare(str) == 0 and str.compare(str) != 0.
+ Finder->addMatcher(
+ binaryOperator(hasAnyOperatorName("==", "!="),
+ hasOperands(StrCompare.bind("compare"),
+ integerLiteral(equals(0)).bind("zero")))
+ .bind("match2"),
+ this);
+}
+
+void StringCompareCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match1")) {
+ diag(Matched->getBeginLoc(), CompareMessage);
+ return;
+ }
+
+ if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match2")) {
+ const ASTContext &Ctx = *Result.Context;
+
+ if (const auto *Zero = Result.Nodes.getNodeAs<Stmt>("zero")) {
+ const auto *Str1 = Result.Nodes.getNodeAs<MemberExpr>("str1");
+ const auto *Str2 = Result.Nodes.getNodeAs<Stmt>("str2");
+ const auto *Compare = Result.Nodes.getNodeAs<Stmt>("compare");
+
+ auto Diag = diag(Matched->getBeginLoc(), CompareMessage);
+
+ if (Str1->isArrow())
+ Diag << FixItHint::CreateInsertion(Str1->getBeginLoc(), "*");
+
+ Diag << tooling::fixit::createReplacement(*Zero, *Str2, Ctx)
+ << tooling::fixit::createReplacement(*Compare, *Str1->getBase(),
+ Ctx);
+ }
+ }
+
+ // FIXME: Add fixit to fix the code for case one and two (match1).
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.h
new file mode 100644
index 0000000000..812736d806
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/StringCompareCheck.h
@@ -0,0 +1,34 @@
+//===--- StringCompareCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STRINGCOMPARECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STRINGCOMPARECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// This check flags all calls compare when used to check for string
+/// equality or inequality.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/string-compare.html
+class StringCompareCheck : public ClangTidyCheck {
+public:
+ StringCompareCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STRINGCOMPARECHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
new file mode 100644
index 0000000000..7fe0c0982d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
@@ -0,0 +1,808 @@
+//===--- SuspiciousCallArgumentCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousCallArgumentCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <optional>
+#include <sstream>
+
+using namespace clang::ast_matchers;
+namespace optutils = clang::tidy::utils::options;
+
+namespace clang::tidy::readability {
+
+namespace {
+struct DefaultHeuristicConfiguration {
+ /// Whether the heuristic is to be enabled by default.
+ const bool Enabled;
+
+ /// The upper bound of % of similarity the two strings might have to be
+ /// considered dissimilar.
+ /// (For purposes of configuration, -1 if the heuristic is not configurable
+ /// with bounds.)
+ const int8_t DissimilarBelow;
+
+ /// The lower bound of % of similarity the two string must have to be
+ /// considered similar.
+ /// (For purposes of configuration, -1 if the heuristic is not configurable
+ /// with bounds.)
+ const int8_t SimilarAbove;
+
+ /// Can the heuristic be configured with bounds?
+ bool hasBounds() const { return DissimilarBelow > -1 && SimilarAbove > -1; }
+};
+} // namespace
+
+static constexpr std::size_t DefaultMinimumIdentifierNameLength = 3;
+
+static constexpr StringRef HeuristicToString[] = {
+ "Equality", "Abbreviation", "Prefix", "Suffix",
+ "Substring", "Levenshtein", "JaroWinkler", "Dice"};
+
+static constexpr DefaultHeuristicConfiguration Defaults[] = {
+ {true, -1, -1}, // Equality.
+ {true, -1, -1}, // Abbreviation.
+ {true, 25, 30}, // Prefix.
+ {true, 25, 30}, // Suffix.
+ {true, 40, 50}, // Substring.
+ {true, 50, 66}, // Levenshtein.
+ {true, 75, 85}, // Jaro-Winkler.
+ {true, 60, 70}, // Dice.
+};
+
+static_assert(
+ sizeof(HeuristicToString) / sizeof(HeuristicToString[0]) ==
+ SuspiciousCallArgumentCheck::HeuristicCount,
+ "Ensure that every heuristic has a corresponding stringified name");
+static_assert(sizeof(Defaults) / sizeof(Defaults[0]) ==
+ SuspiciousCallArgumentCheck::HeuristicCount,
+ "Ensure that every heuristic has a default configuration.");
+
+namespace {
+template <std::size_t I> struct HasWellConfiguredBounds {
+ static constexpr bool Value =
+ !((Defaults[I].DissimilarBelow == -1) ^ (Defaults[I].SimilarAbove == -1));
+ static_assert(Value, "A heuristic must either have a dissimilarity and "
+ "similarity bound, or neither!");
+};
+
+template <std::size_t I> struct HasWellConfiguredBoundsFold {
+ static constexpr bool Value = HasWellConfiguredBounds<I>::Value &&
+ HasWellConfiguredBoundsFold<I - 1>::Value;
+};
+
+template <> struct HasWellConfiguredBoundsFold<0> {
+ static constexpr bool Value = HasWellConfiguredBounds<0>::Value;
+};
+
+struct AllHeuristicsBoundsWellConfigured {
+ static constexpr bool Value =
+ HasWellConfiguredBoundsFold<SuspiciousCallArgumentCheck::HeuristicCount -
+ 1>::Value;
+};
+
+static_assert(AllHeuristicsBoundsWellConfigured::Value);
+} // namespace
+
+static constexpr llvm::StringLiteral DefaultAbbreviations = "addr=address;"
+ "arr=array;"
+ "attr=attribute;"
+ "buf=buffer;"
+ "cl=client;"
+ "cnt=count;"
+ "col=column;"
+ "cpy=copy;"
+ "dest=destination;"
+ "dist=distance"
+ "dst=distance;"
+ "elem=element;"
+ "hght=height;"
+ "i=index;"
+ "idx=index;"
+ "len=length;"
+ "ln=line;"
+ "lst=list;"
+ "nr=number;"
+ "num=number;"
+ "pos=position;"
+ "ptr=pointer;"
+ "ref=reference;"
+ "src=source;"
+ "srv=server;"
+ "stmt=statement;"
+ "str=string;"
+ "val=value;"
+ "var=variable;"
+ "vec=vector;"
+ "wdth=width";
+
+static constexpr std::size_t SmallVectorSize =
+ SuspiciousCallArgumentCheck::SmallVectorSize;
+
+/// Returns how many % X is of Y.
+static inline double percentage(double X, double Y) { return X / Y * 100.0; }
+
+static bool applyEqualityHeuristic(StringRef Arg, StringRef Param) {
+ return Arg.equals_insensitive(Param);
+}
+
+static bool applyAbbreviationHeuristic(
+ const llvm::StringMap<std::string> &AbbreviationDictionary, StringRef Arg,
+ StringRef Param) {
+ if (AbbreviationDictionary.find(Arg) != AbbreviationDictionary.end() &&
+ Param.equals(AbbreviationDictionary.lookup(Arg)))
+ return true;
+
+ if (AbbreviationDictionary.find(Param) != AbbreviationDictionary.end() &&
+ Arg.equals(AbbreviationDictionary.lookup(Param)))
+ return true;
+
+ return false;
+}
+
+/// Check whether the shorter String is a prefix of the longer String.
+static bool applyPrefixHeuristic(StringRef Arg, StringRef Param,
+ int8_t Threshold) {
+ StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
+ StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
+
+ if (Longer.startswith_insensitive(Shorter))
+ return percentage(Shorter.size(), Longer.size()) > Threshold;
+
+ return false;
+}
+
+/// Check whether the shorter String is a suffix of the longer String.
+static bool applySuffixHeuristic(StringRef Arg, StringRef Param,
+ int8_t Threshold) {
+ StringRef Shorter = Arg.size() < Param.size() ? Arg : Param;
+ StringRef Longer = Arg.size() >= Param.size() ? Arg : Param;
+
+ if (Longer.endswith_insensitive(Shorter))
+ return percentage(Shorter.size(), Longer.size()) > Threshold;
+
+ return false;
+}
+
+static bool applySubstringHeuristic(StringRef Arg, StringRef Param,
+ int8_t Threshold) {
+
+ std::size_t MaxLength = 0;
+ SmallVector<std::size_t, SmallVectorSize> Current(Param.size());
+ SmallVector<std::size_t, SmallVectorSize> Previous(Param.size());
+ std::string ArgLower = Arg.lower();
+ std::string ParamLower = Param.lower();
+
+ for (std::size_t I = 0; I < Arg.size(); ++I) {
+ for (std::size_t J = 0; J < Param.size(); ++J) {
+ if (ArgLower[I] == ParamLower[J]) {
+ if (I == 0 || J == 0)
+ Current[J] = 1;
+ else
+ Current[J] = 1 + Previous[J - 1];
+
+ MaxLength = std::max(MaxLength, Current[J]);
+ } else
+ Current[J] = 0;
+ }
+
+ Current.swap(Previous);
+ }
+
+ size_t LongerLength = std::max(Arg.size(), Param.size());
+ return percentage(MaxLength, LongerLength) > Threshold;
+}
+
+static bool applyLevenshteinHeuristic(StringRef Arg, StringRef Param,
+ int8_t Threshold) {
+ std::size_t LongerLength = std::max(Arg.size(), Param.size());
+ double Dist = Arg.edit_distance(Param);
+ Dist = (1.0 - Dist / LongerLength) * 100.0;
+ return Dist > Threshold;
+}
+
+// Based on http://en.wikipedia.org/wiki/Jaro–Winkler_distance.
+static bool applyJaroWinklerHeuristic(StringRef Arg, StringRef Param,
+ int8_t Threshold) {
+ std::size_t Match = 0, Transpos = 0;
+ std::ptrdiff_t ArgLen = Arg.size();
+ std::ptrdiff_t ParamLen = Param.size();
+ SmallVector<int, SmallVectorSize> ArgFlags(ArgLen);
+ SmallVector<int, SmallVectorSize> ParamFlags(ParamLen);
+ std::ptrdiff_t Range =
+ std::max(std::ptrdiff_t{0}, std::max(ArgLen, ParamLen) / 2 - 1);
+
+ // Calculate matching characters.
+ for (std::ptrdiff_t I = 0; I < ParamLen; ++I)
+ for (std::ptrdiff_t J = std::max(I - Range, std::ptrdiff_t{0}),
+ L = std::min(I + Range + 1, ArgLen);
+ J < L; ++J)
+ if (tolower(Param[I]) == tolower(Arg[J]) && !ArgFlags[J]) {
+ ArgFlags[J] = 1;
+ ParamFlags[I] = 1;
+ ++Match;
+ break;
+ }
+
+ if (!Match)
+ return false;
+
+ // Calculate character transpositions.
+ std::ptrdiff_t L = 0;
+ for (std::ptrdiff_t I = 0; I < ParamLen; ++I) {
+ if (ParamFlags[I] == 1) {
+ std::ptrdiff_t J;
+ for (J = L; J < ArgLen; ++J)
+ if (ArgFlags[J] == 1) {
+ L = J + 1;
+ break;
+ }
+
+ if (tolower(Param[I]) != tolower(Arg[J]))
+ ++Transpos;
+ }
+ }
+ Transpos /= 2;
+
+ // Jaro distance.
+ double MatchD = Match;
+ double Dist = ((MatchD / ArgLen) + (MatchD / ParamLen) +
+ ((MatchD - Transpos) / Match)) /
+ 3.0;
+
+ // Calculate common string prefix up to 4 chars.
+ L = 0;
+ for (std::ptrdiff_t I = 0;
+ I < std::min(std::min(ArgLen, ParamLen), std::ptrdiff_t{4}); ++I)
+ if (tolower(Arg[I]) == tolower(Param[I]))
+ ++L;
+
+ // Jaro-Winkler distance.
+ Dist = (Dist + (L * 0.1 * (1.0 - Dist))) * 100.0;
+ return Dist > Threshold;
+}
+
+// Based on http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient
+static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
+ int8_t Threshold) {
+ llvm::StringSet<> ArgBigrams;
+ llvm::StringSet<> ParamBigrams;
+
+ // Extract character bigrams from Arg.
+ for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Arg.size()) - 1;
+ ++I)
+ ArgBigrams.insert(Arg.substr(I, 2).lower());
+
+ // Extract character bigrams from Param.
+ for (std::ptrdiff_t I = 0; I < static_cast<std::ptrdiff_t>(Param.size()) - 1;
+ ++I)
+ ParamBigrams.insert(Param.substr(I, 2).lower());
+
+ std::size_t Intersection = 0;
+
+ // Find the intersection between the two sets.
+ for (auto IT = ParamBigrams.begin(); IT != ParamBigrams.end(); ++IT)
+ Intersection += ArgBigrams.count((IT->getKey()));
+
+ // Calculate Dice coefficient.
+ return percentage(Intersection * 2.0,
+ ArgBigrams.size() + ParamBigrams.size()) > Threshold;
+}
+
+/// Checks if ArgType binds to ParamType regarding reference-ness and
+/// cv-qualifiers.
+static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
+ return !ParamType->isReferenceType() ||
+ ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
+ ArgType.getNonReferenceType());
+}
+
+static bool isPointerOrArray(QualType TypeToCheck) {
+ return TypeToCheck->isPointerType() || TypeToCheck->isArrayType();
+}
+
+/// Checks whether ArgType is an array type identical to ParamType's array type.
+/// Enforces array elements' qualifier compatibility as well.
+static bool isCompatibleWithArrayReference(QualType ArgType,
+ QualType ParamType) {
+ if (!ArgType->isArrayType())
+ return false;
+ // Here, qualifiers belong to the elements of the arrays.
+ if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
+ return false;
+
+ return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
+}
+
+static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) {
+ unsigned CVRqualifiers = 0;
+ // Save array element qualifiers, since getElementType() removes qualifiers
+ // from array elements.
+ if (TypeToConvert->isArrayType())
+ CVRqualifiers = TypeToConvert.getLocalQualifiers().getCVRQualifiers();
+ TypeToConvert = TypeToConvert->isPointerType()
+ ? TypeToConvert->getPointeeType()
+ : TypeToConvert->getAsArrayTypeUnsafe()->getElementType();
+ TypeToConvert = TypeToConvert.withCVRQualifiers(CVRqualifiers);
+ return TypeToConvert;
+}
+
+/// Checks if multilevel pointers' qualifiers compatibility continues on the
+/// current pointer level. For multilevel pointers, C++ permits conversion, if
+/// every cv-qualifier in ArgType also appears in the corresponding position in
+/// ParamType, and if PramType has a cv-qualifier that's not in ArgType, then
+/// every * in ParamType to the right of that cv-qualifier, except the last
+/// one, must also be const-qualified.
+static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
+ bool &IsParamContinuouslyConst) {
+ // The types are compatible, if the parameter is at least as qualified as the
+ // argument, and if it is more qualified, it has to be const on upper pointer
+ // levels.
+ bool AreTypesQualCompatible =
+ ParamType.isAtLeastAsQualifiedAs(ArgType) &&
+ (!ParamType.hasQualifiers() || IsParamContinuouslyConst);
+ // Check whether the parameter's constness continues at the current pointer
+ // level.
+ IsParamContinuouslyConst &= ParamType.isConstQualified();
+
+ return AreTypesQualCompatible;
+}
+
+/// Checks whether multilevel pointers are compatible in terms of levels,
+/// qualifiers and pointee type.
+static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
+ bool IsParamContinuouslyConst) {
+ if (!arePointersStillQualCompatible(ArgType, ParamType,
+ IsParamContinuouslyConst))
+ return false;
+
+ do {
+ // Step down one pointer level.
+ ArgType = convertToPointeeOrArrayElementQualType(ArgType);
+ ParamType = convertToPointeeOrArrayElementQualType(ParamType);
+
+ // Check whether cv-qualifiers permit compatibility on
+ // current level.
+ if (!arePointersStillQualCompatible(ArgType, ParamType,
+ IsParamContinuouslyConst))
+ return false;
+
+ if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+ return true;
+
+ } while (ParamType->isPointerType() && ArgType->isPointerType());
+ // The final type does not match, or pointer levels differ.
+ return false;
+}
+
+/// Checks whether ArgType converts implicitly to ParamType.
+static bool areTypesCompatible(QualType ArgType, QualType ParamType,
+ const ASTContext &Ctx) {
+ if (ArgType.isNull() || ParamType.isNull())
+ return false;
+
+ ArgType = ArgType.getCanonicalType();
+ ParamType = ParamType.getCanonicalType();
+
+ if (ArgType == ParamType)
+ return true;
+
+ // Check for constness and reference compatibility.
+ if (!areRefAndQualCompatible(ArgType, ParamType))
+ return false;
+
+ bool IsParamReference = ParamType->isReferenceType();
+
+ // Reference-ness has already been checked and should be removed
+ // before further checking.
+ ArgType = ArgType.getNonReferenceType();
+ ParamType = ParamType.getNonReferenceType();
+
+ if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+ return true;
+
+ // Arithmetic types are interconvertible, except scoped enums.
+ if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) {
+ if ((ParamType->isEnumeralType() &&
+ ParamType->castAs<EnumType>()->getDecl()->isScoped()) ||
+ (ArgType->isEnumeralType() &&
+ ArgType->castAs<EnumType>()->getDecl()->isScoped()))
+ return false;
+
+ return true;
+ }
+
+ // Check if the argument and the param are both function types (the parameter
+ // decayed to a function pointer).
+ if (ArgType->isFunctionType() && ParamType->isFunctionPointerType()) {
+ ParamType = ParamType->getPointeeType();
+ return ArgType == ParamType;
+ }
+
+ // Arrays or pointer arguments convert to array or pointer parameters.
+ if (!(isPointerOrArray(ArgType) && isPointerOrArray(ParamType)))
+ return false;
+
+ // When ParamType is an array reference, ArgType has to be of the same-sized
+ // array-type with cv-compatible element type.
+ if (IsParamReference && ParamType->isArrayType())
+ return isCompatibleWithArrayReference(ArgType, ParamType);
+
+ bool IsParamContinuouslyConst =
+ !IsParamReference || ParamType.getNonReferenceType().isConstQualified();
+
+ // Remove the first level of indirection.
+ ArgType = convertToPointeeOrArrayElementQualType(ArgType);
+ ParamType = convertToPointeeOrArrayElementQualType(ParamType);
+
+ // Check qualifier compatibility on the next level.
+ if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
+ return false;
+
+ if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
+ return true;
+
+ // At this point, all possible C language implicit conversion were checked.
+ if (!Ctx.getLangOpts().CPlusPlus)
+ return false;
+
+ // Check whether ParamType and ArgType were both pointers to a class or a
+ // struct, and check for inheritance.
+ if (ParamType->isStructureOrClassType() &&
+ ArgType->isStructureOrClassType()) {
+ const auto *ArgDecl = ArgType->getAsCXXRecordDecl();
+ const auto *ParamDecl = ParamType->getAsCXXRecordDecl();
+ if (!ArgDecl || !ArgDecl->hasDefinition() || !ParamDecl ||
+ !ParamDecl->hasDefinition())
+ return false;
+
+ return ArgDecl->isDerivedFrom(ParamDecl);
+ }
+
+ // Unless argument and param are both multilevel pointers, the types are not
+ // convertible.
+ if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
+ return false;
+
+ return arePointerTypesCompatible(ArgType, ParamType,
+ IsParamContinuouslyConst);
+}
+
+static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
+ switch (FD->getOverloadedOperator()) {
+ case OO_None:
+ case OO_Call:
+ case OO_Subscript:
+ case OO_New:
+ case OO_Delete:
+ case OO_Array_New:
+ case OO_Array_Delete:
+ case OO_Conditional:
+ case OO_Coawait:
+ return false;
+
+ default:
+ return FD->getNumParams() <= 2;
+ }
+}
+
+SuspiciousCallArgumentCheck::SuspiciousCallArgumentCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ MinimumIdentifierNameLength(Options.get(
+ "MinimumIdentifierNameLength", DefaultMinimumIdentifierNameLength)) {
+ auto GetToggleOpt = [this](Heuristic H) -> bool {
+ auto Idx = static_cast<std::size_t>(H);
+ assert(Idx < HeuristicCount);
+ return Options.get(HeuristicToString[Idx], Defaults[Idx].Enabled);
+ };
+ auto GetBoundOpt = [this](Heuristic H, BoundKind BK) -> int8_t {
+ auto Idx = static_cast<std::size_t>(H);
+ assert(Idx < HeuristicCount);
+
+ SmallString<32> Key = HeuristicToString[Idx];
+ Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
+ : "SimilarAbove");
+ int8_t Default = BK == BoundKind::DissimilarBelow
+ ? Defaults[Idx].DissimilarBelow
+ : Defaults[Idx].SimilarAbove;
+ return Options.get(Key, Default);
+ };
+ for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
+ auto H = static_cast<Heuristic>(Idx);
+ if (GetToggleOpt(H))
+ AppliedHeuristics.emplace_back(H);
+ ConfiguredBounds.emplace_back(
+ std::make_pair(GetBoundOpt(H, BoundKind::DissimilarBelow),
+ GetBoundOpt(H, BoundKind::SimilarAbove)));
+ }
+
+ for (StringRef Abbreviation : optutils::parseStringList(
+ Options.get("Abbreviations", DefaultAbbreviations))) {
+ auto KeyAndValue = Abbreviation.split("=");
+ assert(!KeyAndValue.first.empty() && !KeyAndValue.second.empty());
+ AbbreviationDictionary.insert(
+ std::make_pair(KeyAndValue.first, KeyAndValue.second.str()));
+ }
+}
+
+void SuspiciousCallArgumentCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "MinimumIdentifierNameLength",
+ MinimumIdentifierNameLength);
+ const auto &SetToggleOpt = [this, &Opts](Heuristic H) -> void {
+ auto Idx = static_cast<std::size_t>(H);
+ Options.store(Opts, HeuristicToString[Idx], isHeuristicEnabled(H));
+ };
+ const auto &SetBoundOpt = [this, &Opts](Heuristic H, BoundKind BK) -> void {
+ auto Idx = static_cast<std::size_t>(H);
+ assert(Idx < HeuristicCount);
+ if (!Defaults[Idx].hasBounds())
+ return;
+
+ SmallString<32> Key = HeuristicToString[Idx];
+ Key.append(BK == BoundKind::DissimilarBelow ? "DissimilarBelow"
+ : "SimilarAbove");
+ Options.store(Opts, Key, *getBound(H, BK));
+ };
+
+ for (std::size_t Idx = 0; Idx < HeuristicCount; ++Idx) {
+ auto H = static_cast<Heuristic>(Idx);
+ SetToggleOpt(H);
+ SetBoundOpt(H, BoundKind::DissimilarBelow);
+ SetBoundOpt(H, BoundKind::SimilarAbove);
+ }
+
+ SmallVector<std::string, 32> Abbreviations;
+ for (const auto &Abbreviation : AbbreviationDictionary) {
+ SmallString<32> EqualSignJoined;
+ EqualSignJoined.append(Abbreviation.first());
+ EqualSignJoined.append("=");
+ EqualSignJoined.append(Abbreviation.second);
+
+ if (!Abbreviation.second.empty())
+ Abbreviations.emplace_back(EqualSignJoined.str());
+ }
+ Options.store(Opts, "Abbreviations",
+ optutils::serializeStringList(std::vector<StringRef>(
+ Abbreviations.begin(), Abbreviations.end())));
+}
+
+bool SuspiciousCallArgumentCheck::isHeuristicEnabled(Heuristic H) const {
+ return llvm::is_contained(AppliedHeuristics, H);
+}
+
+std::optional<int8_t>
+SuspiciousCallArgumentCheck::getBound(Heuristic H, BoundKind BK) const {
+ auto Idx = static_cast<std::size_t>(H);
+ assert(Idx < HeuristicCount);
+
+ if (!Defaults[Idx].hasBounds())
+ return std::nullopt;
+
+ switch (BK) {
+ case BoundKind::DissimilarBelow:
+ return ConfiguredBounds[Idx].first;
+ case BoundKind::SimilarAbove:
+ return ConfiguredBounds[Idx].second;
+ }
+ llvm_unreachable("Unhandled Bound kind.");
+}
+
+void SuspiciousCallArgumentCheck::registerMatchers(MatchFinder *Finder) {
+ // Only match calls with at least 2 arguments.
+ Finder->addMatcher(
+ functionDecl(forEachDescendant(callExpr(unless(anyOf(argumentCountIs(0),
+ argumentCountIs(1))))
+ .bind("functionCall")))
+ .bind("callingFunc"),
+ this);
+}
+
+void SuspiciousCallArgumentCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCallExpr =
+ Result.Nodes.getNodeAs<CallExpr>("functionCall");
+ const auto *Caller = Result.Nodes.getNodeAs<FunctionDecl>("callingFunc");
+ assert(MatchedCallExpr && Caller);
+
+ const Decl *CalleeDecl = MatchedCallExpr->getCalleeDecl();
+ if (!CalleeDecl)
+ return;
+
+ const FunctionDecl *CalleeFuncDecl = CalleeDecl->getAsFunction();
+ if (!CalleeFuncDecl)
+ return;
+ if (CalleeFuncDecl == Caller)
+ // Ignore recursive calls.
+ return;
+ if (isOverloadedUnaryOrBinarySymbolOperator(CalleeFuncDecl))
+ return;
+
+ // Get param attributes.
+ setParamNamesAndTypes(CalleeFuncDecl);
+
+ if (ParamNames.empty())
+ return;
+
+ // Get Arg attributes.
+ std::size_t InitialArgIndex = 0;
+
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(CalleeFuncDecl)) {
+ if (MethodDecl->getParent()->isLambda())
+ // Lambda functions' first Arg are the lambda object.
+ InitialArgIndex = 1;
+ else if (MethodDecl->getOverloadedOperator() == OO_Call)
+ // For custom operator()s, the first Arg is the called object.
+ InitialArgIndex = 1;
+ }
+
+ setArgNamesAndTypes(MatchedCallExpr, InitialArgIndex);
+
+ if (ArgNames.empty())
+ return;
+
+ std::size_t ParamCount = ParamNames.size();
+
+ // Check similarity.
+ for (std::size_t I = 0; I < ParamCount; ++I) {
+ for (std::size_t J = I + 1; J < ParamCount; ++J) {
+ // Do not check if param or arg names are short, or not convertible.
+ if (!areParamAndArgComparable(I, J, *Result.Context))
+ continue;
+ if (!areArgsSwapped(I, J))
+ continue;
+
+ // Warning at the call itself.
+ diag(MatchedCallExpr->getExprLoc(),
+ "%ordinal0 argument '%1' (passed to '%2') looks like it might be "
+ "swapped with the %ordinal3, '%4' (passed to '%5')")
+ << static_cast<unsigned>(I + 1) << ArgNames[I] << ParamNames[I]
+ << static_cast<unsigned>(J + 1) << ArgNames[J] << ParamNames[J]
+ << MatchedCallExpr->getArg(I)->getSourceRange()
+ << MatchedCallExpr->getArg(J)->getSourceRange();
+
+ // Note at the functions declaration.
+ SourceLocation IParNameLoc =
+ CalleeFuncDecl->getParamDecl(I)->getLocation();
+ SourceLocation JParNameLoc =
+ CalleeFuncDecl->getParamDecl(J)->getLocation();
+
+ diag(CalleeFuncDecl->getLocation(), "in the call to %0, declared here",
+ DiagnosticIDs::Note)
+ << CalleeFuncDecl
+ << CharSourceRange::getTokenRange(IParNameLoc, IParNameLoc)
+ << CharSourceRange::getTokenRange(JParNameLoc, JParNameLoc);
+ }
+ }
+}
+
+void SuspiciousCallArgumentCheck::setParamNamesAndTypes(
+ const FunctionDecl *CalleeFuncDecl) {
+ // Reset vectors, and fill them with the currently checked function's
+ // parameters' data.
+ ParamNames.clear();
+ ParamTypes.clear();
+
+ for (const ParmVarDecl *Param : CalleeFuncDecl->parameters()) {
+ ParamTypes.push_back(Param->getType());
+
+ if (IdentifierInfo *II = Param->getIdentifier())
+ ParamNames.push_back(II->getName());
+ else
+ ParamNames.push_back(StringRef());
+ }
+}
+
+void SuspiciousCallArgumentCheck::setArgNamesAndTypes(
+ const CallExpr *MatchedCallExpr, std::size_t InitialArgIndex) {
+ // Reset vectors, and fill them with the currently checked function's
+ // arguments' data.
+ ArgNames.clear();
+ ArgTypes.clear();
+
+ for (std::size_t I = InitialArgIndex, J = MatchedCallExpr->getNumArgs();
+ I < J; ++I) {
+ assert(ArgTypes.size() == I - InitialArgIndex &&
+ ArgNames.size() == ArgTypes.size() &&
+ "Every iteration must put an element into the vectors!");
+
+ if (const auto *ArgExpr = dyn_cast<DeclRefExpr>(
+ MatchedCallExpr->getArg(I)->IgnoreUnlessSpelledInSource())) {
+ if (const auto *Var = dyn_cast<VarDecl>(ArgExpr->getDecl())) {
+ ArgTypes.push_back(Var->getType());
+ ArgNames.push_back(Var->getName());
+ continue;
+ }
+ if (const auto *FCall = dyn_cast<FunctionDecl>(ArgExpr->getDecl())) {
+ if (FCall->getNameInfo().getName().isIdentifier()) {
+ ArgTypes.push_back(FCall->getType());
+ ArgNames.push_back(FCall->getName());
+ continue;
+ }
+ }
+ }
+
+ ArgTypes.push_back(QualType());
+ ArgNames.push_back(StringRef());
+ }
+}
+
+bool SuspiciousCallArgumentCheck::areParamAndArgComparable(
+ std::size_t Position1, std::size_t Position2, const ASTContext &Ctx) const {
+ if (Position1 >= ArgNames.size() || Position2 >= ArgNames.size())
+ return false;
+
+ // Do not report for too short strings.
+ if (ArgNames[Position1].size() < MinimumIdentifierNameLength ||
+ ArgNames[Position2].size() < MinimumIdentifierNameLength ||
+ ParamNames[Position1].size() < MinimumIdentifierNameLength ||
+ ParamNames[Position2].size() < MinimumIdentifierNameLength)
+ return false;
+
+ if (!areTypesCompatible(ArgTypes[Position1], ParamTypes[Position2], Ctx) ||
+ !areTypesCompatible(ArgTypes[Position2], ParamTypes[Position1], Ctx))
+ return false;
+
+ return true;
+}
+
+bool SuspiciousCallArgumentCheck::areArgsSwapped(std::size_t Position1,
+ std::size_t Position2) const {
+ for (Heuristic H : AppliedHeuristics) {
+ bool A1ToP2Similar = areNamesSimilar(
+ ArgNames[Position2], ParamNames[Position1], H, BoundKind::SimilarAbove);
+ bool A2ToP1Similar = areNamesSimilar(
+ ArgNames[Position1], ParamNames[Position2], H, BoundKind::SimilarAbove);
+
+ bool A1ToP1Dissimilar =
+ !areNamesSimilar(ArgNames[Position1], ParamNames[Position1], H,
+ BoundKind::DissimilarBelow);
+ bool A2ToP2Dissimilar =
+ !areNamesSimilar(ArgNames[Position2], ParamNames[Position2], H,
+ BoundKind::DissimilarBelow);
+
+ if ((A1ToP2Similar || A2ToP1Similar) && A1ToP1Dissimilar &&
+ A2ToP2Dissimilar)
+ return true;
+ }
+ return false;
+}
+
+bool SuspiciousCallArgumentCheck::areNamesSimilar(StringRef Arg,
+ StringRef Param, Heuristic H,
+ BoundKind BK) const {
+ int8_t Threshold = -1;
+ if (std::optional<int8_t> GotBound = getBound(H, BK))
+ Threshold = *GotBound;
+
+ switch (H) {
+ case Heuristic::Equality:
+ return applyEqualityHeuristic(Arg, Param);
+ case Heuristic::Abbreviation:
+ return applyAbbreviationHeuristic(AbbreviationDictionary, Arg, Param);
+ case Heuristic::Prefix:
+ return applyPrefixHeuristic(Arg, Param, Threshold);
+ case Heuristic::Suffix:
+ return applySuffixHeuristic(Arg, Param, Threshold);
+ case Heuristic::Substring:
+ return applySubstringHeuristic(Arg, Param, Threshold);
+ case Heuristic::Levenshtein:
+ return applyLevenshteinHeuristic(Arg, Param, Threshold);
+ case Heuristic::JaroWinkler:
+ return applyJaroWinklerHeuristic(Arg, Param, Threshold);
+ case Heuristic::Dice:
+ return applyDiceHeuristic(Arg, Param, Threshold);
+ }
+ llvm_unreachable("Unhandled heuristic kind");
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
new file mode 100644
index 0000000000..38477d0800
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/SuspiciousCallArgumentCheck.h
@@ -0,0 +1,97 @@
+//===--- SuspiciousCallArgumentCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUSCALLARGUMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUSCALLARGUMENTCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "llvm/ADT/StringSet.h"
+#include <optional>
+
+namespace clang::tidy::readability {
+
+/// Finds function calls where the arguments passed are provided out of order,
+/// based on the difference between the argument name and the parameter names
+/// of the function.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/suspicious-call-argument.html
+class SuspiciousCallArgumentCheck : public ClangTidyCheck {
+ enum class Heuristic {
+ Equality,
+ Abbreviation,
+ Prefix,
+ Suffix,
+ Substring,
+ Levenshtein,
+ JaroWinkler,
+ Dice
+ };
+
+ /// When applying a heuristic, the value of this enum decides which kind of
+ /// bound will be selected from the bounds configured for the heuristic.
+ /// This only applies to heuristics that can take bounds.
+ enum class BoundKind {
+ /// Check for dissimilarity of the names. Names are deemed dissimilar if
+ /// the similarity measurement is **below** the configured threshold.
+ DissimilarBelow,
+
+ /// Check for similarity of the names. Names are deemed similar if the
+ /// similarity measurement (the result of heuristic) is **above** the
+ /// configured threshold.
+ SimilarAbove
+ };
+
+public:
+ static constexpr std::size_t SmallVectorSize = 8;
+ static constexpr std::size_t HeuristicCount =
+ static_cast<std::size_t>(Heuristic::Dice) + 1;
+
+ SuspiciousCallArgumentCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const std::size_t MinimumIdentifierNameLength;
+
+ /// The configuration for which heuristics were enabled.
+ SmallVector<Heuristic, HeuristicCount> AppliedHeuristics;
+
+ /// The lower and upper bounds for each heuristic, as configured by the user.
+ SmallVector<std::pair<int8_t, int8_t>, HeuristicCount> ConfiguredBounds;
+
+ /// The abbreviation-to-abbreviated map for the Abbreviation heuristic.
+ llvm::StringMap<std::string> AbbreviationDictionary;
+
+ bool isHeuristicEnabled(Heuristic H) const;
+ std::optional<int8_t> getBound(Heuristic H, BoundKind BK) const;
+
+ // Runtime information of the currently analyzed function call.
+ SmallVector<QualType, SmallVectorSize> ArgTypes;
+ SmallVector<StringRef, SmallVectorSize> ArgNames;
+ SmallVector<QualType, SmallVectorSize> ParamTypes;
+ SmallVector<StringRef, SmallVectorSize> ParamNames;
+
+ void setParamNamesAndTypes(const FunctionDecl *CalleeFuncDecl);
+
+ void setArgNamesAndTypes(const CallExpr *MatchedCallExpr,
+ std::size_t InitialArgIndex);
+
+ bool areParamAndArgComparable(std::size_t Position1, std::size_t Position2,
+ const ASTContext &Ctx) const;
+
+ bool areArgsSwapped(std::size_t Position1, std::size_t Position2) const;
+
+ bool areNamesSimilar(StringRef Arg, StringRef Param, Heuristic H,
+ BoundKind BK) const;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SUSPICIOUSCALLARGUMENTCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp
new file mode 100644
index 0000000000..462085b023
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp
@@ -0,0 +1,82 @@
+//===--- UniqueptrDeleteReleaseCheck.cpp - clang-tidy----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UniqueptrDeleteReleaseCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UniqueptrDeleteReleaseCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "PreferResetCall", PreferResetCall);
+}
+
+UniqueptrDeleteReleaseCheck::UniqueptrDeleteReleaseCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ PreferResetCall(Options.get("PreferResetCall", false)) {}
+
+void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) {
+
+ auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl(
+ hasName("::std::unique_ptr"),
+ hasTemplateArgument(1, refersToType(hasDeclaration(cxxRecordDecl(
+ hasName("::std::default_delete"))))));
+
+ Finder->addMatcher(
+ cxxDeleteExpr(
+ unless(isInTemplateInstantiation()),
+ has(cxxMemberCallExpr(
+ callee(memberExpr(hasObjectExpression(anyOf(
+ hasType(UniquePtrWithDefaultDelete),
+ hasType(pointsTo(
+ UniquePtrWithDefaultDelete)))),
+ member(cxxMethodDecl(hasName("release"))))
+ .bind("release_expr")))
+ .bind("release_call")))
+ .bind("delete"),
+ this);
+}
+
+void UniqueptrDeleteReleaseCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *DeleteExpr = Result.Nodes.getNodeAs<CXXDeleteExpr>("delete");
+ const auto *ReleaseExpr = Result.Nodes.getNodeAs<MemberExpr>("release_expr");
+ const auto *ReleaseCallExpr =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("release_call");
+
+ if (ReleaseExpr->getBeginLoc().isMacroID())
+ return;
+
+ auto D =
+ diag(DeleteExpr->getBeginLoc(), "prefer '%select{= nullptr|reset()}0' "
+ "to reset 'unique_ptr<>' objects");
+ D << PreferResetCall << DeleteExpr->getSourceRange()
+ << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
+ DeleteExpr->getBeginLoc(),
+ DeleteExpr->getArgument()->getBeginLoc()));
+ if (PreferResetCall) {
+ D << FixItHint::CreateReplacement(ReleaseExpr->getMemberLoc(), "reset");
+ } else {
+ if (ReleaseExpr->isArrow())
+ D << FixItHint::CreateInsertion(ReleaseExpr->getBase()->getBeginLoc(),
+ "*");
+ D << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(ReleaseExpr->getOperatorLoc(),
+ ReleaseCallExpr->getEndLoc()),
+ " = nullptr");
+ }
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h
new file mode 100644
index 0000000000..4dfcf36e47
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h
@@ -0,0 +1,37 @@
+//===--- UniqueptrDeleteReleaseCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Flags statements of the form ``delete <unique_ptr expr>.release();`` and
+/// replaces them with: ``<unique_ptr expr> = nullptr;``
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/uniqueptr-delete-release.html
+class UniqueptrDeleteReleaseCheck : public ClangTidyCheck {
+public:
+ UniqueptrDeleteReleaseCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const bool PreferResetCall;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
new file mode 100644
index 0000000000..549331c2d9
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
@@ -0,0 +1,242 @@
+//===--- UppercaseLiteralSuffixCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UppercaseLiteralSuffixCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallString.h"
+#include <cctype>
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+struct IntegerLiteralCheck {
+ using type = clang::IntegerLiteral;
+ static constexpr llvm::StringLiteral Name = llvm::StringLiteral("integer");
+ // What should be skipped before looking for the Suffixes? (Nothing here.)
+ static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("");
+ // Suffix can only consist of 'u' and 'l' chars, and can be a complex number
+ // ('i', 'j'). In MS compatibility mode, suffixes like i32 are supported.
+ static constexpr llvm::StringLiteral Suffixes =
+ llvm::StringLiteral("uUlLiIjJ");
+};
+constexpr llvm::StringLiteral IntegerLiteralCheck::Name;
+constexpr llvm::StringLiteral IntegerLiteralCheck::SkipFirst;
+constexpr llvm::StringLiteral IntegerLiteralCheck::Suffixes;
+
+struct FloatingLiteralCheck {
+ using type = clang::FloatingLiteral;
+ static constexpr llvm::StringLiteral Name =
+ llvm::StringLiteral("floating point");
+ // C++17 introduced hexadecimal floating-point literals, and 'f' is both a
+ // valid hexadecimal digit in a hex float literal and a valid floating-point
+ // literal suffix.
+ // So we can't just "skip to the chars that can be in the suffix".
+ // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point
+ // literals, we first skip everything before the exponent.
+ static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("pP");
+ // Suffix can only consist of 'f', 'l', "f16", 'h', 'q' chars,
+ // and can be a complex number ('i', 'j').
+ static constexpr llvm::StringLiteral Suffixes =
+ llvm::StringLiteral("fFlLhHqQiIjJ");
+};
+constexpr llvm::StringLiteral FloatingLiteralCheck::Name;
+constexpr llvm::StringLiteral FloatingLiteralCheck::SkipFirst;
+constexpr llvm::StringLiteral FloatingLiteralCheck::Suffixes;
+
+struct NewSuffix {
+ SourceRange LiteralLocation;
+ StringRef OldSuffix;
+ std::optional<FixItHint> FixIt;
+};
+
+std::optional<SourceLocation> getMacroAwareLocation(SourceLocation Loc,
+ const SourceManager &SM) {
+ // Do nothing if the provided location is invalid.
+ if (Loc.isInvalid())
+ return std::nullopt;
+ // Look where the location was *actually* written.
+ SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
+ if (SpellingLoc.isInvalid())
+ return std::nullopt;
+ return SpellingLoc;
+}
+
+std::optional<SourceRange> getMacroAwareSourceRange(SourceRange Loc,
+ const SourceManager &SM) {
+ std::optional<SourceLocation> Begin =
+ getMacroAwareLocation(Loc.getBegin(), SM);
+ std::optional<SourceLocation> End = getMacroAwareLocation(Loc.getEnd(), SM);
+ if (!Begin || !End)
+ return std::nullopt;
+ return SourceRange(*Begin, *End);
+}
+
+std::optional<std::string>
+getNewSuffix(llvm::StringRef OldSuffix,
+ const std::vector<StringRef> &NewSuffixes) {
+ // If there is no config, just uppercase the entirety of the suffix.
+ if (NewSuffixes.empty())
+ return OldSuffix.upper();
+ // Else, find matching suffix, case-*insensitive*ly.
+ auto NewSuffix =
+ llvm::find_if(NewSuffixes, [OldSuffix](StringRef PotentialNewSuffix) {
+ return OldSuffix.equals_insensitive(PotentialNewSuffix);
+ });
+ // Have a match, return it.
+ if (NewSuffix != NewSuffixes.end())
+ return NewSuffix->str();
+ // Nope, I guess we have to keep it as-is.
+ return std::nullopt;
+}
+
+template <typename LiteralType>
+std::optional<NewSuffix>
+shouldReplaceLiteralSuffix(const Expr &Literal,
+ const std::vector<StringRef> &NewSuffixes,
+ const SourceManager &SM, const LangOptions &LO) {
+ NewSuffix ReplacementDsc;
+
+ const auto &L = cast<typename LiteralType::type>(Literal);
+
+ // The naive location of the literal. Is always valid.
+ ReplacementDsc.LiteralLocation = L.getSourceRange();
+
+ // Was this literal fully spelled or is it a product of macro expansion?
+ bool RangeCanBeFixed =
+ utils::rangeCanBeFixed(ReplacementDsc.LiteralLocation, &SM);
+
+ // The literal may have macro expansion, we need the final expanded src range.
+ std::optional<SourceRange> Range =
+ getMacroAwareSourceRange(ReplacementDsc.LiteralLocation, SM);
+ if (!Range)
+ return std::nullopt;
+
+ if (RangeCanBeFixed)
+ ReplacementDsc.LiteralLocation = *Range;
+ // Else keep the naive literal location!
+
+ // Get the whole literal from the source buffer.
+ bool Invalid;
+ const StringRef LiteralSourceText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(*Range), SM, LO, &Invalid);
+ assert(!Invalid && "Failed to retrieve the source text.");
+
+ // Make sure the first character is actually a digit, instead of
+ // something else, like a non-type template parameter.
+ if (!std::isdigit(static_cast<unsigned char>(LiteralSourceText.front())))
+ return std::nullopt;
+
+ size_t Skip = 0;
+
+ // Do we need to ignore something before actually looking for the suffix?
+ if (!LiteralType::SkipFirst.empty()) {
+ // E.g. we can't look for 'f' suffix in hexadecimal floating-point literals
+ // until after we skip to the exponent (which is mandatory there),
+ // because hex-digit-sequence may contain 'f'.
+ Skip = LiteralSourceText.find_first_of(LiteralType::SkipFirst);
+ // We could be in non-hexadecimal floating-point literal, with no exponent.
+ if (Skip == StringRef::npos)
+ Skip = 0;
+ }
+
+ // Find the beginning of the suffix by looking for the first char that is
+ // one of these chars that can be in the suffix, potentially starting looking
+ // in the exponent, if we are skipping hex-digit-sequence.
+ Skip = LiteralSourceText.find_first_of(LiteralType::Suffixes, /*From=*/Skip);
+
+ // We can't check whether the *Literal has any suffix or not without actually
+ // looking for the suffix. So it is totally possible that there is no suffix.
+ if (Skip == StringRef::npos)
+ return std::nullopt;
+
+ // Move the cursor in the source range to the beginning of the suffix.
+ Range->setBegin(Range->getBegin().getLocWithOffset(Skip));
+ // And in our textual representation too.
+ ReplacementDsc.OldSuffix = LiteralSourceText.drop_front(Skip);
+ assert(!ReplacementDsc.OldSuffix.empty() &&
+ "We still should have some chars left.");
+
+ // And get the replacement suffix.
+ std::optional<std::string> NewSuffix =
+ getNewSuffix(ReplacementDsc.OldSuffix, NewSuffixes);
+ if (!NewSuffix || ReplacementDsc.OldSuffix == *NewSuffix)
+ return std::nullopt; // The suffix was already the way it should be.
+
+ if (RangeCanBeFixed)
+ ReplacementDsc.FixIt = FixItHint::CreateReplacement(*Range, *NewSuffix);
+
+ return ReplacementDsc;
+}
+
+} // namespace
+
+UppercaseLiteralSuffixCheck::UppercaseLiteralSuffixCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ NewSuffixes(
+ utils::options::parseStringList(Options.get("NewSuffixes", ""))),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+void UppercaseLiteralSuffixCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "NewSuffixes",
+ utils::options::serializeStringList(NewSuffixes));
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void UppercaseLiteralSuffixCheck::registerMatchers(MatchFinder *Finder) {
+ // Sadly, we can't check whether the literal has suffix or not.
+ // E.g. i32 suffix still results in 'BuiltinType::Kind::Int'.
+ // And such an info is not stored in the *Literal itself.
+ Finder->addMatcher(
+ stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name),
+ floatLiteral().bind(FloatingLiteralCheck::Name)),
+ unless(anyOf(hasParent(userDefinedLiteral()),
+ hasAncestor(substNonTypeTemplateParmExpr())))),
+ this);
+}
+
+template <typename LiteralType>
+bool UppercaseLiteralSuffixCheck::checkBoundMatch(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Literal =
+ Result.Nodes.getNodeAs<typename LiteralType::type>(LiteralType::Name);
+ if (!Literal)
+ return false;
+
+ // We won't *always* want to diagnose.
+ // We might have a suffix that is already uppercase.
+ if (auto Details = shouldReplaceLiteralSuffix<LiteralType>(
+ *Literal, NewSuffixes, *Result.SourceManager, getLangOpts())) {
+ if (Details->LiteralLocation.getBegin().isMacroID() && IgnoreMacros)
+ return true;
+ auto Complaint = diag(Details->LiteralLocation.getBegin(),
+ "%0 literal has suffix '%1', which is not uppercase")
+ << LiteralType::Name << Details->OldSuffix;
+ if (Details->FixIt) // Similarly, a fix-it is not always possible.
+ Complaint << *(Details->FixIt);
+ }
+
+ return true;
+}
+
+void UppercaseLiteralSuffixCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (checkBoundMatch<IntegerLiteralCheck>(Result))
+ return; // If it *was* IntegerLiteral, don't check for FloatingLiteral.
+ checkBoundMatch<FloatingLiteralCheck>(Result);
+}
+
+} // namespace clang::tidy::readability
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h
new file mode 100644
index 0000000000..a8af08f5a8
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.h
@@ -0,0 +1,43 @@
+//===--- UppercaseLiteralSuffixCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/OptionsUtils.h"
+
+namespace clang::tidy::readability {
+
+/// Detects when the integral literal or floating point literal has
+/// non-uppercase suffix, and suggests to make the suffix uppercase.
+/// Alternatively, a list of destination suffixes can be provided.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/uppercase-literal-suffix.html
+class UppercaseLiteralSuffixCheck : public ClangTidyCheck {
+public:
+ UppercaseLiteralSuffixCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ template <typename LiteralType>
+ bool checkBoundMatch(const ast_matchers::MatchFinder::MatchResult &Result);
+
+ const std::vector<StringRef> NewSuffixes;
+ const bool IgnoreMacros;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
new file mode 100644
index 0000000000..7cf0e0853f
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -0,0 +1,107 @@
+//===--- UseAnyOfAllOfCheck.cpp - clang-tidy-------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseAnyOfAllOfCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace {
+/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
+/// followed by a Stmt matching the inner matcher.
+AST_MATCHER_P(Stmt, nextStmt, ast_matchers::internal::Matcher<Stmt>,
+ InnerMatcher) {
+ DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+ if (Parents.size() != 1)
+ return false;
+
+ auto *C = Parents[0].get<CompoundStmt>();
+ if (!C)
+ return false;
+
+ const auto *I = llvm::find(C->body(), &Node);
+ assert(I != C->body_end() && "C is parent of Node");
+ if (++I == C->body_end())
+ return false; // Node is last statement.
+
+ return InnerMatcher.matches(**I, Finder, Builder);
+}
+} // namespace
+
+namespace tidy::readability {
+
+void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
+ auto Returns = [](bool V) {
+ return returnStmt(hasReturnValue(cxxBoolLiteral(equals(V))));
+ };
+
+ auto ReturnsButNotTrue =
+ returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
+ auto ReturnsButNotFalse =
+ returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
+
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ nextStmt(Returns(false).bind("final_return")),
+ hasBody(allOf(hasDescendant(Returns(true)),
+ unless(anyOf(hasDescendant(breakStmt()),
+ hasDescendant(gotoStmt()),
+ hasDescendant(ReturnsButNotTrue))))))
+ .bind("any_of_loop"),
+ this);
+
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ nextStmt(Returns(true).bind("final_return")),
+ hasBody(allOf(hasDescendant(Returns(false)),
+ unless(anyOf(hasDescendant(breakStmt()),
+ hasDescendant(gotoStmt()),
+ hasDescendant(ReturnsButNotFalse))))))
+ .bind("all_of_loop"),
+ this);
+}
+
+static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context) {
+
+ ExprMutationAnalyzer Mutations(*S.getBody(), Context);
+ if (Mutations.isMutated(S.getLoopVariable()))
+ return false;
+ const auto Matches =
+ match(findAll(declRefExpr().bind("decl_ref")), *S.getBody(), Context);
+
+ return llvm::none_of(Matches, [&Mutations](auto &DeclRef) {
+ // TODO: allow modifications of loop-local variables
+ return Mutations.isMutated(
+ DeclRef.template getNodeAs<DeclRefExpr>("decl_ref")->getDecl());
+ });
+}
+
+void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
+
+ if (const auto *S = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop")) {
+ if (!isViableLoop(*S, *Result.Context))
+ return;
+
+ diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::any_of()'")
+ << getLangOpts().CPlusPlus20;
+ } else if (const auto *S =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>("all_of_loop")) {
+ if (!isViableLoop(*S, *Result.Context))
+ return;
+
+ diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")
+ << getLangOpts().CPlusPlus20;
+ }
+}
+
+} // namespace tidy::readability
+} // namespace clang
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.h
new file mode 100644
index 0000000000..4e53b3f5a8
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/UseAnyOfAllOfCheck.h
@@ -0,0 +1,36 @@
+//===--- UseAnyOfAllOfCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USEALGORITHMCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USEALGORITHMCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang::tidy::readability {
+
+/// Finds ranged-based for loops that can be replaced by a call to std::any_of
+/// or std::all_of.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/use-anyofallof.html
+class UseAnyOfAllOfCheck : public ClangTidyCheck {
+public:
+ using ClangTidyCheck::ClangTidyCheck;
+
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USEALGORITHMCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/ya.make b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ya.make
new file mode 100644
index 0000000000..59200fc0a9
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/ya.make
@@ -0,0 +1,80 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/clang16
+ contrib/libs/clang16/include
+ contrib/libs/clang16/lib
+ contrib/libs/clang16/lib/AST
+ contrib/libs/clang16/lib/ASTMatchers
+ contrib/libs/clang16/lib/Analysis
+ contrib/libs/clang16/lib/Basic
+ contrib/libs/clang16/lib/Lex
+ contrib/libs/clang16/lib/Tooling
+ contrib/libs/clang16/tools/extra/clang-tidy/utils
+ contrib/libs/llvm16
+ contrib/libs/llvm16/lib/Frontend/OpenMP
+ contrib/libs/llvm16/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/clang16/tools/extra/clang-tidy/readability
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ AvoidConstParamsInDecls.cpp
+ BracesAroundStatementsCheck.cpp
+ ConstReturnTypeCheck.cpp
+ ContainerContainsCheck.cpp
+ ContainerDataPointerCheck.cpp
+ ContainerSizeEmptyCheck.cpp
+ ConvertMemberFunctionsToStatic.cpp
+ DeleteNullPointerCheck.cpp
+ DuplicateIncludeCheck.cpp
+ ElseAfterReturnCheck.cpp
+ FunctionCognitiveComplexityCheck.cpp
+ FunctionSizeCheck.cpp
+ IdentifierLengthCheck.cpp
+ IdentifierNamingCheck.cpp
+ ImplicitBoolConversionCheck.cpp
+ InconsistentDeclarationParameterNameCheck.cpp
+ IsolateDeclarationCheck.cpp
+ MagicNumbersCheck.cpp
+ MakeMemberFunctionConstCheck.cpp
+ MisleadingIndentationCheck.cpp
+ MisplacedArrayIndexCheck.cpp
+ NamedParameterCheck.cpp
+ NamespaceCommentCheck.cpp
+ NonConstParameterCheck.cpp
+ QualifiedAutoCheck.cpp
+ ReadabilityTidyModule.cpp
+ RedundantAccessSpecifiersCheck.cpp
+ RedundantControlFlowCheck.cpp
+ RedundantDeclarationCheck.cpp
+ RedundantFunctionPtrDereferenceCheck.cpp
+ RedundantMemberInitCheck.cpp
+ RedundantPreprocessorCheck.cpp
+ RedundantSmartptrGetCheck.cpp
+ RedundantStringCStrCheck.cpp
+ RedundantStringInitCheck.cpp
+ SimplifyBooleanExprCheck.cpp
+ SimplifySubscriptExprCheck.cpp
+ StaticAccessedThroughInstanceCheck.cpp
+ StaticDefinitionInAnonymousNamespaceCheck.cpp
+ StringCompareCheck.cpp
+ SuspiciousCallArgumentCheck.cpp
+ UniqueptrDeleteReleaseCheck.cpp
+ UppercaseLiteralSuffixCheck.cpp
+ UseAnyOfAllOfCheck.cpp
+)
+
+END()