|  | //===--- 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 |