// Copyright (c) 2021, 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/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import '../dart/resolution/context_collection_resolution.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(UnusedResultTest);
  });
}

@reflectiveTest
class UnusedResultTest extends PubPackageResolutionTest {
  @override
  void setUp() {
    super.setUp();
    writeTestPackageConfigWithMeta();
  }

  test_as() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {}
class B extends A {}

B createB() {
  return createA() as B;
}

@UseResult('')
A createA() {
  return B();
}
''');
  }

  test_as_without_usage() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {}
class B extends A {}

void test() {
  createA() as B;
}

@UseResult('')
A createA() {
  return B();
}
''', [
      error(WarningCode.UNUSED_RESULT, 83, 7),
    ]);
  }

  test_callable() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {

  @useResult
  int call() => 1;
}

void f(A a) {
  a();
}
''', [
      error(WarningCode.UNUSED_RESULT, 96, 1,
          text: "The value of 'a' should be used."),
    ]);
  }

  test_callable_method() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  B b() => B();
}
class B {
  @useResult
  int call(int i) => i;
}

void f(A a) {
  a.b()(5);
}
''', [
      error(WarningCode.UNUSED_RESULT, 130, 1,
          text: "The value of 'b' should be used."),
    ]);
  }

  test_callable_propertyAccess() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  B get b => B();
}
class B {
  @useResult
  int call() => 1;
}

void f(A a) {
  a.b();
}
''', [
      error(WarningCode.UNUSED_RESULT, 127, 1,
          text: "The value of 'b' should be used."),
    ]);
  }

  test_callable_recursive() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  B call(List l) => B();
}
class B {
  C call() => C();
}
class C {
  @useResult
  int call(String s) => 1;
}
void f(A a) {
  a([])()('');
}
''', [
      error(WarningCode.UNUSED_RESULT, 170, 1,
          text: "The value of 'a' should be used."),
    ]);
  }

  test_constructor_result_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  A();
}

void f() {
  var bar = A();
  print(bar);
}
''');
  }

  test_constructor_result_notUsed() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  A();
}

void f() {
  A();
}
''', [
      error(WarningCode.UNUSED_RESULT, 80, 3),
    ]);
  }

  test_field_result_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  var bar = A().foo; // OK
  print(bar);
}
''');
  }

  test_field_result_assigned_conditional_else() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(bool b) {
  var bar = b ? 0 : A().foo; // OK
  print(bar);
}
''');
  }

  test_field_result_assigned_conditional_if() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(bool b) {
  var bar = b ? A().foo : 0; // OK
  print(bar);
}
''');
  }

  test_field_result_assigned_conditional_if_parens() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(bool b) {
  var c = b ? (A().foo) : 0;
  print(c);
}
''');
  }

  test_field_result_assigned_parenthesized() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  var bar = ((A().foo)); // OK
  print(bar);
}
''');
  }

  test_field_result_functionExpression_unused() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  Function foo = () {};
}

void main() {
  A().foo;
}
''', [
      error(WarningCode.UNUSED_RESULT, 104, 3),
    ]);
  }

  test_field_result_functionExpression_used() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  Function foo = () {};
}

void main() {
  A().foo();
}
''');
  }

  test_field_result_passed() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  print(A().foo); // OK
}
''');
  }

  test_field_result_returned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

int f() => A().foo;
int f2() {
  return A().foo;
}
''');
  }

  test_field_result_targetedMethod() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  String foo = '';
}

void main() {
  A().foo.toString(); // OK
}
''');
  }

  test_field_result_targetedProperty() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  String foo = '';
}

void main() {
  A().foo.hashCode; // OK
}
''');
  }

  test_field_result_unassigned() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  A().foo;
}
''', [
      error(WarningCode.UNUSED_RESULT, 95, 3),
    ]);
  }

  test_field_result_unassigned_conditional_if() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(bool b) {
  b ? A().foo : 0;
}
''', [
      error(WarningCode.UNUSED_RESULT, 102, 3),
    ]);
  }

  test_field_result_unassigned_conditional_if_parens() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(bool b) {
  b ? (A().foo) : 0;
}
''', [
      error(WarningCode.UNUSED_RESULT, 103, 3),
    ]);
  }

  test_field_result_unassigned_in_closure() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(Function g) { }

void main() {
  f(() {
    A().foo;
  });
}
''', [
      error(WarningCode.UNUSED_RESULT, 130, 3),
    ]);
  }

  test_field_result_used_conditional_if_parens() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void f(bool b) {
  (b ? A().foo : 0).toString();
}
''');
  }

  test_field_result_used_listLiteral() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  var l = [ A().foo ]; // OK
  print(l);
  [ A().foo ]; // Also OK
}
''');
  }

  test_field_result_used_mapLiteral_key() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  var m = { A().foo : 'baz'}; // OK
  print(m);
}
''');
  }

  test_field_result_used_mapLiteral_value() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  var m = { 'baz': A().foo }; // OK
  print(m);
}
''');
  }

  test_field_result_used_setLiteral() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo = 0;
}

void main() {
  var s = { A().foo }; // OK
  print(s);
}
''');
  }

  test_field_static_result_unassigned() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  static int foo = 0;
}

