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' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89f1dc4..01de5cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,22 +13,50 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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 -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 + 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 matrix: - llvm-toolchain: [9, 10, 11] + 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" @@ -40,9 +68,47 @@ jobs: libclang-${{ matrix.llvm-toolchain }}-dev - 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=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: Test the binary + working-directory: build/ + shell: bash + run: | + ./constexpr-everything -p . ../main.cpp + + - 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 + name: MacOS Clang 15 + steps: + - uses: actions/checkout@v3 + + - name: build + shell: bash + run: | + mkdir build && cd build + 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: Test the binary + working-directory: build/ + shell: bash + run: | + ./constexpr-everything -p . ../main.cpp + + - name: Archive binary artifacts + uses: actions/upload-artifact@v3 + with: + name: constexpr-everything-mac + path: build/constexpr-everything diff --git a/CMakeLists.txt b/CMakeLists.txt index ae6cfdb..8b9e0c6 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 @@ -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) diff --git a/main.cpp b/main.cpp index 0753191..afd69d1 100644 --- a/main.cpp +++ b/main.cpp @@ -24,151 +24,37 @@ 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) { - // 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::err_constexpr_local_var_static) - << 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; - } - - 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; - } - } - - return true; -} // 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 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 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(); - if (!(*i)->isDependentType() && - SemaRef.RequireLiteralType( - ParamLoc, *i, diag::err_constexpr_non_literal_param, ArgIndex + 1, - PD->getSourceRange(), isa(FD))) + if (!(*i)->isDependentType() + && SemaRef.RequireLiteralType(ParamLoc, *i, diag::err_constexpr_non_literal_param, ArgIndex + 1, PD->getSourceRange(), + isa(FD))) { return false; + } } return true; } -} // namespace +} // namespace /* * ConstexprFunctionASTVisitor @@ -176,83 +62,95 @@ 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)) + SourceLocation const loc = func->getSourceRange().getBegin(); + if (!sourceManager_.isWrittenInMainFile(loc)) { return true; + } // Skip existing constExpr functions - if (func->isConstexpr()) + if (func->isConstexpr()) { return true; + } // Don't mark main as constexpr - if (func->isMain()) + if (func->isMain()) { return true; + } // Destructors can't be constexpr - if (isa(func)) + if (isa(func)) { 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 const scope(&lol, returnDiagnostics); #if LLVM_VERSION_MAJOR >= 10 - if (!sema.CheckConstexprFunctionDefinition( - func, Sema::CheckConstexprKind::CheckValid)) + if (!sema.CheckConstexprFunctionDefinition(func, Sema::CheckConstexprKind::CheckValid)) { return true; + } #else - if (!sema.CheckConstexprFunctionDecl(func)) + if (!sema.CheckConstexprFunctionDecl(func)) { return true; + } #endif // We can't check this if we don't have a function body. - if (!func->getBody()) + if (!func->getBody()) { return true; + } #if LLVM_VERSION_MAJOR <= 9 - if (!sema.CheckConstexprFunctionBody(func, func->getBody())) + if (!sema.CheckConstexprFunctionBody(func, func->getBody())) { + return true; + } +#elif LLVM_VERSION_MAJOR >= 12 + if (!sema.CheckConstexprFunctionDefinition(func, Sema::CheckConstexprKind::CheckValid)) { return true; + } #endif - if (!CheckConstexprParameterTypes(sema, func)) + 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 +#if LLVM_VERSION_MAJOR >= 12 + func->setConstexprKind(clang::ConstexprSpecKind::Constexpr); +#else func->setConstexprKind(CSK_constexpr); +#endif // 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); @@ -260,71 +158,86 @@ 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) { - if (!stmt->isSingleDecl()) + bool VisitDeclStmt(clang::DeclStmt* stmt) { + if (!stmt->isSingleDecl()) { return true; + } - clang::VarDecl *var = - clang::dyn_cast(*stmt->decl_begin()); - if (!var) + auto* var = clang::dyn_cast(*stmt->decl_begin()); + if (!var) { return true; + } // Skip variables that are already constexpr - if (var->isConstexpr()) + if (var->isConstexpr()) { return true; + } // Only do locals for right now - if (!var->hasLocalStorage()) + if (!var->hasLocalStorage()) { return true; + } - clang::SourceLocation loc = stmt->getSourceRange().getBegin(); - auto &sema = CI_.getSema(); + clang::SourceLocation const loc = stmt->getSourceRange().getBegin(); + // auto& sema = CI_.getSema(); // var needs an initializer - Expr *Init = var->getInit(); - if (!Init) + Expr* Init = var->getInit(); + if (!Init) { return true; + } // If the var is const we can mark it constexpr - QualType ty = var->getType(); - if (!ty.isConstQualified()) + QualType const ty = var->getType(); + if (!ty.isConstQualified()) { return true; + } // Is init an integral constant expression - if (!var->checkInitIsICE()) +#if LLVM_VERSION_MAJOR >= 12 + if (!var->hasICEInitializer(CI_.getASTContext())) { +#else + if (!var->checkInitIsICE()) { +#endif return true; + } // Does the init function use dependent values - if (Init->isValueDependent()) + if (Init->isValueDependent()) { return true; + } // Can we evaluate the value - if (!var->evaluateValue()) + if (!var->evaluateValue()) { return true; + } // Is init an ice - if (!var->isInitICE()) +#if LLVM_VERSION_MAJOR >= 12 + if (!var->hasConstantInitialization()) { +#else + if (!var->isInitICE()) { +#endif 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); @@ -332,20 +245,21 @@ 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)) + SourceLocation const loc = func->getSourceRange().getBegin(); + 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); @@ -354,26 +268,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; } }; @@ -381,38 +297,45 @@ 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 { - if (inPlaceRewrite) + if (inPlaceRewrite) { rewriter->WriteFixedFiles(); + } } }; -int main(int argc, const char **argv) { +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()); + ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); Tool.run(newFrontendActionFactory().get()); }