Skip to content

Commit 1e87e13

Browse files
authored
ConstantReader.revive implementation now matches the documentation (#376)
* Dartfmt. * Fix markdown.
1 parent fd3e813 commit 1e87e13

File tree

4 files changed

+98
-22
lines changed

4 files changed

+98
-22
lines changed

source_gen/CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## 0.9.1
2+
3+
* The result of `ConstantReader.revive()` now returns a `Revivable` that assumes
4+
access to a private class, constructor, or function _instead_ of `null` where
5+
possible. This allows generators that use `part` files to still use this
6+
functionality _and_ allows generators that use separate libraries to emit more
7+
actionable error messages (i.e. `"cannot use private class _X"`).
8+
9+
* `Revivable.isPrivate` now returns `true` when the underyling class was public
10+
but the constructor was private, or the `Revivable` was pointing to a
11+
top-level or static private field or method. Previously it was only `true`
12+
when referencing a private class.
13+
114
## 0.9.0+1
215

316
* Fix `LibraryReader.classElements` to return classes from parts, if they exist,

source_gen/lib/src/constants/revive.dart

+49-20
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ Revivable reviveInstance(DartObject object, [LibraryElement origin]) {
3434
accessor: '${element.enclosingElement.name}.${element.name}',
3535
);
3636
}
37-
final clazz = element as ClassElement;
3837
// Enums are not included in .definingCompilationUnit.types.
38+
final clazz = element as ClassElement;
3939
if (clazz.isEnum) {
4040
for (final e in clazz.fields.where(
4141
(f) => f.isPublic && f.isConst && f.computeConstantValue() == object)) {
@@ -45,39 +45,55 @@ Revivable reviveInstance(DartObject object, [LibraryElement origin]) {
4545
);
4646
}
4747
}
48+
49+
// We try and return a public accessor/constructor if available.
50+
final allResults = <Revivable>[];
51+
52+
/// Returns whether [result] is an acceptable result to immediately return.
53+
bool tryResult(Revivable result) {
54+
allResults.add(result);
55+
return !result.isPrivate;
56+
}
57+
4858
for (final e in origin.definingCompilationUnit.types
4959
.expand((t) => t.fields)
50-
.where((f) =>
51-
f.isPublic && f.isConst && f.computeConstantValue() == object)) {
52-
return Revivable._(
60+
.where((f) => f.isConst && f.computeConstantValue() == object)) {
61+
final result = Revivable._(
5362
source: url.removeFragment(),
5463
accessor: '${clazz.name}.${e.name}',
5564
);
65+
if (tryResult(result)) {
66+
return result;
67+
}
5668
}
5769
final i = (object as DartObjectImpl).getInvocation();
58-
if (i != null &&
59-
i.constructor.isPublic &&
60-
i.constructor.enclosingElement.isPublic) {
70+
if (i != null) {
6171
url = Uri.parse(urlOfElement(i.constructor.enclosingElement));
62-
return Revivable._(
72+
final result = Revivable._(
6373
source: url,
6474
accessor: i.constructor.name,
6575
namedArguments: i.namedArguments,
6676
positionalArguments: i.positionalArguments,
6777
);
78+
if (tryResult(result)) {
79+
return result;
80+
}
6881
}
69-
if (origin == null) {
70-
return null;
71-
}
72-
for (final e in origin.definingCompilationUnit.topLevelVariables.where(
73-
(f) => f.isPublic && f.isConst && f.computeConstantValue() == object,
74-
)) {
75-
return Revivable._(
76-
source: Uri.parse(urlOfElement(origin)).replace(fragment: ''),
77-
accessor: e.name,
78-
);
82+
if (origin != null) {
83+
for (final e in origin.definingCompilationUnit.topLevelVariables.where(
84+
(f) => f.isConst && f.computeConstantValue() == object,
85+
)) {
86+
final result = Revivable._(
87+
source: Uri.parse(urlOfElement(origin)).replace(fragment: ''),
88+
accessor: e.name,
89+
);
90+
if (tryResult(result)) {
91+
return result;
92+
}
93+
}
7994
}
80-
return null;
95+
// We could try and return the "best" result more intelligently.
96+
return allResults.first;
8197
}
8298

8399
/// Decoded "instructions" for re-creating a const [DartObject] at runtime.
@@ -113,5 +129,18 @@ class Revivable {
113129
///
114130
/// Builds tools may use this to fail when the symbol is expected to be
115131
/// importable (i.e. isn't used with `part of`).
116-
bool get isPrivate => accessor.startsWith('_');
132+
bool get isPrivate {
133+
return source.fragment.startsWith('_') || accessor.startsWith('_');
134+
}
135+
136+
@override
137+
String toString() {
138+
if (source.fragment.isNotEmpty) {
139+
if (accessor.isEmpty) {
140+
return 'const $source';
141+
}
142+
return 'const $source.$accessor';
143+
}
144+
return '$source::$accessor';
145+
}
117146
}

source_gen/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: source_gen
2-
version: 0.9.1-dev
2+
version: 0.9.1
33
author: Dart Team <[email protected]>
44
description: Automated source code generation for Dart.
55
homepage: https://github.com/dart-lang/source_gen

source_gen/test/constants_test.dart

+35-1
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,12 @@ void main() {
197197
@ClassWithStaticField.staticField
198198
@Wrapper(someFunction)
199199
@Wrapper(Wrapper.someFunction)
200+
@_NotAccessible()
201+
@PublicWithPrivateConstructor._()
202+
@_privateField
203+
@Wrapper(_privateFunction)
200204
class Example {}
201-
205+
202206
class Int64Like {
203207
static const Int64Like ZERO = const Int64Like._bits(0, 0, 0);
204208
@@ -248,6 +252,16 @@ void main() {
248252
}
249253
250254
void someFunction(int x, String y) {}
255+
256+
class _NotAccessible {
257+
const _NotAccessible();
258+
}
259+
260+
class PublicWithPrivateConstructor {
261+
const PublicWithPrivateConstructor._();
262+
}
263+
264+
void _privateFunction() {}
251265
''', (resolver) => resolver.findLibraryByName('test_lib'));
252266
constants = library
253267
.getType('Example')
@@ -315,5 +329,25 @@ void main() {
315329
expect(fieldOnly.source.fragment, isEmpty);
316330
expect(fieldOnly.accessor, 'Wrapper.someFunction');
317331
});
332+
333+
test('should decode private classes', () {
334+
final notAccessible = constants[9].revive();
335+
expect(notAccessible.isPrivate, isTrue);
336+
expect(notAccessible.source.fragment, '_NotAccessible');
337+
});
338+
339+
test('should decode private constructors', () {
340+
final notAccessible = constants[10].revive();
341+
expect(notAccessible.isPrivate, isTrue);
342+
expect(notAccessible.source.fragment, 'PublicWithPrivateConstructor');
343+
expect(notAccessible.accessor, '_');
344+
});
345+
346+
test('should decode private functions', () {
347+
final function = constants[12].read('f').revive();
348+
expect(function.isPrivate, isTrue);
349+
expect(function.source.fragment, isEmpty);
350+
expect(function.accessor, '_privateFunction');
351+
});
318352
});
319353
}

0 commit comments

Comments
 (0)