| 1 | //===--- DesignatedInitializers.cpp - clang-tidy --------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// |
| 9 | /// \file |
| 10 | /// This file provides utilities for designated initializers. |
| 11 | /// |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "DesignatedInitializers.h" |
| 15 | #include "clang/AST/DeclCXX.h" |
| 16 | #include "llvm/ADT/DenseSet.h" |
| 17 | #include "llvm/ADT/ScopeExit.h" |
| 18 | |
| 19 | namespace clang::tidy::utils { |
| 20 | |
| 21 | namespace { |
| 22 | |
| 23 | /// Returns true if Name is reserved, like _Foo or __Vector_base. |
| 24 | static inline bool isReservedName(llvm::StringRef Name) { |
| 25 | // This doesn't catch all cases, but the most common. |
| 26 | return Name.size() >= 2 && Name[0] == '_' && |
| 27 | (isUppercase(c: Name[1]) || Name[1] == '_'); |
| 28 | } |
| 29 | |
| 30 | // Helper class to iterate over the designator names of an aggregate type. |
| 31 | // |
| 32 | // For an array type, yields [0], [1], [2]... |
| 33 | // For aggregate classes, yields null for each base, then .field1, .field2, |
| 34 | // ... |
| 35 | class AggregateDesignatorNames { |
| 36 | public: |
| 37 | AggregateDesignatorNames(QualType T) { |
| 38 | if (!T.isNull()) { |
| 39 | T = T.getCanonicalType(); |
| 40 | if (T->isArrayType()) { |
| 41 | IsArray = true; |
| 42 | Valid = true; |
| 43 | return; |
| 44 | } |
| 45 | if (const RecordDecl *RD = T->getAsRecordDecl()) { |
| 46 | Valid = true; |
| 47 | FieldsIt = RD->field_begin(); |
| 48 | FieldsEnd = RD->field_end(); |
| 49 | if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(Val: RD)) { |
| 50 | BasesIt = CRD->bases_begin(); |
| 51 | BasesEnd = CRD->bases_end(); |
| 52 | Valid = CRD->isAggregate(); |
| 53 | } |
| 54 | OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd && |
| 55 | std::next(x: FieldsIt) == FieldsEnd; |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | // Returns false if the type was not an aggregate. |
| 60 | operator bool() { return Valid; } |
| 61 | // Advance to the next element in the aggregate. |
| 62 | void next() { |
| 63 | if (IsArray) |
| 64 | ++Index; |
| 65 | else if (BasesIt != BasesEnd) |
| 66 | ++BasesIt; |
| 67 | else if (FieldsIt != FieldsEnd) |
| 68 | ++FieldsIt; |
| 69 | } |
| 70 | // Print the designator to Out. |
| 71 | // Returns false if we could not produce a designator for this element. |
| 72 | bool append(std::string &Out, bool ForSubobject) { |
| 73 | if (IsArray) { |
| 74 | Out.push_back(c: '['); |
| 75 | Out.append(str: std::to_string(val: Index)); |
| 76 | Out.push_back(c: ']'); |
| 77 | return true; |
| 78 | } |
| 79 | if (BasesIt != BasesEnd) |
| 80 | return false; // Bases can't be designated. Should we make one up? |
| 81 | if (FieldsIt != FieldsEnd) { |
| 82 | llvm::StringRef FieldName; |
| 83 | if (const IdentifierInfo *II = FieldsIt->getIdentifier()) |
| 84 | FieldName = II->getName(); |
| 85 | |
| 86 | // For certain objects, their subobjects may be named directly. |
| 87 | if (ForSubobject && |
| 88 | (FieldsIt->isAnonymousStructOrUnion() || |
| 89 | // std::array<int,3> x = {1,2,3}. Designators not strictly valid! |
| 90 | (OneField && isReservedName(Name: FieldName)))) |
| 91 | return true; |
| 92 | |
| 93 | if (!FieldName.empty() && !isReservedName(Name: FieldName)) { |
| 94 | Out.push_back(c: '.'); |
| 95 | Out.append(first: FieldName.begin(), last: FieldName.end()); |
| 96 | return true; |
| 97 | } |
| 98 | return false; |
| 99 | } |
| 100 | return false; |
| 101 | } |
| 102 | |
| 103 | private: |
| 104 | bool Valid = false; |
| 105 | bool IsArray = false; |
| 106 | bool OneField = false; // e.g. std::array { T __elements[N]; } |
| 107 | unsigned Index = 0; |
| 108 | CXXRecordDecl::base_class_const_iterator BasesIt; |
| 109 | CXXRecordDecl::base_class_const_iterator BasesEnd; |
| 110 | RecordDecl::field_iterator FieldsIt; |
| 111 | RecordDecl::field_iterator FieldsEnd; |
| 112 | }; |
| 113 | |
| 114 | // Collect designator labels describing the elements of an init list. |
| 115 | // |
| 116 | // This function contributes the designators of some (sub)object, which is |
| 117 | // represented by the semantic InitListExpr Sem. |
| 118 | // This includes any nested subobjects, but *only* if they are part of the |
| 119 | // same original syntactic init list (due to brace elision). In other words, |
| 120 | // it may descend into subobjects but not written init-lists. |
| 121 | // |
| 122 | // For example: struct Outer { Inner a,b; }; struct Inner { int x, y; } |
| 123 | // Outer o{{1, 2}, 3}; |
| 124 | // This function will be called with Sem = { {1, 2}, {3, ImplicitValue} } |
| 125 | // It should generate designators '.a:' and '.b.x:'. |
| 126 | // '.a:' is produced directly without recursing into the written sublist. |
| 127 | // (The written sublist will have a separate collectDesignators() call later). |
| 128 | // Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'. |
| 129 | void collectDesignators(const InitListExpr *Sem, |
| 130 | llvm::DenseMap<SourceLocation, std::string> &Out, |
| 131 | const llvm::DenseSet<SourceLocation> &NestedBraces, |
| 132 | std::string &Prefix) { |
| 133 | if (!Sem || Sem->isTransparent()) |
| 134 | return; |
| 135 | assert(Sem->isSemanticForm()); |
| 136 | |
| 137 | // The elements of the semantic form all correspond to direct subobjects of |
| 138 | // the aggregate type. `Fields` iterates over these subobject names. |
| 139 | AggregateDesignatorNames Fields(Sem->getType()); |
| 140 | if (!Fields) |
| 141 | return; |
| 142 | for (const Expr *Init : Sem->inits()) { |
| 143 | auto Next = llvm::make_scope_exit(F: [&, Size(Prefix.size())] { |
| 144 | Fields.next(); // Always advance to the next subobject name. |
| 145 | Prefix.resize(n: Size); // Erase any designator we appended. |
| 146 | }); |
| 147 | // Skip for a broken initializer or if it is a "hole" in a subobject that |
| 148 | // was not explicitly initialized. |
| 149 | if (!Init || llvm::isa<ImplicitValueInitExpr>(Val: Init)) |
| 150 | continue; |
| 151 | |
| 152 | const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Val: Init); |
| 153 | if (BraceElidedSubobject && |
| 154 | NestedBraces.contains(V: BraceElidedSubobject->getLBraceLoc())) |
| 155 | BraceElidedSubobject = nullptr; // there were braces! |
| 156 | |
| 157 | if (!Fields.append(Out&: Prefix, ForSubobject: BraceElidedSubobject != nullptr)) |
| 158 | continue; // no designator available for this subobject |
| 159 | if (BraceElidedSubobject) { |
| 160 | // If the braces were elided, this aggregate subobject is initialized |
| 161 | // inline in the same syntactic list. |
| 162 | // Descend into the semantic list describing the subobject. |
| 163 | // (NestedBraces are still correct, they're from the same syntactic |
| 164 | // list). |
| 165 | collectDesignators(Sem: BraceElidedSubobject, Out, NestedBraces, Prefix); |
| 166 | continue; |
| 167 | } |
| 168 | Out.try_emplace(Init->getBeginLoc(), Prefix); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | } // namespace |
| 173 | |
| 174 | llvm::DenseMap<SourceLocation, std::string> |
| 175 | getUnwrittenDesignators(const InitListExpr *Syn) { |
| 176 | assert(Syn->isSyntacticForm()); |
| 177 | |
| 178 | // collectDesignators needs to know which InitListExprs in the semantic tree |
| 179 | // were actually written, but InitListExpr::isExplicit() lies. |
| 180 | // Instead, record where braces of sub-init-lists occur in the syntactic form. |
| 181 | llvm::DenseSet<SourceLocation> NestedBraces; |
| 182 | for (const Expr *Init : Syn->inits()) |
| 183 | if (auto *Nested = llvm::dyn_cast<InitListExpr>(Val: Init)) |
| 184 | NestedBraces.insert(V: Nested->getLBraceLoc()); |
| 185 | |
| 186 | // Traverse the semantic form to find the designators. |
| 187 | // We use their SourceLocation to correlate with the syntactic form later. |
| 188 | llvm::DenseMap<SourceLocation, std::string> Designators; |
| 189 | std::string EmptyPrefix; |
| 190 | collectDesignators(Sem: Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(), |
| 191 | Out&: Designators, NestedBraces, Prefix&: EmptyPrefix); |
| 192 | return Designators; |
| 193 | } |
| 194 | |
| 195 | } // namespace clang::tidy::utils |
| 196 | |