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