Skip to content

Commit

Permalink
add ResolvedIdentifier class, return that instead of a Uri when build…
Browse files Browse the repository at this point in the history
…ing augmentation libraries, and use it to determine how to qualify identifiers

The main goal here is to be able to distinguish between instance members and static members, so we know what to prefix and with what.

Change-Id: I5189979cd218f1eba8b30cc39a95632edeaf9579
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/231981
Auto-Submit: Jake Macdonald <jakemac@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
  • Loading branch information
jakemac53 authored and Commit Bot committed Feb 10, 2022
1 parent 7605a36 commit 0c7bf4b
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 22 deletions.
37 changes: 36 additions & 1 deletion pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ abstract class MacroExecutor {
/// The [resolveIdentifier] argument should return the import uri to be used
/// for that identifier.
String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
Uri Function(Identifier) resolveIdentifier);
ResolvedIdentifier Function(Identifier) resolveIdentifier);

/// Tell the executor to shut down and clean up any resources it may have
/// allocated.
Expand Down Expand Up @@ -220,6 +220,41 @@ class Arguments implements Serializable {
}
}

/// A resolved [Identifier], this is used when creating augmentation libraries
/// to qualify identifiers where needed.
class ResolvedIdentifier extends Identifier {
/// The import URI for the library that defines the member that is referenced
/// by this identifier.
final Uri uri;

/// Type type of identifier this is (instance, static, top level).
final IdentifierKind kind;

/// The unqualified name of this identifier.
@override
final String name;

/// If this is a static member, then the name of the fully qualified scope
/// surrounding this member. Should not contain a trailing `.`.
///
/// Typically this would just be the name of a type.
final String? staticScope;

ResolvedIdentifier({
required this.kind,
required this.name,
required this.staticScope,
required this.uri,
});
}

/// The types of identifiers.
enum IdentifierKind {
instanceMember,
staticInstanceMember,
topLevelMember,
}

