Move library related operations to DartFileEditBuilderImpl. Initialize enclosing elements into fields lazily.
R=brianwilkerson@google.com
Change-Id: Ic933c9399f2288805e1f450f9d564e174516addc
Reviewed-on: https://dart-review.googlesource.com/55760
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index c465304..4f5a68b 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -70,6 +70,23 @@
List<String> _KNOWN_METHOD_NAME_PREFIXES = ['get', 'is', 'to'];
/**
+ * Whether [_enclosingClass] and [_enclosingExecutable] have been initialized.
+ */
+ bool _hasEnclosingElementsInitialized = false;
+
+ /**
+ * The enclosing class element, possibly `null`.
+ * This field is lazily initialized in [_initializeEnclosingElements].
+ */
+ ClassElement _enclosingClass;
+
+ /**
+ * The enclosing executable element, possibly `null`.
+ * This field is lazily initialized in [_initializeEnclosingElements].
+ */
+ ExecutableElement _enclosingExecutable;
+
+ /**
* If not `null`, [write] will copy everything into this buffer.
*/
StringBuffer _carbonCopyBuffer;
@@ -489,13 +506,7 @@
void writeParameter(String name,
{ExecutableElement methodBeingCopied, DartType type}) {
if (type != null) {
- _EnclosingElementFinder finder = _findEnclosingElements();
- bool hasType = _writeType(
- type,
- finder.enclosingClass,
- finder.enclosingExecutable,
- methodBeingCopied: methodBeingCopied,
- );
+ bool hasType = _writeType(type, methodBeingCopied: methodBeingCopied);
if (name.isNotEmpty) {
if (hasType) {
write(' ');
@@ -616,29 +627,18 @@
ExecutableElement methodBeingCopied,
bool required: false}) {
if (type != null && !type.isDynamic) {
- _EnclosingElementFinder finder = _findEnclosingElements();
if (groupName != null) {
bool wroteType;
addLinkedEdit(groupName, (LinkedEditBuilder builder) {
// TODO(scheglov) ensure that no linked edit if nothing written
- wroteType = _writeType(
- type,
- finder.enclosingClass,
- finder.enclosingExecutable,
- methodBeingCopied: methodBeingCopied,
- );
+ wroteType = _writeType(type, methodBeingCopied: methodBeingCopied);
if (wroteType && addSupertypeProposals) {
_addSuperTypeProposals(builder, type, new Set<DartType>());
}
});
return wroteType;
} else {
- return _writeType(
- type,
- finder.enclosingClass,
- finder.enclosingExecutable,
- methodBeingCopied: methodBeingCopied,
- );
+ return _writeType(type, methodBeingCopied: methodBeingCopied);
}
}
if (required) {
@@ -653,11 +653,8 @@
{ExecutableElement methodBeingCopied}) {
write(typeParameter.name);
if (typeParameter.bound != null) {
- _EnclosingElementFinder finder = _findEnclosingElements();
write(' extends ');
- _writeType(typeParameter.bound, finder.enclosingClass,
- finder.enclosingExecutable,
- methodBeingCopied: methodBeingCopied);
+ _writeType(typeParameter.bound, methodBeingCopied: methodBeingCopied);
}
}
@@ -751,13 +748,6 @@
}
}
- _EnclosingElementFinder _findEnclosingElements() {
- // TODO(scheglov) Stop searching for enclosing class multiple times.
- _EnclosingElementFinder finder = new _EnclosingElementFinder();
- finder.find(dartFileEditBuilder.unit, offset);
- return finder;
- }
-
String _getBaseNameFromExpression(Expression expression) {
if (expression is AsExpression) {
return _getBaseNameFromExpression(expression.expression);
@@ -860,28 +850,6 @@
}
/**
- * Return the import element used to import the given [element] into the given
- * [library], or `null` if the element was not imported, such as when the
- * element is declared in the same library.
- */
- ImportElement _getImportElement(Element element, LibraryElement library) {
- for (ImportElement importElement in library.imports) {
- Map<String, Element> definedNames = _getImportNamespace(importElement);
- if (definedNames.containsValue(element)) {
- return importElement;
- }
- }
- return null;
- }
-
- /**
- * Return the namespace added by the given import [element].
- */
- Map<String, Element> _getImportNamespace(ImportElement element) {
- return element.namespace.definedNames;
- }
-
- /**
* Return a list containing the suggested names for a parameter with the given
* [type] whose value in one location is computed by the given [expression].
* The list will not contain any names in the set of [excluded] names. The
@@ -939,15 +907,15 @@
}
/**
- * If the given [type] is visible in either the [enclosingExecutable] or
- * [enclosingClass], or if there is a local equivalent to the type (such as in
- * the case of a type parameter from a superclass), then return the type that
- * is locally visible. Otherwise, return `null`.
+ * If the given [type] is visible in either the [_enclosingExecutable] or
+ * [_enclosingClass], or if there is a local equivalent to the type (such as
+ * in the case of a type parameter from a superclass), then return the type
+ * that is locally visible. Otherwise, return `null`.
*/
- DartType _getVisibleType(DartType type, ClassElement enclosingClass,
- ExecutableElement enclosingExecutable,
+ DartType _getVisibleType(DartType type,
{ExecutableElement methodBeingCopied}) {
if (type is TypeParameterType) {
+ _initializeEnclosingElements();
TypeParameterElement parameterElement = type.element;
Element parameterParent = parameterElement.enclosingElement;
while (parameterParent is GenericFunctionTypeElement ||
@@ -955,24 +923,24 @@
parameterParent = parameterParent.enclosingElement;
}
// TODO(brianwilkerson) This needs to compare the parameterParent with
- // each of the parents of the enclosingExecutable. (That means that we
+ // each of the parents of the _enclosingExecutable. (That means that we
// only need the most closely enclosing element.)
- if (parameterParent == enclosingExecutable ||
- parameterParent == enclosingClass ||
+ if (parameterParent == _enclosingExecutable ||
+ parameterParent == _enclosingClass ||
parameterParent == methodBeingCopied) {
return type;
}
- if (enclosingClass != null &&
+ if (_enclosingClass != null &&
methodBeingCopied != null &&
parameterParent is ClassElement &&
parameterParent == methodBeingCopied.enclosingElement) {
// The parameter is from the class enclosing the methodBeingCopied. That
// means that somewhere along the inheritance chain there must be a type
// argument corresponding to the type parameter (either a concrete type
- // or a type parameter of the enclosingClass). That's the visible type
+ // or a type parameter of the _enclosingClass). That's the visible type
// that needs to be returned.
_InheritanceChain chain = new _InheritanceChain(
- subtype: enclosingClass, supertype: parameterParent);
+ subtype: _enclosingClass, supertype: parameterParent);
while (chain != null) {
DartType mappedType = chain.mapParameter(parameterElement);
if (mappedType is TypeParameterType) {
@@ -990,41 +958,48 @@
if (element == null) {
return type;
}
- LibraryElement definingLibrary = element.library;
- LibraryElement importingLibrary = dartFileEditBuilder.unit.element.library;
- if (definingLibrary != null &&
- definingLibrary != importingLibrary &&
- element.isPrivate) {
+ if (element.isPrivate && !dartFileEditBuilder._isDefinedLocally(element)) {
return null;
}
return type;
}
/**
- * Arrange to have an import added for the library with the given [uri].
+ * Initialize the [_enclosingClass] and [_enclosingExecutable].
*/
- _LibraryToImport _importLibrary(Uri uri) {
- return dartFileEditBuilder._importLibrary(uri);
+ void _initializeEnclosingElements() {
+ if (!_hasEnclosingElementsInitialized) {
+ _EnclosingElementFinder finder = new _EnclosingElementFinder();
+ finder.find(dartFileEditBuilder.unit, offset);
+ _enclosingClass = finder.enclosingClass;
+ _enclosingExecutable = finder.enclosingExecutable;
+ _hasEnclosingElementsInitialized = true;
+ }
}
/**
* Write the import prefix to reference the [element], if needed.
*
- * The prefix is not needed if there is already an import without prefix
- * that exports the [element]. If there there are no existing import
- * that exports the [element], a library that exports the [element] is
- * scheduled for import, possibly with a prefix.
+ * The prefix is not needed if the [element] is defined in the target library,
+ * or there is already an import without prefix that exports the [element].
+ * If there there are no existing import that exports the [element], a library
+ * that exports the [element] is scheduled for import, possibly with a prefix.
*/
void _writeLibraryReference(Element element) {
- LibraryElement importingLibrary = dartFileEditBuilder.unit.element.library;
- ImportElement importElement = _getImportElement(element, importingLibrary);
- if (importElement != null) {
- if (importElement.prefix != null) {
- write(importElement.prefix.displayName);
+ // If the element is defined in the library, then no prefix needed.
+ if (dartFileEditBuilder._isDefinedLocally(element)) {
+ return;
+ }
+
+ ImportElement import = dartFileEditBuilder._getImportElement(element);
+ if (import != null) {
+ if (import.prefix != null) {
+ write(import.prefix.displayName);
write('.');
}
} else {
- _LibraryToImport import = _importLibrary(element.library.source.uri);
+ Uri library = element.library.source.uri;
+ _LibraryToImport import = dartFileEditBuilder._importLibrary(library);
if (import.prefix != null) {
write(import.prefix);
write('.');
@@ -1035,25 +1010,14 @@
/**
* Write the code to reference [type] in this compilation unit.
*
- * If an [enclosingClass] is provided then the reference is being generated
- * within the class and the type parameters of the class will be considered to
- * be visible.
- *
- * If an [enclosingExecutable] is provided, then the reference is being
- * generated within the class and the type parameters of the method will be
- * considered to be visible.
- *
* If a [methodBeingCopied] is provided, then the type parameters of that
* method will be duplicated in the copy and will therefore be visible.
*
* Causes any libraries whose elements are used by the generated code, to be
* imported.
*/
- bool _writeType(DartType type, ClassElement enclosingClass,
- ExecutableElement enclosingExecutable,
- {ExecutableElement methodBeingCopied}) {
- type = _getVisibleType(type, enclosingClass, enclosingExecutable,
- methodBeingCopied: methodBeingCopied);
+ bool _writeType(DartType type, {ExecutableElement methodBeingCopied}) {
+ type = _getVisibleType(type, methodBeingCopied: methodBeingCopied);
// If not a useful type, don't write it.
if (type == null || type.isDynamic || type.isBottom) {
@@ -1079,12 +1043,7 @@
if (type is FunctionType &&
element is FunctionTypedElement &&
element is! FunctionTypeAliasElement) {
- if (_writeType(
- type.returnType,
- enclosingClass,
- enclosingExecutable,
- methodBeingCopied: methodBeingCopied,
- )) {
+ if (_writeType(type.returnType, methodBeingCopied: methodBeingCopied)) {
write(' ');
}
write('Function');
@@ -1094,14 +1053,8 @@
return true;
}
- // If the element is not local, ensure that it is imported.
- // TODO(scheglov) Remember importing library in the edit class.
- // TODO(scheglov) Check for local element in _writeLibraryReference()?
- LibraryElement definingLibrary = element.library;
- LibraryElement importingLibrary = dartFileEditBuilder.unit.element.library;
- if (definingLibrary != null && definingLibrary != importingLibrary) {
- _writeLibraryReference(element);
- }
+ // Ensure that the element is imported.
+ _writeLibraryReference(element);
// Write the simple name.
String name = element.displayName;
@@ -1116,8 +1069,7 @@
for (DartType argument in arguments) {
hasArguments = hasArguments || !argument.isDynamic;
allArgumentsVisible = allArgumentsVisible &&
- _getVisibleType(argument, enclosingClass, enclosingExecutable,
- methodBeingCopied: methodBeingCopied) !=
+ _getVisibleType(argument, methodBeingCopied: methodBeingCopied) !=
null;
}
// Write type arguments only if they are useful.
@@ -1128,8 +1080,7 @@
if (i != 0) {
write(', ');
}
- _writeType(argument, enclosingClass, enclosingExecutable,
- methodBeingCopied: methodBeingCopied);
+ _writeType(argument, methodBeingCopied: methodBeingCopied);
}
write('>');
}
@@ -1147,7 +1098,12 @@
/**
* The compilation unit to which the code will be added.
*/
- CompilationUnit unit;
+ final CompilationUnit unit;
+
+ /**
+ * The target library, which contains the [unit].
+ */
+ final LibraryElement libraryElement;
/**
* The optional generator of prefixes for new imports.
@@ -1163,11 +1119,12 @@
/**
* Initialize a newly created builder to build a source file edit within the
* change being built by the given [changeBuilder]. The file being edited has
- * the given [source] and [timeStamp], and the given fully resolved [unit].
+ * the given [path] and [timeStamp], and the given fully resolved [unit].
*/
DartFileEditBuilderImpl(DartChangeBuilderImpl changeBuilder, String path,
int timeStamp, this.unit)
- : super(changeBuilder, path, timeStamp);
+ : libraryElement = unit.element.library,
+ super(changeBuilder, path, timeStamp);
@override
void addInsertion(int offset, void buildEdit(DartEditBuilder builder)) =>
@@ -1199,11 +1156,9 @@
@override
Future<Null> finalize() async {
if (librariesToImport.isNotEmpty) {
- CompilationUnitElement unitElement = unit.element;
- LibraryElement libraryElement = unitElement.library;
CompilationUnitElement definingUnitElement =
libraryElement.definingCompilationUnit;
- if (definingUnitElement == unitElement) {
+ if (definingUnitElement == unit.element) {
_addLibraryImports(librariesToImport.values);
} else {
await (changeBuilder as DartChangeBuilder).addFileEdit(
@@ -1247,8 +1202,7 @@
}
/**
- * Adds edits ensure that all the [imports] are imported into the given
- * [targetLibrary].
+ * Adds edits ensure that all the [imports] are imported into the library.
*/
void _addLibraryImports(Iterable<_LibraryToImport> imports) {
// Prepare information about existing imports.
@@ -1419,15 +1373,31 @@
}
/**
- * Computes the best URI to import [what] into [from].
+ * Return the import element used to import the given [element] into the
+ * target library, or `null` if the element was not imported, such as when
+ * the element is declared in the same library.
*/
- String _getLibrarySourceUri(LibraryElement from, Uri what) {
+ ImportElement _getImportElement(Element element) {
+ for (ImportElement import in libraryElement.imports) {
+ Map<String, Element> definedNames = import.namespace.definedNames;
+ if (definedNames.containsValue(element)) {
+ return import;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Computes the best URI to import [what] into the target library.
+ */
+ String _getLibraryUriText(Uri what) {
if (what.scheme == 'file') {
var session = (changeBuilder as DartChangeBuilderImpl).session;
var pathContext = session.resourceProvider.pathContext;
String whatPath = pathContext.fromUri(what);
- String fromFolder = pathContext.dirname(from.source.fullName);
- String relativeFile = pathContext.relative(whatPath, from: fromFolder);
+ String libraryPath = libraryElement.source.fullName;
+ String libraryFolder = pathContext.dirname(libraryPath);
+ String relativeFile = pathContext.relative(whatPath, from: libraryFolder);
return pathContext.split(relativeFile).join('/');
}
return what.toString();
@@ -1439,8 +1409,7 @@
_LibraryToImport _importLibrary(Uri uri) {
var import = librariesToImport[uri];
if (import == null) {
- LibraryElement targetLibrary = unit.element.library;
- String uriText = _getLibrarySourceUri(targetLibrary, uri);
+ String uriText = _getLibraryUriText(uri);
String prefix =
importPrefixGenerator != null ? importPrefixGenerator(uri) : null;
import = new _LibraryToImport(uriText, prefix);
@@ -1450,6 +1419,13 @@
}
/**
+ * Return `true` if the [element] is defined in the target library.
+ */
+ bool _isDefinedLocally(Element element) {
+ return element.library == libraryElement;
+ }
+
+ /**
* Create an edit to replace the return type of the innermost function
* containing the given [node] with the type `Future`. The [typeProvider] is
* used to check the current return type, because if it is already `Future` no