Files
Paul Berry afcfbbeba8 Migrate developer experience packages to new constructor decl syntax.
(Part of https://github.com/dart-lang/sdk/issues/63288)

This change migrates the packages owned by the developer experience
team to use the new constructor declaration syntax, described in
https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations.

This change was performed in an automated fashion, by (a) bumping the
packages' SDK constraints to `3.13.0-0`, (b) enabling the lints
`unnecessary_type_name_in_constructor` and
`unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint
failures using `dart fix`, and then (d) reformatting the affected
files.

To ease code review, I've reverted unrelated formatting changes.

Since this change requires bumping SDK constaints to `3.13.0-0`, it
was only performed on packages that are *not* published on
pub. (Packages that *are* published on pub should remain on lower
language versions until at least after the stable version of 3.13 is
released, so that we don't block users on the stable channel from
receiving updates to those packages.)

Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2026-05-27 14:52:58 -07:00

522 lines
13 KiB
Dart

// Copyright (c) 2014, 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.
/// Data structures representing an API definition, and visitor base classes
/// for visiting those data structures.
library;
import 'dart:collection';
import 'package:analyzer_utilities/html_dom.dart' as dom;
/// Toplevel container for the API.
class Api extends ApiNode {
final String version;
final List<Domain> domains;
final Types types;
final Refactorings refactorings;
new(
this.version,
this.domains,
this.types,
this.refactorings,
dom.Element html, {
super.experimental = false,
}) : super(html, deprecated: false);
}
/// Base class for objects in the API model.
class ApiNode {
/// A flag to indicate if this API is experimental.
final bool experimental;
/// A flag to indicate if this API is deprecated.
final bool deprecated;
/// Html element representing this part of the API, `null` if built-in.
final dom.Element? html;
new(this.html, {this.experimental = false, this.deprecated = false});
}
/// Base class for visiting the API definition.
abstract class ApiVisitor<T> {
/// Dispatch the given [type] to the visitor.
T visitTypeDecl(TypeDecl type) => type.accept(this);
T visitTypeEnum(TypeEnum typeEnum);
T visitTypeList(TypeList typeList);
T visitTypeMap(TypeMap typeMap);
T visitTypeObject(TypeObject typeObject);
T visitTypeReference(TypeReference typeReference);
T visitTypeUnion(TypeUnion typeUnion);
}
/// Definition of a single domain.
class Domain extends ApiNode {
final String name;
final List<Request> requests;
final List<Notification> notifications;
new(
this.name,
this.requests,
this.notifications,
dom.Element html, {
super.experimental = false,
super.deprecated = false,
}) : super(html);
@override
dom.Element get html => super.html!;
}
/// API visitor that visits the entire API hierarchically by default.
class HierarchicalApiVisitor extends ApiVisitor<void> {
/// The API to visit.
final Api api;
new(this.api);
/// If [type] is a [TypeReference] that is defined in the API, follow the
/// chain until a non-[TypeReference] is found, if possible.
///
/// If it is not possible (because the chain ends with a [TypeReference] that
/// is not defined in the API), then that final [TypeReference] is returned.
TypeDecl resolveTypeReferenceChain(TypeDecl type) {
while (type is TypeReference) {
var newTypeRef = api.types[type.typeName];
if (newTypeRef == null) {
break;
}
type = newTypeRef.type;
}
return type;
}
void visitApi() {
api.domains.forEach(visitDomain);
visitTypes(api.types);
visitRefactorings(api.refactorings);
}
void visitDomain(Domain domain) {
domain.requests.forEach(visitRequest);
domain.notifications.forEach(visitNotification);
}
void visitNotification(Notification notification) {
var params = notification.params;
if (params != null) {
visitTypeDecl(params);
}
}
void visitRefactoring(Refactoring refactoring) {
var feedback = refactoring.feedback;
if (feedback != null) {
visitTypeDecl(feedback);
}
var options = refactoring.options;
if (options != null) {
visitTypeDecl(options);
}
}
void visitRefactorings(Refactorings refactorings) {
refactorings.forEach(visitRefactoring);
}
void visitRequest(Request request) {
var params = request.params;
if (params != null) {
visitTypeDecl(params);
}
var result = request.result;
if (result != null) {
visitTypeDecl(result);
}
}
void visitTypeDefinition(TypeDefinition typeDefinition) {
visitTypeDecl(typeDefinition.type);
}
@override
void visitTypeEnum(TypeEnum typeEnum) {
typeEnum.values.forEach(visitTypeEnumValue);
}
void visitTypeEnumValue(TypeEnumValue typeEnumValue) {}
@override
void visitTypeList(TypeList typeList) {
visitTypeDecl(typeList.itemType);
}
@override
void visitTypeMap(TypeMap typeMap) {
visitTypeDecl(typeMap.keyType);
visitTypeDecl(typeMap.valueType);
}
@override
void visitTypeObject(TypeObject typeObject) {
typeObject.fields.forEach(visitTypeObjectField);
}
void visitTypeObjectField(TypeObjectField typeObjectField) {
visitTypeDecl(typeObjectField.type);
}
@override
void visitTypeReference(TypeReference typeReference) {}
void visitTypes(Types types) {
types.forEach(visitTypeDefinition);
}
@override
void visitTypeUnion(TypeUnion typeUnion) {
typeUnion.choices.forEach(visitTypeDecl);
}
}
/// Description of a notification method.
class Notification extends ApiNode {
/// Name of the domain enclosing this request.
final String domainName;
/// Name of the notification, without the domain prefix.
final String event;
/// Type of the object associated with the "params" key in the notification
/// object, or null if the notification has no parameters.
final TypeObject? params;
new(
this.domainName,
this.event,
this.params,
dom.Element html, {
super.experimental = false,
}) : super(html, deprecated: false);
/// Get the name of the notification, including the domain prefix.
String get longEvent => '$domainName.$event';
/// Get the full type of the notification object, including the common "id"
/// and "error" fields.
TypeDecl get notificationType {
var fields = <TypeObjectField>[
TypeObjectField(
'event',
TypeReference('String', null),
null,
value: '$domainName.$event',
),
];
var params = this.params;
if (params != null) {
fields.add(TypeObjectField('params', params, null));
}
return TypeObject(fields, null);
}
}
/// Description of a single refactoring.
class Refactoring extends ApiNode {
/// Name of the refactoring. This should match one of the values allowed for
/// RefactoringKind.
final String kind;
/// Type of the refactoring feedback, or null if the refactoring has no
/// feedback.
final TypeObject? feedback;
/// Type of the refactoring options, or null if the refactoring has no
/// options.
final TypeObject? options;
new(
this.kind,
this.feedback,
this.options,
dom.Element html, {
super.experimental = false,
}) : super(html, deprecated: false);
}
/// A collection of refactoring definitions.
class Refactorings extends ApiNode with IterableMixin<Refactoring> {
final List<Refactoring> refactorings;
new(this.refactorings, dom.Element? html, {super.experimental = false})
: super(html, deprecated: false);
@override
Iterator<Refactoring> get iterator => refactorings.iterator;
}
/// Description of a request method.
class Request extends ApiNode {
/// Name of the domain enclosing this request.
final String domainName;
/// Name of the request, without the domain prefix.
final String method;
/// Type of the object associated with the "params" key in the request object,
/// or null if the request has no parameters.
final TypeObject? params;
/// Type of the object associated with the "result" key in the response
/// object, or `null` if the response has no results.
final TypeObject? result;
new(
this.domainName,
this.method,
this.params,
this.result,
dom.Element html, {
super.experimental = false,
super.deprecated = false,
}) : super(html);
/// Get the name of the request, including the domain prefix.
String get longMethod => '$domainName.$method';
/// Get the full type of the request object, including the common "id" and
/// "method" fields.
TypeDecl get requestType {
var fields = <TypeObjectField>[
TypeObjectField('id', TypeReference('String', null), null),
TypeObjectField(
'method',
TypeReference('String', null),
null,
value: '$domainName.$method',
),
];
var params = this.params;
if (params != null) {
fields.add(TypeObjectField('params', params, null));
}
return TypeObject(fields, null);
}
/// Get the full type of the response object, including the common "id" and
/// "error" fields.
TypeDecl get responseType {
var fields = <TypeObjectField>[
TypeObjectField('id', TypeReference('String', null), null),
TypeObjectField(
'error',
TypeReference('RequestError', null),
null,
optional: true,
),
];
var result = this.result;
if (result != null) {
fields.add(TypeObjectField('result', result, null));
}
return TypeObject(fields, null);
}
}
/// Base class for all possible types.
sealed class TypeDecl extends ApiNode {
new(super.html, {super.experimental, super.deprecated});
T accept<T>(ApiVisitor<T> visitor);
}
/// Description of a named type definition.
class TypeDefinition extends ApiNode {
final String name;
final TypeDecl type;
bool isExternal = false;
new(
this.name,
this.type,
dom.Element html, {
super.experimental = false,
super.deprecated = false,
}) : super(html);
}
/// Type of an enum. We represent enums in JSON as strings, so this type
/// declaration simply lists the allowed values.
class TypeEnum extends TypeDecl {
final List<TypeEnumValue> values;
new(
this.values,
dom.Element html, {
super.experimental = false,
super.deprecated = false,
}) : super(html);
@override
T accept<T>(ApiVisitor<T> visitor) => visitor.visitTypeEnum(this);
}
/// Description of a single allowed value for an enum.
class TypeEnumValue extends ApiNode {
final String value;
new(
this.value,
dom.Element html, {
super.experimental = false,
super.deprecated = false,
}) : super(html);
@override
dom.Element get html => super.html!;
}
/// Type of a JSON list.
class TypeList extends TypeDecl {
final TypeDecl itemType;
new(this.itemType, dom.Element html, {super.experimental = false})
: super(html, deprecated: false);
@override
T accept<T>(ApiVisitor<T> visitor) => visitor.visitTypeList(this);
}
/// Type of a JSON map.
class TypeMap extends TypeDecl {
/// Type of map keys. Note that since JSON map keys must always be strings,
/// this must either be a [TypeReference] for [String], or a [TypeReference]
/// to a type which is defined in the API as an enum or a synonym for
/// [String].
final TypeReference keyType;
/// Type of map values.
final TypeDecl valueType;
new(
this.keyType,
this.valueType,
dom.Element html, {
super.experimental = false,
}) : super(html, deprecated: false);
@override
T accept<T>(ApiVisitor<T> visitor) => visitor.visitTypeMap(this);
}
/// Type of a JSON object with specified fields, some of which may be optional.
class TypeObject extends TypeDecl {
final List<TypeObjectField> fields;
new(
this.fields,
dom.Element? html, {
super.experimental = false,
super.deprecated = false,
}) : super(html);
@override
T accept<T>(ApiVisitor<T> visitor) => visitor.visitTypeObject(this);
/// Return the field with the given [name], or null if there is no such field.
TypeObjectField? getField(String name) {
for (var field in fields) {
if (field.name == name) {
return field;
}
}
return null;
}
}
/// Description of a single field in a [TypeObject].
class TypeObjectField extends ApiNode {
final String name;
final TypeDecl type;
final bool optional;
/// Value that the field is required to contain, or null if it may vary.
final Object? value;
new(
this.name,
this.type,
dom.Element? html, {
this.optional = false,
this.value,
super.experimental = false,
super.deprecated = false,
}) : super(html);
@override
String toString() => name;
}
/// A reference to a type which is either defined elsewhere in the API or which
/// is built-in ([String], [bool], or [int]).
class TypeReference extends TypeDecl {
final String typeName;
new(this.typeName, dom.Element? html, {super.experimental = false})
: super(html, deprecated: false) {
if (typeName.isEmpty) {
throw Exception('Empty type name');
}
}
@override
T accept<T>(ApiVisitor<T> visitor) => visitor.visitTypeReference(this);
}
/// A collection of type definitions.
class Types extends ApiNode with IterableMixin<TypeDefinition> {
final Map<String, TypeDefinition> types;
List<String> importUris = <String>[];
new(this.types, dom.Element? html, {super.experimental = false})
: super(html, deprecated: false);
@override
Iterator<TypeDefinition> get iterator => types.values.iterator;
Iterable<String> get keys => types.keys;
TypeDefinition? operator [](String typeName) => types[typeName];
bool containsKey(String typeName) => types.containsKey(typeName);
}
/// Type which represents a union among multiple choices.
class TypeUnion extends TypeDecl {
final List<TypeDecl> choices;
/// The field that is used to disambiguate this union
final String field;
new(this.choices, this.field, dom.Element html, {super.experimental = false})
: super(html, deprecated: false);
@override
T accept<T>(ApiVisitor<T> visitor) => visitor.visitTypeUnion(this);
}