https://github.com/5chmidti created https://github.com/llvm/llvm-project/pull/146553
This check finds all functions and variables that can be declared as `constexpr`, using the specified standard version to check if the requirements are met. Fixes #115622 >From 35a081fba2d15ee9f4ae825928e5411e296e8196 Mon Sep 17 00:00:00 2001 From: Julian Schmidt <git.julian.schm...@gmail.com> Date: Fri, 6 Sep 2024 22:58:46 +0200 Subject: [PATCH] [clang-tidy] add misc-constexpr check This check finds all functions and variables that can be declared as `constexpr`, using the specified standard version to check if the requirements are met. Fixes #115622 --- .../clang-tidy/misc/CMakeLists.txt | 1 + .../clang-tidy/misc/ConstexprCheck.cpp | 936 ++++++++++++++++++ .../clang-tidy/misc/ConstexprCheck.h | 43 + .../clang-tidy/misc/MiscTidyModule.cpp | 3 + .../docs/clang-tidy/checks/list.rst | 1 + .../docs/clang-tidy/checks/misc/constexpr.rst | 40 + .../checkers/misc/constexpr-cxx20.cpp | 36 + .../checkers/misc/constexpr-ref.cpp | 383 +++++++ .../clang-tidy/checkers/misc/constexpr.cpp | 562 +++++++++++ 9 files changed, 2005 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/misc/ConstexprCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/misc/ConstexprCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/misc/constexpr.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-cxx20.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-ref.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/constexpr.cpp diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index fd7affd22a463..4535df3451c95 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -19,6 +19,7 @@ set_target_properties(genconfusable PROPERTIES FOLDER "Clang Tools Extra/Sourceg add_clang_library(clangTidyMiscModule STATIC ConstCorrectnessCheck.cpp + ConstexprCheck.cpp CoroutineHostileRAIICheck.cpp DefinitionsInHeadersCheck.cpp ConfusableIdentifierCheck.cpp diff --git a/clang-tools-extra/clang-tidy/misc/ConstexprCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstexprCheck.cpp new file mode 100644 index 0000000000000..270ac1dd0fa48 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/ConstexprCheck.cpp @@ -0,0 +1,936 @@ +//===--- ConstexprCheck.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 "ConstexprCheck.h" +#include "../utils/ASTUtils.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 "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; + } + const LangOptions LO = Ctx.getLangOpts(); + const CXXMethodDecl *Method = llvm::dyn_cast<CXXMethodDecl>(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (FDecl->hasBody() && llvm::isa<CoroutineBodyStmt>(FDecl->getBody())) + return false; + + if ((llvm::isa<CXXConstructorDecl>(FDecl) || + llvm::isa<CXXDestructorDecl>(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<Visitor20> { + 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<FunctionDecl *>(FDecl)); + if (!V.Possible) + return false; + + if (const auto *Ctor = llvm::dyn_cast<CXXConstructorDecl>(FDecl)) + satisfiesConstructorPropertiesUntil20(Ctor, Ctx); + + 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(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<FunctionDecl>(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; +} + +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<CXXMethodDecl>(FDecl); + if (Method && !Method->isStatic() && + !Method->getParent()->hasConstexprNonCopyMoveConstructor() && + !AddConstexprToMethodOfClassWithoutConstexprConstructor) + return false; + + if (FDecl->hasBody() && llvm::isa<CoroutineBodyStmt>(FDecl->getBody())) + return false; + + if ((llvm::isa<CXXConstructorDecl>(FDecl) || + llvm::isa<CXXDestructorDecl>(FDecl)) && + llvm::any_of( + Method->getParent()->bases(), + [](const CXXBaseSpecifier &Base) { return Base.isVirtual(); })) + return false; + return true; +} + +// FIXME: add test for uncalled lambda that throws, and called lambda that +// throws +// 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<FunctionDecl>(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 ConstexprCheck::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 ConstexprCheck::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<FunctionDecl>("func")) { + Func = MaybeResolveToTemplateDecl(Func); + if (Func) + Functions.insert(Func); + return; + } + + if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) { + if (const VarDecl *VarTemplate = Var->getTemplateInstantiationPattern()) + Var = VarTemplate; + + VariableMapping.insert({Var, MaybeResolveToTemplateDecl( + llvm::dyn_cast_if_present<FunctionDecl>( + Var->getDeclContext()))}); + return; + } +} + +void ConstexprCheck::onEndOfTranslationUnit() { + 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<FunctionDecl>(D)) + Diag << FixItHint::CreateInsertion(FDecl->getInnerLocStart(), + "constexpr "); + } + for (const auto &[Var, FuncCtx] : VariableMapping) { + if (FuncCtx && getLangOpts().CPlusPlus23 && Var->isStaticLocal() && + Functions.contains(FuncCtx)) + 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(), "constexpr "); + } + + Functions.clear(); + VariableMapping.clear(); +} + +ConstexprCheck::ConstexprCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + ConservativeLiteralType(Options.get("ConservativeLiteralType", true)), + AddConstexprToMethodOfClassWithoutConstexprConstructor(Options.get( + "AddConstexprToMethodOfClassWithoutConstexprConstructor", false)) {} +void ConstexprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ConservativeLiteralType", ConservativeLiteralType); + Options.store(Opts, "AddConstexprToMethodOfClassWithoutConstexprConstructor", + AddConstexprToMethodOfClassWithoutConstexprConstructor); +} +} // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/ConstexprCheck.h b/clang-tools-extra/clang-tidy/misc/ConstexprCheck.h new file mode 100644 index 0000000000000..1dff284a80ecd --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/ConstexprCheck.h @@ -0,0 +1,43 @@ +//===--- ConstexprCheck.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_MISC_CONSTEXPRCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_CONSTEXPRCHECK_H + +#include "../ClangTidyCheck.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" + +namespace clang::tidy::misc { + +/// Find functions and variables that can be declared 'constexpr'. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc/constexpr.html +class ConstexprCheck : public ClangTidyCheck { +public: + ConstexprCheck(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; + llvm::SmallPtrSet<const FunctionDecl *, 32> Functions; + llvm::DenseMap<const VarDecl *, const FunctionDecl *> VariableMapping; +}; + +} // namespace clang::tidy::misc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_CONSTEXPRCHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index 6ddebcbc0e152..ad00cc50f9fd1 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModuleRegistry.h" #include "ConfusableIdentifierCheck.h" #include "ConstCorrectnessCheck.h" +#include "ConstexprCheck.h" #include "CoroutineHostileRAIICheck.h" #include "DefinitionsInHeadersCheck.h" #include "HeaderIncludeCycleCheck.h" @@ -43,6 +44,8 @@ class MiscModule : public ClangTidyModule { "misc-confusable-identifiers"); CheckFactories.registerCheck<ConstCorrectnessCheck>( "misc-const-correctness"); + CheckFactories.registerCheck<ConstexprCheck>( + "misc-constexpr"); CheckFactories.registerCheck<CoroutineHostileRAIICheck>( "misc-coroutine-hostile-raii"); CheckFactories.registerCheck<DefinitionsInHeadersCheck>( diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 5098582d0c42b..97305d1925af4 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -257,6 +257,7 @@ Clang-Tidy Checks :doc:`llvmlibc-restrict-system-libc-headers <llvmlibc/restrict-system-libc-headers>`, "Yes" :doc:`misc-confusable-identifiers <misc/confusable-identifiers>`, :doc:`misc-const-correctness <misc/const-correctness>`, "Yes" + :doc:`misc-constexpr <misc/constexpr>`, "Yes" :doc:`misc-coroutine-hostile-raii <misc/coroutine-hostile-raii>`, :doc:`misc-definitions-in-headers <misc/definitions-in-headers>`, "Yes" :doc:`misc-header-include-cycle <misc/header-include-cycle>`, diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/constexpr.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/constexpr.rst new file mode 100644 index 0000000000000..d53f2754b51de --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/constexpr.rst @@ -0,0 +1,40 @@ +.. title:: clang-tidy - misc-constexpr + +misc-constexpr +============== + +Find 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``. + +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, + so with `ConservativeLiteralType` equal to `true`, variables or funtions + with this type are not considered to support ``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``. + diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-cxx20.cpp new file mode 100644 index 0000000000000..2bf8952c7c17d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-cxx20.cpp @@ -0,0 +1,36 @@ +// RUN: %check_clang_tidy -std=c++20 %s misc-constexpr %t + +namespace std { +template <typename T = void> +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 <typename T> + bool await_suspend(coroutine_handle<T>) const noexcept; +}; + +template <typename T> +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/misc/constexpr-ref.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-ref.cpp new file mode 100644 index 0000000000000..1af9e8bb30e44 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr-ref.cpp @@ -0,0 +1,383 @@ +// RUN: %check_clang_tidy -std=c++11 -check-suffix=11-REF %s misc-constexpr %t -config="{CheckOptions: { misc-constexpr.ConservativeLiteralType: false } }" +// RUN: %check_clang_tidy -std=c++14 -check-suffix=11-REF,14-REF %s misc-constexpr %t -config="{CheckOptions: { misc-constexpr.ConservativeLiteralType: false } }" +// RUN: %check_clang_tidy -std=c++17 -check-suffix=11-REF,14-REF,17-REF %s misc-constexpr %t -config="{CheckOptions: { misc-constexpr.ConservativeLiteralType: false } }" +// RUN: %check_clang_tidy -std=c++20 -check-suffix=11-REF,14-REF,17-REF,20-REF %s misc-constexpr %t -config="{CheckOptions: { misc-constexpr.ConservativeLiteralType: false } }" +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=11-REF,14-REF,17-REF,20-REF,23-REF %s misc-constexpr %t -config="{CheckOptions: { misc-constexpr.ConservativeLiteralType: false } }" + +namespace my { + struct point { + int x; + int y; + }; +} + +namespace function { + struct Empty {}; + + struct Base { + virtual void virt() = 0; + }; + struct Derived : Base { + Derived() {} + void virt() override {} + }; + + static void f1() {} + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:15: warning: function 'f1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static void f1() {} + + static int f2() { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f2' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f2() { return 0; } + + static int f3(int x) { return x; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f3' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f3(int x) { return x; } + + static int f4(Empty x) { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f4' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f4(Empty x) { return 0; } + + static int f5(Empty x) { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f5' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f5(Empty x) { return 0; } + + static int f6(Empty x) { ; return 0; } + // CHECK-MESSAGES-14-REF: :[[@LINE-1]]:14: warning: function 'f6' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-14-REF: constexpr static int f6(Empty x) { ; return 0; } + + static int f7(Empty x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f7' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f8' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f9' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f9(Empty x) { using my::point; return 0; } + + static int f10(Empty x) { return 10; return 0; } + // CHECK-MESSAGES-14-REF: :[[@LINE-1]]:14: warning: function 'f10' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-14-REF: constexpr static int f10(Empty x) { return 10; return 0; } + + static int f11(Empty x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-14-REF: :[[@LINE-1]]:14: warning: function 'f11' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-14-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f12' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f13' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f14' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f14(Empty x) { asm ("mov %rax, %rax"); } + static int f15(Empty x) { int y; return 0; } + // CHECK-MESSAGES-20-REF: :[[@LINE-1]]:14: warning: function 'f15' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-20-REF: constexpr static int f15(Empty x) { int y; return 0; } + static int f16(Empty x) { static int y = 0; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f16' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f17' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f18' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f19' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f20' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f21' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-20-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f22' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f23' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f24' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-17-REF: constexpr static int f24(Empty x) { return [](){ return 0; }(); } + + static int f25(Empty x) { new int; return 0; } + // CHECK-MESSAGES-20-REF: :[[@LINE-1]]:14: warning: function 'f25' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-20-REF: constexpr static int f25(Empty x) { new int; 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-REF: :[[@LINE-1]]:15: warning: function 'f1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static void f1() {} + + static int f2() { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f2' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f2() { return 0; } + + static int f3(int x) { return x; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f3' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f3(int x) { return x; } + + static int f4(NonLiteral x) { return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f4' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f4(NonLiteral x) { return 0; } + + static int f5(NonLiteral x) { return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f5' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f5(NonLiteral x) { return 0; } + + static int f6(NonLiteral x) { ; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f6' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f6(NonLiteral x) { ; return 0; } + + static int f7(NonLiteral x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f7' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f8' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f9' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f9(NonLiteral x) { using my::point; return 0; } + + static int f10(NonLiteral x) { return 10; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f10' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f10(NonLiteral x) { return 10; return 0; } + + static int f11(NonLiteral x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f11' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f12' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f13' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f14' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f14(NonLiteral x) { asm ("mov %rax, %rax"); } + static int f15(NonLiteral x) { int y; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f15' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f15(NonLiteral x) { int y; return 0; } + static int f16(NonLiteral x) { static int y = 0; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f16' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f17' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f18' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f19' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f20' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f21' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f22' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f23' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f24' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f24(NonLiteral x) { return [](){ return 0; }(); } + + static int f25(NonLiteral x) { new int; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f25' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f25(NonLiteral x) { new int; return 0; } +} // 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-REF: :[[@LINE-1]]:15: warning: function 'f1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static void f1() {} + + static int f2() { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f2' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f2() { return 0; } + + static int f3(int x) { return x; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f3' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f3(int x) { return x; } + + static int f4(NonLiteral& x) { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f4' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f4(NonLiteral& x) { return 0; } + + static int f5(NonLiteral& x) { return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f5' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f5(NonLiteral& x) { return 0; } + + static int f6(NonLiteral& x) { ; return 0; } + // CHECK-MESSAGES-14-REF: :[[@LINE-1]]:14: warning: function 'f6' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-14-REF: constexpr static int f6(NonLiteral& x) { ; return 0; } + + static int f7(NonLiteral& x) { static_assert(0 == 0, ""); return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f7' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: 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-11-REF: :[[@LINE-1]]:14: warning: function 'f8' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f8(NonLiteral& x) { using my_int = int; return 0; } + + static int f9(NonLiteral& x) { using my::point; return 0; } + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:14: warning: function 'f9' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static int f9(NonLiteral& x) { using my::point; return 0; } + + static int f10(NonLiteral& x) { return 10; return 0; } + // CHECK-MESSAGES-14-REF: :[[@LINE-1]]:14: warning: function 'f10' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-14-REF: constexpr static int f10(NonLiteral& x) { return 10; return 0; } + + static int f11(NonLiteral& x) { if (true) return 10; return 0; } + // CHECK-MESSAGES-14-REF: :[[@LINE-1]]:14: warning: function 'f11' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-14-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f12' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f13' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f14' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: constexpr static int f14(NonLiteral& x) { asm ("mov %rax, %rax"); } + static int f15(NonLiteral& x) { int y; return 0; } + // CHECK-MESSAGES-20-REF: :[[@LINE-1]]:14: warning: function 'f15' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-20-REF: constexpr static int f15(NonLiteral& x) { int y; return 0; } + static int f16(NonLiteral& x) { static int y = 0; return 0; } + // CHECK-MESSAGES-23-REF: :[[@LINE-1]]:14: warning: function 'f16' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f17' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f18' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f19' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f20' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-20-REF: :[[@LINE-1]]:14: warning: function 'f21' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-20-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f22' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-REF: :[[@LINE-1]]:14: warning: function 'f23' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23-REF: 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-17-REF: :[[@LINE-1]]:14: warning: function 'f24' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-17-REF: constexpr static int f24(NonLiteral& x) { return [](){ return 0; }(); } + + static int f25(NonLiteral& x) { new int; return 0; } + // CHECK-MESSAGES-20-REF: :[[@LINE-1]]:14: warning: function 'f25' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-20-REF: constexpr static int f25(NonLiteral& x) { new int; return 0; } +} // namespace function_non_literal_ref + +namespace variable { + namespace literal_type { + constexpr int f() { return 0; } + int g() { return 0; } + static constexpr int A1 = 0; + static int B1 = 0; + static const int C1 = 0; + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:26: warning: variable 'C1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static const int C1 = 0; + static const int D1 = f(); + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:26: warning: variable 'D1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static const int D1 = f(); + static const int E1 = g(); + } // namespace literal_type + + namespace struct_type { + struct AStruct { int val; }; + constexpr AStruct f() { return {}; } + AStruct g() { return {}; } + static constexpr AStruct A2 = {}; + static AStruct B2 = {}; + static const AStruct C2 = {}; + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:30: warning: variable 'C2' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static const AStruct C2 = {}; + static const AStruct D2 = f(); + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:30: warning: variable 'D2' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static const AStruct D2 = f(); + static const AStruct E2 = g(); + } // namespace struct_type + + namespace struct_type_non_literal { + struct AStruct { ~AStruct(); int val; }; + AStruct g() { return {}; } + static AStruct B3 = {}; + static const AStruct C3 = {}; + static const AStruct E3 = g(); + } // namespace struct_type + + namespace struct_type_non_literal2 { + struct AStruct { volatile int val; }; + AStruct g() { return {}; } + static AStruct B4 = {}; + static const AStruct C4 = {}; + static const AStruct E4 = g(); + } // namespace struct_type + + namespace struct_type_non_literal3 { + struct AStruct { union { int val; float val5; }; }; + constexpr AStruct f() { return {}; } + AStruct g() { return {}; } + static constexpr AStruct A5 = {}; + static AStruct B5 = {}; + static const AStruct C5 = {}; + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:30: warning: variable 'C5' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static const AStruct C5 = {}; + static const AStruct D5 = f(); + // CHECK-MESSAGES-11-REF: :[[@LINE-1]]:30: warning: variable 'D5' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11-REF: constexpr static const AStruct D5 = f(); + static const AStruct E5 = g(); + } // namespace struct_type +} // namespace variable diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr.cpp new file mode 100644 index 0000000000000..a334387f0c8a2 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/constexpr.cpp @@ -0,0 +1,562 @@ +// RUN: %check_clang_tidy -std=c++11 -check-suffix=11 %s misc-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++14 -check-suffix=11,14 %s misc-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++17 -check-suffix=11,14,17 %s misc-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++20 -check-suffix=11,14,17,20 %s misc-constexpr %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=11,14,17,20,23 %s misc-constexpr %t -- -- -fno-delayed-template-parsing + +// RUN: %check_clang_tidy -std=c++11 -check-suffix=11,11-CLT %s misc-constexpr %t -- -config="{CheckOptions: {misc-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++14 -check-suffix=11,11-CLT,14,14-CLT %s misc-constexpr %t -- -config="{CheckOptions: {misc-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 misc-constexpr %t -- -config="{CheckOptions: {misc-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 misc-constexpr %t -- -config="{CheckOptions: {misc-constexpr.ConservativeLiteralType: false}}" -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++23-or-later -check-suffix=11,14,17,20,23 %s misc-constexpr %t -- -config="{CheckOptions: {misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-constexpr] + // CHECK-FIXES-23: constexpr static int f26(Empty x) { + // CHECK-MESSAGES-14-CLT: :[[@LINE-3]]:14: warning: function 'f26' can be declared 'constexpr' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-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' [misc-constexpr] + // CHECK-FIXES-23: constexpr static int f26(NonLiteral& x) { + // CHECK-MESSAGES-14-CLT: :[[@LINE-7]]:14: warning: function 'f26' can be declared 'constexpr' [misc-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' [misc-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' [misc-constexpr] + // CHECK-FIXES-11: constexpr static const int C1 = 0; + static const int D1 = f1(); + // CHECK-MESSAGES-11: :[[@LINE-1]]:26: warning: variable 'D1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11: constexpr static const int D1 = f1(); + static const int E1 = g1(); + + template <typename T> + const T TemplatedVar1 = T{}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:17: warning: variable 'TemplatedVar1' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11: constexpr const T TemplatedVar1 = T{}; + + void h1() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h1' can be declared 'constexpr' [misc-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' [misc-constexpr] + // CHECK-FIXES-11: constexpr const int b1 = 1; + static int c1 = 2; + static const int d1 = 3; + + static auto e1 = TemplatedVar1<int> + TemplatedVar1<unsigned int>; + } + } // 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' [misc-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' [misc-constexpr] + // CHECK-FIXES-11: constexpr static const AStruct C2 = {}; + static const AStruct D2 = f2(); + // CHECK-MESSAGES-11: :[[@LINE-1]]:30: warning: variable 'D2' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11: constexpr static const AStruct D2 = f2(); + static const AStruct E2 = g2(); + void h2() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h2' can be declared 'constexpr' [misc-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' [misc-constexpr] + // CHECK-FIXES-11: constexpr const 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' [misc-constexpr] + // CHECK-FIXES-23: constexpr AStruct g3() { return {}; } + static AStruct B3 = {}; + static const AStruct C3 = {}; + static const AStruct E3 = g3(); + + template <typename T> + const T TemplatedVar2 = T{}; + template <typename T> + const T TemplatedVar2B = T{}; + // CHECK-MESSAGES-11: :[[@LINE-1]]:17: warning: variable 'TemplatedVar2B' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11: constexpr const T TemplatedVar2B = T{}; + + void h3() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h3' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-23: constexpr void h3() { + AStruct a3{}; + const AStruct b3{}; + static AStruct c3{}; + static const AStruct d3{}; + + static auto e1 = TemplatedVar2<AStruct>; + static auto f1 = TemplatedVar2B<AStruct>; + static auto g1 = TemplatedVar2B<int>; + } + } // 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' [misc-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' [misc-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' [misc-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' [misc-constexpr] + // CHECK-FIXES-11: constexpr static const AStruct C5 = {}; + static const AStruct D5 = f5(); + // CHECK-MESSAGES-11: :[[@LINE-1]]:30: warning: variable 'D5' can be declared 'constexpr' [misc-constexpr] + // CHECK-FIXES-11: constexpr static const AStruct D5 = f5(); + static const AStruct E5 = g5(); + void h5() { + // CHECK-MESSAGES-23: :[[@LINE-1]]:14: warning: function 'h5' can be declared 'constexpr' [misc-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' [misc-constexpr] + // CHECK-FIXES-11: constexpr const AStruct b5{}; + static AStruct c5{}; + static const AStruct d5{}; + } + } // namespace struct_type_non_literal3 +} // namespace variable +} // namespace + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits