1525fe449SDanny Mösch //===--- UseDesignatedInitializersCheck.cpp - clang-tidy ------------------===// 2525fe449SDanny Mösch // 3525fe449SDanny Mösch // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4525fe449SDanny Mösch // See https://llvm.org/LICENSE.txt for license information. 5525fe449SDanny Mösch // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6525fe449SDanny Mösch // 7525fe449SDanny Mösch //===----------------------------------------------------------------------===// 8525fe449SDanny Mösch 9525fe449SDanny Mösch #include "UseDesignatedInitializersCheck.h" 10525fe449SDanny Mösch #include "../utils/DesignatedInitializers.h" 11525fe449SDanny Mösch #include "clang/AST/APValue.h" 12525fe449SDanny Mösch #include "clang/AST/Decl.h" 13525fe449SDanny Mösch #include "clang/AST/Expr.h" 14525fe449SDanny Mösch #include "clang/AST/Stmt.h" 15525fe449SDanny Mösch #include "clang/ASTMatchers/ASTMatchFinder.h" 16525fe449SDanny Mösch #include "clang/ASTMatchers/ASTMatchers.h" 17525fe449SDanny Mösch #include "clang/ASTMatchers/ASTMatchersMacros.h" 18525fe449SDanny Mösch #include "clang/Basic/Diagnostic.h" 19525fe449SDanny Mösch #include "clang/Lex/Lexer.h" 20525fe449SDanny Mösch 21525fe449SDanny Mösch using namespace clang::ast_matchers; 22525fe449SDanny Mösch 23525fe449SDanny Mösch namespace clang::tidy::modernize { 24525fe449SDanny Mösch 25525fe449SDanny Mösch static constexpr char IgnoreSingleElementAggregatesName[] = 26525fe449SDanny Mösch "IgnoreSingleElementAggregates"; 27525fe449SDanny Mösch static constexpr bool IgnoreSingleElementAggregatesDefault = true; 28525fe449SDanny Mösch 29525fe449SDanny Mösch static constexpr char RestrictToPODTypesName[] = "RestrictToPODTypes"; 30525fe449SDanny Mösch static constexpr bool RestrictToPODTypesDefault = false; 31525fe449SDanny Mösch 32525fe449SDanny Mösch static constexpr char IgnoreMacrosName[] = "IgnoreMacros"; 33525fe449SDanny Mösch static constexpr bool IgnoreMacrosDefault = true; 34525fe449SDanny Mösch 3531b84d45SPiotr Zegar static constexpr char StrictCStandardComplianceName[] = 3631b84d45SPiotr Zegar "StrictCStandardCompliance"; 3731b84d45SPiotr Zegar static constexpr bool StrictCStandardComplianceDefault = true; 3831b84d45SPiotr Zegar 3931b84d45SPiotr Zegar static constexpr char StrictCppStandardComplianceName[] = 4031b84d45SPiotr Zegar "StrictCppStandardCompliance"; 4131b84d45SPiotr Zegar static constexpr bool StrictCppStandardComplianceDefault = true; 4231b84d45SPiotr Zegar 43525fe449SDanny Mösch namespace { 44525fe449SDanny Mösch 45525fe449SDanny Mösch struct Designators { 46525fe449SDanny Mösch 47525fe449SDanny Mösch Designators(const InitListExpr *InitList) : InitList(InitList) { 48525fe449SDanny Mösch assert(InitList->isSyntacticForm()); 49525fe449SDanny Mösch }; 50525fe449SDanny Mösch 51525fe449SDanny Mösch unsigned size() { return getCached().size(); } 52525fe449SDanny Mösch 53525fe449SDanny Mösch std::optional<llvm::StringRef> operator[](const SourceLocation &Location) { 5421be2fbdSPiotr Zegar const auto &Designators = getCached(); 55525fe449SDanny Mösch const auto Result = Designators.find(Location); 56525fe449SDanny Mösch if (Result == Designators.end()) 57525fe449SDanny Mösch return {}; 58525fe449SDanny Mösch const llvm::StringRef Designator = Result->getSecond(); 59525fe449SDanny Mösch return (Designator.front() == '.' ? Designator.substr(1) : Designator) 60525fe449SDanny Mösch .trim("\0"); // Trim NULL characters appearing on Windows in the 61525fe449SDanny Mösch // name. 62525fe449SDanny Mösch } 63525fe449SDanny Mösch 64525fe449SDanny Mösch private: 65525fe449SDanny Mösch using LocationToNameMap = llvm::DenseMap<clang::SourceLocation, std::string>; 66525fe449SDanny Mösch 67525fe449SDanny Mösch std::optional<LocationToNameMap> CachedDesignators; 68525fe449SDanny Mösch const InitListExpr *InitList; 69525fe449SDanny Mösch 70525fe449SDanny Mösch LocationToNameMap &getCached() { 71525fe449SDanny Mösch return CachedDesignators ? *CachedDesignators 72525fe449SDanny Mösch : CachedDesignators.emplace( 73525fe449SDanny Mösch utils::getUnwrittenDesignators(InitList)); 74525fe449SDanny Mösch } 75525fe449SDanny Mösch }; 76525fe449SDanny Mösch 77525fe449SDanny Mösch unsigned getNumberOfDesignated(const InitListExpr *SyntacticInitList) { 78525fe449SDanny Mösch return llvm::count_if(*SyntacticInitList, [](auto *InitExpr) { 79525fe449SDanny Mösch return isa<DesignatedInitExpr>(InitExpr); 80525fe449SDanny Mösch }); 81525fe449SDanny Mösch } 82525fe449SDanny Mösch 83*27ef549aSz1nke AST_MATCHER(CXXRecordDecl, isAggregate) { 84*27ef549aSz1nke return Node.hasDefinition() && Node.isAggregate(); 85*27ef549aSz1nke } 86525fe449SDanny Mösch 87*27ef549aSz1nke AST_MATCHER(CXXRecordDecl, isPOD) { 88*27ef549aSz1nke return Node.hasDefinition() && Node.isPOD(); 89*27ef549aSz1nke } 90525fe449SDanny Mösch 91525fe449SDanny Mösch AST_MATCHER(InitListExpr, isFullyDesignated) { 92525fe449SDanny Mösch if (const InitListExpr *SyntacticForm = 93525fe449SDanny Mösch Node.isSyntacticForm() ? &Node : Node.getSyntacticForm()) 94525fe449SDanny Mösch return getNumberOfDesignated(SyntacticForm) == SyntacticForm->getNumInits(); 95525fe449SDanny Mösch return true; 96525fe449SDanny Mösch } 97525fe449SDanny Mösch 98525fe449SDanny Mösch AST_MATCHER(InitListExpr, hasMoreThanOneElement) { 99525fe449SDanny Mösch return Node.getNumInits() > 1; 100525fe449SDanny Mösch } 101525fe449SDanny Mösch 102525fe449SDanny Mösch } // namespace 103525fe449SDanny Mösch 104525fe449SDanny Mösch UseDesignatedInitializersCheck::UseDesignatedInitializersCheck( 105525fe449SDanny Mösch StringRef Name, ClangTidyContext *Context) 106525fe449SDanny Mösch : ClangTidyCheck(Name, Context), IgnoreSingleElementAggregates(Options.get( 107525fe449SDanny Mösch IgnoreSingleElementAggregatesName, 108525fe449SDanny Mösch IgnoreSingleElementAggregatesDefault)), 109525fe449SDanny Mösch RestrictToPODTypes( 110525fe449SDanny Mösch Options.get(RestrictToPODTypesName, RestrictToPODTypesDefault)), 111525fe449SDanny Mösch IgnoreMacros( 11231b84d45SPiotr Zegar Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)), 11331b84d45SPiotr Zegar StrictCStandardCompliance(Options.get(StrictCStandardComplianceName, 11431b84d45SPiotr Zegar StrictCStandardComplianceDefault)), 11531b84d45SPiotr Zegar StrictCppStandardCompliance( 11631b84d45SPiotr Zegar Options.get(StrictCppStandardComplianceName, 11731b84d45SPiotr Zegar StrictCppStandardComplianceDefault)) {} 118525fe449SDanny Mösch 119525fe449SDanny Mösch void UseDesignatedInitializersCheck::registerMatchers(MatchFinder *Finder) { 120525fe449SDanny Mösch const auto HasBaseWithFields = 121525fe449SDanny Mösch hasAnyBase(hasType(cxxRecordDecl(has(fieldDecl())))); 122525fe449SDanny Mösch Finder->addMatcher( 123525fe449SDanny Mösch initListExpr( 124525fe449SDanny Mösch hasType(cxxRecordDecl(RestrictToPODTypes ? isPOD() : isAggregate(), 125525fe449SDanny Mösch unless(HasBaseWithFields)) 126525fe449SDanny Mösch .bind("type")), 127525fe449SDanny Mösch IgnoreSingleElementAggregates ? hasMoreThanOneElement() : anything(), 128525fe449SDanny Mösch unless(isFullyDesignated())) 129525fe449SDanny Mösch .bind("init"), 130525fe449SDanny Mösch this); 131525fe449SDanny Mösch } 132525fe449SDanny Mösch 133525fe449SDanny Mösch void UseDesignatedInitializersCheck::check( 134525fe449SDanny Mösch const MatchFinder::MatchResult &Result) { 135525fe449SDanny Mösch const auto *InitList = Result.Nodes.getNodeAs<InitListExpr>("init"); 136525fe449SDanny Mösch const auto *Type = Result.Nodes.getNodeAs<CXXRecordDecl>("type"); 137525fe449SDanny Mösch if (!Type || !InitList) 138525fe449SDanny Mösch return; 139525fe449SDanny Mösch const auto *SyntacticInitList = InitList->getSyntacticForm(); 140525fe449SDanny Mösch if (!SyntacticInitList) 141525fe449SDanny Mösch return; 142525fe449SDanny Mösch Designators Designators{SyntacticInitList}; 143525fe449SDanny Mösch const unsigned NumberOfDesignated = getNumberOfDesignated(SyntacticInitList); 144525fe449SDanny Mösch if (SyntacticInitList->getNumInits() - NumberOfDesignated > 145525fe449SDanny Mösch Designators.size()) 146525fe449SDanny Mösch return; 147525fe449SDanny Mösch 148525fe449SDanny Mösch // If the whole initializer list is un-designated, issue only one warning and 149525fe449SDanny Mösch // a single fix-it for the whole expression. 150525fe449SDanny Mösch if (0 == NumberOfDesignated) { 151525fe449SDanny Mösch if (IgnoreMacros && InitList->getBeginLoc().isMacroID()) 152525fe449SDanny Mösch return; 153525fe449SDanny Mösch { 154525fe449SDanny Mösch DiagnosticBuilder Diag = 155525fe449SDanny Mösch diag(InitList->getLBraceLoc(), 156525fe449SDanny Mösch "use designated initializer list to initialize %0"); 157525fe449SDanny Mösch Diag << Type << InitList->getSourceRange(); 158525fe449SDanny Mösch for (const Stmt *InitExpr : *SyntacticInitList) { 159525fe449SDanny Mösch const auto Designator = Designators[InitExpr->getBeginLoc()]; 160525fe449SDanny Mösch if (Designator && !Designator->empty()) 161525fe449SDanny Mösch Diag << FixItHint::CreateInsertion(InitExpr->getBeginLoc(), 162525fe449SDanny Mösch ("." + *Designator + "=").str()); 163525fe449SDanny Mösch } 164525fe449SDanny Mösch } 165525fe449SDanny Mösch diag(Type->getBeginLoc(), "aggregate type is defined here", 166525fe449SDanny Mösch DiagnosticIDs::Note); 167525fe449SDanny Mösch return; 168525fe449SDanny Mösch } 169525fe449SDanny Mösch 170525fe449SDanny Mösch // In case that only a few elements are un-designated (not all as before), the 171525fe449SDanny Mösch // check offers dedicated issues and fix-its for each of them. 172525fe449SDanny Mösch for (const auto *InitExpr : *SyntacticInitList) { 173525fe449SDanny Mösch if (isa<DesignatedInitExpr>(InitExpr)) 174525fe449SDanny Mösch continue; 175525fe449SDanny Mösch if (IgnoreMacros && InitExpr->getBeginLoc().isMacroID()) 176525fe449SDanny Mösch continue; 177525fe449SDanny Mösch const auto Designator = Designators[InitExpr->getBeginLoc()]; 178525fe449SDanny Mösch if (!Designator || Designator->empty()) { 179525fe449SDanny Mösch // There should always be a designator. If there's unexpectedly none, we 180525fe449SDanny Mösch // at least report a generic diagnostic. 181525fe449SDanny Mösch diag(InitExpr->getBeginLoc(), "use designated init expression") 182525fe449SDanny Mösch << InitExpr->getSourceRange(); 183525fe449SDanny Mösch } else { 184525fe449SDanny Mösch diag(InitExpr->getBeginLoc(), 185525fe449SDanny Mösch "use designated init expression to initialize field '%0'") 186525fe449SDanny Mösch << InitExpr->getSourceRange() << *Designator 187525fe449SDanny Mösch << FixItHint::CreateInsertion(InitExpr->getBeginLoc(), 188525fe449SDanny Mösch ("." + *Designator + "=").str()); 189525fe449SDanny Mösch } 190525fe449SDanny Mösch } 191525fe449SDanny Mösch } 192525fe449SDanny Mösch 193525fe449SDanny Mösch void UseDesignatedInitializersCheck::storeOptions( 194525fe449SDanny Mösch ClangTidyOptions::OptionMap &Opts) { 195525fe449SDanny Mösch Options.store(Opts, IgnoreSingleElementAggregatesName, 196525fe449SDanny Mösch IgnoreSingleElementAggregates); 197525fe449SDanny Mösch Options.store(Opts, RestrictToPODTypesName, RestrictToPODTypes); 198525fe449SDanny Mösch Options.store(Opts, IgnoreMacrosName, IgnoreMacros); 19931b84d45SPiotr Zegar Options.store(Opts, StrictCStandardComplianceName, StrictCStandardCompliance); 20031b84d45SPiotr Zegar Options.store(Opts, StrictCppStandardComplianceName, 20131b84d45SPiotr Zegar StrictCppStandardCompliance); 202525fe449SDanny Mösch } 203525fe449SDanny Mösch 204525fe449SDanny Mösch } // namespace clang::tidy::modernize 205