Record superInterface and use it for OverrideVerifier.

R=brianwilkerson@google.com

Change-Id: Ib6fe2b14affc8f8d00c32f123a4b587ae6d8fb56
Reviewed-on: https://dart-review.googlesource.com/77521
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-10-02 14:02:31 +00:00 committed by commit-bot@chromium.org
parent 158290349a
commit b8c831592a
4 changed files with 115 additions and 91 deletions

View file

@ -39,10 +39,11 @@ import 'package:front_end/src/dependency_walker.dart';
* Analyzer of a single library.
*/
class LibraryAnalyzer {
final AnalysisOptions _analysisOptions;
final AnalysisOptionsImpl _analysisOptions;
final DeclaredVariables _declaredVariables;
final SourceFactory _sourceFactory;
final FileState _library;
final InheritanceManager2 _inheritance;
final bool Function(Uri) _isLibraryUri;
final AnalysisContextImpl _context;
@ -69,7 +70,8 @@ class LibraryAnalyzer {
this._context,
this._resynthesizer,
this._library)
: _typeProvider = _context.typeProvider;
: _inheritance = new InheritanceManager2(_context.typeSystem),
_typeProvider = _context.typeProvider;
/**
* Compute analysis results for all units of the library.
@ -210,15 +212,12 @@ class LibraryAnalyzer {
unit.accept(new Dart2JSVerifier(errorReporter));
}
InheritanceManager inheritanceManager = new InheritanceManager(
_libraryElement,
includeAbstractFromSuperclasses: true);
unit.accept(new BestPracticesVerifier(
errorReporter, _typeProvider, _libraryElement, inheritanceManager,
errorReporter, _typeProvider, _libraryElement,
typeSystem: _context.typeSystem));
unit.accept(new OverrideVerifier(errorReporter, inheritanceManager));
unit.accept(
new OverrideVerifier(_inheritance, _libraryElement, errorReporter));
new ToDoFinder(errorReporter).findIn(unit);
@ -296,14 +295,12 @@ class LibraryAnalyzer {
RecordingErrorListener errorListener = _getErrorListener(file);
AnalysisOptionsImpl options = _analysisOptions as AnalysisOptionsImpl;
var typeSystem = new StrongTypeSystemImpl(_typeProvider,
implicitCasts: options.implicitCasts,
declarationCasts: options.declarationCasts,
nonnullableTypes: options.nonnullableTypes);
CodeChecker checker =
new CodeChecker(_typeProvider, typeSystem, errorListener, options);
CodeChecker checker = new CodeChecker(
_typeProvider,
_context.typeSystem,
errorListener,
_analysisOptions,
);
checker.visitCompilationUnit(unit);
ErrorReporter errorReporter = _getErrorReporter(file);
@ -321,9 +318,8 @@ class LibraryAnalyzer {
//
// Compute inheritance and override errors.
//
var inheritanceManager2 = new InheritanceManager2(typeSystem);
var inheritanceOverrideVerifier = new InheritanceOverrideVerifier(
typeSystem, inheritanceManager2, errorReporter);
_context.typeSystem, _inheritance, errorReporter);
inheritanceOverrideVerifier.verifyUnit(unit);
//
@ -334,7 +330,7 @@ class LibraryAnalyzer {
_libraryElement,
_typeProvider,
new InheritanceManager(_libraryElement),
inheritanceManager2,
_inheritance,
_analysisOptions.enableSuperMixins);
unit.accept(errorVerifier);
}

View file

@ -47,7 +47,7 @@ class InheritanceManager2 {
/// members, not necessary accessible in all libraries.
Interface getInterface(InterfaceType type) {
if (type == null) {
return const Interface._(const {}, const [{}], const []);
return const Interface._(const {}, const {}, const [{}], const []);
}
var result = _interfaces[type];
@ -55,10 +55,16 @@ class InheritanceManager2 {
return result;
}
_interfaces[type] = const Interface._(const {}, const [{}], const []);
_interfaces[type] = const Interface._(
const {},
const {},
const [{}],
const [],
);
Map<Name, FunctionType> superInterface = {};
Map<Name, FunctionType> map = {};
List<Map<Name, FunctionType>> supers = [];
List<Conflict> conflicts = null;
List<Map<Name, FunctionType>> superImplemented = [];
// If a class declaration has a member declaration, the signature of that
// member declaration becomes the signature in the interface.
@ -73,8 +79,12 @@ class InheritanceManager2 {
// `mixin M on S1, S2 {}` can call using `super` any instance member
// from its superclass constraints, whether it is abstract or concrete.
Map<Name, FunctionType> mixinSuperClass = {};
_findMostSpecificFromNamedCandidates(mixinSuperClass, namedCandidates);
supers.add(mixinSuperClass);
_findMostSpecificFromNamedCandidates(
namedCandidates,
superInterface,
mixinSuperClass,
);
superImplemented.add(mixinSuperClass);
} else {
Map<Name, FunctionType> implemented;
@ -82,7 +92,7 @@ class InheritanceManager2 {
_addCandidates(namedCandidates, type.superclass);
implemented = _getImplemented(type.superclass);
supers.add(implemented);
superImplemented.add(implemented);
}
for (var mixin in type.mixins) {
@ -92,7 +102,7 @@ class InheritanceManager2 {
implemented = <Name, FunctionType>{}
..addAll(implemented)
..addAll(implementedInMixin);
supers.add(implemented);
superImplemented.add(implemented);
}
}
@ -106,9 +116,18 @@ class InheritanceManager2 {
// super-interfaces that is a valid override of all the other
// super-interface signatures with the same name. That "most specific"
// signature becomes the signature of the class's interface.
conflicts = _findMostSpecificFromNamedCandidates(map, namedCandidates);
conflicts = _findMostSpecificFromNamedCandidates(
namedCandidates,
superInterface,
map,
);
var interface = new Interface._(map, supers, conflicts ?? const []);
var interface = new Interface._(
map,
superInterface,
superImplemented,
conflicts ?? const [],
);
_interfaces[type] = interface;
return interface;
}
@ -132,7 +151,7 @@ class InheritanceManager2 {
bool forSuper: false,
}) {
if (forSuper) {
var supers = getInterface(type)._supers;
var supers = getInterface(type)._superImplemented;
if (forMixinIndex >= 0) {
return supers[forMixinIndex][name];
}
@ -218,33 +237,38 @@ class InheritanceManager2 {
}
/// The given [namedCandidates] maps names to candidates from direct
/// superinterfaces. Find the most specific signature, and put it into the
/// [map], if there is no one yet (from the class itself). If there is no
/// such single most specific signature (i.e. no valid override), then add a
/// new conflict description.
/// superinterfaces. Find the most specific signature, and put it into both
/// [superInterface]. If the class itself does not define the name, put it
/// also into the class interface [map]. If there is no such single most
/// specific signature (i.e. no valid override), then add a new conflict
/// description.
List<Conflict> _findMostSpecificFromNamedCandidates(
Map<Name, FunctionType> map,
Map<Name, List<FunctionType>> namedCandidates) {
Map<Name, List<FunctionType>> namedCandidates,
Map<Name, FunctionType> superInterface,
Map<Name, FunctionType> map,
) {
List<Conflict> conflicts = null;
for (var name in namedCandidates.keys) {
if (map.containsKey(name)) {
continue;
}
var isDefinedByClass = map.containsKey(name);
var candidates = namedCandidates[name];
// If just one candidate, it is always valid.
if (candidates.length == 1) {
map[name] = candidates[0];
superInterface[name] = candidates[0];
if (!isDefinedByClass) {
map[name] = candidates[0];
}
continue;
}
// Check for a getter/method conflict.
var conflict = _checkForGetterMethodConflict(name, candidates);
if (conflict != null) {
conflicts ??= <Conflict>[];
conflicts.add(conflict);
if (!isDefinedByClass) {
var conflict = _checkForGetterMethodConflict(name, candidates);
if (conflict != null) {
conflicts ??= <Conflict>[];
conflicts.add(conflict);
}
}
FunctionType validOverride;
@ -263,8 +287,11 @@ class InheritanceManager2 {
}
if (validOverride != null) {
map[name] = validOverride;
} else {
superInterface[name] = validOverride;
if (!isDefinedByClass) {
map[name] = validOverride;
}
} else if (!isDefinedByClass) {
conflicts ??= <Conflict>[];
conflicts.add(new Conflict(name, candidates));
}
@ -356,18 +383,26 @@ class Interface {
/// The map of names to their signature in the interface.
final Map<Name, FunctionType> map;
/// The map of names to their signature in superinterfaces.
final Map<Name, FunctionType> superInterface;
/// Each item of this list maps names to their concrete implementations.
/// The first item of the list is the nominal superclass, next the nominal
/// superclass plus the first mixin, etc. So, for the class like
/// `class C extends S with M1, M2`, we get `[S, S&M1, S&M1&M2]`.
final List<Map<Name, FunctionType>> _supers;
final List<Map<Name, FunctionType>> _superImplemented;
/// The list of conflicts between superinterfaces - the nominal superclass,
/// mixins, and interfaces. Does not include conflicts with the declared
/// members of the class.
final List<Conflict> conflicts;
const Interface._(this.map, this._supers, this.conflicts);
const Interface._(
this.map,
this.superInterface,
this._superImplemented,
this.conflicts,
);
}
/// A public name, or a private name qualified by a library URI.

View file

@ -19,6 +19,7 @@ import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager2.dart';
import 'package:analyzer/src/dart/element/member.dart' show ConstructorMember;
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
@ -276,14 +277,11 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
/// The current library
LibraryElement _currentLibrary;
/// The inheritance manager used to find overridden methods.
InheritanceManager _manager;
/// Create a new instance of the [BestPracticesVerifier].
///
/// @param errorReporter the error reporter
BestPracticesVerifier(this._errorReporter, TypeProvider typeProvider,
this._currentLibrary, this._manager,
BestPracticesVerifier(
this._errorReporter, TypeProvider typeProvider, this._currentLibrary,
{TypeSystem typeSystem})
: _nullType = typeProvider.nullType,
_futureNullType = typeProvider.futureNullType,
@ -3895,28 +3893,39 @@ class InstanceFieldResolverVisitor extends ResolverVisitor {
/// compilation unit to verify that if they have an override annotation it is
/// being used correctly.
class OverrideVerifier extends RecursiveAstVisitor {
/// The inheritance manager used to find overridden methods.
final InheritanceManager2 _inheritance;
/// The library being verified.
final LibraryElement _library;
/// The error reporter used to report errors.
final ErrorReporter _errorReporter;
/// The inheritance manager used to find overridden methods.
final InheritanceManager _manager;
/// The interface of the current class.
Interface _currentInterface;
/// Initialize a newly created verifier to look for inappropriate uses of the
/// override annotation.
///
/// @param errorReporter the error reporter used to report errors
/// @param manager the inheritance manager used to find overridden methods
OverrideVerifier(this._errorReporter, this._manager);
OverrideVerifier(this._inheritance, this._library, this._errorReporter);
@override
visitClassDeclaration(ClassDeclaration node) {
_currentInterface = _inheritance.getInterface(node.declaredElement.type);
super.visitClassDeclaration(node);
_currentInterface = null;
}
@override
visitFieldDeclaration(FieldDeclaration node) {
for (VariableDeclaration field in node.fields.variables) {
VariableElement fieldElement = field.declaredElement;
if (fieldElement is FieldElement && _isOverride(fieldElement)) {
PropertyAccessorElement getter = fieldElement.getter;
PropertyAccessorElement setter = fieldElement.setter;
if (!(getter != null && _getOverriddenMember(getter) != null ||
setter != null && _getOverriddenMember(setter) != null)) {
FieldElement fieldElement = field.declaredElement;
if (fieldElement.hasOverride) {
if (!_isOverride(fieldElement.getter) &&
!_isOverride(fieldElement.setter)) {
_errorReporter.reportErrorForNode(
HintCode.OVERRIDE_ON_NON_OVERRIDING_FIELD, field.name);
}
@ -3927,8 +3936,8 @@ class OverrideVerifier extends RecursiveAstVisitor {
@override
visitMethodDeclaration(MethodDeclaration node) {
ExecutableElement element = node.declaredElement;
if (_isOverride(element)) {
if (_getOverriddenMember(element) == null) {
if (element.hasOverride) {
if (!_isOverride(element)) {
if (element is MethodElement) {
_errorReporter.reportErrorForNode(
HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name);
@ -3945,30 +3954,14 @@ class OverrideVerifier extends RecursiveAstVisitor {
}
}
/// Return the member that overrides the given member.
///
/// @param member the member that overrides the returned member
/// @return the member that overrides the given member
ExecutableElement _getOverriddenMember(ExecutableElement member) {
LibraryElement library = member.library;
if (library == null) {
return null;
/// Return `true` if the [member] overrides a member from the superinterface.
bool _isOverride(ExecutableElement member) {
if (member == null) {
return false;
}
ClassElement classElement =
member.getAncestor((element) => element is ClassElement);
if (classElement == null) {
return null;
}
return _manager.lookupInheritance(classElement, member.name);
var name = new Name(_library.source.uri, member.name);
return _currentInterface.superInterface.containsKey(name);
}
/// Return `true` if the given element has an override annotation associated
/// with it.
///
/// @param element the element being tested
/// @return `true` if the element has an override annotation associated with
/// it
bool _isOverride(Element element) => element != null && element.hasOverride;
}
/// An AST visitor that is used to resolve the some of the nodes within a single

View file

@ -2855,15 +2855,15 @@ class GenerateHintsTask extends SourceBasedAnalysisTask {
unit.accept(new Dart2JSVerifier(errorReporter));
}
// Dart best practices.
InheritanceManager inheritanceManager = new InheritanceManager(
libraryElement,
includeAbstractFromSuperclasses: true);
InheritanceManager2 inheritanceManager2 =
new InheritanceManager2(libraryElement.context.typeSystem);
TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
unit.accept(new BestPracticesVerifier(
errorReporter, typeProvider, libraryElement, inheritanceManager,
errorReporter, typeProvider, libraryElement,
typeSystem: typeSystem));
unit.accept(new OverrideVerifier(errorReporter, inheritanceManager));
unit.accept(new OverrideVerifier(
inheritanceManager2, libraryElement, errorReporter));
// Find to-do comments.
new ToDoFinder(errorReporter).findIn(unit);
//