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