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
19namespace clang::tidy::utils {
20
21namespace {
22
23/// Returns true if Name is reserved, like _Foo or __Vector_base.
24static 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// ...
35class AggregateDesignatorNames {
36public:
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
103private:
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:'.
129void 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
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>(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

source code of clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp