// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // @dart = 2.9 import 'dart:io' show Directory, Platform; import 'package:_fe_analyzer_shared/src/testing/id.dart'; import 'package:_fe_analyzer_shared/src/testing/id_testing.dart'; import 'package:front_end/src/api_prototype/experimental_flags.dart'; import 'package:front_end/src/fasta/kernel/class_hierarchy_builder.dart'; import 'package:front_end/src/fasta/kernel/kernel_api.dart'; import 'package:front_end/src/testing/id_extractor.dart'; import 'package:front_end/src/testing/id_testing_helper.dart'; import 'package:front_end/src/testing/id_testing_utils.dart'; import 'package:kernel/ast.dart'; const String cfeFromBuilderMarker = 'cfe:builder'; main(List args) async { Directory dataDir = new Directory.fromUri(Platform.script .resolve('../../../_fe_analyzer_shared/test/inheritance/data')); await runTests(dataDir, args: args, createUriForFileName: createUriForFileName, onFailure: onFailure, runTest: runTestFor(const InheritanceDataComputer(), [ new TestConfig(cfeMarker, 'cfe with nnbd', explicitExperimentalFlags: const { ExperimentalFlag.nonNullable: true }, librariesSpecificationUri: createUriForFileName('libraries.json'), compileSdk: true), new TestConfig(cfeFromBuilderMarker, 'cfe from builder', explicitExperimentalFlags: const { ExperimentalFlag.nonNullable: true }, librariesSpecificationUri: createUriForFileName('libraries.json'), compileSdk: true) ])); } class InheritanceDataComputer extends DataComputer { const InheritanceDataComputer(); /// Function that computes a data mapping for [library]. /// /// Fills [actualMap] with the data. void computeLibraryData( TestConfig config, InternalCompilerResult compilerResult, Library library, Map> actualMap, {bool verbose}) { new InheritanceDataExtractor(config, compilerResult, actualMap) .computeForLibrary(library); } @override void computeClassData( TestConfig config, InternalCompilerResult compilerResult, Class cls, Map> actualMap, {bool verbose}) { new InheritanceDataExtractor(config, compilerResult, actualMap) .computeForClass(cls); } @override bool get supportsErrors => true; @override String computeErrorData(TestConfig config, InternalCompilerResult compiler, Id id, List errors) { return errorsToText(errors, useCodes: true); } @override DataInterpreter get dataValidator => const StringDataInterpreter(); } class InheritanceDataExtractor extends CfeDataExtractor { final TestConfig _config; final InternalCompilerResult _compilerResult; InheritanceDataExtractor( this._config, this._compilerResult, Map> actualMap) : super(_compilerResult, actualMap); ClassHierarchy get _hierarchy => _compilerResult.classHierarchy; CoreTypes get _coreTypes => _compilerResult.coreTypes; ClassHierarchyBuilder get _classHierarchyBuilder => _compilerResult.kernelTargetForTesting.loader.builderHierarchy; @override String computeLibraryValue(Id id, Library node) { return 'nnbd=${node.isNonNullableByDefault}'; } @override void computeForClass(Class node) { super.computeForClass(node); if (node.isAnonymousMixin) return; // TODO(johnniwinther): Also compute member data from builders. Set getters = _hierarchy .getInterfaceMembers(node) .map((Member member) => member.name) .toSet(); Set setters = _hierarchy .getInterfaceMembers(node, setters: true) .where((Member member) => member is Procedure && member.kind == ProcedureKind.Setter || member is Field && member.hasSetter) .map((Member member) => member.name) .toSet(); void addMember(Name name, {bool isSetter}) { Member member = _hierarchy.getInterfaceMember(node, name, setter: isSetter); if (member.enclosingClass == _coreTypes.objectClass) { return; } InterfaceType supertype = _hierarchy.getTypeAsInstanceOf( _coreTypes.thisInterfaceType(node, node.enclosingLibrary.nonNullable), member.enclosingClass, node.enclosingLibrary); Substitution substitution = Substitution.fromInterfaceType(supertype); DartType type; if (member is Procedure) { if (member.kind == ProcedureKind.Getter) { type = substitution.substituteType(member.function.returnType); } else if (member.kind == ProcedureKind.Setter) { type = substitution .substituteType(member.function.positionalParameters.single.type); } else { Nullability functionTypeNullability; if (node.enclosingLibrary.isNonNullableByDefault) { functionTypeNullability = member.enclosingLibrary.nonNullable; } else { // We don't create a member signature when the member is just // a substitution. We should still take the nullability to be // legacy, though. functionTypeNullability = node.enclosingLibrary.nonNullable; } type = substitution.substituteType( member.function.computeThisFunctionType(functionTypeNullability)); } } else if (member is Field) { type = substitution.substituteType(member.type); } if (type == null) { return; } String memberName = name.text; if (isSetter) { memberName += '='; } MemberId id = new MemberId.internal(memberName, className: node.name); TreeNode nodeWithOffset; if (member.enclosingClass == node) { nodeWithOffset = computeTreeNodeWithOffset(member); } else { nodeWithOffset = computeTreeNodeWithOffset(node); } registerValue( nodeWithOffset?.location?.file, nodeWithOffset?.fileOffset, id, typeToText(type, TypeRepresentation.analyzerNonNullableByDefault), member); } for (Name name in getters) { addMember(name, isSetter: false); } for (Name name in setters) { addMember(name, isSetter: true); } } @override String computeClassValue(Id id, Class node) { if (node.isAnonymousMixin) return null; if (_config.marker == cfeMarker) { List supertypes = []; for (Class superclass in computeAllSuperclasses(node)) { Supertype supertype = _hierarchy.getClassAsInstanceOf(node, superclass); if (supertype.classNode.isAnonymousMixin) continue; assert( supertype != null, "No instance of $superclass found for $node."); supertypes.add(supertypeToText( supertype, TypeRepresentation.analyzerNonNullableByDefault)); } supertypes.sort(); return supertypes.join(','); } else if (_config.marker == cfeFromBuilderMarker) { ClassHierarchyNode classHierarchyNode = _classHierarchyBuilder.getNodeFromClass(node); Set supertypes = {}; void addSupertype(Supertype supertype) { if (supertype.classNode.isAnonymousMixin) return; supertypes.add(supertypeToText( supertype, TypeRepresentation.analyzerNonNullableByDefault)); } addSupertype(new Supertype( classHierarchyNode.classBuilder.cls, getAsTypeArguments(classHierarchyNode.classBuilder.cls.typeParameters, classHierarchyNode.classBuilder.library.library))); classHierarchyNode.superclasses.forEach(addSupertype); classHierarchyNode.interfaces.forEach(addSupertype); List sorted = supertypes.toList()..sort(); return sorted.join(','); } return null; } }