blob: 5ab9dcacd64d06815da7b4d5b9da0534a49ce9ca [file] [log] [blame]
// 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.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
/// Information about a class with nested properties.
///
/// If the property value is set, and the expression is an
/// [InstanceCreationExpression], we add nested properties for parameters of
/// the used constructor.
///
/// If the property value is not set, but its type is a class, we still could
/// add nested properties for this property. But we need to know how to
/// materialize it - which constructor to call, and with which arguments.
///
/// This class provides such "how to materialize" information.
class ClassDescription {
final ClassElement element;
final ConstructorElement constructor;
/// Mapping from a parameter to the default code for the corresponding
/// argument in a new instance creation that calls the [constructor].
///
/// TODO(scheglov) Should be a generator, not just [String].
/// In general case we might need to import some libraries.
final Map<ParameterElement, String> parameterToDefaultCode;
ClassDescription(
this.element,
this.constructor,
this.parameterToDefaultCode,
);
}
/// The lazy-fill registry of [ClassDescription].
class ClassDescriptionRegistry {
final Map<ClassElement, ClassDescription> _map = {};
/// Flush all data, because there was a change to a file.
void flush() {
_map.clear();
}
/// If we know how to materialize the [element], return [ClassDescription].
/// Otherwise return `null`.
ClassDescription get(ClassElement element) {
var description = _map[element];
if (description == null) {
description = _classDescription(element);
if (description != null) {
_map[element] = description;
}
}
return description;
}
/// Return `true` if properties should be created for instances of [type].
bool hasNestedProperties(DartType type) {
if (type is InterfaceType) {
return _isOptedInClass(type.element);
}
return false;
}
ClassDescription _classDescription(ClassElement element) {
if (!_isOptedInClass(element)) return null;
var constructor = element.unnamedConstructor;
if (constructor == null) return null;
var parameters = constructor.parameters;
var defaultValueMap = <ParameterElement, String>{};
for (var parameter in parameters) {
if (parameter.isNotOptional || parameter.hasRequired) {
return null;
}
}
return ClassDescription(element, constructor, defaultValueMap);
}
bool _isOptedInClass(ClassElement element) {
return _isClass(
element,
'package:flutter/src/widgets/container.dart',
'Container',
) ||
_isClass(
element,
'package:flutter/src/painting/text_style.dart',
'TextStyle',
);
}
static bool _isClass(ClassElement element, String uri, String name) {
return element.name == name && element.library.source.uri.toString() == uri;
}
}