Skip to content

Commit f58b318

Browse files
committed
fix: orNull-suffixed factories honour their null-return contract
`fromOrNull`, `fromJsonStringOrNull`, and `fromUriOrNull` were violating their named contract by either: - using `!` on a nullable downstream call (latent NPE), or - returning `assertRequired()` on the empty/null input case, which throws instead of returning null. After the fix: - fromOrNull(null) → null - fromJsonStringOrNull(null | '') → null - fromUriOrNull(null | wrongPath) → null Regenerated all integration test fixtures with the updated template.
1 parent 072c569 commit f58b318

26 files changed

Lines changed: 182 additions & 312 deletions

templates/featured_v1.dart.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ class ___CLASS_NAME___ extends ___SUPER_CLASS_NAME___ ___WITH_EQUATABLE___{
8181
static ___CLASS_NAME___? fromOrNull(
8282
BaseModel? another,
8383
) {
84-
return fromJsonOrNull(another?.toJson())!;
84+
if (another == null) return null;
85+
return fromJsonOrNull(another.toJson());
8586
}
8687
8788
@@ -128,13 +129,10 @@ class ___CLASS_NAME___ extends ___SUPER_CLASS_NAME___ ___WITH_EQUATABLE___{
128129
static ___CLASS_NAME___? fromJsonStringOrNull(
129130
String? jsonString,
130131
) {
132+
if (jsonString == null || jsonString.isEmpty) return null;
131133
try {
132-
if (jsonString!.isNotEmpty) {
133-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
134-
return ___CLASS_NAME___.fromJson(decoded);
135-
} else {
136-
return ___CLASS_NAME___.assertRequired();
137-
}
134+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
135+
return ___CLASS_NAME___.fromJsonOrNull(decoded);
138136
} catch (_) {
139137
return null;
140138
}
@@ -190,12 +188,9 @@ class ___CLASS_NAME___ extends ___SUPER_CLASS_NAME___ ___WITH_EQUATABLE___{
190188
static ___CLASS_NAME___? fromUriOrNull(
191189
Uri? uri,
192190
) {
191+
if (uri == null || uri.path != CLASS_NAME) return null;
193192
try {
194-
if (uri != null && uri.path == CLASS_NAME) {
195-
return ___CLASS_NAME___.fromJson(uri.queryParameters);
196-
} else {
197-
return ___CLASS_NAME___.assertRequired();
198-
}
193+
return ___CLASS_NAME___.fromJsonOrNull(uri.queryParameters);
199194
} catch (_) {
200195
return null;
201196
}

test/integration/models/_model_base.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ metadata: metadata,
164164
static ModelBase? fromOrNull(
165165
BaseModel? another,
166166
) {
167-
return fromJsonOrNull(another?.toJson())!;
167+
if (another == null) return null;
168+
return fromJsonOrNull(another.toJson());
168169
}
169170

170171

@@ -211,13 +212,10 @@ metadata: metadata,
211212
static ModelBase? fromJsonStringOrNull(
212213
String? jsonString,
213214
) {
215+
if (jsonString == null || jsonString.isEmpty) return null;
214216
try {
215-
if (jsonString!.isNotEmpty) {
216-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
217-
return ModelBase.fromJson(decoded);
218-
} else {
219-
return ModelBase.assertRequired();
220-
}
217+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
218+
return ModelBase.fromJsonOrNull(decoded);
221219
} catch (_) {
222220
return null;
223221
}
@@ -291,12 +289,9 @@ metadata: metadata,
291289
static ModelBase? fromUriOrNull(
292290
Uri? uri,
293291
) {
292+
if (uri == null || uri.path != CLASS_NAME) return null;
294293
try {
295-
if (uri != null && uri.path == CLASS_NAME) {
296-
return ModelBase.fromJson(uri.queryParameters);
297-
} else {
298-
return ModelBase.assertRequired();
299-
}
294+
return ModelBase.fromJsonOrNull(uri.queryParameters);
300295
} catch (_) {
301296
return null;
302297
}

test/integration/models/_model_comment.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ createdAt: createdAt,
124124
static ModelComment? fromOrNull(
125125
BaseModel? another,
126126
) {
127-
return fromJsonOrNull(another?.toJson())!;
127+
if (another == null) return null;
128+
return fromJsonOrNull(another.toJson());
128129
}
129130

130131

@@ -171,13 +172,10 @@ createdAt: createdAt,
171172
static ModelComment? fromJsonStringOrNull(
172173
String? jsonString,
173174
) {
175+
if (jsonString == null || jsonString.isEmpty) return null;
174176
try {
175-
if (jsonString!.isNotEmpty) {
176-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
177-
return ModelComment.fromJson(decoded);
178-
} else {
179-
return ModelComment.assertRequired();
180-
}
177+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
178+
return ModelComment.fromJsonOrNull(decoded);
181179
} catch (_) {
182180
return null;
183181
}
@@ -241,12 +239,9 @@ createdAt: createdAt,
241239
static ModelComment? fromUriOrNull(
242240
Uri? uri,
243241
) {
242+
if (uri == null || uri.path != CLASS_NAME) return null;
244243
try {
245-
if (uri != null && uri.path == CLASS_NAME) {
246-
return ModelComment.fromJson(uri.queryParameters);
247-
} else {
248-
return ModelComment.assertRequired();
249-
}
244+
return ModelComment.fromJsonOrNull(uri.queryParameters);
250245
} catch (_) {
251246
return null;
252247
}

test/integration/models/_model_firestore_doc.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ legacyStamp: legacyStamp,
148148
static ModelFirestoreDoc? fromOrNull(
149149
BaseModel? another,
150150
) {
151-
return fromJsonOrNull(another?.toJson())!;
151+
if (another == null) return null;
152+
return fromJsonOrNull(another.toJson());
152153
}
153154

154155

@@ -195,13 +196,10 @@ legacyStamp: legacyStamp,
195196
static ModelFirestoreDoc? fromJsonStringOrNull(
196197
String? jsonString,
197198
) {
199+
if (jsonString == null || jsonString.isEmpty) return null;
198200
try {
199-
if (jsonString!.isNotEmpty) {
200-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
201-
return ModelFirestoreDoc.fromJson(decoded);
202-
} else {
203-
return ModelFirestoreDoc.assertRequired();
204-
}
201+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
202+
return ModelFirestoreDoc.fromJsonOrNull(decoded);
205203
} catch (_) {
206204
return null;
207205
}
@@ -271,12 +269,9 @@ legacyStamp: legacyStamp,
271269
static ModelFirestoreDoc? fromUriOrNull(
272270
Uri? uri,
273271
) {
272+
if (uri == null || uri.path != CLASS_NAME) return null;
274273
try {
275-
if (uri != null && uri.path == CLASS_NAME) {
276-
return ModelFirestoreDoc.fromJson(uri.queryParameters);
277-
} else {
278-
return ModelFirestoreDoc.assertRequired();
279-
}
274+
return ModelFirestoreDoc.fromJsonOrNull(uri.queryParameters);
280275
} catch (_) {
281276
return null;
282277
}

test/integration/models/_model_pg_post.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ updatedAt: updatedAt,
164164
static ModelPgPost? fromOrNull(
165165
BaseModel? another,
166166
) {
167-
return fromJsonOrNull(another?.toJson())!;
167+
if (another == null) return null;
168+
return fromJsonOrNull(another.toJson());
168169
}
169170

170171

@@ -211,13 +212,10 @@ updatedAt: updatedAt,
211212
static ModelPgPost? fromJsonStringOrNull(
212213
String? jsonString,
213214
) {
215+
if (jsonString == null || jsonString.isEmpty) return null;
214216
try {
215-
if (jsonString!.isNotEmpty) {
216-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
217-
return ModelPgPost.fromJson(decoded);
218-
} else {
219-
return ModelPgPost.assertRequired();
220-
}
217+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
218+
return ModelPgPost.fromJsonOrNull(decoded);
221219
} catch (_) {
222220
return null;
223221
}
@@ -291,12 +289,9 @@ updatedAt: updatedAt,
291289
static ModelPgPost? fromUriOrNull(
292290
Uri? uri,
293291
) {
292+
if (uri == null || uri.path != CLASS_NAME) return null;
294293
try {
295-
if (uri != null && uri.path == CLASS_NAME) {
296-
return ModelPgPost.fromJson(uri.queryParameters);
297-
} else {
298-
return ModelPgPost.assertRequired();
299-
}
294+
return ModelPgPost.fromJsonOrNull(uri.queryParameters);
300295
} catch (_) {
301296
return null;
302297
}

test/integration/models/_model_pg_user.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ updatedAt: updatedAt,
156156
static ModelPgUser? fromOrNull(
157157
BaseModel? another,
158158
) {
159-
return fromJsonOrNull(another?.toJson())!;
159+
if (another == null) return null;
160+
return fromJsonOrNull(another.toJson());
160161
}
161162

162163

@@ -203,13 +204,10 @@ updatedAt: updatedAt,
203204
static ModelPgUser? fromJsonStringOrNull(
204205
String? jsonString,
205206
) {
207+
if (jsonString == null || jsonString.isEmpty) return null;
206208
try {
207-
if (jsonString!.isNotEmpty) {
208-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
209-
return ModelPgUser.fromJson(decoded);
210-
} else {
211-
return ModelPgUser.assertRequired();
212-
}
209+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
210+
return ModelPgUser.fromJsonOrNull(decoded);
213211
} catch (_) {
214212
return null;
215213
}
@@ -281,12 +279,9 @@ updatedAt: updatedAt,
281279
static ModelPgUser? fromUriOrNull(
282280
Uri? uri,
283281
) {
282+
if (uri == null || uri.path != CLASS_NAME) return null;
284283
try {
285-
if (uri != null && uri.path == CLASS_NAME) {
286-
return ModelPgUser.fromJson(uri.queryParameters);
287-
} else {
288-
return ModelPgUser.assertRequired();
289-
}
284+
return ModelPgUser.fromJsonOrNull(uri.queryParameters);
290285
} catch (_) {
291286
return null;
292287
}

test/integration/models/_model_post_metadata.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ language: language,
108108
static ModelPostMetadata? fromOrNull(
109109
BaseModel? another,
110110
) {
111-
return fromJsonOrNull(another?.toJson())!;
111+
if (another == null) return null;
112+
return fromJsonOrNull(another.toJson());
112113
}
113114

114115

@@ -155,13 +156,10 @@ language: language,
155156
static ModelPostMetadata? fromJsonStringOrNull(
156157
String? jsonString,
157158
) {
159+
if (jsonString == null || jsonString.isEmpty) return null;
158160
try {
159-
if (jsonString!.isNotEmpty) {
160-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
161-
return ModelPostMetadata.fromJson(decoded);
162-
} else {
163-
return ModelPostMetadata.assertRequired();
164-
}
161+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
162+
return ModelPostMetadata.fromJsonOrNull(decoded);
165163
} catch (_) {
166164
return null;
167165
}
@@ -221,12 +219,9 @@ language: language,
221219
static ModelPostMetadata? fromUriOrNull(
222220
Uri? uri,
223221
) {
222+
if (uri == null || uri.path != CLASS_NAME) return null;
224223
try {
225-
if (uri != null && uri.path == CLASS_NAME) {
226-
return ModelPostMetadata.fromJson(uri.queryParameters);
227-
} else {
228-
return ModelPostMetadata.assertRequired();
229-
}
224+
return ModelPostMetadata.fromJsonOrNull(uri.queryParameters);
230225
} catch (_) {
231226
return null;
232227
}

test/integration/models/_model_sqlite_settings.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ avatarCache: avatarCache,
148148
static ModelSqliteSettings? fromOrNull(
149149
BaseModel? another,
150150
) {
151-
return fromJsonOrNull(another?.toJson())!;
151+
if (another == null) return null;
152+
return fromJsonOrNull(another.toJson());
152153
}
153154

154155

@@ -195,13 +196,10 @@ avatarCache: avatarCache,
195196
static ModelSqliteSettings? fromJsonStringOrNull(
196197
String? jsonString,
197198
) {
199+
if (jsonString == null || jsonString.isEmpty) return null;
198200
try {
199-
if (jsonString!.isNotEmpty) {
200-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
201-
return ModelSqliteSettings.fromJson(decoded);
202-
} else {
203-
return ModelSqliteSettings.assertRequired();
204-
}
201+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
202+
return ModelSqliteSettings.fromJsonOrNull(decoded);
205203
} catch (_) {
206204
return null;
207205
}
@@ -271,12 +269,9 @@ avatarCache: avatarCache,
271269
static ModelSqliteSettings? fromUriOrNull(
272270
Uri? uri,
273271
) {
272+
if (uri == null || uri.path != CLASS_NAME) return null;
274273
try {
275-
if (uri != null && uri.path == CLASS_NAME) {
276-
return ModelSqliteSettings.fromJson(uri.queryParameters);
277-
} else {
278-
return ModelSqliteSettings.assertRequired();
279-
}
274+
return ModelSqliteSettings.fromJsonOrNull(uri.queryParameters);
280275
} catch (_) {
281276
return null;
282277
}

test/integration/models/_model_strict_dto.g.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ avatar: avatar,
148148
static ModelStrictDto? fromOrNull(
149149
BaseModel? another,
150150
) {
151-
return fromJsonOrNull(another?.toJson())!;
151+
if (another == null) return null;
152+
return fromJsonOrNull(another.toJson());
152153
}
153154

154155

@@ -195,13 +196,10 @@ avatar: avatar,
195196
static ModelStrictDto? fromJsonStringOrNull(
196197
String? jsonString,
197198
) {
199+
if (jsonString == null || jsonString.isEmpty) return null;
198200
try {
199-
if (jsonString!.isNotEmpty) {
200-
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
201-
return ModelStrictDto.fromJson(decoded);
202-
} else {
203-
return ModelStrictDto.assertRequired();
204-
}
201+
final decoded = letMapOrNull<String, dynamic>(jsonDecode(jsonString));
202+
return ModelStrictDto.fromJsonOrNull(decoded);
205203
} catch (_) {
206204
return null;
207205
}
@@ -271,12 +269,9 @@ avatar: avatar,
271269
static ModelStrictDto? fromUriOrNull(
272270
Uri? uri,
273271
) {
272+
if (uri == null || uri.path != CLASS_NAME) return null;
274273
try {
275-
if (uri != null && uri.path == CLASS_NAME) {
276-
return ModelStrictDto.fromJson(uri.queryParameters);
277-
} else {
278-
return ModelStrictDto.assertRequired();
279-
}
274+
return ModelStrictDto.fromJsonOrNull(uri.queryParameters);
280275
} catch (_) {
281276
return null;
282277
}

0 commit comments

Comments
 (0)