Skip to content

Commit 9c53c3c

Browse files
committed
Move computation of designated initializers to common place
1 parent 189e5bb commit 9c53c3c

File tree

4 files changed

+241
-165
lines changed

4 files changed

+241
-165
lines changed

clang-tools-extra/clangd/InlayHints.cpp

Lines changed: 2 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
#include "clang/Basic/Builtins.h"
2424
#include "clang/Basic/OperatorKinds.h"
2525
#include "clang/Basic/SourceManager.h"
26+
#include "clang/Tooling/DesignatedInitializers.h"
2627
#include "llvm/ADT/DenseSet.h"
27-
#include "llvm/ADT/ScopeExit.h"
2828
#include "llvm/ADT/StringExtras.h"
2929
#include "llvm/ADT/StringRef.h"
3030
#include "llvm/ADT/Twine.h"
@@ -42,169 +42,6 @@ namespace {
4242
// For now, inlay hints are always anchored at the left or right of their range.
4343
enum class HintSide { Left, Right };
4444

45-
// Helper class to iterate over the designator names of an aggregate type.
46-
//
47-
// For an array type, yields [0], [1], [2]...
48-
// For aggregate classes, yields null for each base, then .field1, .field2, ...
49-
class AggregateDesignatorNames {
50-
public:
51-
AggregateDesignatorNames(QualType T) {
52-
if (!T.isNull()) {
53-
T = T.getCanonicalType();
54-
if (T->isArrayType()) {
55-
IsArray = true;
56-
Valid = true;
57-
return;
58-
}
59-
if (const RecordDecl *RD = T->getAsRecordDecl()) {
60-
Valid = true;
61-
FieldsIt = RD->field_begin();
62-
FieldsEnd = RD->field_end();
63-
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) {
64-
BasesIt = CRD->bases_begin();
65-
BasesEnd = CRD->bases_end();
66-
Valid = CRD->isAggregate();
67-
}
68-
OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd &&
69-
std::next(FieldsIt) == FieldsEnd;
70-
}
71-
}
72-
}
73-
// Returns false if the type was not an aggregate.
74-
operator bool() { return Valid; }
75-
// Advance to the next element in the aggregate.
76-
void next() {
77-
if (IsArray)
78-
++Index;
79-
else if (BasesIt != BasesEnd)
80-
++BasesIt;
81-
else if (FieldsIt != FieldsEnd)
82-
++FieldsIt;
83-
}
84-
// Print the designator to Out.
85-
// Returns false if we could not produce a designator for this element.
86-
bool append(std::string &Out, bool ForSubobject) {
87-
if (IsArray) {
88-
Out.push_back('[');
89-
Out.append(std::to_string(Index));
90-
Out.push_back(']');
91-
return true;
92-
}
93-
if (BasesIt != BasesEnd)
94-
return false; // Bases can't be designated. Should we make one up?
95-
if (FieldsIt != FieldsEnd) {
96-
llvm::StringRef FieldName;
97-
if (const IdentifierInfo *II = FieldsIt->getIdentifier())
98-
FieldName = II->getName();
99-
100-
// For certain objects, their subobjects may be named directly.
101-
if (ForSubobject &&
102-
(FieldsIt->isAnonymousStructOrUnion() ||
103-
// std::array<int,3> x = {1,2,3}. Designators not strictly valid!
104-
(OneField && isReservedName(FieldName))))
105-
return true;
106-
107-
if (!FieldName.empty() && !isReservedName(FieldName)) {
108-
Out.push_back('.');
109-
Out.append(FieldName.begin(), FieldName.end());
110-
return true;
111-
}
112-
return false;
113-
}
114-
return false;
115-
}
116-
117-
private:
118-
bool Valid = false;
119-
bool IsArray = false;
120-
bool OneField = false; // e.g. std::array { T __elements[N]; }
121-
unsigned Index = 0;
122-
CXXRecordDecl::base_class_const_iterator BasesIt;
123-
CXXRecordDecl::base_class_const_iterator BasesEnd;
124-
RecordDecl::field_iterator FieldsIt;
125-
RecordDecl::field_iterator FieldsEnd;
126-
};
127-
128-
// Collect designator labels describing the elements of an init list.
129-
//
130-
// This function contributes the designators of some (sub)object, which is
131-
// represented by the semantic InitListExpr Sem.
132-
// This includes any nested subobjects, but *only* if they are part of the same
133-
// original syntactic init list (due to brace elision).
134-
// In other words, it may descend into subobjects but not written init-lists.
135-
//
136-
// For example: struct Outer { Inner a,b; }; struct Inner { int x, y; }
137-
// Outer o{{1, 2}, 3};
138-
// This function will be called with Sem = { {1, 2}, {3, ImplicitValue} }
139-
// It should generate designators '.a:' and '.b.x:'.
140-
// '.a:' is produced directly without recursing into the written sublist.
141-
// (The written sublist will have a separate collectDesignators() call later).
142-
// Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'.
143-
void collectDesignators(const InitListExpr *Sem,
144-
llvm::DenseMap<SourceLocation, std::string> &Out,
145-
const llvm::DenseSet<SourceLocation> &NestedBraces,
146-
std::string &Prefix) {
147-
if (!Sem || Sem->isTransparent())
148-
return;
149-
assert(Sem->isSemanticForm());
150-
151-
// The elements of the semantic form all correspond to direct subobjects of
152-
// the aggregate type. `Fields` iterates over these subobject names.
153-
AggregateDesignatorNames Fields(Sem->getType());
154-
if (!Fields)
155-
return;
156-
for (const Expr *Init : Sem->inits()) {
157-
auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] {
158-
Fields.next(); // Always advance to the next subobject name.
159-
Prefix.resize(Size); // Erase any designator we appended.
160-
});
161-
// Skip for a broken initializer or if it is a "hole" in a subobject that
162-
// was not explicitly initialized.
163-
if (!Init || llvm::isa<ImplicitValueInitExpr>(Init))
164-
continue;
165-
166-
const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init);
167-
if (BraceElidedSubobject &&
168-
NestedBraces.contains(BraceElidedSubobject->getLBraceLoc()))
169-
BraceElidedSubobject = nullptr; // there were braces!
170-
171-
if (!Fields.append(Prefix, BraceElidedSubobject != nullptr))
172-
continue; // no designator available for this subobject
173-
if (BraceElidedSubobject) {
174-
// If the braces were elided, this aggregate subobject is initialized
175-
// inline in the same syntactic list.
176-
// Descend into the semantic list describing the subobject.
177-
// (NestedBraces are still correct, they're from the same syntactic list).
178-
collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix);
179-
continue;
180-
}
181-
Out.try_emplace(Init->getBeginLoc(), Prefix);
182-
}
183-
}
184-
185-
// Get designators describing the elements of a (syntactic) init list.
186-
// This does not produce designators for any explicitly-written nested lists.
187-
llvm::DenseMap<SourceLocation, std::string>
188-
getDesignators(const InitListExpr *Syn) {
189-
assert(Syn->isSyntacticForm());
190-
191-
// collectDesignators needs to know which InitListExprs in the semantic tree
192-
// were actually written, but InitListExpr::isExplicit() lies.
193-
// Instead, record where braces of sub-init-lists occur in the syntactic form.
194-
llvm::DenseSet<SourceLocation> NestedBraces;
195-
for (const Expr *Init : Syn->inits())
196-
if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init))
197-
NestedBraces.insert(Nested->getLBraceLoc());
198-
199-
// Traverse the semantic form to find the designators.
200-
// We use their SourceLocation to correlate with the syntactic form later.
201-
llvm::DenseMap<SourceLocation, std::string> Designators;
202-
std::string EmptyPrefix;
203-
collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(),
204-
Designators, NestedBraces, EmptyPrefix);
205-
return Designators;
206-
}
207-
20845
void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim('_'); }
20946

21047
// getDeclForType() returns the decl responsible for Type's spelling.
@@ -854,7 +691,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
854691
if (Syn->isIdiomaticZeroInitializer(AST.getLangOpts()))
855692
return true;
856693
llvm::DenseMap<SourceLocation, std::string> Designators =
857-
getDesignators(Syn);
694+
clang::tooling::getDesignators(Syn);
858695
for (const Expr *Init : Syn->inits()) {
859696
if (llvm::isa<DesignatedInitExpr>(Init))
860697
continue;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===--- DesignatedInitializers.h -------------------------------*- C++ -*-===//
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 "clang/AST/Expr.h"
15+
#include "clang/Basic/SourceLocation.h"
16+
#include "llvm/ADT/DenseMap.h"
17+
18+
namespace clang::tooling {
19+
20+
/// Get designators describing the elements of a (syntactic) init list.
21+
///
22+
/// Given for example the type
23+
///
24+
/// struct S { int i, j; };
25+
///
26+
/// and the definition
27+
///
28+
/// S s{1, 2};
29+
///
30+
/// calling `getDesignators` for the initializer list expression `{1, 2}`
31+
/// would produce the map `{loc(1): ".i", loc(2): ".j"}`.
32+
///
33+
/// It does not produce designators for any explicitly-written nested lists,
34+
/// e.g. `{1, .j=2}` would only return `{loc(1): ".i"}`.
35+
///
36+
/// It also considers structs with fields of record types like
37+
/// `struct T { S s; };`. In this case, there would be designators of the
38+
/// form
39+
/// `.s.i` and `.s.j` in the returned map.
40+
llvm::DenseMap<clang::SourceLocation, std::string>
41+
getDesignators(const clang::InitListExpr *Syn);
42+
43+
} // namespace clang::tooling

clang/lib/Tooling/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ add_clang_library(clangTooling
106106
ArgumentsAdjusters.cpp
107107
CommonOptionsParser.cpp
108108
CompilationDatabase.cpp
109+
DesignatedInitializers.cpp
109110
Execution.cpp
110111
ExpandResponseFilesCompilationDatabase.cpp
111112
FileMatchTrie.cpp

0 commit comments

Comments
 (0)