//===--- 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(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() const { 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 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 &Out, const llvm::DenseSet &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(Init)) continue; const auto *BraceElidedSubobject = llvm::dyn_cast(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 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 NestedBraces; for (const Expr *Init : Syn->inits()) if (auto *Nested = llvm::dyn_cast(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 Designators; std::string EmptyPrefix; collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(), Designators, NestedBraces, EmptyPrefix); return Designators; } } // namespace clang::tidy::utils