xref: /llvm-project/clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp (revision 525fe4492bbecf357d3580d879f2092bf99c12a2)
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.
isReservedName(llvm::StringRef Name)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(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:
AggregateDesignatorNames(QualType T)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>(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(FieldsIt) == FieldsEnd;
56       }
57     }
58   }
59   // Returns false if the type was not an aggregate.
operator bool()60   operator bool() { return Valid; }
61   // Advance to the next element in the aggregate.
next()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.
append(std::string & Out,bool ForSubobject)72   bool append(std::string &Out, bool ForSubobject) {
73     if (IsArray) {
74       Out.push_back('[');
75       Out.append(std::to_string(Index));
76       Out.push_back(']');
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(FieldName))))
91         return true;
92 
93       if (!FieldName.empty() && !isReservedName(FieldName)) {
94         Out.push_back('.');
95         Out.append(FieldName.begin(), 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:'.
collectDesignators(const InitListExpr * Sem,llvm::DenseMap<SourceLocation,std::string> & Out,const llvm::DenseSet<SourceLocation> & NestedBraces,std::string & Prefix)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([&, Size(Prefix.size())] {
144       Fields.next();       // Always advance to the next subobject name.
145       Prefix.resize(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>(Init))
150       continue;
151 
152     const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init);
153     if (BraceElidedSubobject &&
154         NestedBraces.contains(BraceElidedSubobject->getLBraceLoc()))
155       BraceElidedSubobject = nullptr; // there were braces!
156 
157     if (!Fields.append(Prefix, 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(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>
getUnwrittenDesignators(const InitListExpr * Syn)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>(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
196