diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang14/tools/extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/clang14/tools/extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp')
-rw-r--r-- | contrib/libs/clang14/tools/extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/contrib/libs/clang14/tools/extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp b/contrib/libs/clang14/tools/extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp new file mode 100644 index 0000000000..0f10dd375a --- /dev/null +++ b/contrib/libs/clang14/tools/extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp @@ -0,0 +1,325 @@ +//===--- UseEqualsDefaultCheck.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 "UseEqualsDefaultCheck.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 { +namespace tidy { +namespace modernize { + +static const char SpecialFunction[] = "SpecialFunction"; + +/// Finds all the named non-static fields of \p Record. +static std::set<const FieldDecl *> +getAllNamedFields(const CXXRecordDecl *Record) { + std::set<const FieldDecl *> Result; + for (const auto *Field : Record->fields()) { + // Static data members are not in this range. + if (Field->isUnnamedBitfield()) + continue; + Result.insert(Field); + } + return Result; +} + +/// Returns the names of the direct bases of \p Record, both virtual and +/// non-virtual. +static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) { + std::set<const Type *> Result; + for (auto Base : Record->bases()) { + // CXXBaseSpecifier. + const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr(); + Result.insert(BaseType); + } + return Result; +} + +/// Returns a matcher that matches member expressions where the base is +/// the variable declared as \p Var and the accessed member is the one declared +/// as \p Field. +internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field, + const ValueDecl *Var) { + return ignoringImpCasts( + memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))), + member(fieldDecl(equalsNode(Field))))); +} + +/// Check that the given constructor has copy signature and that it +/// copy-initializes all its bases and members. +static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context, + const CXXConstructorDecl *Ctor) { + // An explicitly-defaulted constructor cannot have default arguments. + if (Ctor->getMinRequiredArguments() != 1) + return false; + + const auto *Record = Ctor->getParent(); + const auto *Param = Ctor->getParamDecl(0); + + // Base classes and members that have to be copied. + auto BasesToInit = getAllDirectBases(Record); + auto FieldsToInit = getAllNamedFields(Record); + + // Ensure that all the bases are copied. + for (const auto *Base : BasesToInit) { + // The initialization of a base class should be a call to a copy + // constructor of the base. + if (match( + traverse(TK_AsIs, + cxxConstructorDecl( + forEachConstructorInitializer(cxxCtorInitializer( + isBaseInitializer(), + withInitializer(cxxConstructExpr( + hasType(equalsNode(Base)), + hasDeclaration( + cxxConstructorDecl(isCopyConstructor())), + argumentCountIs(1), + hasArgument(0, declRefExpr(to(varDecl( + equalsNode(Param))))))))))), + *Ctor, *Context) + .empty()) + return false; + } + + // Ensure that all the members are copied. + for (const auto *Field : FieldsToInit) { + auto AccessToFieldInParam = accessToFieldInVar(Field, Param); + // The initialization is a CXXConstructExpr for class types. + if (match(traverse( + TK_AsIs, + cxxConstructorDecl( + forEachConstructorInitializer(cxxCtorInitializer( + isMemberInitializer(), forField(equalsNode(Field)), + withInitializer(anyOf( + AccessToFieldInParam, + initListExpr(has(AccessToFieldInParam)), + cxxConstructExpr( + hasDeclaration( + cxxConstructorDecl(isCopyConstructor())), + argumentCountIs(1), + hasArgument(0, AccessToFieldInParam)))))))), + *Ctor, *Context) + .empty()) + return false; + } + + // Ensure that we don't do anything else, like initializing an indirect base. + return Ctor->getNumCtorInitializers() == + BasesToInit.size() + FieldsToInit.size(); +} + +/// Checks that the given method is an overloading of the assignment +/// operator, has copy signature, returns a reference to "*this" and copies +/// all its members and subobjects. +static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context, + const CXXMethodDecl *Operator) { + const auto *Record = Operator->getParent(); + const auto *Param = Operator->getParamDecl(0); + + // Base classes and members that have to be copied. + auto BasesToInit = getAllDirectBases(Record); + auto FieldsToInit = getAllNamedFields(Record); + + const auto *Compound = cast<CompoundStmt>(Operator->getBody()); + + // The assignment operator definition has to end with the following return + // statement: + // return *this; + if (Compound->body_empty() || + match(traverse( + TK_AsIs, + returnStmt(has(ignoringParenImpCasts(unaryOperator( + hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())))))), + *Compound->body_back(), *Context) + .empty()) + return false; + + // Ensure that all the bases are copied. + for (const auto *Base : BasesToInit) { + // Assignment operator of a base class: + // Base::operator=(Other); + // + // Clang translates this into: + // ((Base*)this)->operator=((Base)Other); + // + // So we are looking for a member call that fulfills: + if (match(traverse(TK_AsIs, + compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr( + // - The object is an implicit cast of 'this' to a + // pointer to + // a base class. + onImplicitObjectArgument(implicitCastExpr( + hasImplicitDestinationType( + pointsTo(type(equalsNode(Base)))), + hasSourceExpression(cxxThisExpr()))), + // - The called method is the operator=. + callee(cxxMethodDecl(isCopyAssignmentOperator())), + // - The argument is (an implicit cast to a Base of) + // the argument taken by "Operator". + argumentCountIs(1), + hasArgument(0, declRefExpr(to(varDecl( + equalsNode(Param)))))))))), + *Compound, *Context) + .empty()) + return false; + } + + // Ensure that all the members are copied. + for (const auto *Field : FieldsToInit) { + // The assignment of data members: + // Field = Other.Field; + // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr + // otherwise. + auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()), + member(fieldDecl(equalsNode(Field)))); + auto RHS = accessToFieldInVar(Field, Param); + if (match(traverse(TK_AsIs, + compoundStmt(has(ignoringParenImpCasts(binaryOperation( + hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)))))), + *Compound, *Context) + .empty()) + return false; + } + + // Ensure that we don't do anything else. + return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1; +} + +/// Returns false if the body has any non-whitespace character. +static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) { + bool Invalid = false; + StringRef Text = Lexer::getSourceText( + CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1), + Body->getRBracLoc()), + Context->getSourceManager(), Context->getLangOpts(), &Invalid); + return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size(); +} + +UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {} + +void UseEqualsDefaultCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreMacros", IgnoreMacros); +} + +void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) { + // Destructor. + Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction), + this); + Finder->addMatcher( + cxxConstructorDecl( + isDefinition(), + anyOf( + // Default constructor. + allOf(unless(hasAnyConstructorInitializer(isWritten())), + parameterCountIs(0)), + // Copy constructor. + allOf(isCopyConstructor(), + // Discard constructors that can be used as a copy + // constructor because all the other arguments have + // default values. + parameterCountIs(1)))) + .bind(SpecialFunction), + this); + // Copy-assignment operator. + Finder->addMatcher( + cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(), + // isCopyAssignmentOperator() allows the parameter to be + // passed by value, and in this case it cannot be + // defaulted. + hasParameter(0, hasType(lValueReferenceType()))) + .bind(SpecialFunction), + this); +} + +void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) { + // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl. + const auto *SpecialFunctionDecl = + Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction); + + if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID()) + return; + + // Discard explicitly deleted/defaulted special member functions and those + // that are not user-provided (automatically generated). + if (SpecialFunctionDecl->isDeleted() || + SpecialFunctionDecl->isExplicitlyDefaulted() || + SpecialFunctionDecl->isLateTemplateParsed() || + SpecialFunctionDecl->isTemplateInstantiation() || + !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody()) + return; + + const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody()); + if (!Body) + return; + + // If there is code inside the body, don't warn. + if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty()) + return; + + // If there are comments inside the body, don't do the change. + bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() || + bodyEmpty(Result.Context, Body); + + std::vector<FixItHint> RemoveInitializers; + unsigned MemberType; + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) { + if (Ctor->getNumParams() == 0) { + MemberType = 0; + } else { + if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor)) + return; + MemberType = 1; + // If there are constructor initializers, they must be removed. + for (const auto *Init : Ctor->inits()) { + RemoveInitializers.emplace_back( + FixItHint::CreateRemoval(Init->getSourceRange())); + } + } + } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) { + MemberType = 2; + } else { + if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl)) + return; + MemberType = 3; + } + + // The location of the body is more useful inside a macro as spelling and + // expansion locations are reported. + SourceLocation Location = SpecialFunctionDecl->getLocation(); + if (Location.isMacroID()) + Location = Body->getBeginLoc(); + + auto Diag = diag( + Location, + "use '= default' to define a trivial %select{default constructor|copy " + "constructor|destructor|copy-assignment operator}0"); + Diag << MemberType; + + if (ApplyFix) { + // Skipping comments, check for a semicolon after Body->getSourceRange() + Optional<Token> Token = utils::lexer::findNextTokenSkippingComments( + Body->getSourceRange().getEnd().getLocWithOffset(1), + Result.Context->getSourceManager(), Result.Context->getLangOpts()); + StringRef Replacement = + Token && Token->is(tok::semi) ? "= default" : "= default;"; + Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement) + << RemoveInitializers; + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang |