================ @@ -0,0 +1,946 @@ +//===--- UseConstexprCheck.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 "UseConstexprCheck.h" +#include "../utils/ASTUtils.h" +#include "../utils/LexerUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Basic/TokenKinds.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/Casting.h" +#include <cstddef> +#include <functional> + +using namespace clang::ast_matchers; + +namespace clang::tidy::misc { + +namespace { +AST_MATCHER(FunctionDecl, locationPermitsConstexpr) { + const bool IsInMainFile = + Finder->getASTContext().getSourceManager().isInMainFile( + Node.getLocation()); + + if (IsInMainFile && Node.hasExternalFormalLinkage()) + return false; + if (!IsInMainFile && !Node.isInlined()) + return false; + + return true; +} + +AST_MATCHER(Expr, isCXX11ConstantExpr) { + return !Node.isValueDependent() && + Node.isCXX11ConstantExpr(Finder->getASTContext()); +} + +AST_MATCHER(DeclaratorDecl, isInMacro) { + const SourceRange R = + SourceRange(Node.getInnerLocStart(), Node.getLocation()); + + return Node.getLocation().isMacroID() || Node.getEndLoc().isMacroID() || + utils::rangeContainsMacroExpansion( + R, &Finder->getASTContext().getSourceManager()) || + utils::rangeIsEntirelyWithinMacroArgument( + R, &Finder->getASTContext().getSourceManager()); +} + +AST_MATCHER(Decl, hasNoRedecl) { + // There is always the actual declaration + return !Node.redecls().empty() && + std::next(Node.redecls_begin()) == Node.redecls_end(); +} + +AST_MATCHER(Decl, allRedeclsInSameFile) { + const SourceManager &SM = Finder->getASTContext().getSourceManager(); + const SourceLocation L = Node.getLocation(); + for (const Decl *ReDecl : Node.redecls()) { + if (!SM.isWrittenInSameFile(L, ReDecl->getLocation())) + return false; + } + return true; +} + +AST_MATCHER(FunctionDecl, isConstexprSpecified) { + return Node.isConstexprSpecified(); +} + +bool satisfiesConstructorPropertiesUntil20(const CXXConstructorDecl *Ctor, + ASTContext &Ctx) { + const CXXRecordDecl *Rec = Ctor->getParent(); + llvm::SmallPtrSet<const RecordDecl *, 8> Bases{}; + for (const CXXBaseSpecifier Base : Rec->bases()) { + Bases.insert(Base.getType()->getAsRecordDecl()); + } + llvm::SmallPtrSet<const FieldDecl *, 8> Fields{Rec->field_begin(), + Rec->field_end()}; + llvm::SmallPtrSet<const FieldDecl *, 4> Indirects{}; + + for (const CXXCtorInitializer *const Init : Ctor->inits()) { + const Type *InitType = Init->getBaseClass(); + if (InitType && InitType->isRecordType()) { + const auto *ConstructingInit = + llvm::dyn_cast<CXXConstructExpr>(Init->getInit()); + if (ConstructingInit && + !ConstructingInit->getConstructor()->isConstexprSpecified()) + return false; + } + + if (Init->isBaseInitializer()) { + Bases.erase(Init->getBaseClass()->getAsRecordDecl()); + continue; + } + + if (Init->isMemberInitializer()) { + const FieldDecl *Field = Init->getMember(); + + if (Field->isAnonymousStructOrUnion()) + Indirects.insert(Field); + + Fields.erase(Field); + continue; + } + } + + for (const auto &Match : + match(cxxRecordDecl(forEach(indirectFieldDecl().bind("indirect"))), *Rec, + Ctx)) { + const auto *IField = Match.getNodeAs<IndirectFieldDecl>("indirect"); + + size_t NumInitializations = false; + for (const NamedDecl *ND : IField->chain()) + NumInitializations += Indirects.erase(llvm::dyn_cast<FieldDecl>(ND)); + + if (NumInitializations != 1) + return false; + + for (const NamedDecl *ND : IField->chain()) + Fields.erase(llvm::dyn_cast<FieldDecl>(ND)); + } + + if (!Fields.empty()) + return false; + + return true; +} + +const Type *unwrapPointee(const Type *T) { + if (!T->isPointerOrReferenceType()) + return T; + + while (T && T->isPointerOrReferenceType()) { + if (T->isReferenceType()) { + const QualType QType = T->getPointeeType(); + if (!QType.isNull()) + T = QType.getTypePtr(); + } else + T = T->getPointeeOrArrayElementType(); + } + + return T; +} + +bool isLiteralType(QualType QT, const ASTContext &Ctx, + const bool ConservativeLiteralType); + +bool isLiteralType(const Type *T, const ASTContext &Ctx, + const bool ConservativeLiteralType) { + if (!T) + return false; + + if (!T->isLiteralType(Ctx)) + return false; + + if (!ConservativeLiteralType) + return T->isLiteralType(Ctx) && !T->isVoidType(); + + if (T->isIncompleteType() || T->isIncompleteArrayType()) + return false; + + T = unwrapPointee(T); + if (!T) + return false; + + assert(!T->isPointerOrReferenceType()); + + if (T->isIncompleteType() || T->isIncompleteArrayType()) + return false; + + if (T->isLiteralType(Ctx)) + return true; + + if (const auto *Rec = T->getAsCXXRecordDecl()) { + if (llvm::any_of(Rec->ctors(), [](const CXXConstructorDecl *Ctor) { + return !Ctor->isCopyOrMoveConstructor() && + Ctor->isConstexprSpecified(); + })) + return false; + + for (const CXXBaseSpecifier Base : Rec->bases()) { + if (!isLiteralType(Base.getType(), Ctx, ConservativeLiteralType)) + return false; + } + } + + if (const Type *ArrayElementType = T->getArrayElementTypeNoTypeQual()) + return isLiteralType(ArrayElementType, Ctx, ConservativeLiteralType); + + return false; +} + +bool isLiteralType(QualType QT, const ASTContext &Ctx, + const bool ConservativeLiteralType) { + return isLiteralType(QT.getTypePtr(), Ctx, ConservativeLiteralType); +} + +bool satisfiesProperties11( + const FunctionDecl *FDecl, ASTContext &Ctx, + const bool ConservativeLiteralType, + const bool AddConstexprToMethodOfClassWithoutConstexprConstructor) { + if (FDecl->isConstexprSpecified()) { + return true; + } + const LangOptions LO = Ctx.getLangOpts(); + const CXXMethodDecl *Method = llvm::dyn_cast<CXXMethodDecl>(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (Method && + (Method->isVirtual() || + !match(cxxMethodDecl(hasBody(cxxTryStmt())), *Method, Ctx).empty())) + return false; + + if (const auto *Ctor = llvm::dyn_cast<CXXConstructorDecl>(FDecl); + Ctor && (!satisfiesConstructorPropertiesUntil20(Ctor, Ctx) || + llvm::any_of(Ctor->getParent()->bases(), + [](const CXXBaseSpecifier &Base) { + return Base.isVirtual(); + }))) + return false; + + if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(FDecl); + Dtor && !Dtor->isTrivial()) + return false; + + if (!isLiteralType(FDecl->getReturnType(), Ctx, ConservativeLiteralType)) + return false; + + for (const ParmVarDecl *Param : FDecl->parameters()) + if (!isLiteralType(Param->getType(), Ctx, ConservativeLiteralType)) + return false; + + class Visitor11 : public clang::RecursiveASTVisitor<Visitor11> { + public: + using Base = clang::RecursiveASTVisitor<Visitor11>; + bool shouldVisitImplicitCode() const { return true; } + + Visitor11(ASTContext &Ctx, bool ConservativeLiteralType) + : Ctx(Ctx), ConservativeLiteralType(ConservativeLiteralType) {} + + bool WalkUpFromNullStmt(NullStmt *) { + Possible = false; + return true; + } + bool WalkUpFromDeclStmt(DeclStmt *DS) { + for (const Decl *D : DS->decls()) + if (!llvm::isa<StaticAssertDecl, TypedefNameDecl, UsingDecl, + UsingDirectiveDecl>(D)) { + Possible = false; + return false; + } + return true; + } + + bool WalkUpFromExpr(Expr *) { return true; } + bool WalkUpFromCompoundStmt(CompoundStmt *S) { + for (const DynTypedNode &Node : Ctx.getParents(*S)) + if (Node.get<FunctionDecl>() != nullptr) + return true; + + Possible = false; + return false; + } + bool WalkUpFromStmt(Stmt *) { + Possible = false; + return false; + } + + bool WalkUpFromReturnStmt(ReturnStmt *) { + ++NumReturns; + if (NumReturns != 1U) { + Possible = false; + return false; + } + return true; + } + + bool WalkUpFromCastExpr(CastExpr *CE) { + if (llvm::is_contained( + { + CK_LValueBitCast, + CK_IntegralToPointer, + CK_PointerToIntegral, + }, + CE->getCastKind())) { + Possible = false; + return false; + } + return true; + } + + bool TraverseCXXDynamicCastExpr(CXXDynamicCastExpr *) { + Possible = false; + return false; + } + + bool TraverseCXXReinterpretCastExpr(CXXReinterpretCastExpr *) { + Possible = false; + return false; + } + + bool TraverseType(QualType QT) { + if (QT.isNull()) + return true; + if (!isLiteralType(QT, Ctx, ConservativeLiteralType)) { + Possible = false; + return false; + } + return Base::TraverseType(QT); + } + + bool WalkUpFromCXXConstructExpr(CXXConstructExpr *CE) { + if (const auto *Ctor = CE->getConstructor(); + Ctor && !Ctor->isConstexprSpecified()) { + Possible = false; + return false; + } + + return true; + } + bool WalkUpFromCallExpr(CallExpr *CE) { + if (const auto *FDecl = + llvm::dyn_cast_if_present<FunctionDecl>(CE->getCalleeDecl()); + FDecl && !FDecl->isConstexprSpecified()) { + Possible = false; + return false; + } + return true; + } + + bool TraverseCXXNewExpr(CXXNewExpr *) { + Possible = false; + return false; + } + + ASTContext &Ctx; + const bool ConservativeLiteralType; + bool Possible = true; + size_t NumReturns = 0; + }; + + Visitor11 V{Ctx, ConservativeLiteralType}; + V.TraverseDecl(const_cast<FunctionDecl *>(FDecl)); + if (!V.Possible) + return false; + + return true; +} + +// The only difference between C++14 and C++17 is that `constexpr` lambdas +// can be used in C++17. +bool satisfiesProperties1417( + const FunctionDecl *FDecl, ASTContext &Ctx, + const bool ConservativeLiteralType, + const bool AddConstexprToMethodOfClassWithoutConstexprConstructor) { + if (FDecl->isConstexprSpecified()) + return true; + + const LangOptions LO = Ctx.getLangOpts(); + const CXXMethodDecl *Method = llvm::dyn_cast<CXXMethodDecl>(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (Method && Method->isVirtual()) + return false; + + if (llvm::isa<CXXConstructorDecl>(FDecl) && + llvm::any_of( + Method->getParent()->bases(), + [](const CXXBaseSpecifier &Base) { return Base.isVirtual(); })) + return false; + + if (!isLiteralType(FDecl->getReturnType(), Ctx, ConservativeLiteralType)) + return false; + + for (const ParmVarDecl *Param : FDecl->parameters()) + if (!isLiteralType(Param->getType(), Ctx, ConservativeLiteralType)) + return false; + + class Visitor14 : public clang::RecursiveASTVisitor<Visitor14> { + public: + using Base = clang::RecursiveASTVisitor<Visitor14>; + bool shouldVisitImplicitCode() const { return true; } + + Visitor14(bool CXX17, ASTContext &Ctx, bool ConservativeLiteralType, + bool AddConstexprToMethodOfClassWithoutConstexprConstructor) + : CXX17(CXX17), Ctx(Ctx), + ConservativeLiteralType(ConservativeLiteralType), + AddConstexprToMethodOfClassWithoutConstexprConstructor( + AddConstexprToMethodOfClassWithoutConstexprConstructor) {} + + bool TraverseGotoStmt(GotoStmt *) { + Possible = false; + return false; + } + bool TraverseLabelStmt(LabelStmt *) { + Possible = false; + return false; + } + bool TraverseCXXTryStmt(CXXTryStmt *) { + Possible = false; + return false; + } + bool TraverseGCCAsmStmt(GCCAsmStmt *) { + Possible = false; + return false; + } + bool TraverseMSAsmStmt(MSAsmStmt *) { + Possible = false; + return false; + } + bool TraverseDecompositionDecl(DecompositionDecl * /*DD*/) { + Possible = false; + return false; + } + bool TraverseVarDecl(VarDecl *VD) { + const auto StorageDur = VD->getStorageDuration(); + Possible = VD->hasInit() && + isLiteralType(VD->getType(), VD->getASTContext(), + ConservativeLiteralType) && + (StorageDur != StorageDuration::SD_Static && + StorageDur != StorageDuration::SD_Thread); + return Possible && Base::TraverseVarDecl(VD); + } + bool TraverseLambdaExpr(LambdaExpr *LE) { + if (CXX17) { + Possible = satisfiesProperties1417( + LE->getCallOperator(), Ctx, ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor); + return Possible; + } + Possible = false; + return false; + } + bool TraverseCXXNewExpr(CXXNewExpr *) { + Possible = false; + return false; + } + + bool TraverseDeclRefExpr(DeclRefExpr *DRef) { + if (const auto *D = llvm::dyn_cast_if_present<VarDecl>(DRef->getDecl()); + D && !D->isLocalVarDeclOrParm() && D->hasGlobalStorage()) { + Possible = false; + return false; + } + return true; + } + + bool WalkUpFromCastExpr(CastExpr *CE) { + if (llvm::is_contained( + { + CK_LValueBitCast, + CK_IntegralToPointer, + CK_PointerToIntegral, + }, + CE->getCastKind())) { + Possible = false; + return false; + } + return true; + } + + bool TraverseCXXDynamicCastExpr(CXXDynamicCastExpr *) { + Possible = false; + return false; + } + + bool TraverseCXXReinterpretCastExpr(CXXReinterpretCastExpr *) { + Possible = false; + return false; + } + + const bool CXX17; + bool Possible = true; + ASTContext &Ctx; + const bool ConservativeLiteralType; + const bool AddConstexprToMethodOfClassWithoutConstexprConstructor; + }; + + Visitor14 V{Ctx.getLangOpts().CPlusPlus17 != 0, Ctx, ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor}; + V.TraverseDecl(const_cast<FunctionDecl *>(FDecl)); + if (!V.Possible) + return false; + + if (const auto *Ctor = llvm::dyn_cast<CXXConstructorDecl>(FDecl); + Ctor && !satisfiesConstructorPropertiesUntil20(Ctor, Ctx)) + return false; + + if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(FDecl); + Dtor && !Dtor->isTrivial()) + return false; + + class BodyVisitor : public clang::RecursiveASTVisitor<BodyVisitor> { + public: + using Base = clang::RecursiveASTVisitor<BodyVisitor>; + bool shouldVisitImplicitCode() const { return true; } + + explicit BodyVisitor(ASTContext &Ctx, bool ConservativeLiteralType) + : Ctx(Ctx), ConservativeLiteralType(ConservativeLiteralType) {} + + bool TraverseType(QualType QT) { + if (QT.isNull()) + return true; + if (!isLiteralType(QT, Ctx, ConservativeLiteralType)) { + Possible = false; + return false; + } + return Base::TraverseType(QT); + } + + bool WalkUpFromCXXConstructExpr(CXXConstructExpr *CE) { + if (const auto *Ctor = CE->getConstructor(); + Ctor && !Ctor->isConstexprSpecified()) { + Possible = false; + return false; + } + + return true; + } + bool WalkUpFromCallExpr(CallExpr *CE) { + if (const auto *FDecl = + llvm::dyn_cast_if_present<FunctionDecl>(CE->getCalleeDecl()); + FDecl && !FDecl->isConstexprSpecified()) { + Possible = false; + return false; + } + return true; + } + + bool TraverseCXXNewExpr(CXXNewExpr *) { + Possible = false; + return false; + } + + ASTContext &Ctx; + const bool ConservativeLiteralType; + bool Possible = true; + }; + + if (FDecl->hasBody() && ConservativeLiteralType) { + BodyVisitor Visitor(Ctx, ConservativeLiteralType); + Visitor.TraverseStmt(FDecl->getBody()); + if (!Visitor.Possible) + return false; + } + return true; +} + +bool satisfiesProperties20( + const FunctionDecl *FDecl, ASTContext &Ctx, + const bool ConservativeLiteralType, + const bool AddConstexprToMethodOfClassWithoutConstexprConstructor) { + if (FDecl->isConstexprSpecified()) { + return true; + } ---------------- vbvictor wrote:
```suggestion if (FDecl->isConstexprSpecified()) return true; ``` https://github.com/llvm/llvm-project/pull/146553 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits