Skip to content

[record_use] Dart API: Put non-const calls more front and center #2718

Closed

Description

@dcharkes

The current API for retrieving recorded method calls is convenience-oriented, providing methods like constArgumentsFor() and hasNonConstArguments(). This leads users to forget to call hasNonConstArguments.

Proposed Solution:

Refactor the API to provide a single, powerful method that returns all recorded usages for a given method. The different kinds of calls should be represented by a sealed class hierarchy.

API Signature:

Iterable<CallReference> callsFor(Identifier id);

sealed class CallReference { ... }

// A direct call with arguments.
final class CallWithArguments extends CallReference {
  // ... properties for positional and named arguments
}

// A tear-off of the method.
final class CallTearOff extends CallReference { ... }

// A potential dynamic invocation.
final class DynamicInvocation extends CallReference { ... }

Example Usage

This example demonstrates a stricter approach where the presence of any non-const
usages is treated as a failure. It uses a custom Either type to return
either a list of errors (Left) or a list of the successfully extracted constant
values (Right).

import 'package:record_use/record_use.dart';

// A simple Either type to represent success (Right) or failure (Left).
sealed class Either<L, R> {}

class Left<L, R> extends Either<L, R> {
  final L value;
  Left(this.value);
}

class Right<L, R> extends Either<L, R> {
  final R value;
  Right(this.value);
}

Either<List<String>, List<Constant>> processMethodUsages(
    Recordings recordings, Identifier methodId) {
  final calls = recordings.callsFor(methodId);
  final constValues = <Constant>[];
  final errors = <String>[];

  for (final call in calls) {
    switch (call) {
      case CallWithArguments(
          location: final loc,
          namedArguments: {'myArg': final Constant constArg}
        ):
        constValues.add(constArg);
        break;

      case CallTearOff(location: final loc):
        errors.add('Error: Found a tear-off at ${loc.uri}:${loc.line}.');
        break;

      case DynamicInvocation(location: final loc):
        errors.add('Error: Found a dynamic invocation at ${loc.uri}:${loc.line}.');
        break;

      case CallWithArguments(
          location: final loc,
          namedArguments: {'myArg': null}
        ):
        errors.add(
          'Error: Call at ${loc.uri}:${loc.line} had a non-constant \'myArg\'.',
        );
        break;

      case CallWithArguments(location: final loc):
        errors.add(
          'Error: Call at ${loc.uri}:${loc.line} did not provide the \'myArg\' argument.',
        );
        break;
    }
  }

  if (errors.isNotEmpty) {
    return Left(errors);
  }
  return Right(constValues);
}

Activity

  1. sagar-h007 commented on Jan 21, 2026

    @sagar-h007

    @dcharkes I’d like to work on this.

    I agree with the diagnosis: the current API is convenience-oriented in a way that makes it too easy to miss non-const usages. The issue isn’t missing data, but that the API encourages partial inspection instead of forcing callers to look at everything.

    Approach:
    -Introduce a single primitive API: Iterable callsFor(Identifier id);
    -Represent all recorded method usages using a sealed CallReference hierarchy (CallWithArguments, CallTearOff, DynamicInvocation), with non-const arguments represented explicitly (e.g. Constant?).
    -Rebuild existing helpers (constArgumentsFor, hasNonConstArguments, etc.) on top of callsFor and deprecate them, preserving behavior while nudging users toward exhaustive handling.
    -Update docs and examples to use callsFor with pattern matching so non-const and dynamic cases are hard to ignore by accident.

    If this sounds reasonable, I can start by sketching the sealed class model and a minimal callsFor implementation, then follow up with deprecations and example updates.

  2. dcharkes commented on Jan 21, 2026

    @dcharkes
    CollaboratorAuthor

    @sagar-h007 I'm not looking for contributions on this right now. I'm actively working on this package and any breaking changes to the API need to be rolled into the Dart SDK. I will first get the internals right and then work on the API myself. If you want to contribute, please take a look at issues that are labeled with contributions welcome or good first issue.

  3. moved this from Todo to In Progress in Native Assetson Feb 24, 2026
  4. self-assigned this
    on Feb 24, 2026
  5. added a commit that references this issue on Feb 26, 2026
  6. moved this from In Progress to Done in Native Assetson Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions