Skip to content

Commit f18b946

Browse files
committed
Avoid visiting field initializers in aggregate records
In aggregates, the field initializer may be never invoked in which case we do not need to visit and warn on it. FIXMEs are left in place for aggregates where the field initializer is used, but there's currently no warning.
1 parent 324fc76 commit f18b946

File tree

2 files changed

+96
-24
lines changed

2 files changed

+96
-24
lines changed

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,10 +2512,29 @@ class CallableVisitor : public RecursiveASTVisitor<CallableVisitor> {
25122512

25132513
bool VisitFieldDecl(FieldDecl *Node) {
25142514
DeclContext *DeclCtx;
2515-
if (auto *ID = dyn_cast<ObjCIvarDecl>(Node))
2515+
if (auto *ID = dyn_cast<ObjCIvarDecl>(Node)) {
25162516
DeclCtx = cast<DeclContext>(ID->getContainingInterface());
2517-
else
2518-
DeclCtx = cast<DeclContext>(Node->getParent());
2517+
} else {
2518+
RecordDecl *RD = Node->getParent();
2519+
if (auto *CCRD = dyn_cast<CXXRecordDecl>(RD);
2520+
CCRD && CCRD->isAggregate()) {
2521+
// Aggregates have implicitly generated constructors which are not
2522+
// traversed by our AST visitors at this time. The field initializers in
2523+
// an aggregate may never be used if the callers always explicitly
2524+
// initialize them, in which case we do not need to warn on unsafe
2525+
// buffer usage in the initializer.
2526+
//
2527+
// FIXME: We should go through the implicit aggregate initialization
2528+
// (either an implicit CXXConstructExpr for value-init or an implicit
2529+
// ListInitExpr for aggregate-init) to determine if a field's default
2530+
// initializer (CXXDefaultInitExpr) is actually used. If it is, then we
2531+
// should visit it, while retaining a reference to the caller for
2532+
// showing the path to the use of the default initializer in the
2533+
// warning.
2534+
return true;
2535+
}
2536+
DeclCtx = cast<DeclContext>(RD);
2537+
}
25192538
if (DeclCtx->isDependentContext())
25202539
return true; // Not to analyze dependent decl
25212540
if (Node->hasInClassInitializer())

clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,6 @@ struct SubclassUnsafeMembers : public UnsafeMembers {
142142
{}
143143
};
144144

145-
// Without an explicit constructor, there is no CXXDefaultInitExpr to anchor on
146-
// in matchers.
147-
struct AggregateUnsafeMembers {
148-
UnsafeMembers f1;
149-
UnsafeMembers f2{3}; // expected-warning{{function introduces unsafe buffer manipulation}}
150-
};
151-
152145
// https://github.com/llvm/llvm-project/issues/80482
153146
void testClassMembers() {
154147
UnsafeMembers(3); // expected-warning{{function introduces unsafe buffer manipulation}}
@@ -166,22 +159,10 @@ void testClassMembers() {
166159

167160
SubclassUnsafeMembers();
168161
SubclassUnsafeMembers(3); // expected-warning{{function introduces unsafe buffer manipulation}}
169-
170-
(void)AggregateUnsafeMembers{
171-
.f1 = UnsafeMembers(3), // expected-warning{{function introduces unsafe buffer manipulation}}
172-
};
173-
174-
(void)AggregateUnsafeMembers(3); // expected-warning{{function introduces unsafe buffer manipulation}}
175-
176-
// FIXME: This should generate a warning but InitListExpr construction is
177-
// treated as calling an implicit constructor, while ParentListInitExpr (the
178-
// one above) is not. So `MatchDescendantVisitor::shouldVisitImplicitCode()`
179-
// must return true for this to generate a warning. However that moves the
180-
// SourceLocation of warnings from field initializers that construct through
181-
// InitListExpr, preventing us from matching warnings against them.
182-
(void)AggregateUnsafeMembers{3};
183162
}
184163

164+
// Not an aggregate, so its constructor is not implicit code and will be
165+
// visited/checked for warnings.
185166
struct NotCalledHoldsUnsafeMembers {
186167
NotCalledHoldsUnsafeMembers()
187168
: FromCtor(3), // expected-warning{{function introduces unsafe buffer manipulation}}
@@ -192,3 +173,75 @@ struct NotCalledHoldsUnsafeMembers {
192173
UnsafeMembers FromCtor2;
193174
UnsafeMembers FromField{3}; // expected-warning{{function introduces unsafe buffer manipulation}}
194175
};
176+
177+
// An aggregate, so its constructor is implicit code. Since it's not called, it
178+
// is never generated.
179+
struct AggregateUnused {
180+
UnsafeMembers f1;
181+
// While this field would trigger the warning during initialization, since
182+
// it's unused, there's no code generated that does the initialization, so
183+
// no warning.
184+
UnsafeMembers f2{3};
185+
};
186+
187+
struct AggregateExplicitlyInitializedSafe {
188+
UnsafeMembers f1;
189+
// The warning is not fired as the field is always explicltly initialized
190+
// elsewhere. This initializer is never used.
191+
UnsafeMembers f2{3};
192+
};
193+
194+
void testAggregateExplicitlyInitializedSafe() {
195+
AggregateExplicitlyInitializedSafe A{
196+
.f2 = UnsafeMembers(), // A safe constructor.
197+
};
198+
}
199+
200+
struct AggregateExplicitlyInitializedUnsafe {
201+
UnsafeMembers f1;
202+
// The warning is not fired as the field is always explicltly initialized
203+
// elsewhere. This initializer is never used.
204+
UnsafeMembers f2{3};
205+
};
206+
207+
void testAggregateExplicitlyInitializedUnsafe() {
208+
AggregateExplicitlyInitializedUnsafe A{
209+
.f2 = UnsafeMembers(3), // expected-warning{{function introduces unsafe buffer manipulation}}
210+
};
211+
}
212+
213+
struct AggregateViaAggregateInit {
214+
UnsafeMembers f1;
215+
// FIXME: A construction of this class does initialize the field through
216+
// this initializer, so it should warn. Ideally it should also point to
217+
// where the site of the construction is in testAggregateViaAggregateInit().
218+
UnsafeMembers f2{3};
219+
};
220+
221+
void testAggregateViaAggregateInit() {
222+
AggregateViaAggregateInit A{};
223+
};
224+
225+
struct AggregateViaValueInit {
226+
UnsafeMembers f1;
227+
// FIXME: A construction of this class does initialize the field through
228+
// this initializer, so it should warn. Ideally it should also point to
229+
// where the site of the construction is in testAggregateViaValueInit().
230+
UnsafeMembers f2{3};
231+
};
232+
233+
void testAggregateViaValueInit() {
234+
auto A = AggregateViaValueInit();
235+
};
236+
237+
struct AggregateViaDefaultInit {
238+
UnsafeMembers f1;
239+
// FIXME: A construction of this class does initialize the field through
240+
// this initializer, so it should warn. Ideally it should also point to
241+
// where the site of the construction is in testAggregateViaValueInit().
242+
UnsafeMembers f2{3};
243+
};
244+
245+
void testAggregateViaDefaultInit() {
246+
AggregateViaDefaultInit A;
247+
};

0 commit comments

Comments
 (0)