File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -1294,6 +1294,18 @@ class TransferFunctions {
12941294 if (const auto *VD = dyn_cast<VarDecl>(DRE ->getDecl ()))
12951295 return isVarNullable (VD );
12961296 }
1297+ // Member narrowing: this->member or var.member narrowed by null check
1298+ if (const auto *ME = dyn_cast<MemberExpr>(E)) {
1299+ if (const auto *FD = dyn_cast<FieldDecl>(ME ->getMemberDecl ())) {
1300+ const Expr *Base = ME ->getBase ()->IgnoreParenImpCasts ();
1301+ if (isa<CXXThisExpr>(Base) && isThisMemberNarrowed (FD ))
1302+ return false ;
1303+ if (const auto *BaseDRE = dyn_cast<DeclRefExpr>(Base))
1304+ if (const auto *BaseVD = dyn_cast<VarDecl>(BaseDRE->getDecl ()))
1305+ if (isMemberNarrowed (BaseVD, FD ))
1306+ return false ;
1307+ }
1308+ }
12971309 // For non-variable expressions, fall back to type-based check
12981310 if (isNullableType (E->getType (), StrictMode, DefaultNullability))
12991311 return true ;
Original file line number Diff line number Diff line change @@ -367,6 +367,18 @@ struct MemberNarrowObj {
367367 if (arr == nullptr ) return ;
368368 (void )arr[getMember ()]; // OK - function call as index doesn't affect base narrowing
369369 }
370+
371+ // Passing narrowed member to _Nonnull parameter
372+ static void accept_nonnull (int * _Nonnull p);
373+ void test_member_nonnull_arg_after_check () {
374+ if (arr == nullptr ) return ;
375+ accept_nonnull (arr); // OK - member narrowed, safe to pass to _Nonnull
376+ }
377+
378+ void test_member_nonnull_arg_no_check () {
379+ accept_nonnull (arr); // expected-warning {{nullable pointer}} \
380+ // expected-note {{add a null check}}
381+ }
370382};
371383
372384// --- sizeof/alignof don't dereference ---
You can’t perform that action at this time.
0 commit comments