/// An opaque identifier for a macro class, retrieved by
/// [MacroExecutor.loadMacro].
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,44 @@ import '../executor.dart';
mixin AugmentationLibraryBuilder on MacroExecutor {
@override
String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
Uri Function(Identifier) resolveIdentifier) {
ResolvedIdentifier Function(Identifier) resolveIdentifier) {
StringBuffer importsBuffer = new StringBuffer();
StringBuffer directivesBuffer = new StringBuffer();
Map<Uri, String> importPrefixes = {};
int nextPrefix = 0;

// Keeps track of the last part written in `lastDirectivePart`.
String lastDirectivePart = '';
void writeDirectivePart(String part) {
lastDirectivePart = part;
directivesBuffer.write(part);
}

void buildCode(Code code) {
for (Object part in code.parts) {
if (part is String) {
directivesBuffer.write(part);
writeDirectivePart(part);
} else if (part is Code) {
buildCode(part);
} else if (part is Identifier) {
Uri uri = resolveIdentifier(part);
String prefix = importPrefixes.putIfAbsent(uri, () {
ResolvedIdentifier resolved = resolveIdentifier(part);
String prefix = importPrefixes.putIfAbsent(resolved.uri, () {
String prefix = 'i${nextPrefix++}';
importsBuffer.writeln("import '$uri' as $prefix;");
importsBuffer.writeln("import '${resolved.uri}' as $prefix;");
return prefix;
});
directivesBuffer.write('$prefix.${part.name}');
if (resolved.kind == IdentifierKind.instanceMember) {
// Qualify with `this.` if we don't have a receiver.
if (!lastDirectivePart.trimRight().endsWith('.')) {
writeDirectivePart('this.');
}
} else {
writeDirectivePart('${prefix}.');
}
if (resolved.kind == IdentifierKind.staticInstanceMember) {
writeDirectivePart('${resolved.staticScope!}.');
}
writeDirectivePart('${part.name}');
} else {
throw new ArgumentError(
'Code objects only support String, Identifier, and Code '
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
/// These calls are handled by the higher level executor.
@override
String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
Uri Function(Identifier) resolveIdentifier) =>
ResolvedIdentifier Function(Identifier) resolveIdentifier) =>
throw new StateError('Unreachable');

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void main() {
]),
];
var library = _TestExecutor().buildAugmentationLibrary(
results, (Identifier i) => (i as TestIdentifier).libraryImportUri);
results, (Identifier i) => (i as TestIdentifier).resolved);
expect(library, equalsIgnoringWhitespace('''
augment class Foo00 {
int get i => 0;
Expand Down Expand Up @@ -68,15 +68,33 @@ void main() {
var fooIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Foo',
libraryImportUri: Uri.parse('package:foo/foo.dart'));
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:foo/foo.dart'));
var barIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Bar',
libraryImportUri: Uri.parse('package:bar/bar.dart'));
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var builderIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Builder',
libraryImportUri: Uri.parse('package:builder/builder.dart'));
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:builder/builder.dart'));
var barInstanceMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'baz',
kind: IdentifierKind.instanceMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var barStaticMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'zap',
kind: IdentifierKind.staticInstanceMember,
staticScope: 'Bar',
uri: Uri.parse('package:bar/bar.dart'));
var results = [
MacroExecutionResultImpl(augmentations: [
DeclarationCode.fromParts([
Expand All @@ -87,24 +105,32 @@ void main() {
'<',
barIdentifier,
'<T>> {\n',
'late int ${barInstanceMember.name};\n',
barIdentifier,
'<T> build() => new ',
barIdentifier,
'();\n}',
'()..',
barInstanceMember,
' = ',
barStaticMember,
';',
'\n}',
]),
], newTypeNames: [
'FooBuilder',
])
];
var library = _TestExecutor().buildAugmentationLibrary(
results, (Identifier i) => (i as TestIdentifier).libraryImportUri);
results, (Identifier i) => (i as TestIdentifier).resolved);
expect(library, equalsIgnoringWhitespace('''
import 'package:foo/foo.dart' as i0;
import 'package:builder/builder.dart' as i1;
import 'package:bar/bar.dart' as i2;
class FooBuilder<T extends i0.Foo> implements i1.Builder<i2.Bar<T>> {
i2.Bar<T> build() => new i2.Bar();
late int baz;
i2.Bar<T> build() => new i2.Bar()..baz = i2.Bar.zap;
}
'''));
});
Expand Down
22 changes: 16 additions & 6 deletions pkg/_fe_analyzer_shared/test/macros/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:mirrors';

import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'package:_fe_analyzer_shared/src/macros/executor.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';

Expand Down Expand Up @@ -108,13 +109,22 @@ class TestNamedStaticType implements NamedStaticType {
(library == other.library && identifier.name == other.identifier.name);
}

/// An identifier that knows what URI should be used to import it.
/// An identifier that knows the resolved version of itself.
class TestIdentifier extends IdentifierImpl {
final Uri libraryImportUri;

TestIdentifier(
{required int id, required String name, required this.libraryImportUri})
: super(id: id, name: name);
final ResolvedIdentifier resolved;

TestIdentifier({
required int id,
required String name,
required IdentifierKind kind,
required Uri uri,
required String? staticScope,
}) : resolved = ResolvedIdentifier(
kind: kind, name: name, staticScope: staticScope, uri: uri),
super(
id: id,
name: name,
);
}

extension DebugCodeString on Code {
Expand Down
2 changes: 1 addition & 1 deletion pkg/front_end/test/macros/macro_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ class TestMacroExecutor implements MacroExecutor {

@override
String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
Uri Function(Identifier) resolveIdentifier) {
ResolvedIdentifier Function(Identifier) resolveIdentifier) {
// TODO: implement buildAugmentationLibrary
throw UnimplementedError();
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/front_end/test/spell_checking_list_code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,7 @@ q'i
qi
qm
quad
qualify
quantified
queries
quick
Expand Down Expand Up @@ -1369,6 +1370,7 @@ unordered
unpaired
unparsed
unpleasant
unqualified
unreachable
unseen
unset
Expand Down

0 comments on commit 0c7bf4b

Please sign in to comment.