// Copyright (c) 2018, 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:analyzer/dart/element/type.dart'; import 'package:source_gen/source_gen.dart' show TypeChecker; /// A [TypeChecker] for [Iterable]. const coreIterableTypeChecker = TypeChecker.fromUrl('dart:core#Iterable'); const coreStringTypeChecker = TypeChecker.fromRuntime(String); const coreMapTypeChecker = TypeChecker.fromUrl('dart:core#Map'); /// Returns the generic type of the [Iterable] represented by [type]. /// /// If [type] does not extend [Iterable], an error is thrown. DartType coreIterableGenericType(DartType type) => typeArgumentsOf(type, coreIterableTypeChecker).single; /// If [type] is the [Type] or implements the [Type] represented by [checker], /// returns the generic arguments to the [checker] [Type] if there are any. /// /// If the [checker] [Type] doesn't have generic arguments, `null` is returned. List typeArgumentsOf(DartType type, TypeChecker checker) { final implementation = _getImplementationType(type, checker) as InterfaceType; return implementation?.typeArguments; } /// A [TypeChecker] for [String], [bool] and [num]. const simpleJsonTypeChecker = TypeChecker.any([ coreStringTypeChecker, TypeChecker.fromRuntime(bool), TypeChecker.fromRuntime(num) ]); String asStatement(DartType type) { if (type.isDynamic || type.isObject) { return ''; } if (coreIterableTypeChecker.isAssignableFromType(type)) { final itemType = coreIterableGenericType(type); if (itemType.isDynamic || itemType.isObject) { return ' as List'; } } if (coreMapTypeChecker.isAssignableFromType(type)) { final args = typeArgumentsOf(type, coreMapTypeChecker); assert(args.length == 2); if (args.every((dt) => dt.isDynamic || dt.isObject)) { return ' as Map'; } } return ' as $type'; } /// Returns all of the [DartType] types that [type] implements, mixes-in, and /// extends, starting with [type] itself. Iterable typeImplementations(DartType type) sync* { yield type; if (type is InterfaceType) { yield* type.interfaces.expand(typeImplementations); yield* type.mixins.expand(typeImplementations); if (type.superclass != null) { yield* typeImplementations(type.superclass); } } } DartType _getImplementationType(DartType type, TypeChecker checker) => typeImplementations(type) .firstWhere(checker.isExactlyType, orElse: () => null);