| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # REQUIRES: system-linux | ||
|
|
||
| ## Check that BOLT correctly parses the Linux kernel exception table. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr | ||
|
|
||
| ## Verify exception bindings to instructions. | ||
|
|
||
| # RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \ | ||
| # RUN: --bolt-info=0 | FileCheck %s | ||
|
|
||
| ## Verify the bindings again on the rewritten binary with nops removed. | ||
|
|
||
| # RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized | FileCheck %s | ||
|
|
||
| # CHECK: BOLT-INFO: Linux kernel binary detected | ||
| # CHECK: BOLT-INFO: parsed 2 exception table entries | ||
|
|
||
| .text | ||
| .globl _start | ||
| .type _start, %function | ||
| _start: | ||
| # CHECK: Binary Function "_start" | ||
| nop | ||
| .L0: | ||
| mov (%rdi), %rax | ||
| # CHECK: mov | ||
| # CHECK-SAME: ExceptionEntry: 1 # Fixup: [[FIXUP:[a-zA-Z0-9_]+]] | ||
| nop | ||
| .L1: | ||
| mov (%rsi), %rax | ||
| # CHECK: mov | ||
| # CHECK-SAME: ExceptionEntry: 2 # Fixup: [[FIXUP]] | ||
| nop | ||
| ret | ||
| .LF0: | ||
| # CHECK: Secondary Entry Point: [[FIXUP]] | ||
| jmp foo | ||
| .size _start, .-_start | ||
|
|
||
| .globl foo | ||
| .type foo, %function | ||
| foo: | ||
| ret | ||
| .size foo, .-foo | ||
|
|
||
|
|
||
| ## Exception table. | ||
| .section __ex_table,"a",@progbits | ||
| .align 4 | ||
|
|
||
| .long .L0 - . # instruction | ||
| .long .LF0 - . # fixup | ||
| .long 0 # data | ||
|
|
||
| .long .L1 - . # instruction | ||
| .long .LF0 - . # fixup | ||
| .long 0 # data | ||
|
|
||
| ## Fake Linux Kernel sections. | ||
| .section __ksymtab,"a",@progbits | ||
| .section __ksymtab_gpl,"a",@progbits |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| //===--- CrtpConstructorAccessibilityCheck.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 "CrtpConstructorAccessibilityCheck.h" | ||
| #include "../utils/LexerUtils.h" | ||
| #include "clang/ASTMatchers/ASTMatchFinder.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang::tidy::bugprone { | ||
|
|
||
| static bool hasPrivateConstructor(const CXXRecordDecl *RD) { | ||
| return llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) { | ||
| return Ctor->getAccess() == AS_private; | ||
| }); | ||
| } | ||
|
|
||
| static bool isDerivedParameterBefriended(const CXXRecordDecl *CRTP, | ||
| const NamedDecl *Param) { | ||
| return llvm::any_of(CRTP->friends(), [&](const FriendDecl *Friend) { | ||
| const TypeSourceInfo *const FriendType = Friend->getFriendType(); | ||
| if (!FriendType) { | ||
| return false; | ||
| } | ||
|
|
||
| const auto *const TTPT = | ||
| dyn_cast<TemplateTypeParmType>(FriendType->getType()); | ||
|
|
||
| return TTPT && TTPT->getDecl() == Param; | ||
| }); | ||
| } | ||
|
|
||
| static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP, | ||
| const CXXRecordDecl *Derived) { | ||
| return llvm::any_of(CRTP->friends(), [&](const FriendDecl *Friend) { | ||
| const TypeSourceInfo *const FriendType = Friend->getFriendType(); | ||
| if (!FriendType) { | ||
| return false; | ||
| } | ||
|
|
||
| return FriendType->getType()->getAsCXXRecordDecl() == Derived; | ||
| }); | ||
| } | ||
|
|
||
| static const NamedDecl * | ||
| getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP, | ||
| const CXXRecordDecl *Derived) { | ||
| size_t Idx = 0; | ||
| const bool AnyOf = llvm::any_of( | ||
| CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) { | ||
| ++Idx; | ||
| return Arg.getKind() == TemplateArgument::Type && | ||
| Arg.getAsType()->getAsCXXRecordDecl() == Derived; | ||
| }); | ||
|
|
||
| return AnyOf ? CRTP->getSpecializedTemplate() | ||
| ->getTemplateParameters() | ||
| ->getParam(Idx - 1) | ||
| : nullptr; | ||
| } | ||
|
|
||
| static std::vector<FixItHint> | ||
| hintMakeCtorPrivate(const CXXConstructorDecl *Ctor, | ||
| const std::string &OriginalAccess) { | ||
| std::vector<FixItHint> Hints; | ||
|
|
||
| Hints.emplace_back(FixItHint::CreateInsertion( | ||
| Ctor->getBeginLoc().getLocWithOffset(-1), "private:\n")); | ||
|
|
||
| const ASTContext &ASTCtx = Ctor->getASTContext(); | ||
| const SourceLocation CtorEndLoc = | ||
| Ctor->isExplicitlyDefaulted() | ||
| ? utils::lexer::findNextTerminator(Ctor->getEndLoc(), | ||
| ASTCtx.getSourceManager(), | ||
| ASTCtx.getLangOpts()) | ||
| : Ctor->getEndLoc(); | ||
| Hints.emplace_back(FixItHint::CreateInsertion( | ||
| CtorEndLoc.getLocWithOffset(1), '\n' + OriginalAccess + ':' + '\n')); | ||
|
|
||
| return Hints; | ||
| } | ||
|
|
||
| void CrtpConstructorAccessibilityCheck::registerMatchers(MatchFinder *Finder) { | ||
| Finder->addMatcher( | ||
| classTemplateSpecializationDecl( | ||
| decl().bind("crtp"), | ||
| hasAnyTemplateArgument(refersToType(recordType(hasDeclaration( | ||
| cxxRecordDecl( | ||
| isDerivedFrom(cxxRecordDecl(equalsBoundNode("crtp")))) | ||
| .bind("derived")))))), | ||
| this); | ||
| } | ||
|
|
||
| void CrtpConstructorAccessibilityCheck::check( | ||
| const MatchFinder::MatchResult &Result) { | ||
| const auto *CRTPInstantiation = | ||
| Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("crtp"); | ||
| const auto *DerivedRecord = Result.Nodes.getNodeAs<CXXRecordDecl>("derived"); | ||
| const CXXRecordDecl *CRTPDeclaration = | ||
| CRTPInstantiation->getSpecializedTemplate()->getTemplatedDecl(); | ||
|
|
||
| if (!CRTPDeclaration->hasDefinition()) { | ||
| return; | ||
| } | ||
|
|
||
| const auto *DerivedTemplateParameter = | ||
| getDerivedParameter(CRTPInstantiation, DerivedRecord); | ||
|
|
||
| assert(DerivedTemplateParameter && | ||
| "No template parameter corresponds to the derived class of the CRTP."); | ||
|
|
||
| bool NeedsFriend = !isDerivedParameterBefriended(CRTPDeclaration, | ||
| DerivedTemplateParameter) && | ||
| !isDerivedClassBefriended(CRTPDeclaration, DerivedRecord); | ||
|
|
||
| const FixItHint HintFriend = FixItHint::CreateInsertion( | ||
| CRTPDeclaration->getBraceRange().getEnd(), | ||
| "friend " + DerivedTemplateParameter->getNameAsString() + ';' + '\n'); | ||
|
|
||
| if (hasPrivateConstructor(CRTPDeclaration) && NeedsFriend) { | ||
| diag(CRTPDeclaration->getLocation(), | ||
| "the CRTP cannot be constructed from the derived class; consider " | ||
| "declaring the derived class as friend") | ||
| << HintFriend; | ||
| } | ||
|
|
||
| auto WithFriendHintIfNeeded = | ||
| [&](const DiagnosticBuilder &Diag, | ||
| bool NeedsFriend) -> const DiagnosticBuilder & { | ||
| if (NeedsFriend) | ||
| Diag << HintFriend; | ||
|
|
||
| return Diag; | ||
| }; | ||
|
|
||
| if (!CRTPDeclaration->hasUserDeclaredConstructor()) { | ||
| const bool IsStruct = CRTPDeclaration->isStruct(); | ||
|
|
||
| WithFriendHintIfNeeded( | ||
| diag(CRTPDeclaration->getLocation(), | ||
| "the implicit default constructor of the CRTP is publicly " | ||
| "accessible; consider making it private%select{| and declaring " | ||
| "the derived class as friend}0") | ||
| << NeedsFriend | ||
| << FixItHint::CreateInsertion( | ||
| CRTPDeclaration->getBraceRange().getBegin().getLocWithOffset( | ||
| 1), | ||
| (IsStruct ? "\nprivate:\n" : "\n") + | ||
| CRTPDeclaration->getNameAsString() + "() = default;\n" + | ||
| (IsStruct ? "public:\n" : "")), | ||
| NeedsFriend); | ||
| } | ||
|
|
||
| for (auto &&Ctor : CRTPDeclaration->ctors()) { | ||
| if (Ctor->getAccess() == AS_private) | ||
| continue; | ||
|
|
||
| const bool IsPublic = Ctor->getAccess() == AS_public; | ||
| const std::string Access = IsPublic ? "public" : "protected"; | ||
|
|
||
| WithFriendHintIfNeeded( | ||
| diag(Ctor->getLocation(), | ||
| "%0 contructor allows the CRTP to be %select{inherited " | ||
| "from|constructed}1 as a regular template class; consider making " | ||
| "it private%select{| and declaring the derived class as friend}2") | ||
| << Access << IsPublic << NeedsFriend | ||
| << hintMakeCtorPrivate(Ctor, Access), | ||
| NeedsFriend); | ||
| } | ||
| } | ||
|
|
||
| bool CrtpConstructorAccessibilityCheck::isLanguageVersionSupported( | ||
| const LangOptions &LangOpts) const { | ||
| return LangOpts.CPlusPlus11; | ||
| } | ||
| } // namespace clang::tidy::bugprone |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| //===--- CrtpConstructorAccessibilityCheck.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_BUGPRONE_CRTPCONSTRUCTORACCESSIBILITYCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CRTPCONSTRUCTORACCESSIBILITYCHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::bugprone { | ||
|
|
||
| /// Detects error-prone Curiously Recurring Template Pattern usage, when the | ||
| /// CRTP can be constructed outside itself and the derived class. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/crtp-constructor-accessibility.html | ||
| class CrtpConstructorAccessibilityCheck : public ClangTidyCheck { | ||
| public: | ||
| CrtpConstructorAccessibilityCheck(StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context) {} | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| bool isLanguageVersionSupported(const LangOptions &LangOpts) const override; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::bugprone | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CRTPCONSTRUCTORACCESSIBILITYCHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| //===--- UseDesignatedInitializersCheck.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 "UseDesignatedInitializersCheck.h" | ||
| #include "../utils/DesignatedInitializers.h" | ||
| #include "clang/AST/APValue.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/AST/Expr.h" | ||
| #include "clang/AST/Stmt.h" | ||
| #include "clang/ASTMatchers/ASTMatchFinder.h" | ||
| #include "clang/ASTMatchers/ASTMatchers.h" | ||
| #include "clang/ASTMatchers/ASTMatchersMacros.h" | ||
| #include "clang/Basic/Diagnostic.h" | ||
| #include "clang/Lex/Lexer.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang::tidy::modernize { | ||
|
|
||
| static constexpr char IgnoreSingleElementAggregatesName[] = | ||
| "IgnoreSingleElementAggregates"; | ||
| static constexpr bool IgnoreSingleElementAggregatesDefault = true; | ||
|
|
||
| static constexpr char RestrictToPODTypesName[] = "RestrictToPODTypes"; | ||
| static constexpr bool RestrictToPODTypesDefault = false; | ||
|
|
||
| static constexpr char IgnoreMacrosName[] = "IgnoreMacros"; | ||
| static constexpr bool IgnoreMacrosDefault = true; | ||
|
|
||
| namespace { | ||
|
|
||
| struct Designators { | ||
|
|
||
| Designators(const InitListExpr *InitList) : InitList(InitList) { | ||
| assert(InitList->isSyntacticForm()); | ||
| }; | ||
|
|
||
| unsigned size() { return getCached().size(); } | ||
|
|
||
| std::optional<llvm::StringRef> operator[](const SourceLocation &Location) { | ||
| const auto &Designators = getCached(); | ||
| const auto Result = Designators.find(Location); | ||
| if (Result == Designators.end()) | ||
| return {}; | ||
| const llvm::StringRef Designator = Result->getSecond(); | ||
| return (Designator.front() == '.' ? Designator.substr(1) : Designator) | ||
| .trim("\0"); // Trim NULL characters appearing on Windows in the | ||
| // name. | ||
| } | ||
|
|
||
| private: | ||
| using LocationToNameMap = llvm::DenseMap<clang::SourceLocation, std::string>; | ||
|
|
||
| std::optional<LocationToNameMap> CachedDesignators; | ||
| const InitListExpr *InitList; | ||
|
|
||
| LocationToNameMap &getCached() { | ||
| return CachedDesignators ? *CachedDesignators | ||
| : CachedDesignators.emplace( | ||
| utils::getUnwrittenDesignators(InitList)); | ||
| } | ||
| }; | ||
|
|
||
| unsigned getNumberOfDesignated(const InitListExpr *SyntacticInitList) { | ||
| return llvm::count_if(*SyntacticInitList, [](auto *InitExpr) { | ||
| return isa<DesignatedInitExpr>(InitExpr); | ||
| }); | ||
| } | ||
|
|
||
| AST_MATCHER(CXXRecordDecl, isAggregate) { return Node.isAggregate(); } | ||
|
|
||
| AST_MATCHER(CXXRecordDecl, isPOD) { return Node.isPOD(); } | ||
|
|
||
| AST_MATCHER(InitListExpr, isFullyDesignated) { | ||
| if (const InitListExpr *SyntacticForm = | ||
| Node.isSyntacticForm() ? &Node : Node.getSyntacticForm()) | ||
| return getNumberOfDesignated(SyntacticForm) == SyntacticForm->getNumInits(); | ||
| return true; | ||
| } | ||
|
|
||
| AST_MATCHER(InitListExpr, hasMoreThanOneElement) { | ||
| return Node.getNumInits() > 1; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| UseDesignatedInitializersCheck::UseDesignatedInitializersCheck( | ||
| StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context), IgnoreSingleElementAggregates(Options.get( | ||
| IgnoreSingleElementAggregatesName, | ||
| IgnoreSingleElementAggregatesDefault)), | ||
| RestrictToPODTypes( | ||
| Options.get(RestrictToPODTypesName, RestrictToPODTypesDefault)), | ||
| IgnoreMacros( | ||
| Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)) {} | ||
|
|
||
| void UseDesignatedInitializersCheck::registerMatchers(MatchFinder *Finder) { | ||
| const auto HasBaseWithFields = | ||
| hasAnyBase(hasType(cxxRecordDecl(has(fieldDecl())))); | ||
| Finder->addMatcher( | ||
| initListExpr( | ||
| hasType(cxxRecordDecl(RestrictToPODTypes ? isPOD() : isAggregate(), | ||
| unless(HasBaseWithFields)) | ||
| .bind("type")), | ||
| IgnoreSingleElementAggregates ? hasMoreThanOneElement() : anything(), | ||
| unless(isFullyDesignated())) | ||
| .bind("init"), | ||
| this); | ||
| } | ||
|
|
||
| void UseDesignatedInitializersCheck::check( | ||
| const MatchFinder::MatchResult &Result) { | ||
| const auto *InitList = Result.Nodes.getNodeAs<InitListExpr>("init"); | ||
| const auto *Type = Result.Nodes.getNodeAs<CXXRecordDecl>("type"); | ||
| if (!Type || !InitList) | ||
| return; | ||
| const auto *SyntacticInitList = InitList->getSyntacticForm(); | ||
| if (!SyntacticInitList) | ||
| return; | ||
| Designators Designators{SyntacticInitList}; | ||
| const unsigned NumberOfDesignated = getNumberOfDesignated(SyntacticInitList); | ||
| if (SyntacticInitList->getNumInits() - NumberOfDesignated > | ||
| Designators.size()) | ||
| return; | ||
|
|
||
| // If the whole initializer list is un-designated, issue only one warning and | ||
| // a single fix-it for the whole expression. | ||
| if (0 == NumberOfDesignated) { | ||
| if (IgnoreMacros && InitList->getBeginLoc().isMacroID()) | ||
| return; | ||
| { | ||
| DiagnosticBuilder Diag = | ||
| diag(InitList->getLBraceLoc(), | ||
| "use designated initializer list to initialize %0"); | ||
| Diag << Type << InitList->getSourceRange(); | ||
| for (const Stmt *InitExpr : *SyntacticInitList) { | ||
| const auto Designator = Designators[InitExpr->getBeginLoc()]; | ||
| if (Designator && !Designator->empty()) | ||
| Diag << FixItHint::CreateInsertion(InitExpr->getBeginLoc(), | ||
| ("." + *Designator + "=").str()); | ||
| } | ||
| } | ||
| diag(Type->getBeginLoc(), "aggregate type is defined here", | ||
| DiagnosticIDs::Note); | ||
| return; | ||
| } | ||
|
|
||
| // In case that only a few elements are un-designated (not all as before), the | ||
| // check offers dedicated issues and fix-its for each of them. | ||
| for (const auto *InitExpr : *SyntacticInitList) { | ||
| if (isa<DesignatedInitExpr>(InitExpr)) | ||
| continue; | ||
| if (IgnoreMacros && InitExpr->getBeginLoc().isMacroID()) | ||
| continue; | ||
| const auto Designator = Designators[InitExpr->getBeginLoc()]; | ||
| if (!Designator || Designator->empty()) { | ||
| // There should always be a designator. If there's unexpectedly none, we | ||
| // at least report a generic diagnostic. | ||
| diag(InitExpr->getBeginLoc(), "use designated init expression") | ||
| << InitExpr->getSourceRange(); | ||
| } else { | ||
| diag(InitExpr->getBeginLoc(), | ||
| "use designated init expression to initialize field '%0'") | ||
| << InitExpr->getSourceRange() << *Designator | ||
| << FixItHint::CreateInsertion(InitExpr->getBeginLoc(), | ||
| ("." + *Designator + "=").str()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void UseDesignatedInitializersCheck::storeOptions( | ||
| ClangTidyOptions::OptionMap &Opts) { | ||
| Options.store(Opts, IgnoreSingleElementAggregatesName, | ||
| IgnoreSingleElementAggregates); | ||
| Options.store(Opts, RestrictToPODTypesName, RestrictToPODTypes); | ||
| Options.store(Opts, IgnoreMacrosName, IgnoreMacros); | ||
| } | ||
|
|
||
| } // namespace clang::tidy::modernize |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| //===--- UseDesignatedInitializersCheck.h - clang-tidy ----------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEDESIGNATEDINITIALIZERSCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEDESIGNATEDINITIALIZERSCHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::modernize { | ||
|
|
||
| /// Finds initializer lists for aggregate type that could be | ||
| /// written as designated initializers instead. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-designated-initializers.html | ||
| class UseDesignatedInitializersCheck : public ClangTidyCheck { | ||
| public: | ||
| UseDesignatedInitializersCheck(StringRef Name, ClangTidyContext *Context); | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
|
|
||
| std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
| return TK_IgnoreUnlessSpelledInSource; | ||
| } | ||
|
|
||
| private: | ||
| bool IgnoreSingleElementAggregates; | ||
| bool RestrictToPODTypes; | ||
| bool IgnoreMacros; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::modernize | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEDESIGNATEDINITIALIZERSCHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| //===--- DesignatedInitializers.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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file provides utilities for designated initializers. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "DesignatedInitializers.h" | ||
| #include "clang/AST/DeclCXX.h" | ||
| #include "llvm/ADT/DenseSet.h" | ||
| #include "llvm/ADT/ScopeExit.h" | ||
|
|
||
| namespace clang::tidy::utils { | ||
|
|
||
| namespace { | ||
|
|
||
| /// Returns true if Name is reserved, like _Foo or __Vector_base. | ||
| static inline bool isReservedName(llvm::StringRef Name) { | ||
| // This doesn't catch all cases, but the most common. | ||
| return Name.size() >= 2 && Name[0] == '_' && | ||
| (isUppercase(Name[1]) || Name[1] == '_'); | ||
| } | ||
|
|
||
| // Helper class to iterate over the designator names of an aggregate type. | ||
| // | ||
| // For an array type, yields [0], [1], [2]... | ||
| // For aggregate classes, yields null for each base, then .field1, .field2, | ||
| // ... | ||
| class AggregateDesignatorNames { | ||
| public: | ||
| AggregateDesignatorNames(QualType T) { | ||
| if (!T.isNull()) { | ||
| T = T.getCanonicalType(); | ||
| if (T->isArrayType()) { | ||
| IsArray = true; | ||
| Valid = true; | ||
| return; | ||
| } | ||
| if (const RecordDecl *RD = T->getAsRecordDecl()) { | ||
| Valid = true; | ||
| FieldsIt = RD->field_begin(); | ||
| FieldsEnd = RD->field_end(); | ||
| if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) { | ||
| BasesIt = CRD->bases_begin(); | ||
| BasesEnd = CRD->bases_end(); | ||
| Valid = CRD->isAggregate(); | ||
| } | ||
| OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd && | ||
| std::next(FieldsIt) == FieldsEnd; | ||
| } | ||
| } | ||
| } | ||
| // Returns false if the type was not an aggregate. | ||
| operator bool() { return Valid; } | ||
| // Advance to the next element in the aggregate. | ||
| void next() { | ||
| if (IsArray) | ||
| ++Index; | ||
| else if (BasesIt != BasesEnd) | ||
| ++BasesIt; | ||
| else if (FieldsIt != FieldsEnd) | ||
| ++FieldsIt; | ||
| } | ||
| // Print the designator to Out. | ||
| // Returns false if we could not produce a designator for this element. | ||
| bool append(std::string &Out, bool ForSubobject) { | ||
| if (IsArray) { | ||
| Out.push_back('['); | ||
| Out.append(std::to_string(Index)); | ||
| Out.push_back(']'); | ||
| return true; | ||
| } | ||
| if (BasesIt != BasesEnd) | ||
| return false; // Bases can't be designated. Should we make one up? | ||
| if (FieldsIt != FieldsEnd) { | ||
| llvm::StringRef FieldName; | ||
| if (const IdentifierInfo *II = FieldsIt->getIdentifier()) | ||
| FieldName = II->getName(); | ||
|
|
||
| // For certain objects, their subobjects may be named directly. | ||
| if (ForSubobject && | ||
| (FieldsIt->isAnonymousStructOrUnion() || | ||
| // std::array<int,3> x = {1,2,3}. Designators not strictly valid! | ||
| (OneField && isReservedName(FieldName)))) | ||
| return true; | ||
|
|
||
| if (!FieldName.empty() && !isReservedName(FieldName)) { | ||
| Out.push_back('.'); | ||
| Out.append(FieldName.begin(), FieldName.end()); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private: | ||
| bool Valid = false; | ||
| bool IsArray = false; | ||
| bool OneField = false; // e.g. std::array { T __elements[N]; } | ||
| unsigned Index = 0; | ||
| CXXRecordDecl::base_class_const_iterator BasesIt; | ||
| CXXRecordDecl::base_class_const_iterator BasesEnd; | ||
| RecordDecl::field_iterator FieldsIt; | ||
| RecordDecl::field_iterator FieldsEnd; | ||
| }; | ||
|
|
||
| // Collect designator labels describing the elements of an init list. | ||
| // | ||
| // This function contributes the designators of some (sub)object, which is | ||
| // represented by the semantic InitListExpr Sem. | ||
| // This includes any nested subobjects, but *only* if they are part of the | ||
| // same original syntactic init list (due to brace elision). In other words, | ||
| // it may descend into subobjects but not written init-lists. | ||
| // | ||
| // For example: struct Outer { Inner a,b; }; struct Inner { int x, y; } | ||
| // Outer o{{1, 2}, 3}; | ||
| // This function will be called with Sem = { {1, 2}, {3, ImplicitValue} } | ||
| // It should generate designators '.a:' and '.b.x:'. | ||
| // '.a:' is produced directly without recursing into the written sublist. | ||
| // (The written sublist will have a separate collectDesignators() call later). | ||
| // Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'. | ||
| void collectDesignators(const InitListExpr *Sem, | ||
| llvm::DenseMap<SourceLocation, std::string> &Out, | ||
| const llvm::DenseSet<SourceLocation> &NestedBraces, | ||
| std::string &Prefix) { | ||
| if (!Sem || Sem->isTransparent()) | ||
| return; | ||
| assert(Sem->isSemanticForm()); | ||
|
|
||
| // The elements of the semantic form all correspond to direct subobjects of | ||
| // the aggregate type. `Fields` iterates over these subobject names. | ||
| AggregateDesignatorNames Fields(Sem->getType()); | ||
| if (!Fields) | ||
| return; | ||
| for (const Expr *Init : Sem->inits()) { | ||
| auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] { | ||
| Fields.next(); // Always advance to the next subobject name. | ||
| Prefix.resize(Size); // Erase any designator we appended. | ||
| }); | ||
| // Skip for a broken initializer or if it is a "hole" in a subobject that | ||
| // was not explicitly initialized. | ||
| if (!Init || llvm::isa<ImplicitValueInitExpr>(Init)) | ||
| continue; | ||
|
|
||
| const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init); | ||
| if (BraceElidedSubobject && | ||
| NestedBraces.contains(BraceElidedSubobject->getLBraceLoc())) | ||
| BraceElidedSubobject = nullptr; // there were braces! | ||
|
|
||
| if (!Fields.append(Prefix, BraceElidedSubobject != nullptr)) | ||
| continue; // no designator available for this subobject | ||
| if (BraceElidedSubobject) { | ||
| // If the braces were elided, this aggregate subobject is initialized | ||
| // inline in the same syntactic list. | ||
| // Descend into the semantic list describing the subobject. | ||
| // (NestedBraces are still correct, they're from the same syntactic | ||
| // list). | ||
| collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix); | ||
| continue; | ||
| } | ||
| Out.try_emplace(Init->getBeginLoc(), Prefix); | ||
| } | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| llvm::DenseMap<SourceLocation, std::string> | ||
| getUnwrittenDesignators(const InitListExpr *Syn) { | ||
| assert(Syn->isSyntacticForm()); | ||
|
|
||
| // collectDesignators needs to know which InitListExprs in the semantic tree | ||
| // were actually written, but InitListExpr::isExplicit() lies. | ||
| // Instead, record where braces of sub-init-lists occur in the syntactic form. | ||
| llvm::DenseSet<SourceLocation> NestedBraces; | ||
| for (const Expr *Init : Syn->inits()) | ||
| if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init)) | ||
| NestedBraces.insert(Nested->getLBraceLoc()); | ||
|
|
||
| // Traverse the semantic form to find the designators. | ||
| // We use their SourceLocation to correlate with the syntactic form later. | ||
| llvm::DenseMap<SourceLocation, std::string> Designators; | ||
| std::string EmptyPrefix; | ||
| collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(), | ||
| Designators, NestedBraces, EmptyPrefix); | ||
| return Designators; | ||
| } | ||
|
|
||
| } // namespace clang::tidy::utils |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| //===--- DesignatedInitializers.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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file provides utilities for designated initializers. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "clang/AST/Expr.h" | ||
| #include "clang/Basic/SourceLocation.h" | ||
| #include "llvm/ADT/DenseMap.h" | ||
|
|
||
| namespace clang::tidy::utils { | ||
|
|
||
| /// Get designators describing the elements of a (syntactic) init list. | ||
| /// | ||
| /// Given for example the type | ||
| /// \code | ||
| /// struct S { int i, j; }; | ||
| /// \endcode | ||
| /// and the definition | ||
| /// \code | ||
| /// S s{1, 2}; | ||
| /// \endcode | ||
| /// calling `getUnwrittenDesignators` for the initializer list expression | ||
| /// `{1, 2}` would produce the map `{loc(1): ".i", loc(2): ".j"}`. | ||
| /// | ||
| /// It does not produce designators for any explicitly-written nested lists, | ||
| /// e.g. `{1, .j=2}` would only return `{loc(1): ".i"}`. | ||
| /// | ||
| /// It also considers structs with fields of record types like | ||
| /// `struct T { S s; };`. In this case, there would be designators of the | ||
| /// form `.s.i` and `.s.j` in the returned map. | ||
| llvm::DenseMap<clang::SourceLocation, std::string> | ||
| getUnwrittenDesignators(const clang::InitListExpr *Syn); | ||
|
|
||
| } // namespace clang::tidy::utils |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,43 +77,6 @@ class HeuristicResolver { | |
|
|
||
| private: | ||
| ASTContext &Ctx; | ||
| }; | ||
|
|
||
| } // namespace clangd | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| .. title:: clang-tidy - bugprone-crtp-constructor-accessibility | ||
|
|
||
| bugprone-crtp-constructor-accessibility | ||
| ======================================= | ||
|
|
||
| Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP | ||
| can be constructed outside itself and the derived class. | ||
|
|
||
| The CRTP is an idiom, in which a class derives from a template class, where | ||
| itself is the template argument. It should be ensured that if a class is | ||
| intended to be a base class in this idiom, it can only be instantiated if | ||
| the derived class is it's template argument. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| private: | ||
| CRTP() = default; | ||
| friend T; | ||
| }; | ||
|
|
||
| class Derived : CRTP<Derived> {}; | ||
|
|
||
| Below can be seen some common mistakes that will allow the breaking of the | ||
| idiom. | ||
|
|
||
| If the constructor of a class intended to be used in a CRTP is public, then | ||
| it allows users to construct that class on its own. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| public: | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class Good : CRTP<Good> {}; | ||
| Good GoodInstance; | ||
|
|
||
| CRTP<int> BadInstance; | ||
|
|
||
| If the constructor is protected, the possibility of an accidental instantiation | ||
| is prevented, however it can fade an error, when a different class is used as | ||
| the template parameter instead of the derived one. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| protected: | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class Good : CRTP<Good> {}; | ||
| Good GoodInstance; | ||
|
|
||
| class Bad : CRTP<Good> {}; | ||
| Bad BadInstance; | ||
|
|
||
| To ensure that no accidental instantiation happens, the best practice is to | ||
| make the constructor private and declare the derived class as friend. Note | ||
| that as a tradeoff, this also gives the derived class access to every other | ||
| private members of the CRTP. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| CRTP() = default; | ||
| friend T; | ||
| }; | ||
|
|
||
| class Good : CRTP<Good> {}; | ||
| Good GoodInstance; | ||
|
|
||
| class Bad : CRTP<Good> {}; | ||
| Bad CompileTimeError; | ||
|
|
||
| CRTP<int> AlsoCompileTimeError; | ||
|
|
||
| Limitations: | ||
|
|
||
| * The check is not supported below C++11 | ||
|
|
||
| * The check does not handle when the derived class is passed as a variadic | ||
| template argument | ||
|
|
||
| * Accessible functions that can construct the CRTP, like factory functions | ||
| are not checked | ||
|
|
||
| The check also suggests a fix-its in some cases. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| .. title:: clang-tidy - modernize-use-designated-initializers | ||
|
|
||
| modernize-use-designated-initializers | ||
| ===================================== | ||
|
|
||
| Finds initializer lists for aggregate types which could be written as designated | ||
| initializers instead. | ||
|
|
||
| With plain initializer lists, it is very easy to introduce bugs when adding new | ||
| fields in the middle of a struct or class type. The same confusion might arise | ||
| when changing the order of fields. | ||
|
|
||
| C++20 supports the designated initializer syntax for aggregate types. By | ||
| applying it, we can always be sure that aggregates are constructed correctly, | ||
| because every variable being initialized is referenced by its name. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: | ||
| struct S { int i, j; }; | ||
| is an aggregate type that should be initialized as | ||
|
|
||
| .. code-block:: | ||
| S s{.i = 1, .j = 2}; | ||
| instead of | ||
|
|
||
| .. code-block:: | ||
| S s{1, 2}; | ||
| which could easily become an issue when ``i`` and ``j`` are swapped in the | ||
| declaration of ``S``. | ||
|
|
||
| Even when compiling in a language version older than C++20, depending on your | ||
| compiler, designated initializers are potentially supported. Therefore, the | ||
| check is not restricted to C++20 and newer versions. Check out the options | ||
| ``-Wc99-designator`` to get support for mixed designators in initializer list in | ||
| C and ``-Wc++20-designator`` for support of designated initializers in older C++ | ||
| language modes. | ||
|
|
||
| Options | ||
| ------- | ||
|
|
||
| .. option:: IgnoreMacros | ||
|
|
||
| The value `false` specifies that components of initializer lists expanded from | ||
| macros are not checked. The default value is `true`. | ||
|
|
||
| .. option:: IgnoreSingleElementAggregates | ||
|
|
||
| The value `false` specifies that even initializers for aggregate types with | ||
| only a single element should be checked. The default value is `true`. | ||
|
|
||
| .. option:: RestrictToPODTypes | ||
|
|
||
| The value `true` specifies that only Plain Old Data (POD) types shall be | ||
| checked. This makes the check applicable to even older C++ standards. The | ||
| default value is `false`. |