mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:20:31 +00:00
Macro. Fix for 'add constructor', 'augment constructor' sequence.
Change-Id: If9a5a07cefc7dcd782db636ada2dfa02d7b26e4f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/339044 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Phil Quitslund <pquitslund@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
075b9265f5
commit
b22025d5c3
12 changed files with 237 additions and 21 deletions
|
@ -2,6 +2,7 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/dart/analysis/analysis_context.dart';
|
||||
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/analysis/session.dart';
|
||||
|
@ -9,6 +10,7 @@ import 'package:analyzer/dart/ast/ast.dart';
|
|||
import 'package:analyzer/dart/ast/visitor.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/file_system.dart';
|
||||
import 'package:analyzer_utilities/package_root.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
@ -18,7 +20,16 @@ void main() {
|
|||
});
|
||||
|
||||
group('analyzer', () {
|
||||
buildTests(packagePath: 'analyzer');
|
||||
buildTests(
|
||||
packagePath: 'analyzer',
|
||||
analysisContextPredicate: (analysisContext) {
|
||||
final root = analysisContext.contextRoot.root;
|
||||
if (root.endsWithNames(['macro', 'single'])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('analyzer_cli', () {
|
||||
|
@ -30,7 +41,10 @@ void main() {
|
|||
});
|
||||
}
|
||||
|
||||
void buildTests({required String packagePath}) {
|
||||
void buildTests({
|
||||
required String packagePath,
|
||||
bool Function(AnalysisContext)? analysisContextPredicate,
|
||||
}) {
|
||||
var provider = PhysicalResourceProvider.INSTANCE;
|
||||
var pkgRootPath = provider.pathContext.normalize(packageRoot);
|
||||
|
||||
|
@ -40,14 +54,18 @@ void buildTests({required String packagePath}) {
|
|||
includedPaths: <String>[testsPath],
|
||||
resourceProvider: provider,
|
||||
);
|
||||
var contexts = collection.contexts;
|
||||
if (contexts.length != 1) {
|
||||
|
||||
final singleAnalysisContext = collection.contexts
|
||||
.where(analysisContextPredicate ?? (_) => true)
|
||||
.toList()
|
||||
.singleOrNull;
|
||||
if (singleAnalysisContext == null) {
|
||||
fail('The directory $testsPath contains multiple analysis contexts.');
|
||||
}
|
||||
|
||||
test('no @soloTest', () async {
|
||||
var failures = <String>[];
|
||||
await buildTestsIn(contexts[0].currentSession, testsPath,
|
||||
await buildTestsIn(singleAnalysisContext.currentSession, testsPath,
|
||||
provider.getFolder(testsPath), failures);
|
||||
|
||||
if (failures.isNotEmpty) {
|
||||
|
|
|
@ -108,15 +108,32 @@ class MacroElementsMerger {
|
|||
}
|
||||
}
|
||||
|
||||
final containerRef = existingRef.getChild('@method');
|
||||
for (final element in newElement.methods) {
|
||||
final reference = element.reference!;
|
||||
containerRef.addChildReference(element.name, reference);
|
||||
{
|
||||
final containerRef = existingRef.getChild('@method');
|
||||
for (final element in newElement.methods) {
|
||||
final reference = element.reference!;
|
||||
containerRef.addChildReference(element.name, reference);
|
||||
}
|
||||
existingElement.methods = [
|
||||
...existingElement.methods,
|
||||
...newElement.methods,
|
||||
].toFixedList();
|
||||
}
|
||||
|
||||
if (existingElement is InterfaceElementImpl &&
|
||||
newElement is InterfaceElementImpl) {
|
||||
for (final element in newElement.constructors) {
|
||||
final reference = element.reference!;
|
||||
final containerRef = element.isAugmentation
|
||||
? existingRef.getChild('@constructorAugmentation')
|
||||
: existingRef.getChild('@constructor');
|
||||
containerRef.addChildReference(element.name, reference);
|
||||
}
|
||||
existingElement.constructors = [
|
||||
...existingElement.constructors,
|
||||
...newElement.constructors,
|
||||
].toFixedList();
|
||||
}
|
||||
existingElement.methods = [
|
||||
...existingElement.methods,
|
||||
...newElement.methods,
|
||||
].toFixedList();
|
||||
|
||||
// TODO(scheglov): accessors, fields
|
||||
}
|
||||
|
|
|
@ -454,14 +454,15 @@ class TypesBuilder {
|
|||
toDeclaration.mapInterfaceTypes(element.interfaces),
|
||||
);
|
||||
|
||||
augmented.constructors.addAll(
|
||||
element.constructors.notAugmented.map((element) {
|
||||
augmented.constructors = [
|
||||
...augmented.constructors.notAugmented,
|
||||
...element.constructors.notAugmented.map((element) {
|
||||
if (toDeclaration.map.isEmpty) {
|
||||
return element;
|
||||
}
|
||||
return ConstructorMember(typeProvider, element, toDeclaration, false);
|
||||
}),
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
if (element is MixinElementImpl && augmented is AugmentedMixinElementImpl) {
|
||||
|
|
|
@ -46,6 +46,21 @@ extension ListExtension<E> on List<E> {
|
|||
}
|
||||
}
|
||||
|
||||
bool endsWith(List<E> expected) {
|
||||
var thisIndex = length - expected.length;
|
||||
if (thisIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var expectedIndex = 0;
|
||||
for (; expectedIndex < expected.length;) {
|
||||
if (this[thisIndex++] != expected[expectedIndex++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
E? nextOrNull(E element) {
|
||||
final index = indexOf(element);
|
||||
if (index >= 0 && index < length - 1) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
extension FolderExtension on Folder {
|
||||
|
@ -43,4 +44,8 @@ extension ResourceExtension on Resource {
|
|||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
bool endsWithNames(List<String> expected) {
|
||||
return provider.pathContext.split(path).endsWith(expected);
|
||||
}
|
||||
}
|
||||
|
|
1
pkg/analyzer/test/src/summary/macro/single/README.md
Normal file
1
pkg/analyzer/test/src/summary/macro/single/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
Each macro file in this directory is used by a test with the same name.
|
|
@ -0,0 +1,6 @@
|
|||
include: package:lints/recommended.yaml
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
# We use camel case file names in this directory.
|
||||
file_names: ignore
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/macros/api.dart';
|
||||
|
||||
/*macro*/ class AddConstructor implements ClassDeclarationsMacro {
|
||||
const AddConstructor();
|
||||
|
||||
@override
|
||||
buildDeclarationsForClass(clazz, builder) async {
|
||||
// ignore: deprecated_member_use
|
||||
final identifier = await builder.resolveIdentifier(
|
||||
Uri.parse('package:test/a.dart'),
|
||||
'AugmentConstructor',
|
||||
);
|
||||
builder.declareInType(
|
||||
DeclarationCode.fromParts([
|
||||
' @',
|
||||
identifier,
|
||||
'()\n A.named();',
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*macro*/ class AugmentConstructor implements ConstructorDefinitionMacro {
|
||||
const AugmentConstructor();
|
||||
|
||||
@override
|
||||
buildDefinitionForConstructor(constructor, builder) {
|
||||
builder.augment(
|
||||
body: FunctionBodyCode.fromString('{ print(42); }'),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -44,6 +44,8 @@ main() {
|
|||
defineReflectiveTests(MacroTypesTest_fromBytes);
|
||||
defineReflectiveTests(MacroDeclarationsTest_keepLinking);
|
||||
defineReflectiveTests(MacroDeclarationsTest_fromBytes);
|
||||
defineReflectiveTests(MacroDefinitionTest_keepLinking);
|
||||
defineReflectiveTests(MacroDefinitionTest_fromBytes);
|
||||
defineReflectiveTests(MacroElementsTest_keepLinking);
|
||||
defineReflectiveTests(MacroElementsTest_fromBytes);
|
||||
defineReflectiveTests(MacroApplicationOrderTest);
|
||||
|
@ -2169,6 +2171,84 @@ class MacroDeclarationsTest_keepLinking extends MacroDeclarationsTest {
|
|||
bool get keepLinkingLibraries => true;
|
||||
}
|
||||
|
||||
abstract class MacroDefinitionTest extends MacroElementsBaseTest {
|
||||
test_class_addConstructor_augmentConstructor() async {
|
||||
_addSingleMacro('class_addConstructor_augmentConstructor.dart');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart';
|
||||
|
||||
@AddConstructor()
|
||||
class A {}
|
||||
''');
|
||||
|
||||
configuration
|
||||
..withMetadata = false
|
||||
..withReferences = true;
|
||||
checkElementText(library, r'''
|
||||
library
|
||||
reference: self
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
reference: self
|
||||
classes
|
||||
class A @42
|
||||
reference: self::@class::A
|
||||
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
|
||||
augmented
|
||||
constructors
|
||||
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructorAugmentation::named
|
||||
augmentationImports
|
||||
package:test/test.macro.dart
|
||||
reference: self::@augmentation::package:test/test.macro.dart
|
||||
macroGeneratedCode
|
||||
---
|
||||
library augment 'test.dart';
|
||||
|
||||
import 'package:test/a.dart' as prefix0;
|
||||
|
||||
augment class A {
|
||||
@prefix0.AugmentConstructor()
|
||||
A.named();
|
||||
augment A.named() { print(42); }
|
||||
}
|
||||
---
|
||||
imports
|
||||
package:test/a.dart as prefix0 @62
|
||||
definingUnit
|
||||
reference: self::@augmentation::package:test/test.macro.dart
|
||||
classes
|
||||
augment class A @86
|
||||
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
|
||||
augmentationTarget: self::@class::A
|
||||
constructors
|
||||
named @126
|
||||
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructor::named
|
||||
periodOffset: 125
|
||||
nameEnd: 131
|
||||
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructorAugmentation::named
|
||||
augment named @147
|
||||
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructorAugmentation::named
|
||||
periodOffset: 146
|
||||
nameEnd: 152
|
||||
augmentationTarget: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructor::named
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class MacroDefinitionTest_fromBytes extends MacroDefinitionTest {
|
||||
@override
|
||||
bool get keepLinkingLibraries => false;
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class MacroDefinitionTest_keepLinking extends MacroDefinitionTest {
|
||||
@override
|
||||
bool get keepLinkingLibraries => true;
|
||||
}
|
||||
|
||||
abstract class MacroElementsBaseTest extends ElementsBaseTest {
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
|
@ -2185,6 +2265,12 @@ abstract class MacroElementsBaseTest extends ElementsBaseTest {
|
|||
);
|
||||
}
|
||||
|
||||
/// Adds `a.dart` with the content from `single/` directory.
|
||||
void _addSingleMacro(String fileName) {
|
||||
final code = _getMacroCode('single/$fileName');
|
||||
newFile('$testPackageLibPath/a.dart', code);
|
||||
}
|
||||
|
||||
/// Verifies the code of the macro generated augmentation.
|
||||
void _assertMacroCode(LibraryElementImpl library, String expected) {
|
||||
final actual = library.augmentations.single.macroGenerated!.code;
|
||||
|
|
|
@ -50,6 +50,22 @@ class ListExtensionTest {
|
|||
expect([0, 1].elementAtOrNull2(2), isNull);
|
||||
}
|
||||
|
||||
test_endsWith() {
|
||||
expect([0, 1, 2].endsWith([]), isTrue);
|
||||
|
||||
expect([0, 1, 2].endsWith([2]), isTrue);
|
||||
expect([0, 1, 2].endsWith([1]), isFalse);
|
||||
expect([0, 1, 2].endsWith([0]), isFalse);
|
||||
|
||||
expect([0, 1, 2].endsWith([1, 2]), isTrue);
|
||||
expect([0, 1, 2].endsWith([0, 2]), isFalse);
|
||||
|
||||
expect([0, 1, 2].endsWith([0, 1, 2]), isTrue);
|
||||
expect([0, 1, 2].endsWith([0, 0, 2]), isFalse);
|
||||
|
||||
expect([0, 1, 2].endsWith([-1, 0, 1, 2]), isFalse);
|
||||
}
|
||||
|
||||
test_nextOrNull() {
|
||||
var elements = [0, 1, 2];
|
||||
expect(elements.nextOrNull(0), 1);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/file_system.dart';
|
||||
import 'package:analyzer_utilities/package_root.dart' as package_root;
|
||||
import 'package:analyzer_utilities/verify_tests.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
@ -13,7 +14,15 @@ main() {
|
|||
var packageRoot = provider.pathContext.normalize(package_root.packageRoot);
|
||||
var pathToAnalyze = provider.pathContext.join(packageRoot, 'analyzer');
|
||||
var testDirPath = provider.pathContext.join(pathToAnalyze, 'test');
|
||||
_VerifyTests(testDirPath).build();
|
||||
_VerifyTests(testDirPath).build(
|
||||
analysisContextPredicate: (analysisContext) {
|
||||
final root = analysisContext.contextRoot.root;
|
||||
if (root.endsWithNames(['macro', 'single'])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _VerifyTests extends VerifyTests {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/dart/analysis/analysis_context.dart';
|
||||
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/analysis/session.dart';
|
||||
|
@ -23,18 +24,23 @@ class VerifyTests {
|
|||
VerifyTests(this.testDirPath, {this.excludedPaths});
|
||||
|
||||
/// Build tests.
|
||||
void build() {
|
||||
void build({
|
||||
bool Function(AnalysisContext)? analysisContextPredicate,
|
||||
}) {
|
||||
var provider = PhysicalResourceProvider.INSTANCE;
|
||||
var collection = AnalysisContextCollection(
|
||||
resourceProvider: provider,
|
||||
includedPaths: <String>[testDirPath],
|
||||
excludedPaths: excludedPaths);
|
||||
var contexts = collection.contexts;
|
||||
if (contexts.length != 1) {
|
||||
final singleAnalysisContext = collection.contexts
|
||||
.where(analysisContextPredicate ?? (_) => true)
|
||||
.toList()
|
||||
.singleOrNull;
|
||||
if (singleAnalysisContext == null) {
|
||||
fail('The test directory contains multiple analysis contexts.');
|
||||
}
|
||||
|
||||
_buildTestsIn(contexts[0].currentSession, testDirPath,
|
||||
_buildTestsIn(singleAnalysisContext.currentSession, testDirPath,
|
||||
provider.getFolder(testDirPath));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue