fix location tracking transformer for use in flutter

Closes https://github.com/dart-lang/sdk/pull/63551

GitOrigin-RevId: a14cbbd88c605d193e2a1b744727765c45d38278
Change-Id: Ied6c2fc4ee3b5343c6ab8e38080542d667175f4d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/510160
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
Kilian Schulte
2026-06-10 04:15:37 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 034b90af5d
commit dd4f983cee
5 changed files with 714 additions and 87 deletions
@@ -0,0 +1,117 @@
# Copyright (c) 2026, 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.
type: newworld
target: dartdevc
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
main.dart: |
import 'foo.dart';
Foo newFoo = new Foo();
var newFooFunction = Foo.new;
var newFooFunctionCall = newFooFunction();
Foo extensionFoo = null.foo();
var extensionFooFunction = null.foo;
var extensionFooFunctionCall = extensionFooFunction();
Foo extensionBar = null.bar();
Foo extensionBaz = null.baz();
Foo extensionBoz = null.boz();
Foo extensionConstFoo = null.constFoo();
Foo extensionGetterFoo = null.getterFoo;
var extensionSetterFoo = () => null.setterFoo = null;
Foo extensionOperatorFoo = -null;
Foo extensionStaticFoo = FooExtension.staticFoo();
foo.dart: |
import 'package:flutter/src/widgets/framework.dart';
const alias = widgetFactory;
const nullableAlias = false ? widgetFactory : null;
const nonNullableAlias = false ? widgetFactory : alias;
class Foo extends Widget {
factory Foo() => const Foo._();
const Foo._();
}
extension FooExtension on Object? {
@widgetFactory
Foo foo() => Foo._();
@alias
Foo bar() => Foo._();
@nullableAlias
Foo baz() => Foo._();
@nonNullableAlias
Foo boz() => Foo._();
@widgetFactory
Foo constFoo() => const Foo._();
@widgetFactory
Foo get getterFoo => new Foo._();
@widgetFactory
void set setterFoo(_) => new Foo._();
@widgetFactory
Foo operator -() => new Foo._();
@widgetFactory
static Foo staticFoo() => new Foo._();
}
flutter/lib/src/widgets/framework.dart: |
export 'widget_inspector.dart' show widgetFactory;
abstract class Bar {
const Bar();
}
@pragma('track-creation-locations')
abstract class Widget extends Bar {
const Widget();
}
flutter/lib/src/widgets/widget_inspector.dart: |
const widgetFactory = pragma('track-creation-locations');
.dart_tool/package_config.json: |
{
"configVersion": 2,
"packages": [
{
"name": "flutter",
"rootUri": "../flutter",
"packageUri": "lib/"
}
]
}
expectedLibraryCount: 4
- entry: main.dart
worldType: updated
invalidate:
- main.dart
expectInitializeFromDill: false
sources:
main.dart: |
import 'foo.dart';
Foo extensionConstFoo = null.constFoo();
Foo newFoo = new Foo();
var newFooFunction = Foo.new;
var newFooFunctionCall = newFooFunction();
Foo extensionFoo = null.foo();
var extensionFooFunction = null.foo;
var extensionFooFunctionCall = extensionFooFunction();
Foo extensionBar = null.bar();
Foo extensionBaz = null.baz();
Foo extensionBoz = null.boz();
Foo extensionGetterFoo = null.getterFoo;
var extensionSetterFoo = () => null.setterFoo = null;
Foo extensionOperatorFoo = -null;
Foo extensionStaticFoo = FooExtension.staticFoo();
expectedLibraryCount: 4
advancedInvalidation: bodiesOnly
@@ -0,0 +1,194 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
additionalExports = (wid::widgetFactory)
export "package:flutter/src/widgets/widget_inspector.dart" show widgetFactory;
abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
const constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "package:flutter/src/widgets/widget_inspector.dart" as wid {
static const field dart.core::pragma widgetFactory = #C3;
}
library from "org-dartlang-test:///foo.dart" as foo {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C9;
static synthetic method _#new#tearOff() → foo::Foo
return foo::Foo::•($creationLocationd_0dea112b090073317d4: #C11);
static synthetic method _#_#tearOff() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C14);
}
extension FooExtension on dart.core::Object? {
method foo = foo::FooExtension|foo;
method tearoff foo = foo::FooExtension|get#foo;
method bar = foo::FooExtension|bar;
method tearoff bar = foo::FooExtension|get#bar;
method baz = foo::FooExtension|baz;
method tearoff baz = foo::FooExtension|get#baz;
method boz = foo::FooExtension|boz;
method tearoff boz = foo::FooExtension|get#boz;
method constFoo = foo::FooExtension|constFoo;
method tearoff constFoo = foo::FooExtension|get#constFoo;
get getterFoo = foo::FooExtension|get#getterFoo;
set setterFoo = foo::FooExtension|set#setterFoo;
operator unary- = foo::FooExtension|unary-;
static method staticFoo = foo::FooExtension|staticFoo;
}
static const field dart.core::pragma alias = #C3;
static const field dart.core::pragma? nullableAlias = #C2;
static const field dart.core::pragma nonNullableAlias = #C3;
@#C3
static extension-member method FooExtension|foo(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static extension-member method FooExtension|get#foo(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|foo(#this, $creationLocationd_0dea112b090073317d4: #C18);
@#C3
static extension-member method FooExtension|bar(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static extension-member method FooExtension|get#bar(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|bar(#this, $creationLocationd_0dea112b090073317d4: #C21);
@#C2
static extension-member method FooExtension|baz(lowered final dart.core::Object? #this) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C24);
static extension-member method FooExtension|get#baz(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|baz(#this);
@#C3
static extension-member method FooExtension|boz(lowered final dart.core::Object? #this) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C26);
static extension-member method FooExtension|get#boz(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|boz(#this);
@#C3
static extension-member method FooExtension|constFoo(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C29;
static extension-member method FooExtension|get#constFoo(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|constFoo(#this, $creationLocationd_0dea112b090073317d4: #C31);
@#C3
static extension-member method FooExtension|get#getterFoo(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
@#C3
static extension-member method FooExtension|set#setterFoo(lowered final dart.core::Object? #this, wildcard dynamic _#wc0#formal, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → void
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
@#C3
static extension-member method FooExtension|unary-(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
@#C3
static extension-member method FooExtension|staticFoo() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C34);
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///foo.dart";
static field foo::Foo newFoo = foo::Foo::•($creationLocationd_0dea112b090073317d4: #C38);
static field () → foo::Foo newFooFunction = #C39;
static field foo::Foo newFooFunctionCall = main::newFooFunction(){() → foo::Foo};
static field foo::Foo extensionFoo = foo::FooExtension|foo(null, $creationLocationd_0dea112b090073317d4: #C42);
static field () → foo::Foo extensionFooFunction = foo::FooExtension|get#foo(null);
static field foo::Foo extensionFooFunctionCall = main::extensionFooFunction(){() → foo::Foo};
static field foo::Foo extensionBar = foo::FooExtension|bar(null, $creationLocationd_0dea112b090073317d4: #C43);
static field foo::Foo extensionBaz = foo::FooExtension|baz(null);
static field foo::Foo extensionBoz = foo::FooExtension|boz(null);
static field foo::Foo extensionConstFoo = foo::FooExtension|constFoo(null, $creationLocationd_0dea112b090073317d4: #C45);
static field foo::Foo extensionGetterFoo = foo::FooExtension|get#getterFoo(null, $creationLocationd_0dea112b090073317d4: #C49);
static field () → Null extensionSetterFoo = () → Null => let final has-declared-initializer Null #t1 = null in let final void #t2 = foo::FooExtension|set#setterFoo(null, #t1, $creationLocationd_0dea112b090073317d4: #C53) in #t1;
static field foo::Foo extensionOperatorFoo = foo::FooExtension|unary-(null, $creationLocationd_0dea112b090073317d4: #C56);
static field foo::Foo extensionStaticFoo = foo::FooExtension|staticFoo();
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///foo.dart"
#C5 = 8.0
#C6 = 26.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = foo::Foo {_location:#C8}
#C10 = 11.0
#C11 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C10, name:#C7}
#C12 = 10.0
#C13 = 9.0
#C14 = dart.developer::CreationLocation {file:#C4, line:#C12, column:#C13, name:#C7}
#C15 = 14.0
#C16 = 3.0
#C17 = "FooExtension|foo"
#C18 = dart.developer::CreationLocation {file:#C4, line:#C15, column:#C16, name:#C17}
#C19 = 17.0
#C20 = "FooExtension|bar"
#C21 = dart.developer::CreationLocation {file:#C4, line:#C19, column:#C16, name:#C20}
#C22 = 21.0
#C23 = 20.0
#C24 = dart.developer::CreationLocation {file:#C4, line:#C22, column:#C23, name:#C7}
#C25 = 24.0
#C26 = dart.developer::CreationLocation {file:#C4, line:#C25, column:#C23, name:#C7}
#C27 = 27.0
#C28 = dart.developer::CreationLocation {file:#C4, line:#C27, column:#C27, name:#C7}
#C29 = foo::Foo {_location:#C28}
#C30 = "FooExtension|constFoo"
#C31 = dart.developer::CreationLocation {file:#C4, line:#C6, column:#C16, name:#C30}
#C32 = 39.0
#C33 = 33.0
#C34 = dart.developer::CreationLocation {file:#C4, line:#C32, column:#C33, name:#C7}
#C35 = "org-dartlang-test:///main.dart"
#C36 = 2.0
#C37 = 18.0
#C38 = dart.developer::CreationLocation {file:#C35, line:#C36, column:#C37, name:#C7}
#C39 = static-tearoff foo::Foo::_#new#tearOff
#C40 = 5.0
#C41 = 25.0
#C42 = dart.developer::CreationLocation {file:#C35, line:#C40, column:#C41, name:#C17}
#C43 = dart.developer::CreationLocation {file:#C35, line:#C5, column:#C41, name:#C20}
#C44 = 30.0
#C45 = dart.developer::CreationLocation {file:#C35, line:#C10, column:#C44, name:#C30}
#C46 = 12.0
#C47 = 31.0
#C48 = "FooExtension|get#getterFoo"
#C49 = dart.developer::CreationLocation {file:#C35, line:#C46, column:#C47, name:#C48}
#C50 = 13.0
#C51 = 37.0
#C52 = "FooExtension|set#setterFoo"
#C53 = dart.developer::CreationLocation {file:#C35, line:#C50, column:#C51, name:#C52}
#C54 = 28.0
#C55 = "FooExtension|unary-"
#C56 = dart.developer::CreationLocation {file:#C35, line:#C15, column:#C54, name:#C55}
}
Constructor coverage from constants:
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///foo.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Foo._ (from org-dartlang-test:///foo.dart:10:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:9:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:4:9)
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///flutter/lib/src/widgets/widget_inspector.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
@@ -0,0 +1,194 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
additionalExports = (wid::widgetFactory)
export "package:flutter/src/widgets/widget_inspector.dart" show widgetFactory;
abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
const constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "package:flutter/src/widgets/widget_inspector.dart" as wid {
static const field dart.core::pragma widgetFactory = #C3;
}
library from "org-dartlang-test:///foo.dart" as foo {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C9;
static synthetic method _#new#tearOff() → foo::Foo
return foo::Foo::•($creationLocationd_0dea112b090073317d4: #C11);
static synthetic method _#_#tearOff() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C14);
}
extension FooExtension on dart.core::Object? {
method foo = foo::FooExtension|foo;
method tearoff foo = foo::FooExtension|get#foo;
method bar = foo::FooExtension|bar;
method tearoff bar = foo::FooExtension|get#bar;
method baz = foo::FooExtension|baz;
method tearoff baz = foo::FooExtension|get#baz;
method boz = foo::FooExtension|boz;
method tearoff boz = foo::FooExtension|get#boz;
method constFoo = foo::FooExtension|constFoo;
method tearoff constFoo = foo::FooExtension|get#constFoo;
get getterFoo = foo::FooExtension|get#getterFoo;
set setterFoo = foo::FooExtension|set#setterFoo;
operator unary- = foo::FooExtension|unary-;
static method staticFoo = foo::FooExtension|staticFoo;
}
static const field dart.core::pragma alias = #C3;
static const field dart.core::pragma? nullableAlias = #C2;
static const field dart.core::pragma nonNullableAlias = #C3;
@#C3
static extension-member method FooExtension|foo(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static extension-member method FooExtension|get#foo(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|foo(#this, $creationLocationd_0dea112b090073317d4: #C18);
@#C3
static extension-member method FooExtension|bar(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static extension-member method FooExtension|get#bar(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|bar(#this, $creationLocationd_0dea112b090073317d4: #C21);
@#C2
static extension-member method FooExtension|baz(lowered final dart.core::Object? #this) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C24);
static extension-member method FooExtension|get#baz(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|baz(#this);
@#C3
static extension-member method FooExtension|boz(lowered final dart.core::Object? #this) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C26);
static extension-member method FooExtension|get#boz(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|boz(#this);
@#C3
static extension-member method FooExtension|constFoo(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C29;
static extension-member method FooExtension|get#constFoo(lowered final dart.core::Object? #this) → () → foo::Foo
return () → foo::Foo => foo::FooExtension|constFoo(#this, $creationLocationd_0dea112b090073317d4: #C31);
@#C3
static extension-member method FooExtension|get#getterFoo(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
@#C3
static extension-member method FooExtension|set#setterFoo(lowered final dart.core::Object? #this, wildcard dynamic _#wc0#formal, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → void
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
@#C3
static extension-member method FooExtension|unary-(lowered final dart.core::Object? #this, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
@#C3
static extension-member method FooExtension|staticFoo() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C34);
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///foo.dart";
static field foo::Foo extensionConstFoo = foo::FooExtension|constFoo(null, $creationLocationd_0dea112b090073317d4: #C38);
static field foo::Foo newFoo = foo::Foo::•($creationLocationd_0dea112b090073317d4: #C40);
static field () → foo::Foo newFooFunction = #C41;
static field foo::Foo newFooFunctionCall = main::newFooFunction(){() → foo::Foo};
static field foo::Foo extensionFoo = foo::FooExtension|foo(null, $creationLocationd_0dea112b090073317d4: #C44);
static field ({$creationLocationd_0dea112b090073317d4: dart.developer::CreationLocation?}) → foo::Foo extensionFooFunction = foo::FooExtension|get#foo(null);
static field foo::Foo extensionFooFunctionCall = main::extensionFooFunction(){({$creationLocationd_0dea112b090073317d4: dart.developer::CreationLocation?}) → foo::Foo};
static field foo::Foo extensionBar = foo::FooExtension|bar(null, $creationLocationd_0dea112b090073317d4: #C45);
static field foo::Foo extensionBaz = foo::FooExtension|baz(null);
static field foo::Foo extensionBoz = foo::FooExtension|boz(null);
static field foo::Foo extensionGetterFoo = foo::FooExtension|get#getterFoo(null, $creationLocationd_0dea112b090073317d4: #C49);
static field () → Null extensionSetterFoo = () → Null => let final has-declared-initializer Null #t1 = null in let final void #t2 = foo::FooExtension|set#setterFoo(null, #t1, $creationLocationd_0dea112b090073317d4: #C53) in #t1;
static field foo::Foo extensionOperatorFoo = foo::FooExtension|unary-(null, $creationLocationd_0dea112b090073317d4: #C56);
static field foo::Foo extensionStaticFoo = foo::FooExtension|staticFoo();
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///foo.dart"
#C5 = 8.0
#C6 = 26.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = foo::Foo {_location:#C8}
#C10 = 11.0
#C11 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C10, name:#C7}
#C12 = 10.0
#C13 = 9.0
#C14 = dart.developer::CreationLocation {file:#C4, line:#C12, column:#C13, name:#C7}
#C15 = 14.0
#C16 = 3.0
#C17 = "FooExtension|foo"
#C18 = dart.developer::CreationLocation {file:#C4, line:#C15, column:#C16, name:#C17}
#C19 = 17.0
#C20 = "FooExtension|bar"
#C21 = dart.developer::CreationLocation {file:#C4, line:#C19, column:#C16, name:#C20}
#C22 = 21.0
#C23 = 20.0
#C24 = dart.developer::CreationLocation {file:#C4, line:#C22, column:#C23, name:#C7}
#C25 = 24.0
#C26 = dart.developer::CreationLocation {file:#C4, line:#C25, column:#C23, name:#C7}
#C27 = 27.0
#C28 = dart.developer::CreationLocation {file:#C4, line:#C27, column:#C27, name:#C7}
#C29 = foo::Foo {_location:#C28}
#C30 = "FooExtension|constFoo"
#C31 = dart.developer::CreationLocation {file:#C4, line:#C6, column:#C16, name:#C30}
#C32 = 39.0
#C33 = 33.0
#C34 = dart.developer::CreationLocation {file:#C4, line:#C32, column:#C33, name:#C7}
#C35 = "org-dartlang-test:///main.dart"
#C36 = 2.0
#C37 = 30.0
#C38 = dart.developer::CreationLocation {file:#C35, line:#C36, column:#C37, name:#C30}
#C39 = 18.0
#C40 = dart.developer::CreationLocation {file:#C35, line:#C16, column:#C39, name:#C7}
#C41 = static-tearoff foo::Foo::_#new#tearOff
#C42 = 6.0
#C43 = 25.0
#C44 = dart.developer::CreationLocation {file:#C35, line:#C42, column:#C43, name:#C17}
#C45 = dart.developer::CreationLocation {file:#C35, line:#C13, column:#C43, name:#C20}
#C46 = 12.0
#C47 = 31.0
#C48 = "FooExtension|get#getterFoo"
#C49 = dart.developer::CreationLocation {file:#C35, line:#C46, column:#C47, name:#C48}
#C50 = 13.0
#C51 = 37.0
#C52 = "FooExtension|set#setterFoo"
#C53 = dart.developer::CreationLocation {file:#C35, line:#C50, column:#C51, name:#C52}
#C54 = 28.0
#C55 = "FooExtension|unary-"
#C56 = dart.developer::CreationLocation {file:#C35, line:#C15, column:#C54, name:#C55}
}
Constructor coverage from constants:
org-dartlang-test:///foo.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Foo._ (from org-dartlang-test:///foo.dart:10:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:9:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:4:9)
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///flutter/lib/src/widgets/widget_inspector.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
@@ -218,13 +218,19 @@ class _WidgetCallSiteTransformer extends Transformer {
);
return node;
}
if (_isWidgetFactory(target) && _tracker._locationClass != null) {
_addLocationArgument(
node,
target.function,
locationClass: _tracker._locationClass!,
if (_isWidgetFactory(target)) {
final Variable parameter = target.function.namedParameters.firstWhere(
(p) => p.name == _creationLocationParameterName,
);
return node;
final DartType type = parameter.type;
if (type is InterfaceType) {
_addLocationArgument(
node,
target.function,
locationClass: type.classNode,
);
return node;
}
}
return node;
}
@@ -383,7 +389,6 @@ class WidgetCreatorTracker {
void _resolveWellKnownClasses(Iterable<Library> libraries) {
// If the Widget or Debug location classes have been updated we need to get
// the latest version
for (Library library in libraries) {
final Uri importUri = library.importUri;
@@ -589,14 +594,12 @@ class WidgetCreatorTracker {
changedStructureNotifier,
);
}
if (_widgetFactoryClass != null) {
for (Extension extension in library.extensions) {
_transformWidgetFactories(
librariesToTransform,
transformedExtensions,
extension,
);
}
for (Extension extension in library.extensions) {
_transformExtension(
librariesToTransform,
transformedExtensions,
extension,
);
}
}
@@ -612,17 +615,6 @@ class WidgetCreatorTracker {
}
_TrackingClasses? _getTrackingClasses(Class clazz) {
// Legacy Case: Check for widget class.
if (_isSubclassOfWidget(clazz)) {
if (_hasCreationLocationClass != null && _locationClass != null) {
return new _TrackingClasses(
hasCreationLocationClass: _hasCreationLocationClass!,
locationClass: _locationClass!,
locationFieldName: _locationFieldName,
);
}
}
// New Case: Check for 'pragma('track-creation-locations')' annotation.
if (_hasTrackCreationLocationsPragmaAnnotation(clazz)) {
if (_developerHasCreationLocationClass != null &&
@@ -635,6 +627,17 @@ class WidgetCreatorTracker {
}
}
// Legacy Case: Check for widget class.
if (_isSubclassOfWidget(clazz)) {
if (_hasCreationLocationClass != null && _locationClass != null) {
return new _TrackingClasses(
hasCreationLocationClass: _hasCreationLocationClass!,
locationClass: _locationClass!,
locationFieldName: _locationFieldName,
);
}
}
return null;
}
@@ -645,50 +648,81 @@ class WidgetCreatorTracker {
bool _hasTrackCreationLocationsPragmaAnnotation(Class clazz) {
if (_developerHasCreationLocationClass == null) return false;
return _isSubclassWhere(clazz, (Class c) {
for (Expression annotation in c.annotations) {
// Case before constant evaluation (newly compiled modules).
if (annotation is RedirectingFactoryInvocation) {
final expression = annotation.expression;
return _isSubclassWhere(
clazz,
(Class c) => _hasPragmaAnnotation(c, 'track-creation-locations'),
);
}
if (expression is ConstructorInvocation) {
final Class enclosingClass = expression.target.enclosingClass;
bool _hasPragmaAnnotation(Annotatable node, String pragmaName) {
for (Expression annotation in node.annotations) {
if (_getPragmaName(annotation) == pragmaName) {
return true;
}
}
return false;
}
if (enclosingClass.name == 'pragma' &&
enclosingClass.enclosingLibrary.importUri.toString() ==
'dart:core') {
if (expression.arguments.positional.isNotEmpty) {
final Expression firstArg =
expression.arguments.positional.first;
if (firstArg is StringLiteral &&
firstArg.value == 'track-creation-locations') {
return true;
}
}
}
String? _getPragmaName(Expression expression) {
// Direct invocation, e.g. `@pragma('...')`.
// Applies before constant evaluation (newly compiled modules).
if (expression
case RedirectingFactoryInvocation(
expression: final ConstructorInvocation invocation,
) ||
final ConstructorInvocation invocation) {
final Class enclosingClass = invocation.target.enclosingClass;
if (enclosingClass.name == 'pragma' &&
enclosingClass.enclosingLibrary.importUri.toString() == 'dart:core') {
if (invocation.arguments.positional.isNotEmpty) {
final Expression firstArg = invocation.arguments.positional.first;
if (firstArg is StringLiteral) {
return firstArg.value;
}
}
// Case after constant evaluation (incremental or modular compilation).
if (annotation case ConstantExpression(
constant: final InstanceConstant constant,
)) {
final Class enclosingClass = constant.classNode;
}
}
if (enclosingClass.name == 'pragma' &&
enclosingClass.enclosingLibrary.importUri.toString() ==
'dart:core') {
for (final Constant value in constant.fieldValues.values) {
if (value case StringConstant(
value: 'track-creation-locations',
)) {
return true;
}
// Aliased constant, e.g. `@widgetFactory`
// with `const widgetFactory = pragma('...');`.
// Applies before constant evaluation (newly compiled modules).
if (expression is StaticGet) {
final Member target = expression.target;
if (target is Field && target.isConst) {
if (target.initializer != null) {
return _getPragmaName(target.initializer!);
}
}
}
// Evaluated constant pragma.
// Applies after constant evaluation (incremental or modular compilation).
if (expression is ConstantExpression) {
final Constant constant = expression.constant;
if (constant is InstanceConstant) {
final Class enclosingClass = constant.classNode;
if (enclosingClass.name == 'pragma' &&
enclosingClass.enclosingLibrary.importUri.toString() ==
'dart:core') {
for (final Constant value in constant.fieldValues.values) {
if (value is StringConstant) {
return value.value;
}
}
}
}
return false;
});
}
return null;
}
Class? _getFactoryLocationClass(Procedure method) {
if (_hasPragmaAnnotation(method, 'track-creation-locations')) {
return _developerCreationLocationClass;
}
if (_hasWidgetFactoryAnnotation(method)) {
return _locationClass;
}
return null;
}
bool _isSubclassWhere(Class a, bool Function(Class b) predicate) {
@@ -845,14 +879,13 @@ class WidgetCreatorTracker {
clazz.constructors.forEach(handleConstructor);
}
void _transformWidgetFactories(
void _transformExtension(
Set<Library> librariesToBeTransformed,
Set<Extension> transformedExtensions,
Extension extension,
) {
// TODO(johnniwinther): We should have a lint for unsupported use of
// `@widgetFactory`.
assert(_widgetFactoryClass != null);
if (!librariesToBeTransformed.contains(extension.enclosingLibrary) ||
!transformedExtensions.add(extension)) {
@@ -866,35 +899,34 @@ class WidgetCreatorTracker {
continue;
}
final Procedure? method = member.memberReference?.asProcedure;
if (method != null && _hasWidgetFactoryAnnotation(method)) {
_maybeAddNamedParameter(
method.function,
new Variable(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass!,
extension.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
),
);
if (method != null) {
_transformExtensionProcedure(method, extension);
}
final Procedure? tearOff = member.tearOffReference?.asProcedure;
if (tearOff != null && _hasWidgetFactoryAnnotation(tearOff)) {
_maybeAddNamedParameter(
tearOff.function,
new Variable(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass!,
extension.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
),
);
if (tearOff != null) {
_transformExtensionProcedure(tearOff, extension);
}
}
}
void _transformExtensionProcedure(Procedure method, Extension extension) {
final Class? locationClass = _getFactoryLocationClass(method);
if (locationClass == null) {
return;
}
_maybeAddNamedParameter(
method.function,
new Variable(
_creationLocationParameterName,
type: new InterfaceType(
locationClass,
extension.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
),
);
}
}
/// Holds the set of creation location classes, either
@@ -92,6 +92,74 @@ void main() {
);
testLib.addProcedure(mainProcedure);
final Extension extension =
new Extension(name: 'MyExtension', fileUri: testUri)
..fileOffset = fileOffset
..onType = const DynamicType();
testLib.addExtension(extension);
final Procedure factoryMethod =
new Procedure(
new Name('myFactory'),
ProcedureKind.Method,
new FunctionNode(
new Block([
new ReturnStatement(
new ConstructorInvocation(
myWidgetClass.constructors.first,
new Arguments([]),
)..fileOffset = fileOffset,
),
]),
returnType: new InterfaceType(
myWidgetClass,
Nullability.nonNullable,
),
),
isStatic: true,
fileUri: testUri,
)
..fileOffset = fileOffset
..isExtensionMember = true;
factoryMethod.addAnnotation(
new ConstantExpression(
new InstanceConstant(
pragmaClass.reference,
<DartType>[],
<Reference, Constant>{
pragmaNameField.fieldReference: new StringConstant(
'track-creation-locations',
),
},
),
),
);
testLib.addProcedure(factoryMethod);
extension.memberDescriptors.add(
new ExtensionMemberDescriptor(
name: new Name('myFactory'),
kind: ExtensionMemberKind.Method,
memberReference: factoryMethod.reference,
tearOffReference: null,
),
);
final Block newBody = new Block([
new ExpressionStatement(
new ConstructorInvocation(
myWidgetClass.constructors.first,
new Arguments([]),
)..fileOffset = fileOffset,
),
new ExpressionStatement(
new StaticInvocation(factoryMethod, new Arguments([]))
..fileOffset = fileOffset,
),
]);
newBody.parent = mainProcedure.function;
mainProcedure.function.body = newBody;
final WidgetCreatorTracker tracker = new WidgetCreatorTracker();
tracker.transform([testLib], [developerLib, testLib], null);
@@ -124,8 +192,30 @@ void main() {
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);
Expect.isTrue(
factoryMethod.function.namedParameters.any(
(p) => p.name!.startsWith(creationLocationPrefix),
),
);
final ExpressionStatement stmt2 = body.statements[1] as ExpressionStatement;
final StaticInvocation staticInv = stmt2.expression as StaticInvocation;
Expect.isTrue(
staticInv.arguments.named.any(
(n) => n.name.startsWith(creationLocationPrefix),
),
);
final NamedExpression namedArg2 = staticInv.arguments.named.firstWhere(
(n) => n.name.startsWith(creationLocationPrefix),
);
Expect.isTrue(namedArg2.value is ConstructorInvocation);
final ConstructorInvocation locInvocation2 =
namedArg2.value as ConstructorInvocation;
Expect.equals(creationLocationClass, locInvocation2.target.enclosingClass);
}