diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 619a27b2f9bb6..db64050f8a9b9 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -30,6 +30,7 @@ add_clang_library(clangTidyModernizeModule STATIC UnaryStaticAssertCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp + UseConstexprCheck.cpp UseConstraintsCheck.cpp UseDefaultMemberInitCheck.cpp UseDesignatedInitializersCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index fdf38bc4b6308..b33f2ccbd6c21 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -31,6 +31,7 @@ #include "UnaryStaticAssertCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" +#include "UseConstexprCheck.h" #include "UseConstraintsCheck.h" #include "UseDefaultMemberInitCheck.h" #include "UseDesignatedInitializersCheck.h" @@ -76,6 +77,7 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck( "modernize-min-max-use-initializer-list"); CheckFactories.registerCheck("modernize-pass-by-value"); + CheckFactories.registerCheck("modernize-use-constexpr"); CheckFactories.registerCheck( "modernize-use-designated-initializers"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstexprCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstexprCheck.cpp new file mode 100644 index 0000000000000..4f1d5e339d11e --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseConstexprCheck.cpp @@ -0,0 +1,985 @@ +//===--- 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 +#include + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +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(); +} +} // namespace + +static bool +satisfiesConstructorPropertiesUntil20(const CXXConstructorDecl *Ctor, + ASTContext &Ctx) { + const CXXRecordDecl *Rec = Ctor->getParent(); + llvm::SmallPtrSet Bases{}; + for (const CXXBaseSpecifier Base : Rec->bases()) + Bases.insert(Base.getType()->getAsRecordDecl()); + + llvm::SmallPtrSet Fields{Rec->field_begin(), + Rec->field_end()}; + llvm::SmallPtrSet Indirects{}; + + for (const CXXCtorInitializer *const Init : Ctor->inits()) { + const Type *InitType = Init->getBaseClass(); + if (InitType && InitType->isRecordType()) { + const auto *ConstructingInit = + llvm::dyn_cast(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("indirect"); + + size_t NumInitializations = false; + for (const NamedDecl *ND : IField->chain()) + NumInitializations += Indirects.erase(llvm::dyn_cast(ND)); + + if (NumInitializations != 1) + return false; + + for (const NamedDecl *ND : IField->chain()) + Fields.erase(llvm::dyn_cast(ND)); + } + + if (!Fields.empty()) + return false; + + return true; +} + +static 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; +} + +static bool isLiteralType(QualType QT, const ASTContext &Ctx, + const bool ConservativeLiteralType); + +static 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; +} + +static bool isLiteralType(QualType QT, const ASTContext &Ctx, + const bool ConservativeLiteralType) { + return isLiteralType(QT.getTypePtr(), Ctx, ConservativeLiteralType); +} + +static 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(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(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(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 { + public: + using Base = clang::RecursiveASTVisitor; + 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(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() != 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(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(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. +static 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(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (Method && Method->isVirtual()) + return false; + + if (llvm::isa(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 { + public: + using Base = clang::RecursiveASTVisitor; + 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(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(FDecl)); + if (!V.Possible) + return false; + + if (const auto *Ctor = llvm::dyn_cast(FDecl); + Ctor && !satisfiesConstructorPropertiesUntil20(Ctor, Ctx)) + return false; + + if (const auto *Dtor = llvm::dyn_cast(FDecl); + Dtor && !Dtor->isTrivial()) + return false; + + class BodyVisitor : public clang::RecursiveASTVisitor { + public: + using Base = clang::RecursiveASTVisitor; + 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(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; +} + +static bool satisfiesProperties20( + 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(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (FDecl->hasBody() && llvm::isa(FDecl->getBody())) + return false; + + if ((llvm::isa(FDecl) || + llvm::isa(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 Visitor20 : public clang::RecursiveASTVisitor { + public: + bool shouldVisitImplicitCode() const { return true; } + + Visitor20(bool ConservativeLiteralType) + : ConservativeLiteralType(ConservativeLiteralType) {} + + 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 = isLiteralType(VD->getType(), VD->getASTContext(), + ConservativeLiteralType) && + (StorageDur != StorageDuration::SD_Static && + StorageDur != StorageDuration::SD_Thread); + return Possible; + } + + bool WalkUpFromCastExpr(CastExpr *CE) { + if (llvm::is_contained( + { + CK_LValueBitCast, + CK_IntegralToPointer, + CK_PointerToIntegral, + }, + CE->getCastKind())) { + Possible = false; + return false; + } + return true; + } + + bool TraverseCXXReinterpretCastExpr(CXXReinterpretCastExpr *) { + Possible = false; + return false; + } + + bool Possible = true; + bool ConservativeLiteralType; + }; + + Visitor20 V{ConservativeLiteralType}; + V.TraverseDecl(const_cast(FDecl)); + if (!V.Possible) + return false; + + if (const auto *Ctor = llvm::dyn_cast(FDecl)) + satisfiesConstructorPropertiesUntil20(Ctor, Ctx); + + if (const auto *Dtor = llvm::dyn_cast(FDecl); + Dtor && !Dtor->isTrivial()) + return false; + + class BodyVisitor : public clang::RecursiveASTVisitor { + public: + using Base = clang::RecursiveASTVisitor; + bool shouldVisitImplicitCode() const { return true; } + + explicit BodyVisitor(const ASTContext &Ctx, bool ConservativeLiteralType) + : Ctx(Ctx), LO(Ctx.getLangOpts()), + 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(CE->getCalleeDecl()); + FDecl && !FDecl->isConstexprSpecified()) { + Possible = false; + return false; + } + return true; + } + + const ASTContext &Ctx; + const LangOptions &LO; + 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; +} + +static bool satisfiesProperties2326( + const FunctionDecl *FDecl, ASTContext &Ctx, + const bool AddConstexprToMethodOfClassWithoutConstexprConstructor) { + if (FDecl->isConstexprSpecified()) + return true; + + const LangOptions LO = Ctx.getLangOpts(); + const CXXMethodDecl *Method = llvm::dyn_cast(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (FDecl->hasBody() && llvm::isa(FDecl->getBody())) + return false; + + if ((llvm::isa(FDecl) || + llvm::isa(FDecl)) && + llvm::any_of( + Method->getParent()->bases(), + [](const CXXBaseSpecifier &Base) { return Base.isVirtual(); })) + return false; + return true; +} + +namespace { +// FIXME: fix CXX23 allowing decomposition decls, but it is only a feature since +// CXX26 +AST_MATCHER_P2(FunctionDecl, satisfiesProperties, bool, ConservativeLiteralType, + bool, AddConstexprToMethodOfClassWithoutConstexprConstructor) { + ASTContext &Ctx = Finder->getASTContext(); + const LangOptions LO = Ctx.getLangOpts(); + + if (LO.CPlusPlus26) { + return satisfiesProperties2326( + &Node, Ctx, AddConstexprToMethodOfClassWithoutConstexprConstructor); + } + if (LO.CPlusPlus23) { + return satisfiesProperties2326( + &Node, Ctx, AddConstexprToMethodOfClassWithoutConstexprConstructor); + } + if (LO.CPlusPlus20) { + return satisfiesProperties20( + &Node, Ctx, ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor); + } + if (LO.CPlusPlus17) { + return satisfiesProperties1417( + &Node, Ctx, ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor); + } + if (LO.CPlusPlus14) { + return satisfiesProperties1417( + &Node, Ctx, ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor); + } + if (LO.CPlusPlus11) + return satisfiesProperties11( + &Node, Ctx, ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor); + + return false; +} + +AST_MATCHER_P(VarDecl, satisfiesVariableProperties, bool, + ConservativeLiteralType) { + ASTContext &Ctx = Finder->getASTContext(); + const LangOptions LO = Ctx.getLangOpts(); + + const QualType QT = Node.getType(); + const Type *T = QT.getTypePtr(); + if (!T) + return false; + + if (!isLiteralType(T, Ctx, ConservativeLiteralType)) + return false; + + const bool IsDeclaredInsideConstexprFunction = std::invoke([&Node]() { + const auto *Func = llvm::dyn_cast(Node.getDeclContext()); + if (!Func) + return false; + return !Func->isConstexpr(); + }); + + if (!Finder->getASTContext().getLangOpts().CPlusPlus23 && + Node.isStaticLocal() && IsDeclaredInsideConstexprFunction) + return false; + + if (!Finder->getASTContext().getLangOpts().CPlusPlus20) + return true; + + const auto *RDecl = T->getAsCXXRecordDecl(); + const auto *const ArrayOrPtrElement = T->getPointeeOrArrayElementType(); + if (ArrayOrPtrElement) + RDecl = ArrayOrPtrElement->getAsCXXRecordDecl(); + + if (RDecl && (!RDecl->hasDefinition() || !RDecl->hasConstexprDestructor())) + return false; + + return true; +} +} // namespace + +void UseConstexprCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + functionDecl( + isDefinition(), + unless(anyOf(isConstexpr(), isImplicit(), hasExternalFormalLinkage(), + isInMacro(), isMain(), isInStdNamespace(), + isExpansionInSystemHeader(), isExternC())), + locationPermitsConstexpr(), allRedeclsInSameFile(), + satisfiesProperties( + ConservativeLiteralType, + AddConstexprToMethodOfClassWithoutConstexprConstructor)) + .bind("func"), + this); + + Finder->addMatcher( + functionDecl(isConstexpr(), isImplicit(), unless(isConstexprSpecified()), + unless(anyOf(isInStdNamespace(), isExpansionInSystemHeader(), + isInMacro())), + allRedeclsInSameFile()) + .bind("func"), + this); + + Finder->addMatcher( + varDecl( + unless(anyOf(parmVarDecl(), isImplicit(), isInStdNamespace(), + isExpansionInSystemHeader(), isConstexpr(), isExternC(), + hasExternalFormalLinkage(), isInMacro())), + hasNoRedecl(), hasType(qualType(isConstQualified())), + satisfiesVariableProperties(ConservativeLiteralType), + hasInitializer(expr(isCXX11ConstantExpr()))) + .bind("var"), + this); +} + +void UseConstexprCheck::check(const MatchFinder::MatchResult &Result) { + constexpr const auto MaybeResolveToTemplateDecl = + [](const FunctionDecl *Func) { + if (Func && Func->isTemplateInstantiation()) + Func = Func->getTemplateInstantiationPattern(); + return Func; + }; + + if (const auto *Func = Result.Nodes.getNodeAs("func")) { + Func = MaybeResolveToTemplateDecl(Func); + if (Func) + Functions.insert(Func); + return; + } + + if (const auto *Var = Result.Nodes.getNodeAs("var")) { + if (const VarDecl *VarTemplate = Var->getTemplateInstantiationPattern()) + Var = VarTemplate; + + VariableMapping.insert({Var, MaybeResolveToTemplateDecl( + llvm::dyn_cast_if_present( + Var->getDeclContext()))}); + return; + } +} + +void UseConstexprCheck::onEndOfTranslationUnit() { + const std::string FunctionReplacement = ConstexprString + " "; + for (const FunctionDecl *Func : Functions) { + const SourceRange R = + SourceRange(Func->getInnerLocStart(), Func->getLocation()); + auto Diag = + diag(Func->getLocation(), "function %0 can be declared 'constexpr'") + << Func << R; + + for (const Decl *D : Func->redecls()) + if (const auto *FDecl = llvm::dyn_cast(D)) + Diag << FixItHint::CreateInsertion(FDecl->getInnerLocStart(), + FunctionReplacement); + } + + const std::string VariableReplacementWithStatic = StaticConstexprString + " "; + const auto VariableReplacement = + [&FunctionReplacement, this, &VariableReplacementWithStatic]( + const VarDecl *Var, const FunctionDecl *FuncCtx, + const bool IsAddingConstexprToFuncCtx) -> const std::string & { + if (!FuncCtx) + return FunctionReplacement; + + if (!getLangOpts().CPlusPlus23) + return FunctionReplacement; + + // We'll prefer the function to be constexpr over the function not being + // constexpr just for the var to be static constexpr instead of just + // constexpr. + if (IsAddingConstexprToFuncCtx) + return FunctionReplacement; + + if (Var->isStaticLocal()) + return FunctionReplacement; + + return VariableReplacementWithStatic; + }; + + for (const auto &[Var, FuncCtx] : VariableMapping) { + const auto IsAddingConstexprToFuncCtx = Functions.contains(FuncCtx); + if (FuncCtx && getLangOpts().CPlusPlus23 && Var->isStaticLocal() && + IsAddingConstexprToFuncCtx) + continue; + const SourceRange R = + SourceRange(Var->getInnerLocStart(), Var->getLocation()); + auto Diag = + diag(Var->getLocation(), "variable %0 can be declared 'constexpr'") + << Var << R + << FixItHint::CreateInsertion( + Var->getInnerLocStart(), + VariableReplacement(Var, FuncCtx, IsAddingConstexprToFuncCtx)); + // 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. + const CharSourceRange FileRange = Lexer::makeFileCharRange( + CharSourceRange::getCharRange(Var->getInnerLocStart(), + Var->getLocation()), + Var->getASTContext().getSourceManager(), getLangOpts()); + if (const std::optional ConstToken = + utils::lexer::getQualifyingToken( + tok::TokenKind::kw_const, FileRange, Var->getASTContext(), + Var->getASTContext().getSourceManager())) { + Diag << FixItHint::CreateRemoval(ConstToken->getLocation()); + } + } + + Functions.clear(); + VariableMapping.clear(); +} + +UseConstexprCheck::UseConstexprCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + ConservativeLiteralType(Options.get("ConservativeLiteralType", true)), + AddConstexprToMethodOfClassWithoutConstexprConstructor(Options.get( + "AddConstexprToMethodOfClassWithoutConstexprConstructor", false)), + ConstexprString(Options.get("ConstexprString", "constexpr")), + StaticConstexprString( + Options.get("StaticConstexprString", "static " + ConstexprString)) {} +void UseConstexprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ConservativeLiteralType", ConservativeLiteralType); + Options.store(Opts, "AddConstexprToMethodOfClassWithoutConstexprConstructor", + AddConstexprToMethodOfClassWithoutConstexprConstructor); + Options.store(Opts, "ConstexprString", ConstexprString); + Options.store(Opts, "StaticConstexprString", StaticConstexprString); +} +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstexprCheck.h b/clang-tools-extra/clang-tidy/modernize/UseConstexprCheck.h new file mode 100644 index 0000000000000..061ee8aa58a30 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseConstexprCheck.h @@ -0,0 +1,45 @@ +//===--- UseUseConstexprCheck.h - clang-tidy --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONSTEXPRCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONSTEXPRCHECK_H + +#include "../ClangTidyCheck.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" + +namespace clang::tidy::modernize { + +/// Finds functions and variables that can be declared 'constexpr'. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-constexpr.html +class UseConstexprCheck : public ClangTidyCheck { +public: + UseConstexprCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus11; + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void onEndOfTranslationUnit() override; + +private: + const bool ConservativeLiteralType; + const bool AddConstexprToMethodOfClassWithoutConstexprConstructor; + const std::string ConstexprString; + const std::string StaticConstexprString; + llvm::SmallPtrSet Functions; + llvm::DenseMap VariableMapping; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONSTEXPRCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8c331e0b0a403..d91b558ff5393 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -142,6 +142,10 @@ New checks Finds unscoped (non-class) ``enum`` declarations and suggests using ``enum class`` instead. +- New :doc:`modernize-use-constexpr + ` check that finds functions and + variables that can be declared `constexpr`. + - New :doc:`modernize-use-scoped-lock ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 5098582d0c42b..d5c7b055b506b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -300,6 +300,7 @@ Clang-Tidy Checks :doc:`modernize-unary-static-assert `, "Yes" :doc:`modernize-use-auto `, "Yes" :doc:`modernize-use-bool-literals `, "Yes" + :doc:`modernize-use-constexpr `, "Yes" :doc:`modernize-use-constraints `, "Yes" :doc:`modernize-use-default-member-init `, "Yes" :doc:`modernize-use-designated-initializers `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constexpr.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constexpr.rst new file mode 100644 index 0000000000000..d73c0ef9e5e76 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constexpr.rst @@ -0,0 +1,64 @@ +.. title:: clang-tidy - modernize-use-constexpr + +modernize-use-constexpr +======================= + +Finds functions and variables that can be declared 'constexpr'. + +The check analyses any function and variable according to the rules defined +for the language version that the code is compiled with. +Changing to a newer language standard may therefore offer additional opportunity +to declare a function or variable as ``constexpr``. +Furthermore, this check can be incremental in terms of its diagnostics. For +example, declaring a function ``constepxr`` might create new opportunities of +marking additional variables or function ``constexpr``, which can only be found +in subsequent runs of this check. + +For variables, the check will only detect variables that can be declared +``constexpr`` if they are already ``const``. +This is because this check would have to duplicate the expensive analysis of the +:doc:`misc-const-correctness<../misc/const-correctness>` check. +Therefore, it is recommended to have +:doc:`misc-const-correctness<../misc/const-correctness>` enabled +in the Clang-Tidy config when this check is, so that all opportunities for +``const`` and also ``constexpr`` are explored. + +Options +------- + +.. option:: ConservativeLiteralType + + With this option enabled, only literal types that can be constructed at + compile-time are considered to supoprt ``constexpr``. + + .. code-block:: c++ + + struct NonLiteral{ + NonLiteral(); + ~NonLiteral(); + int &ref; + }; + + This type is a literal type, but can not be constructed at compile-time. + With `ConservativeLiteralType` equal to `true`, variables or funtions + with this type are not diagnosed to add ``constexpr``. Default is + `true`. + +.. option:: AddConstexprToMethodOfClassWithoutConstexprConstructor + + While a function of a class or struct could be declared ``constexpr``, when + the class itself can never be constructed at compile-time, then adding + ``constexpr`` to a member function is superfluous. This option controls if + ``constexpr`` should be added anyways. Default is ``false``. + +.. option:: ConstexprString + + The string to use to specify a variable or function as ``constexpr``, for + example, a macro. Default is ``constexpr``. + +.. option:: ConstexprString + + The string to use with C++23 to specify a function-local variable as + ``static constexpr``, for example, a macro. Default is ``static constexpr`` + (concatenating ``static`` with the `ConstexprString` option). + diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-cxx20.cpp new file mode 100644 index 0000000000000..dd400fe5185fc --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-cxx20.cpp @@ -0,0 +1,36 @@ +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constexpr %t + +namespace std { +template +struct coroutine_handle { + static constexpr coroutine_handle from_address(void* addr) { + return {}; + } +}; + +struct always_suspend { + bool await_ready() const noexcept; + bool await_resume() const noexcept; + template + bool await_suspend(coroutine_handle) const noexcept; +}; + +template +struct coroutine_traits { + using promise_type = T::promise_type; +}; +} // namespace std + +struct generator { + struct promise_type { + void return_value(int v); + std::always_suspend yield_value(int&&); + std::always_suspend initial_suspend() const noexcept; + std::always_suspend final_suspend() const noexcept; + void unhandled_exception(); + generator get_return_object(); + }; +}; + + +generator f25() { co_return 10; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-locals.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-locals.cpp new file mode 100644 index 0000000000000..e4bcdc7b3dae5 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-locals.cpp @@ -0,0 +1,20 @@ +// RUN: %check_clang_tidy -std=c++11 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++14 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++17 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=23 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing + + +#define FUNC(N) void func##N() +FUNC(0) { + static int f1 = 1; + static const int f2 = 2; + // CHECK-MESSAGES-23: :[[@LINE-1]]:22: warning: variable 'f2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f2 = 2; + const int f3 = 3; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: variable 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES: constexpr int f3 = 3; + // CHECK-MESSAGES-23: :[[@LINE-3]]:15: warning: variable 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: static constexpr int f3 = 3; +} + diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-replacement-opts.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-replacement-opts.cpp new file mode 100644 index 0000000000000..39d3cb837f0d3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr-replacement-opts.cpp @@ -0,0 +1,32 @@ +// RUN: %check_clang_tidy -std=c++11 %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConstexprString: 'CXPR'}}" +// RUN: %check_clang_tidy -std=c++14 %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConstexprString: 'CXPR'}}" +// RUN: %check_clang_tidy -std=c++17 %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConstexprString: 'CXPR'}}" +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConstexprString: 'CXPR'}}" +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=23 %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConstexprString: 'CXPR'}}" +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=23-STATIC %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConstexprString: 'CXPR', modernize-use-constexpr.StaticConstexprString: 'STATIC_CXPR'}}" + +static int f1() { return 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: function 'f1' can be declared 'constexpr' [modernize-use-constexpr] +// CHECK-FIXES: CXPR static int f1() { return 0; } +// CHECK-MESSAGES-23: :[[@LINE-3]]:12: warning: function 'f1' can be declared 'constexpr' [modernize-use-constexpr] +// CHECK-FIXES-23: CXPR static int f1() { return 0; } +// CHECK-MESSAGES-23-STATIC: :[[@LINE-5]]:12: warning: function 'f1' can be declared 'constexpr' [modernize-use-constexpr] +// CHECK-FIXES-23-STATIC: CXPR static int f1() { return 0; } + +#define FUNC(N) void func##N() +FUNC(0) { + static int f1 = 1; + static const int f2 = 2; + // CHECK-MESSAGES-23: :[[@LINE-1]]:22: warning: variable 'f2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: CXPR static int f2 = 2; + // CHECK-MESSAGES-23-STATIC: :[[@LINE-3]]:22: warning: variable 'f2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23-STATIC: CXPR static int f2 = 2; + const int f3 = 3; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: variable 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES: CXPR int f3 = 3; + // CHECK-MESSAGES-23: :[[@LINE-3]]:15: warning: variable 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: static CXPR int f3 = 3; + // CHECK-MESSAGES-23-STATIC: :[[@LINE-5]]:15: warning: variable 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23-STATIC: STATIC_CXPR int f3 = 3; +} + diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr.cpp new file mode 100644 index 0000000000000..9fca53b08f35c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constexpr.cpp @@ -0,0 +1,566 @@ +// RUN: %check_clang_tidy -std=c++11 -check-suffix=11 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++14 -check-suffix=11,14 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++17 -check-suffix=11,14,17 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++20 -check-suffix=11,14,17,20 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=11,14,17,20,23 %s modernize-use-constexpr %t -- -- -fno-delayed-template-parsing + +// RUN: %check_clang_tidy -std=c++11 -check-suffix=11,11-CLT %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++14 -check-suffix=11,11-CLT,14,14-CLT %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++17 -check-suffix=11,11-CLT,14,14-CLT,17,17-CLT %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++20 -check-suffix=11,11-CLT,14,14-CLT,17,17-CLT,20,20-CLT %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=11,14,17,20,23 %s modernize-use-constexpr %t -- -config="{CheckOptions: {modernize-use-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing + +namespace { +namespace my { + struct point { + constexpr point() {} + int get_x() const { return x; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:9: warning: function 'get_x' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr int get_x() const { return x; } + int x; + int y; + }; + + struct point2 { + point2(); + int get_x() const { return x; } + int x; + }; +} // namespace my +} // namespace + +namespace function { + struct Empty {}; + + struct Base { + virtual void virt() = 0; + }; + struct Derived : Base { + Derived() {} + void virt() override {} + }; + + static void f1() {} + // CHECK-MESSAGES-23: :[[@LINE-1]]:15: warning: function 'f1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static void f1() {} + + static int f2() { return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f2() { return 0; } + + static int f3(int x) { return x; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f3(int x) { return x; } + + static int f4(Empty x) { return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f4' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f4(Empty x) { return 0; } + + static int f5(Empty x) { return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f5(Empty x) { return 0; } + + static int f6(Empty x) { ; return 0; } + // CHECK-MESSAGES-14: :[[@LINE-1]]:14: warning: function 'f6' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14: constexpr static int f6(Empty x) { ; return 0; } + + static int f7(Empty x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f7' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f7(Empty x) { static_assert(0 == 0, ""); return 0; } + + static int f8(Empty x) { using my_int = int; return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f8' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f8(Empty x) { using my_int = int; return 0; } + + static int f9(Empty x) { using my::point; return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f9' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f9(Empty x) { using my::point; return 0; } + + static int f10(Empty x) { return 10; return 0; } + // CHECK-MESSAGES-14: :[[@LINE-1]]:14: warning: function 'f10' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14: constexpr static int f10(Empty x) { return 10; return 0; } + + static int f11(Empty x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-14: :[[@LINE-1]]:14: warning: function 'f11' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14: constexpr static int f11(Empty x) { if (true) return 10; return 0; } + + static int f12(Empty x) { label: ; goto label; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f12' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f12(Empty x) { label: ; goto label; return 0; } + static int f13(Empty x) { try { throw 0; } catch(int) {}; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f13' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f13(Empty x) { try { throw 0; } catch(int) {}; return 0; } + static int f14(Empty x) { asm ("mov %rax, %rax"); } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f14' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f14(Empty x) { asm ("mov %rax, %rax"); } + static int f15(Empty x) { int y; return 0; } + // CHECK-MESSAGES-20: :[[@LINE-1]]:14: warning: function 'f15' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-20: constexpr static int f15(Empty x) { int y; return 0; } + static int f16(Empty x) { static int y = 0; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f16' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f16(Empty x) { static int y = 0; return 0; } + static int f17(Empty x) { thread_local int y = 0; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f17' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f17(Empty x) { thread_local int y = 0; return 0; } + static int f18(Empty x) { [](){ label: ; goto label; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f18' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f18(Empty x) { [](){ label: ; goto label; return 0; }; return 0; } + static int f19(Empty x) { [](){ try { throw 0; } catch(int) {}; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f19' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f19(Empty x) { [](){ try { throw 0; } catch(int) {}; return 0; }; return 0; } + static int f20(Empty x) { [](){ asm ("mov %rax, %rax"); }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f20' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f20(Empty x) { [](){ asm ("mov %rax, %rax"); }; return 0; } + static int f21(Empty x) { [](){ int y; return 0; }; return 0; } + // CHECK-MESSAGES-20: :[[@LINE-1]]:14: warning: function 'f21' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-20: constexpr static int f21(Empty x) { [](){ int y; return 0; }; return 0; } + static int f22(Empty x) { [](){ static int y = 0; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f22' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f22(Empty x) { [](){ static int y = 0; return 0; }; return 0; } + static int f23(Empty x) { [](){ thread_local int y = 0; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f23' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f23(Empty x) { [](){ thread_local int y = 0; return 0; }; return 0; } + + static int f24(Empty x) { return [](){ return 0; }(); } + // CHECK-MESSAGES-17: :[[@LINE-1]]:14: warning: function 'f24' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-17: constexpr static int f24(Empty x) { return [](){ return 0; }(); } + + static int f25(Empty x) { new int; return 0; } + // CHECK-MESSAGES-20: :[[@LINE-1]]:14: warning: function 'f25' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-20: constexpr static int f25(Empty x) { new int; return 0; } + + struct Range0To10 { + struct iterator { + int operator*() const; + void operator++(); + friend bool operator!=(const iterator&lhs, const iterator&rhs) { return lhs.i == rhs.i; } + int i; + }; + iterator begin() const; + iterator end() const; + }; + static int f26(Empty x) { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f26' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f26(Empty x) { + // CHECK-MESSAGES-14-CLT: :[[@LINE-3]]:14: warning: function 'f26' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14-CLT: constexpr static int f26(Empty x) { + auto R = Range0To10{}; + for (const int i: R) { } + return 0; + } +} // namespace function +namespace function_non_literal { + struct NonLiteral{ + NonLiteral(); + ~NonLiteral(); + int &ref; + }; + + struct Base { + virtual void virt() = 0; + }; + struct Derived : Base { + Derived() {} + void virt() override {} + }; + + static void f1() {} + // CHECK-MESSAGES-23: :[[@LINE-1]]:15: warning: function 'f1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static void f1() {} + + static int f2() { return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f2() { return 0; } + + static int f3(int x) { return x; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f3(int x) { return x; } + + static int f4(NonLiteral x) { return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f4' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f4(NonLiteral x) { return 0; } + + static int f5(NonLiteral x) { return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f5(NonLiteral x) { return 0; } + + static int f6(NonLiteral x) { ; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f6' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f6(NonLiteral x) { ; return 0; } + + static int f7(NonLiteral x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f7' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f7(NonLiteral x) { static_assert(0 == 0, ""); return 0; } + + static int f8(NonLiteral x) { using my_int = int; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f8' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f8(NonLiteral x) { using my_int = int; return 0; } + + static int f9(NonLiteral x) { using my::point; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f9' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f9(NonLiteral x) { using my::point; return 0; } + + static int f10(NonLiteral x) { return 10; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f10' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f10(NonLiteral x) { return 10; return 0; } + + static int f11(NonLiteral x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f11' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f11(NonLiteral x) { if (true) return 10; return 0; } + + static int f12(NonLiteral x) { label: ; goto label; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f12' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f12(NonLiteral x) { label: ; goto label; return 0; } + static int f13(NonLiteral x) { try { throw 0; } catch(int) {}; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f13' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f13(NonLiteral x) { try { throw 0; } catch(int) {}; return 0; } + static int f14(NonLiteral x) { asm ("mov %rax, %rax"); } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f14' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f14(NonLiteral x) { asm ("mov %rax, %rax"); } + static int f15(NonLiteral x) { int y; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f15' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f15(NonLiteral x) { int y; return 0; } + static int f16(NonLiteral x) { static int y = 0; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f16' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f16(NonLiteral x) { static int y = 0; return 0; } + static int f17(NonLiteral x) { thread_local int y = 0; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f17' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f17(NonLiteral x) { thread_local int y = 0; return 0; } + static int f18(NonLiteral x) { [](){ label: ; goto label; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f18' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f18(NonLiteral x) { [](){ label: ; goto label; return 0; }; return 0; } + static int f19(NonLiteral x) { [](){ try { throw 0; } catch(int) {}; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f19' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f19(NonLiteral x) { [](){ try { throw 0; } catch(int) {}; return 0; }; return 0; } + static int f20(NonLiteral x) { [](){ asm ("mov %rax, %rax"); }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f20' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f20(NonLiteral x) { [](){ asm ("mov %rax, %rax"); }; return 0; } + static int f21(NonLiteral x) { [](){ int y; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f21' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f21(NonLiteral x) { [](){ int y; return 0; }; return 0; } + static int f22(NonLiteral x) { [](){ static int y = 0; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f22' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f22(NonLiteral x) { [](){ static int y = 0; return 0; }; return 0; } + static int f23(NonLiteral x) { [](){ thread_local int y = 0; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f23' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f23(NonLiteral x) { [](){ thread_local int y = 0; return 0; }; return 0; } + + static int f24(NonLiteral x) { return [](){ return 0; }(); } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f24' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f24(NonLiteral x) { return [](){ return 0; }(); } + + static int f25(NonLiteral x) { new int; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f25' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f25(NonLiteral x) { new int; return 0; } + + struct Range0To10 { + struct iterator { + int operator*() const { return i; } + void operator++() { ++i; } + friend bool operator!=(const iterator&lhs, const iterator&rhs) { return lhs.i == rhs.i; } + int i; + }; + iterator begin() const { return { 0 }; } + iterator end() const { return { 10 }; } + }; + static int f26(NonLiteral x) { + auto R = Range0To10{}; + for (const int i: R) { } + return 0; + } + // CHECK-MESSAGES-23: :[[@LINE-5]]:14: warning: function 'f26' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f26(NonLiteral x) { +} // namespace function_non_literal +namespace function_non_literal_ref { + struct NonLiteral{ + NonLiteral(); + ~NonLiteral(); + int &ref; + }; + + struct Base { + virtual void virt() = 0; + }; + struct Derived : Base { + Derived() {} + void virt() override {} + }; + + static void f1() {} + // CHECK-MESSAGES-23: :[[@LINE-1]]:15: warning: function 'f1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static void f1() {} + + static int f2() { return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f2() { return 0; } + + static int f3(int x) { return x; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:14: warning: function 'f3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int f3(int x) { return x; } + + static int f4(NonLiteral& x) { return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f4' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f4(NonLiteral& x) { return 0; } + // CHECK-MESSAGES-11-CLT: :[[@LINE-3]]:14: warning: function 'f4' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11-CLT: constexpr static int f4(NonLiteral& x) { return 0; } + + static int f5(NonLiteral& x) { return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f5(NonLiteral& x) { return 0; } + // CHECK-MESSAGES-11-CLT: :[[@LINE-3]]:14: warning: function 'f5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11-CLT: constexpr static int f5(NonLiteral& x) { return 0; } + + static int f6(NonLiteral& x) { ; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f6' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f6(NonLiteral& x) { ; return 0; } + // CHECK-MESSAGES-14-CLT: :[[@LINE-3]]:14: warning: function 'f6' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14-CLT: constexpr static int f6(NonLiteral& x) { ; return 0; } + + static int f7(NonLiteral& x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f7' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f7(NonLiteral& x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-11-CLT: :[[@LINE-3]]:14: warning: function 'f7' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11-CLT: constexpr static int f7(NonLiteral& x) { static_assert(0 == 0, ""); return 0; } + + static int f8(NonLiteral& x) { using my_int = int; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f8' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f8(NonLiteral& x) { using my_int = int; return 0; } + // CHECK-MESSAGES-11-CLT: :[[@LINE-3]]:14: warning: function 'f8' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11-CLT: constexpr static int f8(NonLiteral& x) { using my_int = int; return 0; } + + static int f9(NonLiteral& x) { using my::point; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f9' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f9(NonLiteral& x) { using my::point; return 0; } + // CHECK-MESSAGES-11-CLT: :[[@LINE-3]]:14: warning: function 'f9' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11-CLT: constexpr static int f9(NonLiteral& x) { using my::point; return 0; } + + static int f10(NonLiteral& x) { return 10; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f10' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f10(NonLiteral& x) { return 10; return 0; } + // CHECK-MESSAGES-14-CLT: :[[@LINE-3]]:14: warning: function 'f10' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14-CLT: constexpr static int f10(NonLiteral& x) { return 10; return 0; } + + static int f11(NonLiteral& x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f11' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f11(NonLiteral& x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-14-CLT: :[[@LINE-3]]:14: warning: function 'f11' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14-CLT: constexpr static int f11(NonLiteral& x) { if (true) return 10; return 0; } + + static int f12(NonLiteral& x) { label: ; goto label; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f12' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f12(NonLiteral& x) { label: ; goto label; return 0; } + static int f13(NonLiteral& x) { try { throw 0; } catch(int) {}; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f13' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f13(NonLiteral& x) { try { throw 0; } catch(int) {}; return 0; } + static int f14(NonLiteral& x) { asm ("mov %rax, %rax"); } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f14' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f14(NonLiteral& x) { asm ("mov %rax, %rax"); } + static int f15(NonLiteral& x) { int y; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f15' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f15(NonLiteral& x) { int y; return 0; } + // CHECK-MESSAGES-20-CLT: :[[@LINE-3]]:14: warning: function 'f15' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-20-CLT: constexpr static int f15(NonLiteral& x) { int y; return 0; } + static int f16(NonLiteral& x) { static int y = 0; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f16' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f16(NonLiteral& x) { static int y = 0; return 0; } + static int f17(NonLiteral& x) { thread_local int y = 0; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f17' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f17(NonLiteral& x) { thread_local int y = 0; return 0; } + static int f18(NonLiteral& x) { [](){ label: ; goto label; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f18' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f18(NonLiteral& x) { [](){ label: ; goto label; return 0; }; return 0; } + static int f19(NonLiteral& x) { [](){ try { throw 0; } catch(int) {}; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f19' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f19(NonLiteral& x) { [](){ try { throw 0; } catch(int) {}; return 0; }; return 0; } + static int f20(NonLiteral& x) { [](){ asm ("mov %rax, %rax"); }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f20' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f20(NonLiteral& x) { [](){ asm ("mov %rax, %rax"); }; return 0; } + static int f21(NonLiteral& x) { [](){ int y; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f21' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f21(NonLiteral& x) { [](){ int y; return 0; }; return 0; } + // CHECK-MESSAGES-20-CLT: :[[@LINE-3]]:14: warning: function 'f21' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-20-CLT: constexpr static int f21(NonLiteral& x) { [](){ int y; return 0; }; return 0; } + static int f22(NonLiteral& x) { [](){ static int y = 0; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f22' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f22(NonLiteral& x) { [](){ static int y = 0; return 0; }; return 0; } + static int f23(NonLiteral& x) { [](){ thread_local int y = 0; return 0; }; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f23' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f23(NonLiteral& x) { [](){ thread_local int y = 0; return 0; }; return 0; } + + static int f24(NonLiteral& x) { return [](){ return 0; }(); } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f24' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f24(NonLiteral& x) { return [](){ return 0; }(); } + // CHECK-MESSAGES-17-CLT: :[[@LINE-3]]:14: warning: function 'f24' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-17-CLT: constexpr static int f24(NonLiteral& x) { return [](){ return 0; }(); } + + static int f25(NonLiteral& x) { new int; return 0; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'f25' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f25(NonLiteral& x) { new int; return 0; } + // CHECK-MESSAGES-20-CLT: :[[@LINE-3]]:14: warning: function 'f25' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-20-CLT: constexpr static int f25(NonLiteral& x) { new int; return 0; } + + struct Range0To10 { + struct iterator { + int operator*() const { return i; } + void operator++() { ++i; } + friend bool operator!=(const iterator&lhs, const iterator&rhs) { return lhs.i == rhs.i; } + int i; + }; + iterator begin() const { return { 0 }; } + iterator end() const { return { 10 }; } + }; + static int f26(NonLiteral& x) { + auto R = Range0To10{}; + for (const int i: R) { } + return 0; + } + // CHECK-MESSAGES-23: :[[@LINE-5]]:14: warning: function 'f26' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr static int f26(NonLiteral& x) { + // CHECK-MESSAGES-14-CLT: :[[@LINE-7]]:14: warning: function 'f26' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-14-CLT: constexpr static int f26(NonLiteral& x) { +} // namespace function_non_literal_ref + +namespace { +namespace variable { + namespace literal_type { + constexpr int f1() { return 0; } + int g1() { return 0; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:13: warning: function 'g1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr int g1() { return 0; } + static constexpr int A1 = 0; + static int B1 = 0; + static const int C1 = 0; + // CHECK-MESSAGES-11: :[[@LINE-1]]:26: warning: variable 'C1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int C1 = 0; + static const int D1 = f1(); + // CHECK-MESSAGES-11: :[[@LINE-1]]:26: warning: variable 'D1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static int D1 = f1(); + static const int E1 = g1(); + + template + const T TemplatedVar1 = T{}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:17: warning: variable 'TemplatedVar1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr T TemplatedVar1 = T{}; + + void h1() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr void h1() { + int a1 = 0; + const int b1 = 1; + // CHECK-MESSAGES-11: :[[@LINE-1]]:23: warning: variable 'b1' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr int b1 = 1; + static int c1 = 2; + static const int d1 = 3; + + static auto e1 = TemplatedVar1 + TemplatedVar1; + + const auto check = [](const int & ref) { }; + // CHECK-MESSAGES-17: :[[@LINE-1]]:24: warning: variable 'check' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-17: constexpr auto check = [](const int & ref) { }; + } + } // namespace literal_type + + namespace struct_type { + struct AStruct { int val; }; + constexpr AStruct f2() { return {}; } + AStruct g2() { return {}; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:17: warning: function 'g2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr AStruct g2() { return {}; } + static constexpr AStruct A2 = {}; + static AStruct B2 = {}; + static const AStruct C2 = {}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:30: warning: variable 'C2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static AStruct C2 = {}; + static const AStruct D2 = f2(); + // CHECK-MESSAGES-11: :[[@LINE-1]]:30: warning: variable 'D2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static AStruct D2 = f2(); + static const AStruct E2 = g2(); + void h2() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr void h2() { + AStruct a2{}; + const AStruct b2{}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:27: warning: variable 'b2' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr AStruct b2{}; + static AStruct c2{}; + static const AStruct d2{}; + } + } // namespace struct_type + + namespace struct_type_non_literal { + struct AStruct { ~AStruct(); int val; }; + AStruct g3() { return {}; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:17: warning: function 'g3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr AStruct g3() { return {}; } + static AStruct B3 = {}; + static const AStruct C3 = {}; + static const AStruct E3 = g3(); + + template + const T TemplatedVar2 = T{}; + template + const T TemplatedVar2B = T{}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:17: warning: variable 'TemplatedVar2B' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr T TemplatedVar2B = T{}; + + void h3() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h3' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr void h3() { + AStruct a3{}; + const AStruct b3{}; + static AStruct c3{}; + static const AStruct d3{}; + + static auto e1 = TemplatedVar2; + static auto f1 = TemplatedVar2B; + static auto g1 = TemplatedVar2B; + } + } // namespace struct_type_non_literal + + namespace struct_type_non_literal2 { + struct AStruct { volatile int Val; }; + AStruct g4() { return {}; } + // CHECK-MESSAGES-23: :[[@LINE-1]]:17: warning: function 'g4' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr AStruct g4() { return {}; } + static AStruct B4 = {}; + static const AStruct C4 = {}; + static const AStruct E4 = g4(); + void h4() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h4' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr void h4() { + AStruct a4{}; + const AStruct b4{}; + static AStruct c4{}; + static const AStruct d4{}; + } + } // namespace struct_type_non_literal2 + + namespace struct_type_non_literal3 { + struct AStruct { union { int val; float val5; }; }; + constexpr AStruct f5() { return {}; } + AStruct g5() { return {}; } + // CHECK-MESSAGES-11: :[[@LINE-1]]:17: warning: function 'g5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr AStruct g5() { return {}; } + static constexpr AStruct A5 = {}; + static AStruct B5 = {}; + static const AStruct C5 = {}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:30: warning: variable 'C5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static AStruct C5 = {}; + static const AStruct D5 = f5(); + // CHECK-MESSAGES-11: :[[@LINE-1]]:30: warning: variable 'D5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr static AStruct D5 = f5(); + static const AStruct E5 = g5(); + void h5() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-23: constexpr void h5() { + AStruct a5{}; + const AStruct b5{}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:27: warning: variable 'b5' can be declared 'constexpr' [modernize-use-constexpr] + // CHECK-FIXES-11: constexpr AStruct b5{}; + static AStruct c5{}; + static const AStruct d5{}; + } + } // namespace struct_type_non_literal3 +} // namespace variable +} // namespace +