Skip to content

Commit

Permalink
Elements. Migrate MustCallSuperVerifier.
Browse files Browse the repository at this point in the history
Change-Id: Id2e7caded4218ff2e248d0639cd2e382033cec9d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395047
Reviewed-by: Phil Quitslund <[email protected]>
Commit-Queue: Konstantin Shcheglov <[email protected]>
  • Loading branch information
scheglov authored and Commit Queue committed Nov 14, 2024
1 parent fadaa36 commit 0015935
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 49 deletions.
1 change: 0 additions & 1 deletion pkg/analyzer/analyzer_use_new_elements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions pkg/analyzer/lib/dart/element/element2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
28 changes: 26 additions & 2 deletions pkg/analyzer/lib/src/dart/element/element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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('=')) {
Expand All @@ -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);
Expand Down Expand Up @@ -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<FormalParameterElement> get formalParameters =>
Expand Down
143 changes: 97 additions & 46 deletions pkg/analyzer/lib/src/error/must_call_super_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
}
}
}
Expand All @@ -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<InterfaceElement?>();
var superclasses = Queue<InterfaceElement2?>();

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 = <InterfaceElement>{};
var visitedClasses = <InterfaceElement2>{};
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;
Expand All @@ -129,17 +154,18 @@ class MustCallSuperVerifier {
return true;
}

if (classElement is MixinElement &&
if (classElement is MixinElement2 &&
classElement.superclassConstraints.any((c) => c.isConcrete(name))) {
return true;
}

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`.
Expand All @@ -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;
}
}

0 comments on commit 0015935

Please sign in to comment.