diff --git a/pkg/analyzer/analyzer_use_new_elements.txt b/pkg/analyzer/analyzer_use_new_elements.txt index df0a1e097c5f..1b42a4522811 100644 --- a/pkg/analyzer/analyzer_use_new_elements.txt +++ b/pkg/analyzer/analyzer_use_new_elements.txt @@ -113,7 +113,6 @@ lib/src/error/getter_setter_types_verifier.dart lib/src/error/imports_verifier.dart lib/src/error/inheritance_override.dart lib/src/error/literal_element_verifier.dart -lib/src/error/must_call_super_verifier.dart lib/src/error/nullable_dereference_verifier.dart lib/src/error/override_verifier.dart lib/src/error/required_parameters_verifier.dart diff --git a/pkg/analyzer/lib/dart/element/element2.dart b/pkg/analyzer/lib/dart/element/element2.dart index e50f0b9a7e17..b86356abc009 100644 --- a/pkg/analyzer/lib/dart/element/element2.dart +++ b/pkg/analyzer/lib/dart/element/element2.dart @@ -1139,6 +1139,18 @@ abstract class InstanceElement2 /// The type of a `this` expression. DartType get thisType; + + /// Returns the field from [fields2] that has the given [name]. + FieldElement2? getField2(String name); + + /// Returns the getter from [getters2] that has the given [name]. + GetterElement? getGetter2(String name); + + /// Returns the method from [methods2] that has the given [name]. + MethodElement2? getMethod2(String name); + + /// Returns the setter from [setters2] that has the given [name]. + SetterElement? getSetter2(String name); } /// The portion of an [InstanceElement2] contributed by a single declaration. diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 494d1ca684a4..448e19161033 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart @@ -3789,6 +3789,11 @@ abstract class ExecutableElementImpl2 extends FunctionTypedElementImpl2 implements ExecutableElement2 { @override ExecutableElement2 get baseElement => this; + + bool get invokesSuperSelf { + var firstFragment = this.firstFragment as ExecutableElementImpl; + return firstFragment.hasModifier(Modifier.INVOKES_SUPER_SELF); + } } /// A concrete implementation of an [ExtensionElement]. @@ -5477,6 +5482,11 @@ abstract class InstanceElementImpl2 extends ElementImpl2 return null; } + @override + FieldElement2? getField2(String name) { + return fields2.firstWhereOrNull((e) => e.name3 == name); + } + @override PropertyAccessorElement? getGetter(String name) { var length = accessors.length; @@ -5489,6 +5499,11 @@ abstract class InstanceElementImpl2 extends ElementImpl2 return null; } + @override + GetterElement? getGetter2(String name) { + return getters2.firstWhereOrNull((e) => e.name3 == name); + } + @override MethodElement? getMethod(String name) { var length = methods.length; @@ -5501,6 +5516,11 @@ abstract class InstanceElementImpl2 extends ElementImpl2 return null; } + @override + MethodElement2? getMethod2(String name) { + return methods2.firstWhereOrNull((e) => e.name3 == name); + } + @override PropertyAccessorElement? getSetter(String name) { if (!name.endsWith('=')) { @@ -5510,6 +5530,11 @@ abstract class InstanceElementImpl2 extends ElementImpl2 (accessor) => accessor.isSetter && accessor.name == name); } + @override + SetterElement? getSetter2(String name) { + return setters2.firstWhereOrNull((e) => e.name3 == name); + } + @override bool isAccessibleIn2(LibraryElement2 library) => firstFragment.isAccessibleIn(library as LibraryElement); @@ -7058,8 +7083,7 @@ class LocalFunctionElementImpl extends ExecutableElementImpl2 Element2? get enclosingElement2 => null; @override - LocalFunctionFragment get firstFragment => - _wrappedElement as LocalFunctionFragment; + FunctionElementImpl get firstFragment => _wrappedElement; @override List get formalParameters => diff --git a/pkg/analyzer/lib/src/error/must_call_super_verifier.dart b/pkg/analyzer/lib/src/error/must_call_super_verifier.dart index 71a55f147206..6d5392ed1758 100644 --- a/pkg/analyzer/lib/src/error/must_call_super_verifier.dart +++ b/pkg/analyzer/lib/src/error/must_call_super_verifier.dart @@ -5,10 +5,11 @@ import 'dart:collection'; import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/element2.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; import 'package:analyzer/src/error/codes.g.dart'; class MustCallSuperVerifier { @@ -20,44 +21,50 @@ class MustCallSuperVerifier { if (node.isStatic || node.isAbstract) { return; } - var element = node.declaredElement!; + var element = node.declaredFragment!.element; + var overridden = _findOverriddenMemberWithMustCallSuper(element); if (overridden == null) { return; } - if (element is MethodElement && _hasConcreteSuperMethod(element)) { + var overriddenName = overridden.name3; + if (overriddenName == null) { + return; + } + + if (element is MethodElement2 && _hasConcreteSuperMethod(element)) { _verifySuperIsCalled( - node, overridden.name, overridden.enclosingElement3.name); + node, overriddenName, overridden.enclosingElement2?.name3); return; } - var enclosingElement = element.enclosingElement3; - if (enclosingElement is! ClassElement) { + var enclosingElement = element.enclosingElement2; + if (enclosingElement is! ClassElement2) { return; } - if (element is PropertyAccessorElement && element.isGetter) { - var inheritedConcreteGetter = enclosingElement - .lookUpInheritedConcreteGetter(element.name, element.library); - if (inheritedConcreteGetter != null) { + if (element is GetterElement) { + var inheritedConcreteGetter = + enclosingElement.lookupInheritedConcreteMember(element); + if (inheritedConcreteGetter is GetterElement) { _verifySuperIsCalled( - node, overridden.name, overridden.enclosingElement3.name); + node, overriddenName, overridden.enclosingElement2?.name3); } return; } - if (element is PropertyAccessorElement && element.isSetter) { - var inheritedConcreteSetter = enclosingElement - .lookUpInheritedConcreteSetter(element.name, element.library); - if (inheritedConcreteSetter != null) { - var name = overridden.name; + if (element is SetterElement) { + var inheritedConcreteSetter = + enclosingElement.lookupInheritedConcreteMember(element); + if (inheritedConcreteSetter is SetterElement) { + var name = overriddenName; // For a setter, give the name without the trailing '=' to the verifier, // in order to check against property access. if (name.endsWith('=')) { name = name.substring(0, name.length - 1); } - _verifySuperIsCalled(node, name, overridden.enclosingElement3.name); + _verifySuperIsCalled(node, name, overridden.enclosingElement2?.name3); } } } @@ -70,56 +77,74 @@ class MustCallSuperVerifier { /// `@mustCallSuper`. /// /// [1]: https://pub.dev/documentation/meta/latest/meta/mustCallSuper-constant.html - ExecutableElement? _findOverriddenMemberWithMustCallSuper( - ExecutableElement element) { - //Element member = node.declaredElement; - if (element.enclosingElement3 is! InterfaceElement) { + ExecutableElement2? _findOverriddenMemberWithMustCallSuper( + ExecutableElement2 element) { + var classElement = element.enclosingElement2; + if (classElement is! InterfaceElement2) { + return null; + } + + var name = element.name3; + if (name == null) { return null; } - var classElement = element.enclosingElement3 as InterfaceElement; - String name = element.name; // Walk up the type hierarchy from [classElement], ignoring direct // interfaces. - var superclasses = Queue(); + var superclasses = Queue(); - void addToQueue(InterfaceElement element) { - superclasses.addAll(element.mixins.map((i) => i.element)); - superclasses.add(element.supertype?.element); - if (element is MixinElement) { + void addToQueue(InterfaceElement2 element) { + superclasses.addAll(element.mixins.map((i) => i.element3)); + superclasses.add(element.supertype?.element3); + if (element is MixinElement2) { superclasses - .addAll(element.superclassConstraints.map((i) => i.element)); + .addAll(element.superclassConstraints.map((i) => i.element3)); } } - var visitedClasses = {}; + var visitedClasses = {}; addToQueue(classElement); while (superclasses.isNotEmpty) { var ancestor = superclasses.removeFirst(); if (ancestor == null || !visitedClasses.add(ancestor)) { continue; } - var member = ancestor.getMethod(name) ?? - ancestor.getGetter(name) ?? - ancestor.getSetter(name); - if (member is MethodElement && member.hasMustCallSuper) { + + ExecutableElement2? member; + switch (element) { + case MethodElement2(): + member = ancestor.getMethod2(name); + case GetterElement(): + member = ancestor.getMethod2(name) ?? ancestor.getGetter2(name); + case SetterElement(): + member = ancestor.getSetter2(name); + } + + if (member is MethodElement2 && member.metadata2.hasMustCallSuper) { + return member; + } + if (member is GetterElement && member.metadata2.hasMustCallSuper) { return member; } - if (member is PropertyAccessorElement && member.hasMustCallSuper) { - // TODO(srawlins): What about a field annotated with `@mustCallSuper`? - // This might seem a legitimate case, but is not called out in the - // documentation of [mustCallSuper]. + if (member is SetterElement && member.metadata2.hasMustCallSuper) { return member; } + // TODO(srawlins): What about a field annotated with `@mustCallSuper`? + // This might seem a legitimate case, but is not called out in the + // documentation of [mustCallSuper]. addToQueue(ancestor); } return null; } /// Returns whether [element] overrides a concrete method. - bool _hasConcreteSuperMethod(ExecutableElement element) { - var classElement = element.enclosingElement3 as InterfaceElement; - String name = element.name; + bool _hasConcreteSuperMethod(ExecutableElement2 element) { + var classElement = element.enclosingElement2 as InterfaceElement2; + + var name = element.name3; + if (name == null) { + return true; + } if (classElement.supertype.isConcrete(name)) { return true; @@ -129,7 +154,7 @@ class MustCallSuperVerifier { return true; } - if (classElement is MixinElement && + if (classElement is MixinElement2 && classElement.superclassConstraints.any((c) => c.isConcrete(name))) { return true; } @@ -137,9 +162,10 @@ class MustCallSuperVerifier { return false; } - void _verifySuperIsCalled(MethodDeclaration node, String methodName, + void _verifySuperIsCalled(MethodDeclaration node, String? methodName, String? overriddenEnclosingName) { - var declaredElement = node.declaredElement as ExecutableElementImpl; + var declaredFragment = node.declaredFragment!; + var declaredElement = declaredFragment.element as ExecutableElementImpl2; if (!declaredElement.invokesSuperSelf) { // Overridable elements are always enclosed in named elements, so it is // safe to assume [overriddenEnclosingName] is non-`null`. @@ -153,11 +179,36 @@ class MustCallSuperVerifier { } } +extension on InterfaceElement2 { + ExecutableElement2? lookupInheritedConcreteMember( + ExecutableElement2 element) { + var nameObj = Name.forElement(element); + if (nameObj == null) { + return null; + } + + var library = element.library2 as LibraryElementImpl; + var inheritanceManager = library.session.inheritanceManager; + return inheritanceManager.getMember4(this, nameObj, forSuper: true); + } +} + extension on InterfaceType? { bool isConcrete(String name) { var self = this; if (self == null) return false; - var element = self.element; - return element.lookUpConcreteMethod(name, element.library) != null; + var element = self.element3; + + var library = element.library2 as LibraryElementImpl; + var libraryUri = library.firstFragment.source.uri; + + var inheritanceManager = library.session.inheritanceManager; + var concrete = inheritanceManager.getMember4( + element, + Name(libraryUri, name), + concrete: true, + ); + + return concrete != null; } }