Skip to content

[Request] Warn when using private members of the same interface but a different instance #48918

Open
@jellynoone

Description

@jellynoone

Consider the following example:

/// lib1.dart
class Addable {
  final int _value;

  Addable(this._value);

  operator +(Addable addable) {
    return Addable(_value + addable._value);
  }
}
/// lib2.dart
import 'lib1.dart';

class DecoratedAddable implements Addable {
  final Addable _decorated;

  DecoratedAddable(this._decorated);

  @override
  operator +(Addable addable) {
    print('Adding');
    return _decorated + addable;
  }
}
/// main.dart
import 'lib1.dart';
import 'lib2.dart';

void main() {
  Addable(1) + DecoratedAddable(Addable(2));
}

Running dart run main.dart issues the expected error:

Unhandled exception:
NoSuchMethodError: Class 'DecoratedAddable' has no instance getter '_value'.
Receiver: Instance of 'DecoratedAddable'
Tried calling: _value
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
dart-lang/sdk#57147      DecoratedAddable._value (lib1.dart:2:13)
dart-lang/sdk#57148      Addable.+ (lib1.dart:7:37)
dart-lang/sdk#57149      main (main.dart:5:14)
dart-lang/sdk#57150      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
dart-lang/sdk#57151      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

My desire is if the analyzer could warn whenever you are using a private member of a different instance of a class which can be implemented by a foreign library. In this case, if a warning could be issue in Addable when performing: Addable(_value + addable._value); that addable._value is unsafe since the passed in Addable may be a different implementation.

Of course if Addable was private this shouldn't warn since we can be certain all the fields will be implemented.

This issue has been on my mind for quite some time since it requires you to be constantly focused on what you're doing with your classes.

To illustrate this point further. Here is at least one example of this issue in the sdk, in the Duration class:

Duration operator +(Duration other) {
return Duration._microseconds(_duration + other._duration);
}
/// Subtracts [other] from this Duration and
/// returns the difference as a new Duration object.
Duration operator -(Duration other) {
return Duration._microseconds(_duration - other._duration);
}

and some more unsafe usages at other operators:

/// Whether this [Duration] is shorter than [other].
bool operator <(Duration other) => this._duration < other._duration;
/// Whether this [Duration] is longer than [other].
bool operator >(Duration other) => this._duration > other._duration;
/// Whether this [Duration] is shorter than or equal to [other].
bool operator <=(Duration other) => this._duration <= other._duration;
/// Whether this [Duration] is longer than or equal to [other].
bool operator >=(Duration other) => this._duration >= other._duration;

While the fix is trivial here, as is, if a client wanted to, for some reason, decorate the Duration class, and through implements rather than extend, these methods would fail.

Another way to safe-guard against this issue, would be either that no public class that defines a private field / method can be implemented by a different library only extended. I assume this would require some specification changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3A lower priority bug or feature requestarea-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-warningIssues with the analyzer's Warning codestype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions