blob: a2cf17beed5b4d968cf8959406095c2943f77c80 [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/src/summary2/scope.dart';
/// Indirection between a name and the corresponding [Element].
///
/// References are organized in a prefix tree.
/// Each reference knows its parent, children, and the [Element].
///
/// Library:
/// URI of library
///
/// Class:
/// Reference of the enclosing library
/// "@class"
/// Name of the class
///
/// Method:
/// Reference of the enclosing class
/// "@method"
/// Name of the method
///
/// There is only one reference object per [Element].
class Reference {
/// The parent of this reference, or `null` if the root.
final Reference? parent;
/// The simple name of the reference in its [parent].
final String name;
/// The node accessor, used to read nodes lazily.
/// Or `null` if a named container.
ReferenceNodeAccessor? nodeAccessor;
/// The corresponding [AstNode], or `null` if a named container.
/// TODO(scheglov) remove it
AstNode? node;
/// The corresponding [Element], or `null` if a named container.
Element? element;
/// Temporary index used during serialization and linking.
int? index;
Map<String, Reference>? _children;
/// If this reference is an import prefix, the scope of this prefix.
Scope? prefixScope;
Reference.root() : this._(null, '');
Reference._(this.parent, this.name);
Iterable<Reference> get children {
if (_children != null) {
return _children!.values;
}
return const [];
}
bool get isClass => parent != null && parent!.name == '@class';
bool get isConstructor => parent != null && parent!.name == '@constructor';
bool get isDynamic => name == 'dynamic' && parent?.name == 'dart:core';
bool get isEnum => parent != null && parent!.name == '@enum';
bool get isGetter => parent != null && parent!.name == '@getter';
bool get isLibrary => parent != null && parent!.isRoot;
bool get isParameter => parent != null && parent!.name == '@parameter';
bool get isPrefix => parent != null && parent!.name == '@prefix';
bool get isRoot => parent == null;
bool get isSetter => parent != null && parent!.name == '@setter';
bool get isTypeAlias => parent != null && parent!.name == '@typeAlias';
bool get isUnit => parent != null && parent!.name == '@unit';
/// Return the child with the given name, or `null` if does not exist.
Reference? operator [](String name) {
return _children != null ? _children![name] : null;
}
/// Return the child with the given name, create if does not exist yet.
Reference getChild(String name) {
var map = _children ??= <String, Reference>{};
return map[name] ??= Reference._(this, name);
}
/// If the reference has element, and it is for the [node], return `true`.
///
/// The element might be not `null`, but the node is different in case of
/// duplicate declarations.
bool hasElementFor(AstNode node) {
if (element != null && this.node == node) {
return true;
} else {
if (this.node == null) {
this.node = node;
}
return false;
}
}
void removeChild(String name) {
_children!.remove(name);
}
@override
String toString() => parent == null ? 'root' : '$parent::$name';
}
abstract class ReferenceNodeAccessor {
/// Return the node that corresponds to this [Reference], read it if not yet.
AstNode get node;
/// Fill [Reference.nodeAccessor] for children.
///
/// TODO(scheglov) only class reader has a meaningful implementation.
void readIndex();
}