void main() {
  A.foo;
}
''', [
      error(WarningCode.UNUSED_RESULT, 100, 3),
    ]);
  }

  test_getter_expressionStatement_id_dotResult_dotId() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int get foo => 0;
}

void f(A a) {
  a.foo.isEven;
}
''');
  }

  test_getter_expressionStatement_result() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int get foo => 0;

void f() {
  foo;
}
''', [
      error(WarningCode.UNUSED_RESULT, 77, 3),
    ]);
  }

  test_getter_expressionStatement_result_dotId() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int get foo => 0;

void f() {
  foo.isEven;
}
''');
  }

  test_getter_expressionStatement_result_dotId_dotId() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int get foo => 0;

void f() {
  foo.isEven.hashCode;
}
''');
  }

  test_getter_result_passed() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int get foo => 0;
}

void main() {
  print(A().foo); // OK
}
''');
  }

  test_getter_result_returned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int get foo => 0;
}

int f() => A().foo;
int f2() {
  return A().foo;
}
''');
  }

  test_getter_result_targetedMethod() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  String get foo =>  '';
}

void main() {
  A().foo.toString(); // OK
}
''');
  }

  test_getter_result_targetedProperty() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  String get foo => '';
}

void main() {
  A().foo.hashCode; // OK
}
''');
  }

  test_getter_result_unassigned() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int get foo => 0;
}

void main() {
  A().foo;
}
''', [
      error(WarningCode.UNUSED_RESULT, 100, 3),
    ]);
  }

  test_import_hide() async {
    newFile('$testPackageLibPath/a.dart', r'''
import 'package:meta/meta.dart';

@useResult
bool foo() => true;

bool bar() => true;
''');

    await assertNoErrorsInCode(r'''
import 'a.dart' hide foo;

bool f() {
  return bar();
}
''');
  }

  test_import_show() async {
    newFile('$testPackageLibPath/a.dart', r'''
import 'package:meta/meta.dart';

@useResult
bool foo() => true;
''');

    await assertNoErrorsInCode(r'''
import 'a.dart' show foo;

bool f() {
  return foo();
}
''');
  }

  test_interpolationExpression() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
String get f => '';

String g() => '$f';
''');
  }

  test_method_result_assertInitializer() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

class B {
  B(A a) :
    assert(a.foo() != 7);
}
''');
  }

  test_method_result_assertStatement() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

void f(A a) {
  assert(a.foo() != 7);
}
''');
  }

  test_method_result_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 0;
}

void main() {
  var bar = A().foo(); // OK
  print(bar);
}
''');
  }

  test_method_result_assigned_wildcard_unused() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 0;
}

void main() {
  var _ = A().foo();
}
''');
  }

  test_method_result_assigned_wildcard_unused_preWildcards() async {
    await assertNoErrorsInCode(r'''
// @dart = 3.4
// (pre wildcard-variables)

import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 0;
}

void main() {
  var _ = A().foo();
}
''');
  }

  test_method_result_binaryExpression() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

void f(A a) {
  1 + a.foo();
}
''');
  }

  test_method_result_conditional() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  if (a.foo()) {}
}
''');
  }

  test_method_result_constructorCall() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

class B {
  B(int i);
}

void f(A a) {
  new B(a.foo());
}
''');
  }

  test_method_result_doWhile() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  do {}
  while (a.foo());
}
''');
  }

  test_method_result_fieldInitializer() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

class B {
  int i;
  B(A a) : i = a.foo();
}
''');
  }

  test_method_result_for() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

void f(A a) {
  for (var i = 1; i < a.foo(); i++) {}
}
''');
  }

  test_method_result_for_updaters() async {
    await assertErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

void f(A a) {
  for (var i = 1; i < 7; a.foo()) {}
}
''', [
      error(WarningCode.UNUSED_RESULT, 119, 3),
    ]);
  }

  test_method_result_forElement() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  List<int> foo() => [];
}

void f(A a) {
  // Note that the list literal is unused, but we unconditionally consider use
  // within a list literal to be "use of result."
  [
    for (var e in a.foo()) e,
  ];
}
''');
  }

  test_method_result_forIn() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  List<int> foo() => [];
}

void f(A a) {
  for (var _ in a.foo()) {}
}
''');
  }

  test_method_result_ifElement() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  // Note that the list literal is unused, but we unconditionally consider use
  // within a list literal to be "use of result."
  [
    if (a.foo()) 1,
  ];
}
''');
  }

  test_method_result_ifNull() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int? foo() => 1;
}

int f(A a) {
  return a.foo() ?? 7;
}
''');
  }

  test_method_result_indexExpression() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

