// Copyright (c) 2024, 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:expect/expect.dart'; import 'package:kernel/ast.dart'; import 'package:kernel/transformations/track_widget_constructor_locations.dart'; void main() { final Uri developerUri = Uri.parse('dart:developer'); final Library developerLib = new Library(developerUri, fileUri: developerUri); final Class hasCreationLocationClass = new Class( name: '_HasCreationLocation', isAbstract: true, fileUri: developerUri, ); developerLib.addClass(hasCreationLocationClass); final Class creationLocationClass = new Class( name: 'CreationLocation', fileUri: developerUri, ); final Constructor creationLocationConstructor = new Constructor( new FunctionNode( null, namedParameters: [ new VariableDeclaration('file', type: const DynamicType()), new VariableDeclaration('line', type: const DynamicType()), new VariableDeclaration('column', type: const DynamicType()), new VariableDeclaration('name', type: const DynamicType()), ], ), name: new Name('_', developerLib), fileUri: developerUri, ); creationLocationClass.addConstructor(creationLocationConstructor); developerLib.addClass(creationLocationClass); final Uri coreUri = Uri.parse('dart:core'); final Library coreLib = new Library(coreUri, fileUri: coreUri); final Class pragmaClass = new Class(name: 'pragma', fileUri: coreUri); coreLib.addClass(pragmaClass); final Field pragmaNameField = new Field.immutable( new Name('name'), fileUri: coreUri, ); pragmaClass.addField(pragmaNameField); final Uri testUri = Uri.parse('package:test/test.dart'); final Library testLib = new Library(testUri, fileUri: testUri); final Class myWidgetClass = new Class(name: 'MyWidget', fileUri: testUri); myWidgetClass.addAnnotation( new ConstantExpression( new InstanceConstant( pragmaClass.reference, [], { pragmaNameField.fieldReference: new StringConstant( 'track-creation-locations', ), }, ), ), ); myWidgetClass.addConstructor( new Constructor( new FunctionNode(new Block([])), name: new Name(''), fileUri: testUri, ), ); testLib.addClass(myWidgetClass); const int fileOffset = 100; final Procedure mainProcedure = new Procedure( new Name('main'), ProcedureKind.Method, new FunctionNode( new Block([ new ExpressionStatement( new ConstructorInvocation( myWidgetClass.constructors.first, new Arguments([]), )..fileOffset = fileOffset, ), ]), ), isStatic: true, fileUri: testUri, ); testLib.addProcedure(mainProcedure); final WidgetCreatorTracker tracker = new WidgetCreatorTracker(); tracker.transform([testLib], [developerLib, testLib], null); // Verification Expect.isTrue( myWidgetClass.implementedTypes.any( (s) => s.classNode == hasCreationLocationClass, ), ); Expect.isTrue(myWidgetClass.fields.any((f) => f.name.text == '_location')); final Constructor constructor = myWidgetClass.constructors.first; const String creationLocationPrefix = r'$creationLocation'; Expect.isTrue( constructor.function.namedParameters.any( (p) => p.name!.startsWith(creationLocationPrefix), ), ); final Block body = mainProcedure.function.body as Block; final ExpressionStatement stmt = body.statements.first as ExpressionStatement; final ConstructorInvocation invocation = stmt.expression as ConstructorInvocation; Expect.isTrue( invocation.arguments.named.any( (n) => n.name.startsWith(creationLocationPrefix), ), ); final NamedExpression namedArg = invocation.arguments.named.firstWhere( (n) => n.name.startsWith(creationLocationPrefix), ); Expect.isTrue(namedArg.value is ConstructorInvocation); final ConstructorInvocation locInvocation = namedArg.value as ConstructorInvocation; Expect.equals(creationLocationClass, locInvocation.target.enclosingClass); }