Skip to content

Commit 275196d

Browse files
authored
[clang][nullability] Don't return null fields from getReferencedDecls(). (#94983)
The patch includes a repro for a case where we were returning a null `FieldDecl` when calling `getReferencedDecls()` on the `InitListExpr` for a union. Also, I noticed while working on this that `RecordInitListHelper` has a bug where it doesn't work correctly for empty unions. This patch also includes a repro and fix for this bug.
1 parent 05e87a5 commit 275196d

File tree

5 files changed

+98
-4
lines changed

5 files changed

+98
-4
lines changed

clang/docs/tools/clang-formatted-files.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ clang/tools/libclang/CXCursor.h
622622
clang/tools/scan-build-py/tests/functional/src/include/clean-one.h
623623
clang/unittests/Analysis/CFGBuildResult.h
624624
clang/unittests/Analysis/MacroExpansionContextTest.cpp
625+
clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
625626
clang/unittests/Analysis/FlowSensitive/CNFFormula.cpp
626627
clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
627628
clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp

clang/lib/Analysis/FlowSensitive/ASTOps.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ getFieldsForInitListExpr(const InitListT *InitList) {
100100
std::vector<const FieldDecl *> Fields;
101101

102102
if (InitList->getType()->isUnionType()) {
103-
Fields.push_back(InitList->getInitializedFieldInUnion());
103+
if (const FieldDecl *Field = InitList->getInitializedFieldInUnion())
104+
Fields.push_back(Field);
104105
return Fields;
105106
}
106107

@@ -137,9 +138,11 @@ RecordInitListHelper::RecordInitListHelper(
137138
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
138139
SmallVector<Expr *> InitsForUnion;
139140
if (Ty->isUnionType() && Inits.empty()) {
140-
assert(Fields.size() == 1);
141-
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
142-
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
141+
assert(Fields.size() <= 1);
142+
if (!Fields.empty()) {
143+
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
144+
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
145+
}
143146
Inits = InitsForUnion;
144147
}
145148

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//===- unittests/Analysis/FlowSensitive/ASTOpsTest.cpp --------------------===//
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+
#include "clang/Analysis/FlowSensitive/ASTOps.h"
10+
#include "TestingSupport.h"
11+
#include "gmock/gmock.h"
12+
#include "gtest/gtest.h"
13+
#include <memory>
14+
15+
namespace {
16+
17+
using namespace clang;
18+
using namespace dataflow;
19+
20+
using ast_matchers::cxxRecordDecl;
21+
using ast_matchers::hasName;
22+
using ast_matchers::hasType;
23+
using ast_matchers::initListExpr;
24+
using ast_matchers::match;
25+
using ast_matchers::selectFirst;
26+
using test::findValueDecl;
27+
using testing::IsEmpty;
28+
using testing::UnorderedElementsAre;
29+
30+
TEST(ASTOpsTest, RecordInitListHelperOnEmptyUnionInitList) {
31+
// This is a regression test: The `RecordInitListHelper` used to assert-fail
32+
// when called for the `InitListExpr` of an empty union.
33+
std::string Code = R"cc(
34+
struct S {
35+
S() : UField{} {};
36+
37+
union U {} UField;
38+
};
39+
)cc";
40+
std::unique_ptr<ASTUnit> Unit =
41+
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
42+
auto &ASTCtx = Unit->getASTContext();
43+
44+
ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);
45+
46+
auto *InitList = selectFirst<InitListExpr>(
47+
"init",
48+
match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"),
49+
ASTCtx));
50+
ASSERT_NE(InitList, nullptr);
51+
52+
RecordInitListHelper Helper(InitList);
53+
EXPECT_THAT(Helper.base_inits(), IsEmpty());
54+
EXPECT_THAT(Helper.field_inits(), IsEmpty());
55+
}
56+
57+
TEST(ASTOpsTest, ReferencedDeclsOnUnionInitList) {
58+
// This is a regression test: `getReferencedDecls()` used to return a null
59+
// `FieldDecl` in this case (in addition to the correct non-null `FieldDecl`)
60+
// because `getInitializedFieldInUnion()` returns null for the syntactic form
61+
// of the `InitListExpr`.
62+
std::string Code = R"cc(
63+
struct S {
64+
S() : UField{0} {};
65+
66+
union U {
67+
int I;
68+
} UField;
69+
};
70+
)cc";
71+
std::unique_ptr<ASTUnit> Unit =
72+
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
73+
auto &ASTCtx = Unit->getASTContext();
74+
75+
ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);
76+
77+
auto *InitList = selectFirst<InitListExpr>(
78+
"init",
79+
match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"),
80+
ASTCtx));
81+
ASSERT_NE(InitList, nullptr);
82+
auto *IDecl = cast<FieldDecl>(findValueDecl(ASTCtx, "I"));
83+
84+
EXPECT_THAT(getReferencedDecls(*InitList).Fields,
85+
UnorderedElementsAre(IDecl));
86+
}
87+
88+
} // namespace

clang/unittests/Analysis/FlowSensitive/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
55

66
add_clang_unittest(ClangAnalysisFlowSensitiveTests
77
ArenaTest.cpp
8+
ASTOpsTest.cpp
89
CFGMatchSwitchTest.cpp
910
ChromiumCheckModelTest.cpp
1011
DataflowAnalysisContextTest.cpp

llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ unittest("ClangAnalysisFlowSensitiveTests") {
1818
"//llvm/lib/Testing/Support",
1919
]
2020
sources = [
21+
"ASTOpsTest.cpp",
2122
"ArenaTest.cpp",
2223
"CFGMatchSwitchTest.cpp",
2324
"ChromiumCheckModelTest.cpp",

0 commit comments

Comments
 (0)