dart-lang / language

Design of the Dart language
Other
2.66k stars 205 forks source link

Extension methods prefers global over member #1949

Open tp opened 2 years ago

tp commented 2 years ago

Today I was surprised by a difference in variable resolution between class methods and extension methods.

I have a global variable and an instance field of the same name. In the class method the instances gets picked even when written without this., while in the extension method the global is picked instead.

final title = 'global title';

void main() {
  final x = X()..title = "x's title";

  x..printTitle()
  ..extensionPrintTitle();
}

class X {
 late String title;

  void printTitle() {
    print(title); 
  }
}

extension XE on X {
  void extensionPrintTitle() {
    print(title); 
  }
}

outputs

x's title
global title

https://dartpad.dev/?null_safety=true&id=3e26f557acea0c0a96533174833269fc

I wasn't able to figure out whether this is an open issue in the spec, an implementation issue, or the desired behavior for some reason (which I was unable to find in the issues).

My basic assumption as a developer was that the resolution should work the same (for public fields) as if I were in a class method.

eernstg commented 2 years ago

That's true, in Dart the lexical scope always wins. For example:

final title = 'global title';

class A {
  String title = 'inherited title';
}

class B extends A {
  void printTitle() => print(title); // 'global title'.
}

In the subclass B (and in an extension), an identifier which is not in scope is looked up by prepending this., but in this case the lookup finds the global variable, so that's the result and we never reach the point where this. is prepended.

Further details here, which is also available in a more readable version as section 'Lexical Lookup' in the PDF spec.