Repository navigation
[checks_codegen] Generate full field types with imports - #2781
Conversation
Generated getters referenced the field type by the name of its element only, so type arguments were never imported and function typed fields were skipped entirely. Build the type reference recursively from the analyzer `DartType` with a switch expression over the kinds of type, delegating interface, function, and record types to separate helpers: - Interface types import the library for each element, including within type arguments. - Function types, including generic function types with bounds, and record types are emitted structurally. - `dynamic`, `void`, and `Never` are emitted directly. - Type parameters of the checked class are replaced with their bounds by reading getter types from the class instantiated to bounds. The extension is on the raw type, so this matches the static type of `v.field`. - Unresolvable types (`InvalidType`) fail with a hint to check for a missing build dependency. Function typed fields now get getters. Fields without a getter, such as those introduced by a lone setter, are skipped.
PR HealthPublish ✔️
Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. This check can be disabled by tagging the PR with Changelog Entry ✔️
Changes to files need to be accounted for in their respective changelogs. This check can be disabled by tagging the PR with |
| ]; | ||
| return TypeReference( | ||
| (b) => b | ||
| ..symbol = type.element.name |
There was a problem hiding this comment.
Private types still don't compile, now as _i2._Private, including inside type arguments: List<_Private> get g; gives Subject<List<_i2._Private>> (analyzer: non_type_as_type_argument). #2782 skips private members but not public members with private types; this seems the natural place to handle it, or a follow-up.
There was a problem hiding this comment.
Will handle skipping public members whose types reference private types in #2782 once this lands.
| InterfaceType type, | ||
| Future<String?> Function(Element) importFor, | ||
| ) async { | ||
| final import = await importFor(type.element); |
There was a problem hiding this comment.
importFor searches only the test library's imports, so a type that foo.dart reached through a re-export falls back to its defining library: with import 'dart:io'; and HttpClient get c;, the output imports dart:_http, which doesn't compile; a barrel re-export of src/ gives a direct src/ import. Pre-existing for top-level field types, but this PR extends it to type arguments, e.g. List<HttpClient>.
Prior art in Mockito:
There was a problem hiding this comment.
Done — switched to a two-pass search modeled on Mockito: collect all referenced interface elements first, then search imports of the test library, imports of the declaring libraries (preferring libraries outside src/), and for private SDK elements the reachable public SDK libraries before falling back.
| FunctionType() => await _functionTypeReference(type, importFor), | ||
| RecordType() => await _recordTypeReference(type, importFor), | ||
| _ => throw InvalidGenerationSourceError( | ||
| 'Failed to resolve the type $type. ' |
There was a problem hiding this comment.
Nit: for an unresolved type, $type prints InvalidType, so the message doesn't say which field or class failed; passing element: or the field name would help.
There was a problem hiding this comment.
Done — the error now names Class.field and passes element: field for a source span.
The error for an unresolved field type interpolated the type, which prints as `InvalidType` and doesn't say where the problem is. Report the class and field name, and attach the field element so the error includes a source span for the declaration.
Previously a type was imported through one of the test library's imports if one exported it, and otherwise from its declaring library. A type that the declaring library of the checked class reached through a re-export, such as HttpClient from dart:io or a class re-exported by a package's public library, was imported from a src/ library, or from dart:_http which doesn't compile. Follow the structure of mockito: first walk the checked classes and their field types to collect every referenced class, then resolve an import for each. A class is imported through the first library that exports it from: 1. The imports of the test library. 2. The imports of every library that declares a referenced class. 3. For classes declared in private SDK libraries, the public SDK libraries. Those reachable through imports and exports from the test library and the declaring libraries come first, so that HttpStatus comes from dart:io rather than dart:html in a VM program. The first two groups prefer libraries outside of src/. If no library exports a class it is still imported from its declaring library, which may be in src/. It is an error if that is a private SDK library. Resolving all imports up front makes code generation synchronous.
# Conflicts: # pkgs/checks_codegen/test/builder_test.dart
Revisions updated by `dart tools/rev_sdk_deps.dart`. core (https://github.com/dart-lang/core/compare/36f57a9..fa0c6b7): fa0c6b77 Fri Nov 6 12:56:58 2026 +0100 Michael Thomsen Release os_detect 2.0.4 (dart-lang/core#1038) dartdoc (https://github.com/dart-lang/dartdoc/compare/03ec6df..7e2508c): 7e2508c6 Wed Nov 11 14:52:22 2026 +0200 Sergey G. Grekhov Add constructor doc comment tests for enums and extension types (dart-lang/dartdoc#4316) 03dfb467 Mon Nov 9 11:00:32 2026 +0200 Sergey G. Grekhov `#3330`. Split test for parameters to separate tests (dart-lang/dartdoc#4314) 23302d53 Mon Nov 9 10:56:34 2026 +0200 Sergey G. Grekhov Add tests for primary constructors (dart-lang/dartdoc#4313) ecosystem (https://github.com/dart-lang/ecosystem/compare/54e1673..1d2b5ff): 1d2b5ff Tue Nov 10 09:04:24 2026 -0800 Kevin Moore feat(repo_manage): add --replace-pattern to report changelog (dart-lang/ecosystem#482) http (https://github.com/dart-lang/http/compare/4646fe6..2bac218): 2bac218 Fri Nov 6 17:49:23 2026 -0800 dart-ecosystem-bot[bot] Tidy: package:cronet_http cdf967c Fri Nov 6 15:40:49 2026 -0800 Brian Quinlan chore(groundkeeper): use stable for flutter (dart-lang/http#2066) 6582177 Thu Nov 5 13:04:35 2026 -0800 Brian Quinlan doc: use timeout as an abort example (dart-lang/http#2062) native (https://github.com/dart-lang/native/compare/6287745..b21ef2d): b21ef2da3 Wed Nov 11 16:07:34 2026 +0530 dhairya05 [json_syntax_generator] Pass the parameter name to ArgumentError.value (dart-lang/native#3787) 20a607e48 Wed Nov 11 07:28:03 2026 -0300 Daco Harkes [code_assets] [hooks] [hooks_runner] Adopt `.g.dart` for generated files (dart-lang/native#3790) cc974ddec Wed Nov 11 19:59:39 2026 +1100 Liam Appelbe [objective_c] Fix `isValidObject` bug on iOS simulator (dart-lang/native#3788) f7edbdee3 Sun Nov 8 13:18:25 2026 +1100 Liam Appelbe [jni] Fix possible receiver NPEs (dart-lang/native#3784) 0ca7d6745 Fri Nov 6 10:20:21 2026 -0300 Daco Harkes [code_assets] [hooks_runner] [native_toolchain_c] Prepare for release (dart-lang/native#3781) baad15ab4 Fri Nov 6 02:56:52 2026 +0100 Kravchenko Igor [objective_c] Link Foundation in the native support library (dart-lang/native#3782) shelf (https://github.com/dart-lang/shelf/compare/9946de5..b175e9f): b175e9f Tue Nov 10 09:14:31 2026 +0530 Muhammad Uvesh shelf_static: ignore invalid If-Modified-Since headers (dart-lang/shelf#557) 02b5f94 Sat Nov 7 23:09:28 2026 +0530 dhairya05 Fix typo in shelf_router docs (dart-lang/shelf#556) test (https://github.com/dart-lang/test/compare/2a9e017..3533ae1): 3533ae1e Tue Nov 10 15:06:08 2026 -0800 Nate Bosch Never retry tearDownAll (dart-lang/test#2815) 5680b0b1 Fri Nov 6 12:52:11 2026 +0100 Sigurd Meldgaard Add globalSetup and addGlobalTearDown APIs for cross-suite setup and teardown (dart-lang/test#2714) b8dc888a Thu Nov 5 18:11:37 2026 -0800 Nate Bosch [checks_codegen] Skip private fields and fields referencing private types (dart-lang/test#2782) 66e062c4 Thu Nov 5 16:05:36 2026 -0800 Nate Bosch [checks_codegen] Generate full field types with imports (dart-lang/test#2781) tools (https://github.com/dart-lang/tools/compare/727fd9c..589d844): 589d844c4 Wed Nov 11 20:27:32 2026 +0300 Hasan Armoush fix(markdown): combine adjacent Text nodes when the element is last in its list (dart-lang/tools#2598) Change-Id: I13acaa7378c86f2c33d88f3c4b163acf2b48ff08 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/576820 Commit-Queue: Nate Bosch <[email protected]> Reviewed-by: Nicholas Shahan <[email protected]>
Generated getters referenced the field type by the name of its element
only, so type arguments were never imported and function typed fields
were skipped entirely. Imports were also searched only among the test
library's direct imports, so types reached by the checked class through
a re-export fell back to
src/libraries or uncompilabledart:_*SDKlibraries.
First walk the checked classes and their field types to collect every
referenced interface element and resolve an import URI for each, then
emit type references synchronously from the analyzer
DartType:type arguments.
record types are emitted structurally.
dynamic,void, andNeverare emitted directly.reading getter types from the class instantiated to bounds. The
extension is on the raw type, so this matches the static type of
v.field.InvalidType) fail naming the class and field,with a source span on the field and a hint to check for a missing
build dependency.
Function typed fields now get getters. Fields without a getter, such as
those introduced by a lone setter, are skipped.
Import resolution searches for a library exporting each non-public-SDK
element in order:
dart:_*libraries, public SDK libraries:first those reachable through imports and exports from the test and
declaring libraries (so e.g.
HttpStatusprefersdart:iooverdart:htmlin a VM program), then any remaining public SDKlibraries, each sorted by URI.
The first two groups prefer libraries outside
src/. If no libraryexports an element, fall back to its declaring library (which may be in
src/), or fail if that is adart:_*library.Note on conditional directives:
build_runnerresolves code with emptyDeclaredVariables, so conditionalimport/exportdirectives alwaysfollow their default URI during codegen, never an
if (dart.library.*)branch. When a barrel conditionally exports
src/implementations,codegen now imports the barrel rather than the default
src/stub, sotest compilation evaluates the condition for the target platform. When a
declaring library uses a conditional
importdirectly, codegen stillsees only the default branch: a stub default yields the stub's types as
before, while an IO default exposing types like
HttpHeadersorHttpStatusnow emitsdart:io(compiling on VM, instead of emittingdart:_http/dart:_internalwhich failed everywhere) but still willnot compile for web unless the test library imports a compatible
exporter.