From 43c09b35c1b3cd01d6cd3ff72f68d31d9a34f54a Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 26 Jan 2023 22:31:50 +0100 Subject: [PATCH 01/15] `clang-tidy -p build --checks=-*,readability-braces-around-statements -fix-errors --fix main.cpp` --- main.cpp | 134 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 58 deletions(-) diff --git a/main.cpp b/main.cpp index 0753191..06b3e98 100644 --- a/main.cpp +++ b/main.cpp @@ -74,12 +74,11 @@ bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::Enum: case Decl::CXXRecord: // C++1y allows types to be defined, not just declared. - if (cast(DclIt)->isThisDeclarationADefinition()) + if (cast(DclIt)->isThisDeclarationADefinition()) { SemaRef.Diag(DS->getBeginLoc(), - SemaRef.getLangOpts().CPlusPlus14 - ? diag::warn_cxx11_compat_constexpr_type_definition - : diag::ext_constexpr_type_definition) - << isa(Dcl); + SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_type_definition : diag::ext_constexpr_type_definition) + << isa(Dcl); + } continue; case Decl::EnumConstant: @@ -97,17 +96,15 @@ bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, const auto *VD = cast(DclIt); if (VD->isThisDeclarationADefinition()) { if (VD->isStaticLocal()) { - SemaRef.Diag(VD->getLocation(), diag::err_constexpr_local_var_static) - << isa(Dcl) - << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) + << isa(Dcl) << (VD->getTLSKind() == VarDecl::TLS_Dynamic); return false; } - if (!VD->getType()->isDependentType() && - SemaRef.RequireLiteralType( - VD->getLocation(), VD->getType(), - diag::err_constexpr_local_var_non_literal_type, - isa(Dcl))) + if (!VD->getType()->isDependentType() + && SemaRef.RequireLiteralType(VD->getLocation(), VD->getType(), diag::err_constexpr_local_var_non_literal_type, + isa(Dcl))) { return false; + } if (!VD->getType()->isDependentType() && !VD->hasInit() && !VD->isCXXForRangeDecl()) { #if (LLVM_VERSION_MAJOR >= 10) @@ -132,8 +129,9 @@ bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::Function: // These are disallowed in C++11 and permitted in C++1y. Allow them // everywhere as an extension. - if (!Cxx1yLoc.isValid()) + if (!Cxx1yLoc.isValid()) { Cxx1yLoc = DS->getBeginLoc(); + } continue; default: @@ -160,11 +158,11 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef, i != e; ++i, ++ArgIndex) { const ParmVarDecl *PD = FD->getParamDecl(ArgIndex); SourceLocation ParamLoc = PD->getLocation(); - if (!(*i)->isDependentType() && - SemaRef.RequireLiteralType( - ParamLoc, *i, diag::err_constexpr_non_literal_param, ArgIndex + 1, - PD->getSourceRange(), isa(FD))) - return false; + if (!(*i)->isDependentType() + && SemaRef.RequireLiteralType(ParamLoc, *i, diag::err_constexpr_non_literal_param, ArgIndex + 1, PD->getSourceRange(), + isa(FD))) { + return false; + } } return true; } @@ -191,20 +189,24 @@ class ConstexprFunctionASTVisitor // Only functions in our TU SourceLocation loc = func->getSourceRange().getBegin(); - if (!sourceManager_.isWrittenInMainFile(loc)) - return true; + if (!sourceManager_.isWrittenInMainFile(loc)) { + return true; + } // Skip existing constExpr functions - if (func->isConstexpr()) - return true; + if (func->isConstexpr()) { + return true; + } // Don't mark main as constexpr - if (func->isMain()) - return true; + if (func->isMain()) { + return true; + } // Destructors can't be constexpr - if (isa(func)) - return true; + if (isa(func)) { + return true; + } auto &sema = CI_.getSema(); @@ -220,30 +222,33 @@ class ConstexprFunctionASTVisitor &lol, returnDiagnostics); #if LLVM_VERSION_MAJOR >= 10 - if (!sema.CheckConstexprFunctionDefinition( - func, Sema::CheckConstexprKind::CheckValid)) - return true; + if (!sema.CheckConstexprFunctionDefinition(func, Sema::CheckConstexprKind::CheckValid)) { + return true; + } #else if (!sema.CheckConstexprFunctionDecl(func)) return true; #endif // We can't check this if we don't have a function body. - if (!func->getBody()) - return true; + if (!func->getBody()) { + return true; + } #if LLVM_VERSION_MAJOR <= 9 if (!sema.CheckConstexprFunctionBody(func, func->getBody())) return true; #endif - if (!CheckConstexprParameterTypes(sema, func)) - return true; + if (!CheckConstexprParameterTypes(sema, func)) { + return true; + } } SmallVector Diags; - if (!Expr::isPotentialConstantExpr(func, Diags)) + if (!Expr::isPotentialConstantExpr(func, Diags)) { return true; + } // Mark function as constexpr, the next ast visitor will use this // information to find constexpr vardecls @@ -276,50 +281,60 @@ class ConstexprVarDeclFunctionASTVisitor : CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} bool VisitDeclStmt(clang::DeclStmt *stmt) { - if (!stmt->isSingleDecl()) - return true; + if (!stmt->isSingleDecl()) { + return true; + } clang::VarDecl *var = clang::dyn_cast(*stmt->decl_begin()); - if (!var) - return true; + if (!var) { + return true; + } // Skip variables that are already constexpr - if (var->isConstexpr()) - return true; + if (var->isConstexpr()) { + return true; + } // Only do locals for right now - if (!var->hasLocalStorage()) - return true; + if (!var->hasLocalStorage()) { + return true; + } clang::SourceLocation loc = stmt->getSourceRange().getBegin(); auto &sema = CI_.getSema(); // var needs an initializer Expr *Init = var->getInit(); - if (!Init) - return true; + if (!Init) { + return true; + } // If the var is const we can mark it constexpr QualType ty = var->getType(); - if (!ty.isConstQualified()) - return true; + if (!ty.isConstQualified()) { + return true; + } // Is init an integral constant expression - if (!var->checkInitIsICE()) - return true; + if (!var->checkInitIsICE()) { + return true; + } // Does the init function use dependent values - if (Init->isValueDependent()) - return true; + if (Init->isValueDependent()) { + return true; + } // Can we evaluate the value - if (!var->evaluateValue()) - return true; + if (!var->evaluateValue()) { + return true; + } // Is init an ice - if (!var->isInitICE()) - return true; + if (!var->isInitICE()) { + return true; + } // Create Diagnostic/FixIt const auto FixIt = clang::FixItHint::CreateInsertion(loc, "constexpr "); @@ -340,12 +355,14 @@ class ConstexprVarDeclFunctionASTVisitor bool VisitFunctionDecl(clang::FunctionDecl *func) { // Only functions in our TU SourceLocation loc = func->getSourceRange().getBegin(); - if (!sourceManager_.isWrittenInMainFile(loc)) + if (!sourceManager_.isWrittenInMainFile(loc)) { return true; + } // Don't go through functions that are already constexpr - if (func->isConstexpr()) + if (func->isConstexpr()) { return true; + } ConstexprVarDeclVisitor vd(CI_); vd.TraverseFunctionDecl(func); @@ -404,8 +421,9 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction { } void EndSourceFileAction() override { - if (inPlaceRewrite) + if (inPlaceRewrite) { rewriter->WriteFixedFiles(); + } } }; From bd2cbfb6552307aa31003508f77375685a941336 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 26 Jan 2023 22:39:03 +0100 Subject: [PATCH 02/15] clang format --- main.cpp | 345 +++++++++++++++++++++++++------------------------------ 1 file changed, 158 insertions(+), 187 deletions(-) diff --git a/main.cpp b/main.cpp index 06b3e98..5d01d13 100644 --- a/main.cpp +++ b/main.cpp @@ -24,120 +24,108 @@ llvm::cl::extrahelp ConstexprCategoryHelp(R"( Use clang's existing constexpr validation code to automatically apply constexpr where appropriate )"); -llvm::cl::opt - ConstExprFixItOption("fix", llvm::cl::init(false), - llvm::cl::desc("apply fix-its to existing code"), - llvm::cl::cat(ConstexprCategory)); +llvm::cl::opt ConstExprFixItOption("fix", llvm::cl::init(false), llvm::cl::desc("apply fix-its to existing code"), + llvm::cl::cat(ConstexprCategory)); -llvm::cl::extrahelp - CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage); -} // namespace +llvm::cl::extrahelp CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage); +} // namespace namespace { // These functions are stolen from clang::Sema, where they're private. // From lib/Sema/SemaDeclCXX.cpp -bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, - DeclStmt *DS, SourceLocation &Cxx1yLoc) { +bool CheckConstexprDeclStmt(Sema& SemaRef, const FunctionDecl* Dcl, DeclStmt* DS, SourceLocation& Cxx1yLoc) { // C++11 [dcl.constexpr]p3 and p4: // The definition of a constexpr function(p3) or constructor(p4) [...] shall // contain only - for (const auto *DclIt : DS->decls()) { + for (const auto* DclIt : DS->decls()) { switch (DclIt->getKind()) { - case Decl::StaticAssert: - case Decl::Using: - case Decl::UsingShadow: - case Decl::UsingDirective: - case Decl::UnresolvedUsingTypename: - case Decl::UnresolvedUsingValue: - // - static_assert-declarations - // - using-declarations, - // - using-directives, - continue; - - case Decl::Typedef: - case Decl::TypeAlias: { - // - typedef declarations and alias-declarations that do not define - // classes or enumerations, - const auto *TN = cast(DclIt); - if (TN->getUnderlyingType()->isVariablyModifiedType()) { - // Don't allow variably-modified types in constexpr functions. - TypeLoc TL = TN->getTypeSourceInfo()->getTypeLoc(); - SemaRef.Diag(TL.getBeginLoc(), diag::err_constexpr_vla) - << TL.getSourceRange() << TL.getType() - << isa(Dcl); - return false; - } - continue; - } - - case Decl::Enum: - case Decl::CXXRecord: - // C++1y allows types to be defined, not just declared. - if (cast(DclIt)->isThisDeclarationADefinition()) { - SemaRef.Diag(DS->getBeginLoc(), - SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_type_definition : diag::ext_constexpr_type_definition) - << isa(Dcl); - } - continue; - - case Decl::EnumConstant: - case Decl::IndirectField: - case Decl::ParmVar: - // These can only appear with other declarations which are banned in - // C++11 and permitted in C++1y, so ignore them. - continue; - - case Decl::Var: - case Decl::Decomposition: { - // C++1y [dcl.constexpr]p3 allows anything except: - // a definition of a variable of non-literal type or of static or - // thread storage duration or for which no initialization is performed. - const auto *VD = cast(DclIt); - if (VD->isThisDeclarationADefinition()) { - if (VD->isStaticLocal()) { - SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) - << isa(Dcl) << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + case Decl::StaticAssert: + case Decl::Using: + case Decl::UsingShadow: + case Decl::UsingDirective: + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + // - static_assert-declarations + // - using-declarations, + // - using-directives, + continue; + + case Decl::Typedef: + case Decl::TypeAlias: { + // - typedef declarations and alias-declarations that do not define + // classes or enumerations, + const auto* TN = cast(DclIt); + if (TN->getUnderlyingType()->isVariablyModifiedType()) { + // Don't allow variably-modified types in constexpr functions. + TypeLoc TL = TN->getTypeSourceInfo()->getTypeLoc(); + SemaRef.Diag(TL.getBeginLoc(), diag::err_constexpr_vla) << TL.getSourceRange() << TL.getType() << isa(Dcl); return false; } - if (!VD->getType()->isDependentType() - && SemaRef.RequireLiteralType(VD->getLocation(), VD->getType(), diag::err_constexpr_local_var_non_literal_type, - isa(Dcl))) { - return false; + continue; + } + + case Decl::Enum: + case Decl::CXXRecord: + // C++1y allows types to be defined, not just declared. + if (cast(DclIt)->isThisDeclarationADefinition()) { + SemaRef.Diag(DS->getBeginLoc(), + SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_type_definition : diag::ext_constexpr_type_definition) + << isa(Dcl); } - if (!VD->getType()->isDependentType() && !VD->hasInit() && - !VD->isCXXForRangeDecl()) { + continue; + + case Decl::EnumConstant: + case Decl::IndirectField: + case Decl::ParmVar: + // These can only appear with other declarations which are banned in + // C++11 and permitted in C++1y, so ignore them. + continue; + + case Decl::Var: + case Decl::Decomposition: { + // C++1y [dcl.constexpr]p3 allows anything except: + // a definition of a variable of non-literal type or of static or + // thread storage duration or for which no initialization is performed. + const auto* VD = cast(DclIt); + if (VD->isThisDeclarationADefinition()) { + if (VD->isStaticLocal()) { + SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) + << isa(Dcl) << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + return false; + } + if (!VD->getType()->isDependentType() + && SemaRef.RequireLiteralType(VD->getLocation(), VD->getType(), diag::err_constexpr_local_var_non_literal_type, + isa(Dcl))) { + return false; + } + if (!VD->getType()->isDependentType() && !VD->hasInit() && !VD->isCXXForRangeDecl()) { #if (LLVM_VERSION_MAJOR >= 10) - SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) - << isa(Dcl); + SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) << isa(Dcl); #else - SemaRef.Diag(VD->getLocation(), diag::err_constexpr_local_var_no_init) - << isa(Dcl); + SemaRef.Diag(VD->getLocation(), diag::err_constexpr_local_var_no_init) << isa(Dcl); #endif - return false; + return false; + } } - } - SemaRef.Diag(VD->getLocation(), - SemaRef.getLangOpts().CPlusPlus14 - ? diag::warn_cxx11_compat_constexpr_local_var - : diag::ext_constexpr_local_var) + SemaRef.Diag(VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_local_var : diag::ext_constexpr_local_var) << isa(Dcl); - continue; - } - - case Decl::NamespaceAlias: - case Decl::Function: - // These are disallowed in C++11 and permitted in C++1y. Allow them - // everywhere as an extension. - if (!Cxx1yLoc.isValid()) { - Cxx1yLoc = DS->getBeginLoc(); + continue; } - continue; - default: - SemaRef.Diag(DS->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) - << isa(Dcl); - return false; + case Decl::NamespaceAlias: + case Decl::Function: + // These are disallowed in C++11 and permitted in C++1y. Allow them + // everywhere as an extension. + if (!Cxx1yLoc.isValid()) { + Cxx1yLoc = DS->getBeginLoc(); + } + continue; + + default: + SemaRef.Diag(DS->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) << isa(Dcl); + return false; } } @@ -149,24 +137,21 @@ bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, // diagnostic and return false. // from lib/Sema/SemaDeclCXX.cpp -static bool CheckConstexprParameterTypes(Sema &SemaRef, - const FunctionDecl *FD) { +static bool CheckConstexprParameterTypes(Sema& SemaRef, const FunctionDecl* FD) { unsigned ArgIndex = 0; - const FunctionProtoType *FT = FD->getType()->getAs(); - for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(), - e = FT->param_type_end(); - i != e; ++i, ++ArgIndex) { - const ParmVarDecl *PD = FD->getParamDecl(ArgIndex); + const FunctionProtoType* FT = FD->getType()->getAs(); + for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(), e = FT->param_type_end(); i != e; ++i, ++ArgIndex) { + const ParmVarDecl* PD = FD->getParamDecl(ArgIndex); SourceLocation ParamLoc = PD->getLocation(); if (!(*i)->isDependentType() && SemaRef.RequireLiteralType(ParamLoc, *i, diag::err_constexpr_non_literal_param, ArgIndex + 1, PD->getSourceRange(), isa(FD))) { - return false; + return false; } } return true; } -} // namespace +} // namespace /* * ConstexprFunctionASTVisitor @@ -174,74 +159,68 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef, * Find all functions that can be constexpr but arent. Create diagnostics for * them and mark them constexpr for the next pass. */ -class ConstexprFunctionASTVisitor - : public clang::RecursiveASTVisitor { - clang::SourceManager &sourceManager_; - clang::CompilerInstance &CI_; - clang::DiagnosticsEngine &DE; +class ConstexprFunctionASTVisitor : public clang::RecursiveASTVisitor +{ + clang::SourceManager& sourceManager_; + clang::CompilerInstance& CI_; + clang::DiagnosticsEngine& DE; -public: - explicit ConstexprFunctionASTVisitor(clang::SourceManager &sm, - clang::CompilerInstance &ci) - : sourceManager_(sm), CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} + public: + explicit ConstexprFunctionASTVisitor(clang::SourceManager& sm, clang::CompilerInstance& ci) + : sourceManager_(sm), CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} - bool VisitFunctionDecl(clang::FunctionDecl *func) { + bool VisitFunctionDecl(clang::FunctionDecl* func) { // Only functions in our TU SourceLocation loc = func->getSourceRange().getBegin(); if (!sourceManager_.isWrittenInMainFile(loc)) { - return true; + return true; } // Skip existing constExpr functions if (func->isConstexpr()) { - return true; + return true; } // Don't mark main as constexpr if (func->isMain()) { - return true; + return true; } // Destructors can't be constexpr if (isa(func)) { - return true; + return true; } - auto &sema = CI_.getSema(); + auto& sema = CI_.getSema(); // Temporarily disable diagnostics for these next functions, use a // unique_ptr deleter to handle restoring it sema.getDiagnostics().setSuppressAllDiagnostics(true); { - auto returnDiagnostics = [&sema](int *) { - sema.getDiagnostics().setSuppressAllDiagnostics(false); - }; + auto returnDiagnostics = [&sema](int*) { sema.getDiagnostics().setSuppressAllDiagnostics(false); }; int lol = 0; - std::unique_ptr scope( - &lol, returnDiagnostics); + std::unique_ptr scope(&lol, returnDiagnostics); #if LLVM_VERSION_MAJOR >= 10 if (!sema.CheckConstexprFunctionDefinition(func, Sema::CheckConstexprKind::CheckValid)) { - return true; + return true; } #else - if (!sema.CheckConstexprFunctionDecl(func)) - return true; + if (!sema.CheckConstexprFunctionDecl(func)) return true; #endif // We can't check this if we don't have a function body. if (!func->getBody()) { - return true; + return true; } #if LLVM_VERSION_MAJOR <= 9 - if (!sema.CheckConstexprFunctionBody(func, func->getBody())) - return true; + if (!sema.CheckConstexprFunctionBody(func, func->getBody())) return true; #endif if (!CheckConstexprParameterTypes(sema, func)) { - return true; + return true; } } @@ -256,8 +235,7 @@ class ConstexprFunctionASTVisitor // Create diagnostic const auto FixIt = clang::FixItHint::CreateInsertion(loc, "constexpr "); - const auto ID = DE.getCustomDiagID(clang::DiagnosticsEngine::Warning, - "function can be constexpr"); + const auto ID = DE.getCustomDiagID(clang::DiagnosticsEngine::Warning, "function can be constexpr"); DE.Report(loc, ID).AddFixItHint(FixIt); @@ -265,81 +243,78 @@ class ConstexprFunctionASTVisitor } }; -class ConstexprVarDeclFunctionASTVisitor - : public clang::RecursiveASTVisitor { - clang::SourceManager &sourceManager_; - clang::CompilerInstance &CI_; - clang::DiagnosticsEngine &DE; +class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitor +{ + clang::SourceManager& sourceManager_; + clang::CompilerInstance& CI_; + clang::DiagnosticsEngine& DE; - class ConstexprVarDeclVisitor - : public clang::RecursiveASTVisitor { - clang::CompilerInstance &CI_; - clang::DiagnosticsEngine &DE; + class ConstexprVarDeclVisitor : public clang::RecursiveASTVisitor + { + clang::CompilerInstance& CI_; + clang::DiagnosticsEngine& DE; - public: - explicit ConstexprVarDeclVisitor(clang::CompilerInstance &ci) - : CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} + public: + explicit ConstexprVarDeclVisitor(clang::CompilerInstance& ci) : CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} - bool VisitDeclStmt(clang::DeclStmt *stmt) { + bool VisitDeclStmt(clang::DeclStmt* stmt) { if (!stmt->isSingleDecl()) { - return true; + return true; } - clang::VarDecl *var = - clang::dyn_cast(*stmt->decl_begin()); + clang::VarDecl* var = clang::dyn_cast(*stmt->decl_begin()); if (!var) { - return true; + return true; } // Skip variables that are already constexpr if (var->isConstexpr()) { - return true; + return true; } // Only do locals for right now if (!var->hasLocalStorage()) { - return true; + return true; } clang::SourceLocation loc = stmt->getSourceRange().getBegin(); - auto &sema = CI_.getSema(); + auto& sema = CI_.getSema(); // var needs an initializer - Expr *Init = var->getInit(); + Expr* Init = var->getInit(); if (!Init) { - return true; + return true; } // If the var is const we can mark it constexpr QualType ty = var->getType(); if (!ty.isConstQualified()) { - return true; + return true; } // Is init an integral constant expression if (!var->checkInitIsICE()) { - return true; + return true; } // Does the init function use dependent values if (Init->isValueDependent()) { - return true; + return true; } // Can we evaluate the value if (!var->evaluateValue()) { - return true; + return true; } // Is init an ice if (!var->isInitICE()) { - return true; + return true; } // Create Diagnostic/FixIt const auto FixIt = clang::FixItHint::CreateInsertion(loc, "constexpr "); - const auto ID = DE.getCustomDiagID(clang::DiagnosticsEngine::Warning, - "variable can be constexpr"); + const auto ID = DE.getCustomDiagID(clang::DiagnosticsEngine::Warning, "variable can be constexpr"); DE.Report(loc, ID).AddFixItHint(FixIt); @@ -347,12 +322,11 @@ class ConstexprVarDeclFunctionASTVisitor } }; -public: - explicit ConstexprVarDeclFunctionASTVisitor(clang::SourceManager &sm, - clang::CompilerInstance &ci) - : sourceManager_(sm), CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} + public: + explicit ConstexprVarDeclFunctionASTVisitor(clang::SourceManager& sm, clang::CompilerInstance& ci) + : sourceManager_(sm), CI_(ci), DE(ci.getASTContext().getDiagnostics()) {} - bool VisitFunctionDecl(clang::FunctionDecl *func) { + bool VisitFunctionDecl(clang::FunctionDecl* func) { // Only functions in our TU SourceLocation loc = func->getSourceRange().getBegin(); if (!sourceManager_.isWrittenInMainFile(loc)) { @@ -371,26 +345,28 @@ class ConstexprVarDeclFunctionASTVisitor } }; -class ConstexprEverythingASTConsumer : public clang::ASTConsumer { +class ConstexprEverythingASTConsumer : public clang::ASTConsumer +{ ConstexprFunctionASTVisitor functionVisitor; ConstexprVarDeclFunctionASTVisitor varDeclVisitor; -public: + public: // override the constructor in order to pass CI - explicit ConstexprEverythingASTConsumer(clang::CompilerInstance &ci) - : functionVisitor(ci.getSourceManager(), ci), - varDeclVisitor(ci.getSourceManager(), ci) {} + explicit ConstexprEverythingASTConsumer(clang::CompilerInstance& ci) + : functionVisitor(ci.getSourceManager(), ci), varDeclVisitor(ci.getSourceManager(), ci) {} - void HandleTranslationUnit(clang::ASTContext &astContext) override { + void HandleTranslationUnit(clang::ASTContext& astContext) override { functionVisitor.TraverseDecl(astContext.getTranslationUnitDecl()); varDeclVisitor.TraverseDecl(astContext.getTranslationUnitDecl()); } }; -class FunctionDeclFrontendAction : public clang::ASTFrontendAction { +class FunctionDeclFrontendAction : public clang::ASTFrontendAction +{ - class ConstexprFixItOptions : public clang::FixItOptions { - std::string RewriteFilename(const std::string &Filename, int &fd) override { + class ConstexprFixItOptions : public clang::FixItOptions + { + std::string RewriteFilename(const std::string& Filename, int& fd) override { return Filename; } }; @@ -398,26 +374,22 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction { std::unique_ptr rewriter = nullptr; bool inPlaceRewrite; -public: + public: FunctionDeclFrontendAction() : inPlaceRewrite(ConstExprFixItOption) {} - std::unique_ptr - CreateASTConsumer(clang::CompilerInstance &CI, - clang::StringRef file) override { + std::unique_ptr CreateASTConsumer(clang::CompilerInstance& CI, clang::StringRef file) override { if (inPlaceRewrite) { ConstexprFixItOptions fixItOptions; fixItOptions.InPlace = inPlaceRewrite; - rewriter = std::make_unique( - CI.getDiagnostics(), CI.getASTContext().getSourceManager(), - CI.getASTContext().getLangOpts(), &fixItOptions); + rewriter = std::make_unique(CI.getDiagnostics(), CI.getASTContext().getSourceManager(), CI.getASTContext().getLangOpts(), + &fixItOptions); CI.getDiagnostics().setClient(rewriter.get(), false); } - return std::make_unique( - CI); // pass CI pointer to ASTConsumer + return std::make_unique(CI); // pass CI pointer to ASTConsumer } void EndSourceFileAction() override { @@ -427,10 +399,9 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction { } }; -int main(int argc, const char **argv) { +int main(int argc, const char** argv) { CommonOptionsParser OptionsParser(argc, argv, ConstexprCategory); - ClangTool Tool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); + ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); Tool.run(newFrontendActionFactory().get()); } From 41ace23c13bbdaba2029956c5e15131ec22b9b76 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 26 Jan 2023 23:43:55 +0100 Subject: [PATCH 03/15] upgrade for clang 13 14 And 15 --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 6 +- main.cpp | 161 +++++++++++++-------------------------- 3 files changed, 59 insertions(+), 110 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89f1dc4..05142bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - llvm-toolchain: [9, 10, 11] + llvm-toolchain: [9, 10, 11, 12, 13, 14, 15] steps: - uses: actions/checkout@v2 diff --git a/CMakeLists.txt b/CMakeLists.txt index ae6cfdb..11911ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.8) -project(constexpr-everything CXX) +project(constexpr-everything CXX C) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -42,8 +42,8 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") if((${LLVM_PACKAGE_VERSION} VERSION_LESS "9.0.0") - OR (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL "12")) - message(FATAL_ERROR "Only LLVM 9 through 11 are supported.") + OR (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL "16")) + message(FATAL_ERROR "Only LLVM 9 through 15 are supported.") endif() # The clang package doesn't appear to provide a version diff --git a/main.cpp b/main.cpp index 5d01d13..4160936 100644 --- a/main.cpp +++ b/main.cpp @@ -34,102 +34,25 @@ namespace { // These functions are stolen from clang::Sema, where they're private. // From lib/Sema/SemaDeclCXX.cpp -bool CheckConstexprDeclStmt(Sema& SemaRef, const FunctionDecl* Dcl, DeclStmt* DS, SourceLocation& Cxx1yLoc) { - // C++11 [dcl.constexpr]p3 and p4: - // The definition of a constexpr function(p3) or constructor(p4) [...] shall - // contain only - for (const auto* DclIt : DS->decls()) { - switch (DclIt->getKind()) { - case Decl::StaticAssert: - case Decl::Using: - case Decl::UsingShadow: - case Decl::UsingDirective: - case Decl::UnresolvedUsingTypename: - case Decl::UnresolvedUsingValue: - // - static_assert-declarations - // - using-declarations, - // - using-directives, - continue; - - case Decl::Typedef: - case Decl::TypeAlias: { - // - typedef declarations and alias-declarations that do not define - // classes or enumerations, - const auto* TN = cast(DclIt); - if (TN->getUnderlyingType()->isVariablyModifiedType()) { - // Don't allow variably-modified types in constexpr functions. - TypeLoc TL = TN->getTypeSourceInfo()->getTypeLoc(); - SemaRef.Diag(TL.getBeginLoc(), diag::err_constexpr_vla) << TL.getSourceRange() << TL.getType() << isa(Dcl); - return false; - } - continue; - } - case Decl::Enum: - case Decl::CXXRecord: - // C++1y allows types to be defined, not just declared. - if (cast(DclIt)->isThisDeclarationADefinition()) { - SemaRef.Diag(DS->getBeginLoc(), - SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_type_definition : diag::ext_constexpr_type_definition) - << isa(Dcl); - } - continue; - - case Decl::EnumConstant: - case Decl::IndirectField: - case Decl::ParmVar: - // These can only appear with other declarations which are banned in - // C++11 and permitted in C++1y, so ignore them. - continue; - - case Decl::Var: - case Decl::Decomposition: { - // C++1y [dcl.constexpr]p3 allows anything except: - // a definition of a variable of non-literal type or of static or - // thread storage duration or for which no initialization is performed. - const auto* VD = cast(DclIt); - if (VD->isThisDeclarationADefinition()) { - if (VD->isStaticLocal()) { - SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) - << isa(Dcl) << (VD->getTLSKind() == VarDecl::TLS_Dynamic); - return false; - } - if (!VD->getType()->isDependentType() - && SemaRef.RequireLiteralType(VD->getLocation(), VD->getType(), diag::err_constexpr_local_var_non_literal_type, - isa(Dcl))) { - return false; - } - if (!VD->getType()->isDependentType() && !VD->hasInit() && !VD->isCXXForRangeDecl()) { -#if (LLVM_VERSION_MAJOR >= 10) - SemaRef.Diag(VD->getLocation(), diag::ext_constexpr_local_var_no_init) << isa(Dcl); -#else - SemaRef.Diag(VD->getLocation(), diag::err_constexpr_local_var_no_init) << isa(Dcl); -#endif - return false; - } - } - SemaRef.Diag(VD->getLocation(), - SemaRef.getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_constexpr_local_var : diag::ext_constexpr_local_var) - << isa(Dcl); - continue; - } +/// Check that the given type is a literal type. Issue a diagnostic if not, +/// if Kind is Diagnose. +/// \return \c true if a problem has been found (and optionally diagnosed). +template +static bool CheckLiteralType(Sema& SemaRef, Sema::CheckConstexprKind Kind, SourceLocation Loc, QualType T, unsigned DiagID, Ts&&... DiagArgs) { + if (T->isDependentType()) { + return false; + } - case Decl::NamespaceAlias: - case Decl::Function: - // These are disallowed in C++11 and permitted in C++1y. Allow them - // everywhere as an extension. - if (!Cxx1yLoc.isValid()) { - Cxx1yLoc = DS->getBeginLoc(); - } - continue; - - default: - SemaRef.Diag(DS->getBeginLoc(), diag::err_constexpr_body_invalid_stmt) << isa(Dcl); - return false; - } + switch (Kind) { + case Sema::CheckConstexprKind::Diagnose: + return SemaRef.RequireLiteralType(Loc, T, DiagID, std::forward(DiagArgs)...); + + case Sema::CheckConstexprKind::CheckValid: + return !T->isLiteralType(SemaRef.Context); } - return true; + llvm_unreachable("unknown CheckConstexprKind"); } // CheckConstexprParameterTypes - Check whether a function's parameter types @@ -139,7 +62,7 @@ bool CheckConstexprDeclStmt(Sema& SemaRef, const FunctionDecl* Dcl, DeclStmt* DS // from lib/Sema/SemaDeclCXX.cpp static bool CheckConstexprParameterTypes(Sema& SemaRef, const FunctionDecl* FD) { unsigned ArgIndex = 0; - const FunctionProtoType* FT = FD->getType()->getAs(); + const auto* FT = FD->getType()->getAs(); for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(), e = FT->param_type_end(); i != e; ++i, ++ArgIndex) { const ParmVarDecl* PD = FD->getParamDecl(ArgIndex); SourceLocation ParamLoc = PD->getLocation(); @@ -172,7 +95,7 @@ class ConstexprFunctionASTVisitor : public clang::RecursiveASTVisitorgetSourceRange().getBegin(); + SourceLocation const loc = func->getSourceRange().getBegin(); if (!sourceManager_.isWrittenInMainFile(loc)) { return true; } @@ -200,14 +123,16 @@ class ConstexprFunctionASTVisitor : public clang::RecursiveASTVisitor scope(&lol, returnDiagnostics); + std::unique_ptr const scope(&lol, returnDiagnostics); #if LLVM_VERSION_MAJOR >= 10 if (!sema.CheckConstexprFunctionDefinition(func, Sema::CheckConstexprKind::CheckValid)) { return true; } #else - if (!sema.CheckConstexprFunctionDecl(func)) return true; + if (!sema.CheckConstexprFunctionDecl(func)) { + return true; + } #endif // We can't check this if we don't have a function body. @@ -216,7 +141,9 @@ class ConstexprFunctionASTVisitor : public clang::RecursiveASTVisitorgetBody())) return true; + if (!sema.CheckConstexprFunctionBody(func, func->getBody())) { + return true; + } #endif if (!CheckConstexprParameterTypes(sema, func)) { @@ -231,8 +158,11 @@ class ConstexprFunctionASTVisitor : public clang::RecursiveASTVisitorsetConstexprKind(CSK_constexpr); - +#if LLVM_VERSION_MAJOR >= 12 + func->setConstexprKind(clang::ConstexprSpecKind::Constexpr); +#else + func->setConstexprKind(CSK_unspecified); +#endif // Create diagnostic const auto FixIt = clang::FixItHint::CreateInsertion(loc, "constexpr "); const auto ID = DE.getCustomDiagID(clang::DiagnosticsEngine::Warning, "function can be constexpr"); @@ -262,7 +192,7 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitor(*stmt->decl_begin()); + auto* var = clang::dyn_cast(*stmt->decl_begin()); if (!var) { return true; } @@ -277,8 +207,8 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitorgetSourceRange().getBegin(); - auto& sema = CI_.getSema(); + clang::SourceLocation const loc = stmt->getSourceRange().getBegin(); + // auto& sema = CI_.getSema(); // var needs an initializer Expr* Init = var->getInit(); @@ -287,13 +217,17 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitorgetType(); + QualType const ty = var->getType(); if (!ty.isConstQualified()) { return true; } // Is init an integral constant expression +#if LLVM_VERSION_MAJOR >= 12 + if (!var->hasICEInitializer(stmt->getSingleDecl()->getASTContext())) { +#else if (!var->checkInitIsICE()) { +#endif return true; } @@ -308,7 +242,11 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitor= 12 + if (!var->hasConstantInitialization()) { +#else if (!var->isInitICE()) { +#endif return true; } @@ -328,7 +266,7 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitorgetSourceRange().getBegin(); + SourceLocation const loc = func->getSourceRange().getBegin(); if (!sourceManager_.isWrittenInMainFile(loc)) { return true; } @@ -366,7 +304,7 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction class ConstexprFixItOptions : public clang::FixItOptions { - std::string RewriteFilename(const std::string& Filename, int& fd) override { + std::string RewriteFilename(const std::string& Filename, int& /*fd*/) override { return Filename; } }; @@ -377,7 +315,7 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction public: FunctionDeclFrontendAction() : inPlaceRewrite(ConstExprFixItOption) {} - std::unique_ptr CreateASTConsumer(clang::CompilerInstance& CI, clang::StringRef file) override { + std::unique_ptr CreateASTConsumer(clang::CompilerInstance& CI, clang::StringRef /*file*/) override { if (inPlaceRewrite) { ConstexprFixItOptions fixItOptions; @@ -400,7 +338,18 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction }; int main(int argc, const char** argv) { + +#if LLVM_VERSION_MAJOR >= 13 + auto ExpectedParser = + CommonOptionsParser::create(argc, argv, ConstexprCategory, llvm::cl::ZeroOrMore, "Clang-based refactoring tool for constexpr everything"); + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + CommonOptionsParser& OptionsParser = ExpectedParser.get(); +#else CommonOptionsParser OptionsParser(argc, argv, ConstexprCategory); +#endif ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); Tool.run(newFrontendActionFactory().get()); From 89a69b0ff184c0da3b8a4eb9f491fb35be35f893 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 26 Jan 2023 23:47:39 +0100 Subject: [PATCH 04/15] update clang format for ubuntu-latest (22.04 LTS currently) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05142bb..e61d4df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,10 @@ jobs: - uses: actions/checkout@v2 - name: deps - run: sudo apt install clang-format-9 + run: sudo apt install clang-format-14 - name: lint - run: clang-format-9 -i main.cpp && git diff --exit-code + run: clang-format-14 -i main.cpp && git diff --exit-code build: runs-on: ubuntu-20.04 strategy: From cf1ff3762312092487bb5f5c8a3fef21949c1abc Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 26 Jan 2023 23:48:41 +0100 Subject: [PATCH 05/15] don't fail fast on GHA --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e61d4df..de956c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: build: runs-on: ubuntu-20.04 strategy: + # fail-fast: Default is true, switch to false to allow one platform to fail and still run others + fail-fast: false matrix: llvm-toolchain: [9, 10, 11, 12, 13, 14, 15] steps: From f7d4f2a5de443d511e8f345bccab1c2f96ebc7c7 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 26 Jan 2023 23:52:16 +0100 Subject: [PATCH 06/15] Add a clang format and clang tidy file --- .clang-format | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ .clang-tidy | 85 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..216801b --- /dev/null +++ b/.clang-format @@ -0,0 +1,99 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: false +BreakStringLiterals: true +ColumnLimit: 150 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentWidth: 2 +IndentPPDirectives: AfterHash +IndentWrappedFunctionNames: true +NamespaceIndentation: None # Could consider Inner +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: false +SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false # No longer available in clang-format 6.0 +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +# SpaceBeforeCpp11BracedList: true # No longer available in clang-format 6.0 +# SpaceBeforeCtorInitializerColon: true # No longer available in clang-format 6.0 +# SpaceBeforeInheritanceColon: true # No longer available in clang-format 6.0 +SpaceBeforeParens: ControlStatements +# SpaceBeforeRangeBasedForLoopColon: true # No longer available in clang-format 6.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SortIncludes: false +SortUsingDeclarations: true +Standard: c++17 +TabWidth: 2 +UseTab: Never +--- +Language: JavaScript +BasedOnStyle: Mozilla +# Use 100 columns for JS. +ColumnLimit: 180 +JavaScriptQuotes: Single +SpacesInContainerLiterals: false +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..664cc75 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,85 @@ +--- + +# magic numbers are useful to layout stuff in Qt... +# -readability-magic-numbers and its alias cppcoreguidelines-avoid-magic-numbers + +# `protected`: followed by `protected slots:` would trigger it +# -readability-redundant-access-specifiers, + +# Problem with OS_ASSERT macro +# -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + +# We use raw pointers for Qt, since usually the memory is then owned by the parent +# -cppcoreguidelines-owning-memory + +# Because of Google Tests +# -cppcoreguidelines-avoid-non-const-global-variables + +# I don't think this really helps clarify the intent +# -readability-else-after-return +# -modernize-concat-nested-namespaces + +# Aliases +# - cppcoreguidelines-avoid-c-arrays => modernize-avoid-c-arrays +# - cppcoreguidelines-non-private-member-variables-in-classes => misc-non-private-member-variables-in-classes +# - cppcoreguidelines-explicit-virtual-functions, hicpp-use-override => modernize-use-override +# - bugprone-narrowing-conversions => cppcoreguidelines-narrowing-conversions + +# Annoying: some config options exist only in later versions... +# cppcoreguidelines-narrowing-conversions.WarnOnEquivalentBitWidth was added in clang-tidy 13, and that would allow avoiding uint->int narrowing conversions +# Instead I have to disable the entire check... + +Checks: | + *, + -fuchsia-*, + -google-*, + -zircon-*, + -abseil-*, + -llvm*, + -altera*, + -modernize-use-trailing-return-type, + -cppcoreguidelines-avoid-magic-numbers, + -readability-magic-numbers, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-constant-array-index, + -readability-redundant-access-specifiers, + -cppcoreguidelines-explicit-virtual-functions, + -readability-else-after-return, + -modernize-concat-nested-namespaces, + -hicpp-*, + -hicpp-avoid-goto, + hicpp-exception-baseclass, + hicpp-multiway-paths-covered, + hicpp-no-assembler, + hicpp-signed-bitwise, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-non-private-member-variables-in-classes, + -bugprone-narrowing-conversions, + -cppcoreguidelines-narrowing-conversions, + -readability-function-cognitive-complexity, + -cppcoreguidelines-avoid-non-const-global-variables, + -modernize-use-override, + -readability-uppercase-literal-suffix, + -readability-identifier-length, + -bugprone-easily-swappable-parameters, + -modernize-use-nodiscard, + -cert-err58-cpp, + +WarningsAsErrors: '*' +HeaderFilterRegex: '*' +FormatStyle: 'file' +UseColor: 'true' +CheckOptions: + - key: modernize-use-override.AllowOverrideAndFinal + value: 'true' + - key: modernize-use-override.IgnoreDestructors + value: 'true' + - key: performance-for-range-copy.WarnOnAllAutoCopies + value: 'true' + - key: cppcoreguidelines-narrowing-conversions.WarnOnEquivalentBitWidth + value: 'false' + - key: readability-implicit-bool-conversion.AllowPointerConditions + value: 'true' + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: 'true' From 1aea5b498b971a28502d91257b1797c10e30ea22 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 00:08:50 +0100 Subject: [PATCH 07/15] improve clang format job (upload patch when failed), upload exes as artifact, add a macos runner --- .github/workflows/ci.yml | 60 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de956c8..6eb9f9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,31 @@ jobs: run: sudo apt install clang-format-14 - name: lint - run: clang-format-14 -i main.cpp && git diff --exit-code + run: | + clang-format-14 -style=file -i main.cpp + # clang-format will auto correct files so prepare the diff and use this as artifact + git diff > clang_format.patch + + # Delete if nothing otherwise exit 1 to indicate a failed job + if [ ! -s clang_format.patch ] + then + rm clang_format.patch + exit 0 + else + echo "clang-format auto corrected files:" + git diff --name-only + echo -e "\nPlease correct these files. Patch is uploaded as an artifact for convenience." + exit 1 + fi + # git diff --exit-code + + - name: Upload clang-format patch as artifact + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: constexpr-everything-clang_format.patch + path: clang_format.patch + build: runs-on: ubuntu-20.04 strategy: @@ -28,9 +52,10 @@ jobs: matrix: llvm-toolchain: [9, 10, 11, 12, 13, 14, 15] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: deps + shell: bash run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{ matrix.llvm-toolchain }} main" @@ -42,9 +67,34 @@ jobs: libclang-${{ matrix.llvm-toolchain }}-dev - name: build + shell: bash + run: | + mkdir build && cd build + cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_DIR:PATH=/usr/lib/llvm-${{ matrix.llvm-toolchain }}/lib/cmake/llvm \ + -DClang_DIR:PATH=/usr/lib/llvm-${{ matrix.llvm-toolchain }}/lib/cmake/clang .. + cmake --build . + + - name: Archive binary artifacts + uses: actions/upload-artifact@v3 + with: + name: constexpr-everything-ubuntu-clang${{ matrix.llvm-toolchain }} + path: build/constexpr-everything + + build-mac: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - name: build + shell: bash run: | - export LLVM_DIR=/usr/lib/llvm-${{ matrix.llvm-toolchain }}/lib/cmake/llvm - export Clang_DIR=/usr/lib/llvm-${{ matrix.llvm-toolchain }}/lib/cmake/clang mkdir build && cd build - cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_BUILD_TYPE:STRING=Debug -DLLVM_DIR:PATH=$(brew --prefix llvm@15)/lib/cmake/llvm \ + -DClang_DIR:PATH=$(brew --prefix llvm@15)/lib/cmake/clang .. cmake --build . + + - name: Archive binary artifacts + uses: actions/upload-artifact@v3 + with: + name: constexpr-everything-mac + path: build/constexpr-everything From dcac89a3c8ac51eac0d40d353c97ef3ad26fc6ea Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 00:13:57 +0100 Subject: [PATCH 08/15] unused (I guess I added it before I deleted another unused one?) --- main.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/main.cpp b/main.cpp index 4160936..5f521d1 100644 --- a/main.cpp +++ b/main.cpp @@ -35,26 +35,6 @@ namespace { // These functions are stolen from clang::Sema, where they're private. // From lib/Sema/SemaDeclCXX.cpp -/// Check that the given type is a literal type. Issue a diagnostic if not, -/// if Kind is Diagnose. -/// \return \c true if a problem has been found (and optionally diagnosed). -template -static bool CheckLiteralType(Sema& SemaRef, Sema::CheckConstexprKind Kind, SourceLocation Loc, QualType T, unsigned DiagID, Ts&&... DiagArgs) { - if (T->isDependentType()) { - return false; - } - - switch (Kind) { - case Sema::CheckConstexprKind::Diagnose: - return SemaRef.RequireLiteralType(Loc, T, DiagID, std::forward(DiagArgs)...); - - case Sema::CheckConstexprKind::CheckValid: - return !T->isLiteralType(SemaRef.Context); - } - - llvm_unreachable("unknown CheckConstexprKind"); -} - // CheckConstexprParameterTypes - Check whether a function's parameter types // are all literal types. If so, return true. If not, produce a suitable // diagnostic and return false. From 3719210cfa95391f4c51fc3ef3006b8818a05775 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 00:14:23 +0100 Subject: [PATCH 09/15] Not sure which one of the two to pick --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 5f521d1..1924611 100644 --- a/main.cpp +++ b/main.cpp @@ -204,7 +204,8 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitor= 12 - if (!var->hasICEInitializer(stmt->getSingleDecl()->getASTContext())) { + if (!var->hasICEInitializer(CI_.getASTContext())) { + //if (!var->hasICEInitializer(stmt->getSingleDecl()->getASTContext())) { #else if (!var->checkInitIsICE()) { #endif From d84e425a6fbe3dc5a7c6937f954bf37e6d9d54ed Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 00:16:48 +0100 Subject: [PATCH 10/15] tweak ci names --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6eb9f9c..fea8eab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,7 @@ jobs: build: runs-on: ubuntu-20.04 + name: Ubuntu Clang ${{ matrix.llvm-toolchain }} strategy: # fail-fast: Default is true, switch to false to allow one platform to fail and still run others fail-fast: false @@ -82,6 +83,7 @@ jobs: build-mac: runs-on: macos-latest + name: MacOS Clang 15 steps: - uses: actions/checkout@v3 From 6b6a59847381d0b26ad179066e298f7fb669f485 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 00:21:06 +0100 Subject: [PATCH 11/15] Update main.cpp Add support for LLVM 13.0.0 Update CMakeLists.txt Update CMakeList to enable LLVM 13 Fix a missing parenthesis Fix a missing parenthesis which causes the build failure. --- CMakeLists.txt | 3 ++- main.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae6cfdb..b135805 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,9 @@ find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") -if((${LLVM_PACKAGE_VERSION} VERSION_LESS "9.0.0") +if(((${LLVM_PACKAGE_VERSION} VERSION_LESS "9.0.0") OR (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL "12")) + AND (NOT (${LLVM_PACKAGE_VERSION} VERSION_EQUAL "13"))) message(FATAL_ERROR "Only LLVM 9 through 11 are supported.") endif() diff --git a/main.cpp b/main.cpp index 0753191..6735b5c 100644 --- a/main.cpp +++ b/main.cpp @@ -235,6 +235,9 @@ class ConstexprFunctionASTVisitor #if LLVM_VERSION_MAJOR <= 9 if (!sema.CheckConstexprFunctionBody(func, func->getBody())) return true; +#elif LLVM_VERSION_MAJOR == 13 + if(!sema.CheckConstexprFunctionDefinition(func, Sema::CheckConstexprKind::CheckValid)) + return true; #endif if (!CheckConstexprParameterTypes(sema, func)) @@ -247,7 +250,11 @@ class ConstexprFunctionASTVisitor // Mark function as constexpr, the next ast visitor will use this // information to find constexpr vardecls +#if LLVM_VERSION_MAJOR == 13 + func->setConstexprKind(ConstexprSpecKind::Constexpr); +#else func->setConstexprKind(CSK_constexpr); +#endif // Create diagnostic const auto FixIt = clang::FixItHint::CreateInsertion(loc, "constexpr "); @@ -305,9 +312,11 @@ class ConstexprVarDeclFunctionASTVisitor if (!ty.isConstQualified()) return true; +#if LLVM_VERSION_MAJOR != 13 // Is init an integral constant expression if (!var->checkInitIsICE()) return true; +#endif // Does the init function use dependent values if (Init->isValueDependent()) @@ -317,9 +326,17 @@ class ConstexprVarDeclFunctionASTVisitor if (!var->evaluateValue()) return true; +#if LLVM_VERSION_MAJOR == 13 + if(!var->hasConstantInitialization()) + return true; + + if (!var->hasICEInitializer(CI_.getASTContext())) + return true; +#else // Is init an ice if (!var->isInitICE()) return true; +#endif // Create Diagnostic/FixIt const auto FixIt = clang::FixItHint::CreateInsertion(loc, "constexpr "); @@ -410,8 +427,19 @@ class FunctionDeclFrontendAction : public clang::ASTFrontendAction { }; int main(int argc, const char **argv) { + +#if LLVM_VERSION_MAJOR == 13 + auto ExpectedParser = CommonOptionsParser::create(argc, argv, ConstexprCategory); + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + CommonOptionsParser& OptionsParser = ExpectedParser.get(); +#else CommonOptionsParser OptionsParser(argc, argv, ConstexprCategory); +#endif + ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); Tool.run(newFrontendActionFactory().get()); From 1f080f5b6db4c9008fa42944bacd68659fd577b6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 00:57:27 +0100 Subject: [PATCH 12/15] Add a test to CI: run the binary on main.cpp --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fea8eab..c2e2978 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,12 @@ jobs: -DClang_DIR:PATH=/usr/lib/llvm-${{ matrix.llvm-toolchain }}/lib/cmake/clang .. cmake --build . + - name: Test the binary + working-directory: build/ + shell: bash + run: | + ./constexpr-everything -p . ../main.cpp + - name: Archive binary artifacts uses: actions/upload-artifact@v3 with: @@ -95,6 +101,12 @@ jobs: -DClang_DIR:PATH=$(brew --prefix llvm@15)/lib/cmake/clang .. cmake --build . + - name: Test the binary + working-directory: build/ + shell: bash + run: | + ./constexpr-everything -p . ../main.cpp + - name: Archive binary artifacts uses: actions/upload-artifact@v3 with: From 0faa8a8484245c222672b8842eb671a8ee8801f6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 01:00:19 +0100 Subject: [PATCH 13/15] Avoid redefinition error on Ubuntu by linking to LLVM not independent modules --- CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11911ec..8b9e0c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,11 +65,4 @@ target_link_libraries(${PROJECT_NAME} ${LIBRARY_LIST}) target_link_libraries( ${PROJECT_NAME} - LLVMTransformUtils - LLVMAnalysis - LLVMTarget - LLVMOption # Support - LLVMObject # BitReader, Core, Support - LLVMBitReader # Core, Support - LLVMCore # Support - LLVMSupport) + LLVM) From 00ab93e6eb27c03aeb6a5f116fe81a1140369ef6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 27 Jan 2023 01:02:17 +0100 Subject: [PATCH 14/15] bump actions/checkout --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2e2978..01de5cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: deps run: sudo apt install clang-format-14 From b6429d893a8ce3a5f4f1e9b099ba82c41f5ad810 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 30 Jan 2023 12:26:16 -0500 Subject: [PATCH 15/15] Update main.cpp --- main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/main.cpp b/main.cpp index b6c5e06..afd69d1 100644 --- a/main.cpp +++ b/main.cpp @@ -210,7 +210,6 @@ class ConstexprVarDeclFunctionASTVisitor : public clang::RecursiveASTVisitor= 12 if (!var->hasICEInitializer(CI_.getASTContext())) { - //if (!var->hasICEInitializer(stmt->getSingleDecl()->getASTContext())) { #else if (!var->checkInitIsICE()) { #endif