Skip to content

Commit ba2f02d

Browse files
committed
Reverse-null surface-level fixes
1 parent 4a8e0df commit ba2f02d

File tree

6 files changed

+82
-138
lines changed

6 files changed

+82
-138
lines changed

clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,18 @@ analyzeFunction(const FunctionDecl &FuncDecl) {
6666
NullCheckAfterDereferenceDiagnoser Diagnoser;
6767
NullCheckAfterDereferenceDiagnoser::ResultType Diagnostics;
6868

69-
using LatticeState = DataflowAnalysisState<NullPointerAnalysisModel::Lattice>;
70-
using DetailMaybeLatticeStates = std::vector<std::optional<LatticeState>>;
69+
using State = DataflowAnalysisState<NullPointerAnalysisModel::Lattice>;
70+
using DetailMaybeStates = std::vector<std::optional<State>>;
7171

7272
auto DiagnoserImpl = [&ASTCtx, &Diagnoser,
7373
&Diagnostics](const CFGElement &Elt,
74-
const LatticeState &S) mutable -> void {
74+
const State &S) mutable -> void {
7575
auto EltDiagnostics = Diagnoser.diagnose(ASTCtx, &Elt, S.Env);
7676
llvm::move(EltDiagnostics.first, std::back_inserter(Diagnostics.first));
7777
llvm::move(EltDiagnostics.second, std::back_inserter(Diagnostics.second));
7878
};
7979

80-
Expected<DetailMaybeLatticeStates> BlockToOutputState =
80+
Expected<DetailMaybeStates> BlockToOutputState =
8181
dataflow::runDataflowAnalysis(*Context, Analysis, Env, DiagnoserImpl);
8282

8383
if (llvm::Error E = BlockToOutputState.takeError()) {
@@ -116,16 +116,16 @@ analyzeFunction(const FunctionDecl &FuncDecl) {
116116
void NullCheckAfterDereferenceCheck::registerMatchers(MatchFinder *Finder) {
117117
using namespace ast_matchers;
118118

119-
auto hasPointerValue =
119+
auto containsPointerValue =
120120
hasDescendant(NullPointerAnalysisModel::ptrValueMatcher());
121121
Finder->addMatcher(
122122
decl(anyOf(functionDecl(unless(isExpansionInSystemHeader()),
123123
// FIXME: Remove the filter below when lambdas are
124124
// well supported by the check.
125125
unless(hasDeclContext(cxxRecordDecl(isLambda()))),
126-
hasBody(hasPointerValue)),
126+
hasBody(containsPointerValue)),
127127
cxxConstructorDecl(hasAnyConstructorInitializer(
128-
withInitializer(hasPointerValue)))))
128+
withInitializer(containsPointerValue)))))
129129
.bind(FuncID),
130130
this);
131131
}
@@ -141,10 +141,10 @@ void NullCheckAfterDereferenceCheck::check(
141141
return;
142142

143143
if (const auto Diagnostics = analyzeFunction(*FuncDecl)) {
144-
const auto &[CheckWhenNullLocations, CheckAfterDereferenceLocations] =
144+
const auto &[CheckWhenNullLocations, CheckWhenNonnullLocations] =
145145
*Diagnostics;
146146

147-
for (const auto [WarningLoc, DerefLoc] : CheckAfterDereferenceLocations) {
147+
for (const auto [WarningLoc, DerefLoc] : CheckWhenNonnullLocations) {
148148
diag(WarningLoc, "pointer value is checked even though "
149149
"it cannot be null at this point");
150150

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ New checks
148148
Finds cases when an uninstantiated virtual member function in a template class
149149
causes cross-compiler incompatibility.
150150

151+
- New :doc:`bugprone-null-check-after-dereference
152+
<clang-tidy/checks/bugprone/null-check-after-dereference>` check.
153+
154+
Finds locations where a pointer's nullability is checked after it has already
155+
been dereferenced.
156+
151157
New check aliases
152158
^^^^^^^^^^^^^^^^^
153159

clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ bugprone-null-check-after-dereference
77

88
This check uses a flow-sensitive static analysis to produce its
99
results. Therefore, it may be more resource intensive (RAM, CPU) than the
10-
average clang-tidy check.
10+
average Clang-tidy check.
1111

1212
This check identifies redundant pointer null-checks, by finding cases where the
1313
pointer cannot be null at the location of the null-check.
@@ -33,9 +33,9 @@ Supported pointer operations
3333
Pointer null-checks
3434
-------------------
3535

36-
The checker currently supports null-checks on pointers that use
36+
The check currently supports null-checks on pointers that use
3737
``operator bool``, such as when being used as the condition
38-
for an `if` statement.
38+
for an ``if`` statement.
3939

4040
.. code-block:: c++
4141

@@ -69,8 +69,8 @@ Pointer star- and arrow-dereferences are supported.
6969
Null-pointer and other value assignments
7070
----------------------------------------
7171

72-
The checker supports assigning various values to pointers, making them *null*
73-
or *non-null*. The checker also supports passing pointers of a pointer to
72+
The check supports assigning various values to pointers, making them *null*
73+
or *non-null*. The check also supports passing pointers of a pointer to
7474
external functions.
7575

7676
.. code-block:: c++
@@ -103,7 +103,7 @@ Limitations
103103

104104
The check only supports C++ due to limitations in the data-flow framework.
105105

106-
The annotations ``_nullable`` and ``_nonnull`` are not supported.
106+
The annotations ``_Nullable`` and ``_Nonnull`` are not supported.
107107

108108
.. code-block:: c++
109109

clang-tools-extra/test/clang-tidy/checkers/bugprone/null-check-after-dereference.cpp

Lines changed: 23 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ int else_branch_warning(int *p, bool b) {
7070
// CHECK-MESSAGES: :[[@LINE-7]]:5: note: one of the locations where the pointer's value cannot be null
7171
return 0;
7272
} else {
73-
*p += 20;
7473
return *p;
7574
}
7675
}
@@ -89,39 +88,17 @@ int two_branches_warning(int *p, bool b) {
8988
// CHECK-MESSAGES: :[[@LINE-9]]:5: note: one of the locations where the pointer's value cannot be null
9089
return 0;
9190
} else {
92-
*p += 20;
9391
return *p;
9492
}
9593
}
9694

97-
int two_branches_reversed(int *p, bool b) {
98-
if (!b) {
99-
*p = 42;
100-
}
101-
102-
if (b) {
103-
*p = 20;
104-
}
105-
106-
if (p) {
107-
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point
108-
// CHECK-MESSAGES: :[[@LINE-9]]:5: note: one of the locations where the pointer's value cannot be null
109-
return 0;
110-
} else {
111-
*p += 20;
112-
return *p;
113-
}
114-
}
115-
116-
11795
int regular_assignment(int *p, int *q) {
11896
*p = 42;
11997
q = p;
12098

12199
if (q) {
122100
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point
123101
// CHECK-MESSAGES: :[[@LINE-5]]:3: note: one of the locations where the pointer's value cannot be null
124-
*p += 20;
125102
return *p;
126103
} else {
127104
return 0;
@@ -139,29 +116,26 @@ int nullptr_assignment(int *nullptr_param, bool b) {
139116
}
140117

141118
if (nullptr_assigned) {
142-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point
143-
*nullptr_assigned = 20;
144119
return *nullptr_assigned;
145120
} else {
146121
return 0;
147122
}
148123
}
149124

150-
extern int *fncall();
151-
extern void refresh_ref(int *&ptr);
152-
extern void refresh_ptr(int **ptr);
125+
extern int *external_fn();
126+
extern void ref_fn(int *&ptr);
127+
extern void ptr_fn(int **ptr);
153128

154129
int fncall_reassignment(int *fncall_reassigned) {
155130
*fncall_reassigned = 42;
156131

157-
fncall_reassigned = fncall();
132+
fncall_reassigned = external_fn();
158133

159134
if (fncall_reassigned) {
160-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point
161135
*fncall_reassigned = 42;
162136
}
163137

164-
fncall_reassigned = fncall();
138+
fncall_reassigned = external_fn();
165139

166140
*fncall_reassigned = 42;
167141

@@ -171,19 +145,33 @@ int fncall_reassignment(int *fncall_reassigned) {
171145
*fncall_reassigned = 42;
172146
}
173147

174-
refresh_ptr(&fncall_reassigned);
148+
ptr_fn(&fncall_reassigned);
175149

176150
if (fncall_reassigned) {
177-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point
151+
// FIXME: References of a pointer passed to external functions do not invalidate its value
152+
// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: pointer value is checked even though it cannot be null at this point
153+
// CHECK-MESSAGES: :[[@LINE-8]]:5: note: one of the locations where the pointer's value cannot be null
154+
*fncall_reassigned = 42;
155+
}
156+
157+
*fncall_reassigned = 42;
158+
159+
ref_fn(fncall_reassigned);
160+
161+
if (fncall_reassigned) {
162+
// FIXME: References of a pointer passed to external functions do not invalidate its value
163+
// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: pointer value is checked even though it cannot be null at this point
164+
// CHECK-MESSAGES: :[[@LINE-19]]:5: note: one of the locations where the pointer's value cannot be null
178165
*fncall_reassigned = 42;
179166
}
180167

181-
refresh_ptr(&fncall_reassigned);
168+
ptr_fn(&fncall_reassigned);
182169
*fncall_reassigned = 42;
183170

184171
if (fncall_reassigned) {
185172
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point
186-
// CHECK-MESSAGES: :[[@LINE-4]]:3: note: one of the locations where the pointer's value cannot be null
173+
// FIXME: Better note tag support, preferably after the reassignment/refresh
174+
// CHECK-MESSAGES: :[[@LINE-29]]:5: note: one of the locations where the pointer's value cannot be null
187175
*fncall_reassigned = 42;
188176
return *fncall_reassigned;
189177
} else {
@@ -294,33 +282,6 @@ int cxx17_crash(int *p) {
294282
return 0;
295283
}
296284

297-
void external_by_ref(int *&p);
298-
void external_by_ptr(int **p);
299-
300-
int external_invalidates() {
301-
int *p = nullptr;
302-
303-
external_by_ref(p);
304-
305-
if (p) {
306-
// FIXME: References of a pointer passed to external functions do not invalidate its value
307-
// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: pointer value is checked but it can only be null at this point
308-
return *p;
309-
}
310-
311-
p = nullptr;
312-
313-
external_by_ptr(&p);
314-
315-
if (p) {
316-
// FIXME: References of a pointer passed to external functions do not invalidate its value
317-
// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: pointer value is checked but it can only be null at this point
318-
return *p;
319-
} else {
320-
return 0;
321-
}
322-
}
323-
324285
int note_tags() {
325286
// FIXME: Note tags are not appended to declarations
326287
int *ptr = nullptr;

clang/include/clang/Analysis/FlowSensitive/Models/NullPointerAnalysisModel.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ class NullPointerAnalysisModel
5858

5959
static ast_matchers::StatementMatcher ptrValueMatcher();
6060

61-
// Used to initialize the storage locations of function arguments.
62-
// Required to merge these values within the environment.
63-
void initializeFunctionParameters(const ControlFlowContext &CFCtx,
64-
Environment &Env);
65-
6661
void transfer(const CFGElement &E, NoopLattice &State, Environment &Env);
6762

6863
void transferBranch(bool Branch, const Stmt *E, NoopLattice &State,
@@ -88,6 +83,8 @@ class NullCheckAfterDereferenceDiagnoser {
8883
const Environment &Env;
8984
};
9085

86+
/// Checked when known to be null, and checked after already dereferenced,
87+
/// respectively.
9188
using ResultType =
9289
std::pair<std::vector<SourceLocation>, std::vector<SourceLocation>>;
9390

0 commit comments

Comments
 (0)