| // Copyright (c) 2024, 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. | |
| import 'package:_fe_analyzer_shared/src/metadata/expressions.dart' as shared; | |
| import 'package:_fe_analyzer_shared/src/metadata/parser.dart' as shared; | |
| import 'package:_fe_analyzer_shared/src/metadata/proto.dart' as shared; | |
| import 'package:_fe_analyzer_shared/src/metadata/references.dart' as shared; | |
| import 'package:_fe_analyzer_shared/src/metadata/scope.dart' as shared; | |
| import 'package:_fe_analyzer_shared/src/metadata/type_annotations.dart' | |
| as shared; | |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart'; | |
| import 'package:kernel/ast.dart'; | |
| import '../../api_prototype/experimental_flags.dart'; | |
| import '../../base/loader.dart'; | |
| import '../../base/scope.dart'; | |
| import '../../builder/builder.dart'; | |
| import '../../builder/declaration_builders.dart'; | |
| import '../../builder/dynamic_type_declaration_builder.dart'; | |
| import '../../builder/future_or_type_declaration_builder.dart'; | |
| import '../../builder/member_builder.dart'; | |
| import '../../builder/method_builder.dart'; | |
| import '../../builder/never_type_declaration_builder.dart'; | |
| import '../../builder/null_type_declaration_builder.dart'; | |
| import '../../builder/prefix_builder.dart'; | |
| import '../../builder/property_builder.dart'; | |
| import '../../source/source_property_builder.dart'; | |
| // Coverage-ignore(suite): Not run. | |
| final Uri dummyUri = Uri.parse('dummy:uri'); | |
| // Coverage-ignore(suite): Not run. | |
| bool _isDartLibrary(Uri importUri, Uri fileUri) { | |
| return importUri.isScheme("dart") || fileUri.isScheme("org-dartlang-sdk"); | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| /// Creates a [shared.Expression] for the annotation at [atToken]. | |
| /// | |
| /// If [delayLookupForTesting] is `true`, identifiers are not looked up in their | |
| /// corresponding scopes. This means that the return expression will contain | |
| /// [shared.UnresolvedIdentifier] nodes, as if the identifier wasn't in scope. | |
| /// A subsequent call to [shared.Expression.resolve] will perform the lookup | |
| /// a create the resolved expression. This is used in testing to mimic the | |
| /// scenario in which the declaration is added to the scope via macros. | |
| shared.Expression parseAnnotation( | |
| Loader loader, | |
| Token atToken, | |
| Uri importUri, | |
| Uri fileUri, | |
| LookupScope scope, | |
| LibraryFeatures libraryFeatures, { | |
| bool delayLookupForTesting = false, | |
| }) { | |
| return shared.parseAnnotation( | |
| atToken, | |
| fileUri, | |
| new AnnotationScope(scope), | |
| new References(loader), | |
| new LibraryExperimentalFeatures(libraryFeatures), | |
| isDartLibrary: _isDartLibrary(importUri, fileUri), | |
| delayLookupForTesting: delayLookupForTesting, | |
| ); | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| /// Creates a [shared.Expression] for the initializer at [initializerToken]. | |
| /// | |
| /// If [delayLookupForTesting] is `true`, identifiers are not looked up in their | |
| /// corresponding scopes. This means that the return expression will contain | |
| /// [shared.UnresolvedIdentifier] nodes, as if the identifier wasn't in scope. | |
| /// A subsequent call to [shared.Expression.resolve] will perform the lookup | |
| /// a create the resolved expression. This is used in testing to mimic the | |
| /// scenario in which the declaration is added to the scope via macros. | |
| shared.Expression parseFieldInitializer( | |
| Loader loader, | |
| Token initializerToken, | |
| Uri importUri, | |
| Uri fileUri, | |
| LookupScope scope, | |
| LibraryFeatures libraryFeatures, { | |
| bool delayLookupForTesting = false, | |
| }) { | |
| return shared.parseExpression( | |
| initializerToken, | |
| fileUri, | |
| new AnnotationScope(scope), | |
| new References(loader), | |
| new LibraryExperimentalFeatures(libraryFeatures), | |
| isDartLibrary: _isDartLibrary(importUri, fileUri), | |
| delayLookupForTesting: delayLookupForTesting, | |
| ); | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| /// Returns the [shared.Expression] for the constant initializer of [reference]. | |
| shared.Expression? getFieldInitializer(shared.FieldReference reference) { | |
| if (reference is PropertyReference) { | |
| PropertyBuilder element = reference.builder; | |
| if (element is SourcePropertyBuilder) { | |
| return element.initializerExpression; | |
| } | |
| } else { | |
| assert( | |
| false, | |
| "Unexpected field reference $reference (${reference.runtimeType})", | |
| ); | |
| } | |
| return null; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| shared.Proto builderToProto(Builder builder, String name) { | |
| if (builder is PropertyBuilder) { | |
| return new shared.FieldProto(new PropertyReference(builder)); | |
| } else if (builder is MethodBuilder) { | |
| return new shared.FunctionProto(new MethodReference(builder)); | |
| } else if (builder is PrefixBuilder) { | |
| return new shared.PrefixProto(name, new PrefixScope(builder)); | |
| } else if (builder is ClassBuilder) { | |
| if (builder.isEnum) { | |
| shared.EnumReference enumReference = new EnumReference(builder); | |
| return new shared.EnumProto( | |
| enumReference, | |
| new EnumScope(builder, enumReference), | |
| ); | |
| } else if (builder.isMixinDeclaration) { | |
| shared.MixinReference mixinReference = new MixinReference(builder); | |
| return new shared.MixinProto( | |
| mixinReference, | |
| new MixinScope(builder, mixinReference), | |
| ); | |
| } else { | |
| shared.ClassReference classReference = new ClassReference(builder); | |
| return new shared.ClassProto( | |
| classReference, | |
| new ClassScope(builder, classReference), | |
| ); | |
| } | |
| } else if (builder is DynamicTypeDeclarationBuilder) { | |
| return new shared.DynamicProto(new TypeReference(builder)); | |
| } else if (builder is TypeAliasBuilder) { | |
| shared.TypedefReference typedefReference = new TypedefReference(builder); | |
| return new shared.TypedefProto( | |
| typedefReference, | |
| new TypedefScope(builder, typedefReference), | |
| ); | |
| } else if (builder is ExtensionBuilder) { | |
| shared.ExtensionReference extensionReference = new ExtensionReference( | |
| builder, | |
| ); | |
| return new shared.ExtensionProto( | |
| extensionReference, | |
| new ExtensionScope(builder, extensionReference), | |
| ); | |
| } else if (builder is ExtensionTypeDeclarationBuilder) { | |
| shared.ExtensionTypeReference extensionReference = | |
| new ExtensionTypeReference(builder); | |
| return new shared.ExtensionTypeProto( | |
| extensionReference, | |
| new ExtensionTypeScope(builder, extensionReference), | |
| ); | |
| } else { | |
| // TODO(johnniwinther): Support extension types. | |
| throw new UnsupportedError("Unsupported builder $builder for $name"); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class References implements shared.References { | |
| final Loader loader; | |
| late final DynamicTypeDeclarationBuilder dynamicDeclaration = | |
| new DynamicTypeDeclarationBuilder( | |
| const DynamicType(), | |
| loader.coreLibrary, | |
| -1, | |
| ); | |
| late final NeverTypeDeclarationBuilder neverDeclaration = | |
| new NeverTypeDeclarationBuilder( | |
| const NeverType.nonNullable(), | |
| loader.coreLibrary, | |
| -1, | |
| ); | |
| late final NullTypeDeclarationBuilder nullDeclaration = | |
| new NullTypeDeclarationBuilder(const NullType(), loader.coreLibrary, -1); | |
| late final FutureOrTypeDeclarationBuilder futureOrDeclaration = | |
| new FutureOrTypeDeclarationBuilder( | |
| new FutureOrType(const DynamicType(), Nullability.nonNullable), | |
| loader.coreLibrary, | |
| -1, | |
| ); | |
| @override | |
| late final shared.TypeReference dynamicReference = new TypeReference( | |
| dynamicDeclaration, | |
| ); | |
| @override | |
| shared.TypeReference get voidReference => const VoidTypeReference(); | |
| new(this.loader); | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class AnnotationScope implements shared.Scope { | |
| final LookupScope scope; | |
| new(this.scope); | |
| @override | |
| shared.Proto lookup(String name) { | |
| Builder? builder = scope.lookup(name)?.getable; | |
| if (builder == null) { | |
| return new shared.UnresolvedIdentifier(this, name); | |
| } else { | |
| return builderToProto(builder, name); | |
| } | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| final class ClassScope extends shared.BaseClassScope { | |
| final ClassBuilder builder; | |
| @override | |
| final shared.ClassReference classReference; | |
| new(this.builder, this.classReference); | |
| @override | |
| shared.Proto lookup( | |
| String name, [ | |
| List<shared.TypeAnnotation>? typeArguments, | |
| ]) { | |
| MemberBuilder? constructor = builder.nameSpace | |
| .lookupConstructor(name) | |
| ?.getable; | |
| if (constructor != null) { | |
| return createConstructorProto( | |
| typeArguments, | |
| new ConstructorReference(constructor), | |
| ); | |
| } | |
| Builder? member = builder.lookupLocalMember(name)?.getable; | |
| return createMemberProto(typeArguments, name, member, builderToProto); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| final class EnumScope extends shared.BaseEnumScope { | |
| final ClassBuilder builder; | |
| @override | |
| final shared.EnumReference enumReference; | |
| new(this.builder, this.enumReference); | |
| @override | |
| shared.Proto lookup( | |
| String name, [ | |
| List<shared.TypeAnnotation>? typeArguments, | |
| ]) { | |
| Builder? member = builder.lookupLocalMember(name)?.getable; | |
| return createMemberProto(typeArguments, name, member, builderToProto); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| final class MixinScope extends shared.BaseMixinScope { | |
| final ClassBuilder builder; | |
| @override | |
| final shared.MixinReference mixinReference; | |
| new(this.builder, this.mixinReference); | |
| @override | |
| shared.Proto lookup( | |
| String name, [ | |
| List<shared.TypeAnnotation>? typeArguments, | |
| ]) { | |
| Builder? member = builder.lookupLocalMember(name)?.getable; | |
| return createMemberProto(typeArguments, name, member, builderToProto); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| final class ExtensionScope extends shared.BaseExtensionScope { | |
| final ExtensionBuilder builder; | |
| @override | |
| final shared.ExtensionReference extensionReference; | |
| new(this.builder, this.extensionReference); | |
| @override | |
| shared.Proto lookup( | |
| String name, [ | |
| List<shared.TypeAnnotation>? typeArguments, | |
| ]) { | |
| Builder? member = builder.lookupLocalMember(name)?.getable; | |
| return createMemberProto(typeArguments, name, member, builderToProto); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| final class ExtensionTypeScope extends shared.BaseExtensionTypeScope { | |
| final ExtensionTypeDeclarationBuilder builder; | |
| @override | |
| final shared.ExtensionTypeReference extensionTypeReference; | |
| new(this.builder, this.extensionTypeReference); | |
| @override | |
| shared.Proto lookup( | |
| String name, [ | |
| List<shared.TypeAnnotation>? typeArguments, | |
| ]) { | |
| MemberBuilder? constructor = builder.nameSpace | |
| .lookupConstructor(name) | |
| ?.getable; | |
| if (constructor != null) { | |
| return createConstructorProto( | |
| typeArguments, | |
| new ConstructorReference(constructor), | |
| ); | |
| } | |
| Builder? member = builder.lookupLocalMember(name)?.getable; | |
| return createMemberProto(typeArguments, name, member, builderToProto); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| final class TypedefScope extends shared.BaseTypedefScope { | |
| final TypeAliasBuilder builder; | |
| @override | |
| final shared.TypedefReference typedefReference; | |
| new(this.builder, this.typedefReference); | |
| @override | |
| shared.Proto lookup( | |
| String name, [ | |
| List<shared.TypeAnnotation>? typeArguments, | |
| ]) { | |
| TypeDeclarationBuilder? typeDeclaration = builder.unaliasDeclaration(null); | |
| if (typeDeclaration is ClassBuilder) { | |
| MemberBuilder? constructor = typeDeclaration.nameSpace | |
| .lookupConstructor(name) | |
| ?.getable; | |
| if (constructor != null) { | |
| return createConstructorProto( | |
| typeArguments, | |
| new ConstructorReference(constructor), | |
| ); | |
| } | |
| } | |
| return createMemberProto(typeArguments, name); | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class TypeReference extends shared.TypeReference { | |
| final TypeDeclarationBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| class VoidTypeReference extends shared.TypeReference { | |
| const new(); | |
| @override | |
| // Coverage-ignore(suite): Not run. | |
| String get name => 'void'; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class PrefixScope implements shared.Scope { | |
| final PrefixBuilder prefixBuilder; | |
| new(this.prefixBuilder); | |
| @override | |
| shared.Proto lookup(String name) { | |
| Builder? builder = prefixBuilder.lookup(name)?.getable; | |
| if (builder == null) { | |
| return new shared.UnresolvedIdentifier(this, name); | |
| } else { | |
| return builderToProto(builder, name); | |
| } | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class PropertyReference extends shared.FieldReference { | |
| final PropertyBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class MethodReference extends shared.FunctionReference { | |
| final MethodBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class ConstructorReference extends shared.ConstructorReference { | |
| final MemberBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name.isEmpty ? 'new' : builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class ClassReference extends shared.ClassReference { | |
| final ClassBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class EnumReference extends shared.EnumReference { | |
| final ClassBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class MixinReference extends shared.MixinReference { | |
| final ClassBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class ExtensionReference extends shared.ExtensionReference { | |
| final ExtensionBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class ExtensionTypeReference extends shared.ExtensionTypeReference { | |
| final ExtensionTypeDeclarationBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| class TypedefReference extends shared.TypedefReference { | |
| final TypeAliasBuilder builder; | |
| new(this.builder); | |
| @override | |
| String get name => builder.name; | |
| } |