void f(A a, List<int> list) {
  list[a.foo()];
}
''');
  }

  test_method_result_listPattern_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

f() {
  var [a] = g();
  print(a);
}

@useResult
List<String> g() => [];
''');
  }

  test_method_result_mapPattern_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

f() {
  var {'a' : a} = g();
  print(a);
}

@useResult
Map<String, String> g() => {};
''');
  }

  test_method_result_nullCheck_isUsed() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int? foo() => 1;
}

int f(A a) {
  return a.foo()!;
}
''');
  }

  test_method_result_nullCheck_notUsed() async {
    await assertErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int? foo() => 1;
}

void f(A a) {
  a.foo()!;
}
''', [
      error(WarningCode.UNUSED_RESULT, 97, 3),
    ]);
  }

  test_method_result_objectPattern_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  int a;
  A(this.a);
}
f() {
  var A(a: b) = g();
  print(b);
}

@useResult
A g() => A(1);
''');
  }

  test_method_result_passed() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 0;
}

void main() {
  print(A().foo()); // OK
}
''');
  }

  test_method_result_recordPattern() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

f() {
  g();
}

@useResult
(int, int) g() => (0, 0);
''', [
      error(WarningCode.UNUSED_RESULT, 42, 1),
    ]);
  }

  test_method_result_recordPattern_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

f() {
  var (x, y) = g();
  print('$x$y');
}

@useResult
(int, int) g() => (0, 0);
''');
  }

  test_method_result_returned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 0;
}

int f() => A().foo();
int f2() {
  return A().foo();
}
''');
  }

  test_method_result_spread() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  List<int> foo() => [];
}

void f(A a) {
  // Note that the list literal is unused, but we unconditionally consider use
  // within a list literal to be "use of result."
  [...a.foo()];
}
''');
  }

  test_method_result_superInitializer() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

class B {
  B(int i);
}

class C extends B {
  C(A a) : super(a.foo());
}
''');
  }

  test_method_result_switchCondition() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  switch (a.foo()) {
    default:
  }
}
''');
  }

  test_method_result_switchCondition_language219() async {
    await assertNoErrorsInCode('''
// @dart = 2.19
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  switch (a.foo()) {}
}
''');
  }

  test_method_result_targetedMethod() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';
class A {
  @useResult
  String foo() => '';
}
void main() {
  A().foo().toString(); // OK
}
''');
  }

  test_method_result_targetedProperty() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  String foo() => '';
}

void main() {
  A().foo().hashCode; // OK
}
''');
  }

  test_method_result_thrown() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  throw a.foo();
}
''');
  }

  test_method_result_unassigned() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 0;
}

void main() {
  A().foo();
}
''', [
      error(WarningCode.UNUSED_RESULT, 98, 3,
          text: "The value of 'foo' should be used."),
    ]);
  }

  test_method_result_unassigned_cascade() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

class C {
  @useResult
  C m1() => throw '';

  C m2() => throw '';

  void m3() {
    m2()..m1();
  }
}
''', [
      error(WarningCode.UNUSED_RESULT, 127, 2,
          text: "The value of 'm1' should be used."),
    ]);
  }

  test_method_result_unassigned_parameterDefined() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @UseResult.unless(parameterDefined: 'value')
  int foo([int? value]) => value ?? 0;
}

void main() {
  A().foo(3);
}
''');
  }

  test_method_result_unassigned_parameterNotDefinedAndCascaded() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @UseResult.unless(parameterDefined: 'value')
  int foo([int? value]) => value ?? 0;
}

void main() {
  A().foo()..toString();
}
''');
  }

  test_method_result_while() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool foo() => false;
}

void f(A a) {
  while (a.foo()) {}
}
''');
  }

  test_method_result_yield() async {
    await assertNoErrorsInCode('''
import 'package:meta/meta.dart';

class A {
  @useResult
  int foo() => 1;
}

Stream<int> f(A a) async* {
  yield a.foo();
}
''');
  }

  test_namedExpression() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  int m() => 1;
}

void g({int? i}) {}

void f() {
  g(i: A().m());
}
''');
  }

  /// https://github.com/dart-lang/sdk/issues/47181
  test_prefixed_classMember() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool b() => true;
}

/// [A.b].
const a = 'a';
''');
  }

  /// https://github.com/dart-lang/sdk/issues/47181
  test_prefixed_importedMember() async {
    newFile('$testPackageLibPath/c.dart', '''
import 'package:meta/meta.dart';

class A {
  @useResult
  bool b() => true;
}
 ''');

    await assertNoErrorsInCode(r'''
import 'c.dart' as c;

