| // Copyright (c) 2026, 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. |
| |
| /// Domain models and convenience extensions for contractual annotations |
| /// extracted from `package:meta`. |
| library; |
| |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:collection/collection.dart'; |
| |
| import 'api_facet.dart'; |
| |
| /// Contractual annotations from `package:meta`. |
| enum MetaContract implements Comparable<MetaContract> { |
| experimental('experimental'), |
| immutable('immutable'), |
| internal('internal'), |
| mustBeOverridden('must be overridden'), |
| mustCallSuper('must call super'), |
| nonVirtual('non-virtual'), |
| protected('protected'), |
| useResult('use result'), |
| visibleForOverriding('visible for overriding'), |
| visibleForTesting('visible for testing'); |
| |
| /// The human-readable label used for text representation in summaries. |
| final String textLabel; |
| const MetaContract(this.textLabel); |
| |
| @override |
| int compareTo(MetaContract other) => name.compareTo(other.name); |
| } |
| |
| /// An [ApiFacet] capturing `package:meta` contracts. |
| final class MetaContractFacet implements ApiFacet { |
| @override |
| String get namespace => 'meta'; |
| |
| /// The set of active metadata contracts associated with this facet. |
| final Set<MetaContract> contracts; |
| |
| MetaContractFacet(Iterable<MetaContract> contracts) |
| : contracts = contracts.isEmpty |
| ? const {} |
| : Set.unmodifiable(contracts.sorted()); |
| |
| /// Extracts active `package:meta` contracts from [element], or returns `null` |
| /// if none are present. |
| static MetaContractFacet? fromElement(Element element) { |
| final contracts = <MetaContract>{}; |
| void check(Metadata metadata) { |
| if (metadata.hasExperimental) contracts.add(MetaContract.experimental); |
| if (metadata.hasImmutable) contracts.add(MetaContract.immutable); |
| if (metadata.hasInternal) contracts.add(MetaContract.internal); |
| if (metadata.hasMustBeOverridden) { |
| contracts.add(MetaContract.mustBeOverridden); |
| } |
| if (metadata.hasMustCallSuper) contracts.add(MetaContract.mustCallSuper); |
| if (metadata.hasNonVirtual) contracts.add(MetaContract.nonVirtual); |
| if (metadata.hasProtected) contracts.add(MetaContract.protected); |
| if (metadata.hasUseResult) contracts.add(MetaContract.useResult); |
| if (metadata.hasVisibleForOverriding) { |
| contracts.add(MetaContract.visibleForOverriding); |
| } |
| if (metadata.hasVisibleForTesting) { |
| contracts.add(MetaContract.visibleForTesting); |
| } |
| } |
| |
| check(element.nonSynthetic.metadata); |
| if (element is PropertyAccessorElement) { |
| check(element.variable.nonSynthetic.metadata); |
| } |
| |
| return contracts.isNotEmpty ? MetaContractFacet(contracts) : null; |
| } |
| |
| factory MetaContractFacet.fromJson(Map<String, dynamic> json) { |
| final list = json['contracts'] as List<dynamic>? ?? const []; |
| final set = <MetaContract>{}; |
| for (final item in list) { |
| if (item is String) { |
| final contract = MetaContract.values |
| .where((e) => e.name == item) |
| .firstOrNull; |
| if (contract != null) { |
| set.add(contract); |
| } |
| } |
| } |
| return MetaContractFacet(set); |
| } |
| |
| @override |
| Map<String, dynamic> toJson() => { |
| 'namespace': namespace, |
| 'contracts': contracts.map((e) => e.name).toList(), |
| }; |
| |
| @override |
| List<String> get parentheticalSegments => |
| contracts.map((e) => e.textLabel).toList(); |
| |
| @override |
| int compareTo(ApiFacet other) { |
| final diff = namespace.compareTo(other.namespace); |
| if (diff != 0) return diff; |
| if (other is MetaContractFacet) { |
| final thisStr = contracts.map((e) => e.name).join(','); |
| final otherStr = other.contracts.map((e) => e.name).join(','); |
| return thisStr.compareTo(otherStr); |
| } |
| return 0; |
| } |
| |
| @override |
| bool operator ==(Object other) => |
| identical(this, other) || |
| other is MetaContractFacet && |
| const SetEquality<MetaContract>().equals(contracts, other.contracts); |
| |
| @override |
| int get hashCode => |
| Object.hash(namespace, const SetEquality<MetaContract>().hash(contracts)); |
| } |