// Copyright (c) 2016, 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:analyzer/dart/ast/ast.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import '../src/dart/resolution/context_collection_resolution.dart'; main() { defineReflectiveSuite(() { defineReflectiveTests(SimpleResolverTest); }); } @reflectiveTest class SimpleResolverTest extends PubPackageResolutionTest { test_argumentResolution_required_matching() async { await resolveTestCode(r''' class A { void f() { g(1, 2, 3); } void g(a, b, c) {} }'''); await _validateArgumentResolution([0, 1, 2]); } test_argumentResolution_required_tooFew() async { await resolveTestCode(r''' class A { void f() { g(1, 2); } void g(a, b, c) {} }'''); await _validateArgumentResolution([0, 1]); } test_argumentResolution_required_tooMany() async { await resolveTestCode(r''' class A { void f() { g(1, 2, 3); } void g(a, b) {} }'''); await _validateArgumentResolution([0, 1, -1]); } test_argumentResolution_requiredAndNamed_extra() async { await resolveTestCode(''' class A { void f() { g(1, 2, c: 3, d: 4); } void g(a, b, {c}) {} }'''); await _validateArgumentResolution([0, 1, 2, -1]); } test_argumentResolution_requiredAndNamed_matching() async { await resolveTestCode(r''' class A { void f() { g(1, 2, c: 3); } void g(a, b, {c}) {} }'''); await _validateArgumentResolution([0, 1, 2]); } test_argumentResolution_requiredAndNamed_missing() async { await resolveTestCode(''' class A { void f() { g(1, 2, d: 3); } void g(a, b, {c, d}) {} }'''); await _validateArgumentResolution([0, 1, 3]); } test_argumentResolution_requiredAndPositional_fewer() async { await resolveTestCode(''' class A { void f() { g(1, 2, 3); } void g(a, b, [c, d]) {} }'''); await _validateArgumentResolution([0, 1, 2]); } test_argumentResolution_requiredAndPositional_matching() async { await resolveTestCode(r''' class A { void f() { g(1, 2, 3, 4); } void g(a, b, [c, d]) {} }'''); await _validateArgumentResolution([0, 1, 2, 3]); } test_argumentResolution_requiredAndPositional_more() async { await resolveTestCode(r''' class A { void f() { g(1, 2, 3, 4); } void g(a, b, [c]) {} }'''); await _validateArgumentResolution([0, 1, 2, -1]); } test_argumentResolution_setter_propagated() async { await resolveTestCode(r''' main() { var a = new A(); a.sss = 0; } class A { set sss(x) {} }'''); var rhs = findNode.assignment(' = 0;').rightHandSide; expect(rhs.correspondingParameter, findElement2.parameter('x')); } test_argumentResolution_setter_propagated_propertyAccess() async { await resolveTestCode(r''' main() { var a = new A(); a.b.sss = 0; } class A { B b = new B(); } class B { set sss(x) {} }'''); var rhs = findNode.assignment(' = 0;').rightHandSide; expect(rhs.correspondingParameter, findElement2.parameter('x')); } test_argumentResolution_setter_static() async { await resolveTestCode(r''' main() { A a = new A(); a.sss = 0; } class A { set sss(x) {} }'''); var rhs = findNode.assignment(' = 0;').rightHandSide; expect(rhs.correspondingParameter, findElement2.parameter('x')); } test_argumentResolution_setter_static_propertyAccess() async { await resolveTestCode(r''' main() { A a = new A(); a.b.sss = 0; } class A { B b = new B(); } class B { set sss(x) {} }'''); var rhs = findNode.assignment(' = 0;').rightHandSide; expect(rhs.correspondingParameter, findElement2.parameter('x')); } test_breakTarget_labeled() async { // Verify that the target of the label is correctly found and is recorded // as the unlabeled portion of the statement. await resolveTestCode(r''' void f() { loop1: while (true) { loop2: for (int i = 0; i < 10; i++) { break loop1; break loop2; } } } '''); var break1 = findNode.breakStatement('break loop1;'); var whileStatement = findNode.whileStatement('while ('); expect(break1.target, same(whileStatement)); var break2 = findNode.breakStatement('break loop2;'); var forStatement = findNode.forStatement('for ('); expect(break2.target, same(forStatement)); } test_breakTarget_unlabeledBreakFromDo() async { await resolveTestCode(''' void f() { do { break; } while (true); } '''); var doStatement = findNode.doStatement('do {'); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, same(doStatement)); } test_breakTarget_unlabeledBreakFromFor() async { await resolveTestCode(r''' void f() { for (int i = 0; i < 10; i++) { break; } } '''); var forStatement = findNode.forStatement('for ('); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, same(forStatement)); } test_breakTarget_unlabeledBreakFromForEach() async { await resolveTestCode(''' void f() { for (x in []) { break; } } '''); var forStatement = findNode.forStatement('for ('); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, same(forStatement)); } test_breakTarget_unlabeledBreakFromSwitch() async { await resolveTestCode(r''' void f() { while (true) { switch (0) { case 0: break; } } } '''); var switchStatement = findNode.switchStatement('switch ('); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, same(switchStatement)); } test_breakTarget_unlabeledBreakFromSwitch_language219() async { await resolveTestCode(r''' // @dart = 2.19 void f() { while (true) { switch (0) { case 0: break; } } } '''); var switchStatement = findNode.switchStatement('switch ('); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, same(switchStatement)); } test_breakTarget_unlabeledBreakFromWhile() async { await resolveTestCode(r''' void f() { while (true) { break; } } '''); var whileStatement = findNode.whileStatement('while ('); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, same(whileStatement)); } test_breakTarget_unlabeledBreakToOuterFunction() async { // Verify that unlabeled break statements can't resolve to loops in an // outer function. await resolveTestCode(r''' void f() { while (true) { void g() { break; } } } '''); var breakStatement = findNode.breakStatement('break;'); expect(breakStatement.target, isNull); } test_class_definesCall() async { await assertNoErrorsInCode(r''' class A { int call(int x) { return x; } } int f(A a) { return a(0); }'''); } test_class_extends_implements() async { await assertNoErrorsInCode(r''' class A extends B implements C {} class B {} class C {}'''); } test_continueTarget_labeled() async { // Verify that the target of the label is correctly found and is recorded // as the unlabeled portion of the statement. await resolveTestCode(''' void f() { loop1: while (true) { loop2: for (int i = 0; i < 10; i++) { continue loop1; continue loop2; } } } '''); var continue1 = findNode.continueStatement('continue loop1'); var whileStatement = findNode.whileStatement('while ('); expect(continue1.target, same(whileStatement)); var continue2 = findNode.continueStatement('continue loop2'); var forStatement = findNode.forStatement('for ('); expect(continue2.target, same(forStatement)); } test_continueTarget_unlabeledContinueFromDo() async { await resolveTestCode(''' void f() { do { continue; } while (true); } '''); var doStatement = findNode.doStatement('do {'); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, same(doStatement)); } test_continueTarget_unlabeledContinueFromFor() async { await resolveTestCode(''' void f() { for (int i = 0; i < 10; i++) { continue; } } '''); var forStatement = findNode.forStatement('for ('); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, same(forStatement)); } test_continueTarget_unlabeledContinueFromForEach() async { await resolveTestCode(r''' void f() { for (x in []) { continue; } } '''); var forStatement = findNode.forStatement('for ('); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, same(forStatement)); } test_continueTarget_unlabeledContinueFromWhile() async { await resolveTestCode(r''' void f() { while (true) { continue; } } '''); var whileStatement = findNode.whileStatement('while ('); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, same(whileStatement)); } test_continueTarget_unlabeledContinueSkipsSwitch() async { await resolveTestCode(r''' void f() { while (true) { switch (0) { case 0: continue; } } } '''); var whileStatement = findNode.whileStatement('while ('); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, same(whileStatement)); } test_continueTarget_unlabeledContinueSkipsSwitch_language219() async { await resolveTestCode(r''' // @dart = 2.19 void f() { while (true) { switch (0) { case 0: continue; } } } '''); var whileStatement = findNode.whileStatement('while ('); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, same(whileStatement)); } test_continueTarget_unlabeledContinueToOuterFunction() async { // Verify that unlabeled continue statements can't resolve to loops in an // outer function. await resolveTestCode(r''' void f() { while (true) { void g() { continue; } } } '''); var continueStatement = findNode.continueStatement('continue;'); expect(continueStatement.target, isNull); } test_empty() async { await assertNoErrorsInCode(''); } test_entryPoint_exported() async { newFile('$testPackageLibPath/a.dart', r''' main() {} '''); await assertNoErrorsInCode(r''' export 'a.dart'; '''); var library = result.libraryElement; var main = library.entryPoint!; expect(main, isNotNull); expect(main.library, isNot(same(library))); } test_entryPoint_local() async { await assertNoErrorsInCode(r''' main() {} '''); var library = result.libraryElement; var main = library.entryPoint!; expect(main, isNotNull); expect(main.library, same(library)); } test_entryPoint_none() async { await assertNoErrorsInCode(''); var library = result.libraryElement; expect(library.entryPoint, isNull); } test_enum_externalLibrary() async { newFile('$testPackageLibPath/a.dart', r''' enum EEE {A, B, C} '''); await assertNoErrorsInCode(r''' import 'a.dart'; void f(EEE e) {} '''); } test_extractedMethodAsConstant() async { await assertNoErrorsInCode(r''' abstract class Comparable { int compareTo(T other); static int compare(Comparable a, Comparable b) => a.compareTo(b); } class A { void sort([compare = Comparable.compare]) {} }'''); } test_fieldFormalParameter() async { await assertNoErrorsInCode(r''' class A { int x; int y; A(this.x) : y = x {} }'''); var xParameter = findNode.fieldFormalParameter('this.x'); var xParameterElement = xParameter.declaredFragment!.element; expect(xParameterElement.field, findElement2.field('x')); assertResolvedNodeText(findNode.simple('x {}'), r''' SimpleIdentifier token: x element: ::@class::A::@constructor::new::@formalParameter::x staticType: int '''); } test_forEachLoops_nonConflicting() async { await assertErrorsInCode( r''' f() { List list = [1,2,3]; for (int x in list) {} for (int x in list) {} } ''', [ error(WarningCode.unusedLocalVariable, 40, 1), error(WarningCode.unusedLocalVariable, 65, 1), ], ); } test_forLoops_nonConflicting() async { await assertNoErrorsInCode(r''' f() { for (int i = 0; i < 3; i++) { } for (int i = 0; i < 3; i++) { } }'''); } test_functionTypeAlias() async { await assertNoErrorsInCode(r''' typedef bool P(e); class A { late P p; m(e) { if (p(e)) {} } }'''); } test_getter_fromMixins_bare_identifier() async { await assertNoErrorsInCode(''' class B {} mixin M1 { get x => null; } mixin M2 { get x => null; } class C extends B with M1, M2 { f() { return x; } } '''); // Verify that the getter for "x" in C.f() refers to the getter defined in // M2. assertResolvedNodeText(findNode.simple('x;'), r''' SimpleIdentifier token: x element: ::@mixin::M2::@getter::x staticType: dynamic '''); } test_getter_fromMixins_property_access() async { await assertErrorsInCode( ''' class B {} mixin M1 { get x => null; } mixin M2 { get x => null; } class C extends B with M1, M2 {} void main() { var y = new C().x; } ''', [error(WarningCode.unusedLocalVariable, 124, 1)], ); // Verify that the getter for "x" in "new C().x" refers to the getter // defined in M2. assertResolvedNodeText(findNode.simple('x;'), r''' SimpleIdentifier token: x element: ::@mixin::M2::@getter::x staticType: dynamic '''); } test_import_hide() async { newFile('$testPackageLibPath/lib1.dart', r''' set foo(value) {} class A {}'''); newFile('$testPackageLibPath/lib2.dart', r''' set foo(value) {}'''); await assertNoErrorsInCode(r''' import 'lib1.dart' hide foo; import 'lib2.dart'; main() { foo = 0; } A a = A();'''); } test_import_prefix() async { newFile('$testPackageLibPath/a.dart', r''' f(int x) { return x * x; }'''); await assertNoErrorsInCode(r''' import 'a.dart' as _a; main() { _a.f(0); }'''); } test_import_prefix_doesNotExist() async { // // The primary purpose of this test is to ensure that we are only getting a // single error generated when the only problem is that an imported file // does not exist. // await assertErrorsInCode( ''' import 'missing.dart' as p; int a = p.q + p.r.s; String b = p.t(a) + p.u(v: 0); p.T c = new p.T(); class D extends p.T { D(int i) : super(i); p.U f = new p.V(); } class F implements p.T { p.T m(p.U u) => null; } class G extends Object with p.V {} class H extends D { H(int i) : super(i); } ''', [error(CompileTimeErrorCode.uriDoesNotExist, 7, 14)], ); } test_import_show_doesNotExist() async { // // The primary purpose of this test is to ensure that we are only getting a // single error generated when the only problem is that an imported file // does not exist. // await assertErrorsInCode( ''' import 'missing.dart' show q, r, t, u, T, U, V, W; int a = q + r.s; String b = t(a) + u(v: 0); T c = new T(); class D extends T { D(int i) : super(i); U f = new V(); } class F implements T { T m(U u) => null; } class G extends Object with V {} class H extends D { H(int i) : super(i); } ''', [error(CompileTimeErrorCode.uriDoesNotExist, 7, 14)], ); } test_import_spaceInUri() async { newFile('$testPackageLibPath/sub folder/a.dart', r''' foo() {}'''); await assertNoErrorsInCode(r''' import 'sub folder/a.dart'; main() { foo(); }'''); } test_indexExpression_typeParameters() async { await assertNoErrorsInCode(r''' f() { List a = []; a[0]; List> b = []; b[0][0]; List>> c = []; c[0][0][0]; }'''); } test_indexExpression_typeParameters_invalidAssignmentWarning() async { await assertErrorsInCode( r''' f() { List> b = []; b[0][0] = 'hi'; }''', [error(CompileTimeErrorCode.invalidAssignment, 44, 4)], ); } test_indirectOperatorThroughCall() async { await assertNoErrorsInCode(r''' class A { B call() { return new B(); } } class B { int operator [](int i) { return i; } } A f = new A(); g(int x) {} main() { g(f()[0]); }'''); } test_invoke_dynamicThroughGetter() async { await assertNoErrorsInCode(r''' class A { List get X => [() => 0]; m(A a) { X.last; } }'''); } test_isValidMixin_badSuperclass() async { await assertErrorsInCode( r''' class A extends B {} class B {} class C = Object with A;''', [error(CompileTimeErrorCode.mixinInheritsFromNotObject, 54, 1)], ); var a = findElement2.class_('A'); expect(a.isValidMixin, isFalse); } test_isValidMixin_constructor() async { await assertErrorsInCode( r''' class A { A() {} } class C = Object with A;''', [error(CompileTimeErrorCode.mixinClassDeclaresConstructor, 43, 1)], ); var a = findElement2.class_('A'); expect(a.isValidMixin, isFalse); } test_isValidMixin_factoryConstructor() async { await assertNoErrorsInCode(r''' mixin class A { factory A() => throw 0; } class C = Object with A;'''); var a = findElement2.class_('A'); expect(a.isValidMixin, isTrue); } test_isValidMixin_super_toString() async { await assertNoErrorsInCode(r''' mixin class A { toString() { return super.toString(); } } class C = Object with A;'''); var a = findElement2.class_('A'); expect(a.isValidMixin, isTrue); } test_isValidMixin_valid() async { await assertNoErrorsInCode(''' mixin class A {} class C = Object with A;'''); var a = findElement2.class_('A'); expect(a.isValidMixin, isTrue); } test_labels_switch() async { await assertNoErrorsInCode(r''' void doSwitch(int target) { switch (target) { l0: case 0: continue l1; l1: case 1: continue l0; default: continue l1; } }'''); } test_labels_switch_language219() async { await assertNoErrorsInCode(r''' // @dart = 2.19 void doSwitch(int target) { switch (target) { l0: case 0: continue l1; l1: case 1: continue l0; default: continue l1; } }'''); } test_localVariable_types_invoked() async { await resolveTestCode(r''' const A = null; main() { var myVar = (int p) => 'foo'; myVar(42); }'''); var node = findNode.simple('myVar(42)'); assertType(node, 'String Function(int)'); } test_metadata_class() async { await assertNoErrorsInCode(r''' const A = null; @A class C {}'''); var annotations = findElement2.class_('C').metadata.annotations; expect(annotations, hasLength(1)); var cDeclaration = findNode.classDeclaration('C'); assertResolvedNodeText(cDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_classTypeAlias() async { await assertNoErrorsInCode(r''' const A = null; @A class C = D with E; class D {} mixin E {} '''); var annotations = findElement2.class_('C').metadata.annotations; expect(annotations, hasLength(1)); var cDeclaration = findNode.classTypeAlias('C'); assertResolvedNodeText(cDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_enum() async { await assertNoErrorsInCode(''' const A = null; @A enum E { A, B } '''); var annotations = findElement2.enum_('E').metadata.annotations; expect(annotations, hasLength(1)); var eDeclaration = findNode.enumDeclaration('E'); assertResolvedNodeText(eDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_extension() async { await assertNoErrorsInCode(r''' const A = null; @A extension E on List {}'''); var annotations = findElement2.extension_('E').metadata.annotations; expect(annotations, hasLength(1)); var cDeclaration = findNode.extensionDeclaration('E'); assertResolvedNodeText(cDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_field() async { await assertNoErrorsInCode(r''' const A = null; class C { @A int f = 1; }'''); var metadata = findElement2.field('f').metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_fieldFormalParameter() async { await assertNoErrorsInCode(r''' const A = null; class C { int f; C(@A this.f); }'''); var metadata = findElement2.fieldFormalParameter('f').metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_function() async { await assertNoErrorsInCode(r''' const A = null; @A f() {}'''); var annotations = findElement2.topFunction('f').metadata.annotations; expect(annotations, hasLength(1)); } test_metadata_function_generic() async { await assertNoErrorsInCode(r''' const A = null; @A f() {}'''); var annotations = findElement2.topFunction('f').metadata.annotations; expect(annotations, hasLength(1)); var fDeclaration = findNode.functionDeclaration('f'); assertResolvedNodeText(fDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_functionTypeAlias() async { await assertNoErrorsInCode(''' const A = null; @A typedef F(int A); '''); var annotations = findElement2.typeAlias('F').metadata.annotations; expect(annotations, hasLength(1)); var fDeclaration = findNode.functionTypeAlias('F'); assertResolvedNodeText(fDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_functionTypedParameter() async { await assertNoErrorsInCode(r''' const A = null; f(@A int p(int x)) {}'''); var metadata = findElement2.parameter('p').metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_functionTypedParameter_generic() async { await assertNoErrorsInCode(r''' const A = null; f(@A int p(int x)) {}'''); var annotations = findElement2.parameter('p').metadata.annotations; expect(annotations, hasLength(1)); var pDeclaration = findNode.functionTypedFormalParameter('p'); assertResolvedNodeText(pDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_genericTypeAlias() async { await assertNoErrorsInCode(r''' const A = null; @A typedef F = A Function(); '''); var annotations = findElement2.typeAlias('F').metadata.annotations; expect(annotations, hasLength(1)); var fDeclaration = findNode.genericTypeAlias('F'); assertResolvedNodeText(fDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_libraryDirective() async { await assertNoErrorsInCode(r''' @A library lib; const A = null;'''); var metadata = result.libraryElement.metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_method() async { await assertNoErrorsInCode(r''' const A = null; class C { @A void m() {} }'''); var metadata = findElement2.method('m').metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_method_generic() async { await assertNoErrorsInCode(r''' const A = null; class C { @A void m() {} }'''); var annotations = findElement2.method('m').metadata.annotations; expect(annotations, hasLength(1)); var mDeclaration = findNode.methodDeclaration('m'); assertResolvedNodeText(mDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_mixin() async { await assertNoErrorsInCode(r''' const A = null; @A mixin M on Object {}'''); var annotations = findElement2.mixin('M').metadata.annotations; expect(annotations, hasLength(1)); var mDeclaration = findNode.mixinDeclaration('M'); assertResolvedNodeText(mDeclaration.metadata[0], r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_metadata_namedParameter() async { await assertNoErrorsInCode(r''' const A = null; f({@A int p = 0}) {}'''); var metadata = findElement2.parameter('p').metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_positionalParameter() async { await assertNoErrorsInCode(r''' const A = null; f([@A int p = 0]) {}'''); var metadata = findElement2.parameter('p').metadata.annotations; expect(metadata, hasLength(1)); } test_metadata_simpleParameter() async { await assertNoErrorsInCode(r''' const A = null; f(@A p1, @A int p2) {}'''); expect(findElement2.parameter('p1').metadata.annotations, hasLength(1)); expect(findElement2.parameter('p2').metadata.annotations, hasLength(1)); } test_metadata_typedef() async { await assertNoErrorsInCode(r''' const A = null; @A typedef F();'''); expect(findElement2.typeAlias('F').metadata.annotations, hasLength(1)); var annotation = findNode.annotation('@A'); assertResolvedNodeText(annotation, r''' Annotation atSign: @ name: SimpleIdentifier token: A element: ::@getter::A staticType: null element: ::@getter::A '''); } test_method_fromMixin() async { await assertNoErrorsInCode(r''' class B { bar() => 1; } mixin class A { foo() => 2; } class C extends B with A { bar() => super.bar(); foo() => super.foo(); }'''); } test_method_fromMixins() async { await assertNoErrorsInCode(''' class B {} mixin M1 { void f() {} } mixin M2 { void f() {} } class C extends B with M1, M2 {} void main() { new C().f(); } '''); assertResolvedNodeText(findNode.simple('f();'), r''' SimpleIdentifier token: f element: ::@mixin::M2::@method::f staticType: void Function() '''); } test_method_fromMixins_bare_identifier() async { await assertNoErrorsInCode(''' class B {} mixin M1 { void f() {} } mixin M2 { void f() {} } class C extends B with M1, M2 { void g() { f(); } } '''); assertResolvedNodeText(findNode.simple('f();'), r''' SimpleIdentifier token: f element: ::@mixin::M2::@method::f staticType: void Function() '''); } test_method_fromMixins_invoked_from_outside_class() async { await assertNoErrorsInCode(''' class B {} mixin M1 { void f() {} } mixin M2 { void f() {} } class C extends B with M1, M2 {} void main() { new C().f(); } '''); assertResolvedNodeText(findNode.simple('f();'), r''' SimpleIdentifier token: f element: ::@mixin::M2::@method::f staticType: void Function() '''); } test_method_fromSuperclassMixin() async { await assertNoErrorsInCode(r''' mixin A { void m1() {} } class B extends Object with A { } class C extends B { } f(C c) { c.m1(); }'''); } test_methodCascades() async { await assertNoErrorsInCode(r''' class A { void m1() {} void m2() {} void m() { A a = new A(); a..m1() ..m2(); } }'''); } test_methodCascades_withSetter() async { await assertNoErrorsInCode(r''' class A { String name = ''; void m1() {} void m2() {} void m() { A a = new A(); a..m1() ..name = 'name' ..m2(); } }'''); } test_resolveAgainstNull() async { await assertNoErrorsInCode(r''' f(var p) { return null == p; }'''); } test_setter_static() async { await assertNoErrorsInCode(r''' set s(x) { } main() { s = 123; }'''); } /// Resolve the test file and verify that the arguments in a specific method /// invocation were correctly resolved. /// /// The file is expected to define a method named `g`, and has exactly one /// [MethodInvocation] in a statement ending with `);`. It is the arguments to /// that method invocation that are tested. The method invocation can contain /// errors. /// /// The arguments were resolved correctly if the number of expressions in the /// list matches the length of the array of indices and if, for each index in /// the array of indices, the parameter to which the argument expression was /// resolved is the parameter in the invoked method's list of parameters at /// that index. Arguments that should not be resolved to a parameter because /// of an error can be denoted by including a negative index in the array of /// indices. /// /// @param indices the array of indices used to associate arguments with /// parameters /// @throws Exception if the source could not be resolved or if the structure /// of the source is not valid Future _validateArgumentResolution(List indices) async { var g = findElement2.method('g'); var parameters = g.formalParameters; var invocation = findNode.methodInvocation(');'); var arguments = invocation.argumentList.arguments; var argumentCount = arguments.length; expect(argumentCount, indices.length); for (var i = 0; i < argumentCount; i++) { var argument = arguments[i]; var actualParameter = argument.correspondingParameter; var index = indices[i]; if (index < 0) { expect(actualParameter, isNull); } else { var expectedParameter = parameters[index]; expect(actualParameter, same(expectedParameter)); } } } }