/// [c.A.b].
const a = 'a';
''');
  }

  test_recordLiteral() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class C {
  (bool, bool) m() {
    return (true, methodWithAnnotation());
  }

  @useResult
  bool methodWithAnnotation() => true;
}
''');
  }

  /// https://github.com/dart-lang/sdk/issues/52314
  test_switchExpression() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class C {
  bool m(Object o) {
    return switch (o) {
      String() => methodWithAnnotation(),
      _ => false,
    };
  }

  @useResult
  bool methodWithAnnotation() => true;
}
''');
  }

  test_topLevelFunction_prefixExpression_bang() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
bool foo() => true;

bool f() {
  return !foo();
}
''');
  }

  test_topLevelFunction_prefixExpression_decrement() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo = 1;

int f() {
  return --foo;
}
''');
  }

  test_topLevelFunction_prefixExpression_increment() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo = 1;

int f() {
  return ++foo;
}
''');
  }

  test_topLevelFunction_prefixExpression_minus() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 1;

int f() {
  return -foo();
}
''');
  }

  test_topLevelFunction_prefixExpression_tilde() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 1;

int f() {
  return ~foo();
}
''');
  }

  test_topLevelFunction_result_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 0;

void main() {
  var x = foo(); // OK
  print(x);
}
''');
  }

  test_topLevelFunction_result_assigned_cascade() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 0;

void main() {
  var x = foo()..toString(); // OK
  print(x);
}
''');
  }

  /// https://github.com/dart-lang/sdk/issues/47473
  test_topLevelFunction_result_assigned_if() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
String foo() => '';

String f(bool b) {
  var f = '';
  if (b) f = foo();
  return f;
}
''');
  }

  test_topLevelFunction_result_awaited_future_passed() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
Future<List<String>> load() async => [];

void f() async {
  var l = [];
  l.add(await load());
}
''');
  }

  test_topLevelFunction_result_optionNamedParam_unassigned_parameterDefined() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@UseResult.unless(parameterDefined: 'value')
int foo({int? value}) => value ?? 0;

void main() {
  foo(value: 3);
}
''');
  }

  test_topLevelFunction_result_passed() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 0;

void main() {
  print(foo()); // OK
}
''');
  }

  test_topLevelFunction_result_targetedMethod() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
String foo() => '';

void main() {
  foo().toString();
}
''');
  }

  test_topLevelFunction_result_targetedProperty() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
String foo() => '';

void main() {
  foo().length;
}
''');
  }

  test_topLevelFunction_result_unassigned() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 0;
void bar() {}
int baz() => 0;

void main() {
  foo();
  bar(); // OK
  baz(); // OK
}
''', [
      error(WarningCode.UNUSED_RESULT, 108, 3),
    ]);
  }

  test_topLevelFunction_result_unassigned_parameterDefined() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@UseResult.unless(parameterDefined: 'value')
int foo([int? value]) => value ?? 0;

void main() {
  foo(3);
}
''');
  }

  test_topLevelFunction_result_unassigned_parameterUnDefined() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

@UseResult.unless(parameterDefined: 'value')
int foo([int? value]) => value ?? 0;

void main() {
  foo();
}
''', [
      error(WarningCode.UNUSED_RESULT, 133, 3),
    ]);
  }

  test_topLevelFunction_result_unassigned_parameterUnDefined2() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

@UseResult.unless(parameterDefined: 'value')
int foo([String? msg, int? value]) => value ?? 0;

void main() {
  foo('none');
}
''', [
      error(WarningCode.UNUSED_RESULT, 146, 3),
    ]);
  }

  test_topLevelFunction_result_used_in_cascade() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo() => 0;

void main() {
  foo()..toString();
}
''');
  }

  test_topLevelVariable_assigned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo = 0;

void main() {
  var bar = foo; // OK
  print(bar);
}
''');
  }

  test_topLevelVariable_passed() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo = 0;

void main() {
  print(foo); // OK
}
''');
  }

  test_topLevelVariable_result_unusedInDoc() async {
    // https://github.com/dart-lang/sdk/issues/47181
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int get f => 1;

/// I love [f].
int g = 1;
''');
  }

  test_topLevelVariable_returned() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo = 0;

int bar() => foo; // OK
int baz() {
  return foo; // OK
}
''');
  }

  test_topLevelVariable_unused() async {
    await assertErrorsInCode(r'''
import 'package:meta/meta.dart';

@useResult
int foo = 0;

void main() {
  foo;
}
''', [
      error(WarningCode.UNUSED_RESULT, 75, 3),
    ]);
  }

  test_whenClause() async {
    await assertNoErrorsInCode(r'''
import 'package:meta/meta.dart';

class C {
  void m(Object o) {
    if (o case String() when methodWithAnnotation()) print(o);
  }

  @useResult
  bool methodWithAnnotation() => true;
}
''');
  }
}
