Index: pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart |
diff --git a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart |
index 37b9dff61342765ee0e4a640486e2f17733b9498..b402ec76be9e262174057ac536c374bd3e06e8d0 100644 |
--- a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart |
+++ b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart |
@@ -2,10 +2,14 @@ |
// 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/element.dart' as analyzer; |
+import 'package:analyzer/dart/element/type.dart' as analyzer; |
import 'package:analyzer/error/error.dart' as analyzer; |
+import 'package:analyzer/exception/exception.dart' as analyzer; |
import 'package:analyzer/source/error_processor.dart' as analyzer; |
import 'package:analyzer/src/generated/engine.dart' as analyzer; |
import 'package:analyzer/src/generated/source.dart' as analyzer; |
+import 'package:analyzer/src/generated/utilities_dart.dart' as analyzer; |
import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin; |
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin; |
@@ -78,6 +82,73 @@ class AnalyzerConverter { |
} |
/** |
+ * Convert the given [element] from the 'analyzer' package to an element |
+ * defined by the plugin API. |
+ */ |
+ plugin.Element convertElement(analyzer.Element element) { |
+ plugin.ElementKind kind = _convertElementToElementKind(element); |
+ return new plugin.Element( |
+ kind, |
+ element.displayName, |
+ plugin.Element.makeFlags( |
+ isPrivate: element.isPrivate, |
+ isDeprecated: element.isDeprecated, |
+ isAbstract: _isAbstract(element), |
+ isConst: _isConst(element), |
+ isFinal: _isFinal(element), |
+ isStatic: _isStatic(element)), |
+ location: _locationFromElement(element), |
+ typeParameters: _getTypeParametersString(element), |
+ parameters: _getParametersString(element), |
+ returnType: _getReturnTypeString(element)); |
+ } |
+ |
+ /** |
+ * Convert the element [kind] from the 'analyzer' package to an element kind |
+ * defined by the plugin API. |
+ * |
+ * This method does not take into account that an instance of [ClassElement] |
+ * can be an enum and an instance of [FieldElement] can be an enum constant. |
+ * Use [_convertElementToElementKind] where possible. |
+ */ |
+ plugin.ElementKind convertElementKind(analyzer.ElementKind kind) { |
+ if (kind == analyzer.ElementKind.CLASS) { |
+ return plugin.ElementKind.CLASS; |
+ } else if (kind == analyzer.ElementKind.COMPILATION_UNIT) { |
+ return plugin.ElementKind.COMPILATION_UNIT; |
+ } else if (kind == analyzer.ElementKind.CONSTRUCTOR) { |
+ return plugin.ElementKind.CONSTRUCTOR; |
+ } else if (kind == analyzer.ElementKind.FIELD) { |
+ return plugin.ElementKind.FIELD; |
+ } else if (kind == analyzer.ElementKind.FUNCTION) { |
+ return plugin.ElementKind.FUNCTION; |
+ } else if (kind == analyzer.ElementKind.FUNCTION_TYPE_ALIAS) { |
+ return plugin.ElementKind.FUNCTION_TYPE_ALIAS; |
+ } else if (kind == analyzer.ElementKind.GETTER) { |
+ return plugin.ElementKind.GETTER; |
+ } else if (kind == analyzer.ElementKind.LABEL) { |
+ return plugin.ElementKind.LABEL; |
+ } else if (kind == analyzer.ElementKind.LIBRARY) { |
+ return plugin.ElementKind.LIBRARY; |
+ } else if (kind == analyzer.ElementKind.LOCAL_VARIABLE) { |
+ return plugin.ElementKind.LOCAL_VARIABLE; |
+ } else if (kind == analyzer.ElementKind.METHOD) { |
+ return plugin.ElementKind.METHOD; |
+ } else if (kind == analyzer.ElementKind.PARAMETER) { |
+ return plugin.ElementKind.PARAMETER; |
+ } else if (kind == analyzer.ElementKind.PREFIX) { |
+ return plugin.ElementKind.PREFIX; |
+ } else if (kind == analyzer.ElementKind.SETTER) { |
+ return plugin.ElementKind.SETTER; |
+ } else if (kind == analyzer.ElementKind.TOP_LEVEL_VARIABLE) { |
+ return plugin.ElementKind.TOP_LEVEL_VARIABLE; |
+ } else if (kind == analyzer.ElementKind.TYPE_PARAMETER) { |
+ return plugin.ElementKind.TYPE_PARAMETER; |
+ } |
+ return plugin.ElementKind.UNKNOWN; |
+ } |
+ |
+ /** |
* Convert the error [severity] from the 'analyzer' package to an analysis |
* error severity defined by the plugin API. |
*/ |
@@ -91,4 +162,211 @@ class AnalyzerConverter { |
*/ |
plugin.AnalysisErrorType convertErrorType(analyzer.ErrorType type) => |
new plugin.AnalysisErrorType(type.name); |
+ |
+ /** |
+ * Convert the element kind of the [element] from the 'analyzer' package to an |
+ * element kind defined by the plugin API. |
+ */ |
+ plugin.ElementKind _convertElementToElementKind(analyzer.Element element) { |
+ if (element is analyzer.ClassElement && element.isEnum) { |
+ return plugin.ElementKind.ENUM; |
+ } else if (element is analyzer.FieldElement && |
+ element.isEnumConstant && |
+ // MyEnum.values and MyEnum.one.index return isEnumConstant = true |
+ // so these additional checks are necessary. |
+ // TODO(danrubel) MyEnum.values is constant, but is a list |
+ // so should it return isEnumConstant = true? |
+ // MyEnum.one.index is final but *not* constant |
+ // so should it return isEnumConstant = true? |
+ // Or should we return ElementKind.ENUM_CONSTANT here |
+ // in either or both of these cases? |
+ element.type != null && |
+ element.type.element == element.enclosingElement) { |
+ return plugin.ElementKind.ENUM_CONSTANT; |
+ } |
+ return convertElementKind(element.kind); |
+ } |
+ |
+ /** |
+ * Return a textual representation of the parameters of the given [element], |
+ * or `null` if the element does not have any parameters. |
+ */ |
+ String _getParametersString(analyzer.Element element) { |
+ // TODO(scheglov) expose the corresponding feature from ExecutableElement |
+ List<analyzer.ParameterElement> parameters; |
+ if (element is analyzer.ExecutableElement) { |
+ // valid getters don't have parameters |
+ if (element.kind == analyzer.ElementKind.GETTER && |
+ element.parameters.isEmpty) { |
+ return null; |
+ } |
+ parameters = element.parameters; |
+ } else if (element is analyzer.FunctionTypeAliasElement) { |
+ parameters = element.parameters; |
+ } else { |
+ return null; |
+ } |
+ StringBuffer buffer = new StringBuffer(); |
+ String closeOptionalString = ''; |
+ buffer.write('('); |
+ for (int i = 0; i < parameters.length; i++) { |
+ analyzer.ParameterElement parameter = parameters[i]; |
+ if (i > 0) { |
+ buffer.write(', '); |
+ } |
+ if (closeOptionalString.isEmpty) { |
+ analyzer.ParameterKind kind = parameter.parameterKind; |
+ if (kind == analyzer.ParameterKind.NAMED) { |
+ buffer.write('{'); |
+ closeOptionalString = '}'; |
+ } |
+ if (kind == analyzer.ParameterKind.POSITIONAL) { |
+ buffer.write('['); |
+ closeOptionalString = ']'; |
+ } |
+ } |
+ parameter.appendToWithoutDelimiters(buffer); |
+ } |
+ buffer.write(closeOptionalString); |
+ buffer.write(')'); |
+ return buffer.toString(); |
+ } |
+ |
+ /** |
+ * Return a textual representation of the return type of the given [element], |
+ * or `null` if the element does not have a return type. |
+ */ |
+ String _getReturnTypeString(analyzer.Element element) { |
+ if (element is analyzer.ExecutableElement) { |
+ if (element.kind == analyzer.ElementKind.SETTER) { |
+ return null; |
+ } |
+ return element.returnType?.toString(); |
+ } else if (element is analyzer.VariableElement) { |
+ analyzer.DartType type = element.type; |
+ return type != null ? type.displayName : 'dynamic'; |
+ } else if (element is analyzer.FunctionTypeAliasElement) { |
+ return element.returnType.toString(); |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * Return a textual representation of the type parameters of the given |
+ * [element], or `null` if the element does not have type parameters. |
+ */ |
+ String _getTypeParametersString(analyzer.Element element) { |
+ if (element is analyzer.TypeParameterizedElement) { |
+ List<analyzer.TypeParameterElement> typeParameters = |
+ element.typeParameters; |
+ if (typeParameters == null || typeParameters.isEmpty) { |
+ return null; |
+ } |
+ return '<${typeParameters.join(', ')}>'; |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * Return the compilation unit containing the given [element]. |
+ */ |
+ analyzer.CompilationUnitElement _getUnitElement(analyzer.Element element) { |
+ if (element is analyzer.CompilationUnitElement) { |
+ return element; |
+ } |
+ if (element?.enclosingElement is analyzer.LibraryElement) { |
+ element = element.enclosingElement; |
+ } |
+ if (element is analyzer.LibraryElement) { |
+ return element.definingCompilationUnit; |
+ } |
+ for (; element != null; element = element.enclosingElement) { |
+ if (element is analyzer.CompilationUnitElement) { |
+ return element; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ bool _isAbstract(analyzer.Element element) { |
+ // TODO(scheglov) add isAbstract to Element API |
+ if (element is analyzer.ClassElement) { |
+ return element.isAbstract; |
+ } else if (element is analyzer.MethodElement) { |
+ return element.isAbstract; |
+ } else if (element is analyzer.PropertyAccessorElement) { |
+ return element.isAbstract; |
+ } |
+ return false; |
+ } |
+ |
+ bool _isConst(analyzer.Element element) { |
+ // TODO(scheglov) add isConst to Element API |
+ if (element is analyzer.ConstructorElement) { |
+ return element.isConst; |
+ } else if (element is analyzer.VariableElement) { |
+ return element.isConst; |
+ } |
+ return false; |
+ } |
+ |
+ bool _isFinal(analyzer.Element element) { |
+ // TODO(scheglov) add isFinal to Element API |
+ if (element is analyzer.VariableElement) { |
+ return element.isFinal; |
+ } |
+ return false; |
+ } |
+ |
+ bool _isStatic(analyzer.Element element) { |
+ // TODO(scheglov) add isStatic to Element API |
+ if (element is analyzer.ExecutableElement) { |
+ return element.isStatic; |
+ } else if (element is analyzer.PropertyInducingElement) { |
+ return element.isStatic; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Create and return a location within the given [unitElement] at the given |
+ * [range]. |
+ */ |
+ plugin.Location _locationForArgs( |
+ analyzer.CompilationUnitElement unitElement, analyzer.SourceRange range) { |
+ int startLine = 0; |
+ int startColumn = 0; |
+ try { |
+ analyzer.LineInfo lineInfo = unitElement.lineInfo; |
+ if (lineInfo != null) { |
+ analyzer.LineInfo_Location offsetLocation = |
+ lineInfo.getLocation(range.offset); |
+ startLine = offsetLocation.lineNumber; |
+ startColumn = offsetLocation.columnNumber; |
+ } |
+ } on analyzer.AnalysisException { |
+ // Ignore exceptions |
+ } |
+ return new plugin.Location(unitElement.source.fullName, range.offset, |
+ range.length, startLine, startColumn); |
+ } |
+ |
+ /** |
+ * Create a location based on an the given [element]. |
+ */ |
+ plugin.Location _locationFromElement(analyzer.Element element) { |
+ if (element == null || element.source == null) { |
+ return null; |
+ } |
+ int offset = element.nameOffset; |
+ int length = element.nameLength; |
+ if (element is analyzer.CompilationUnitElement || |
+ (element is analyzer.LibraryElement && offset < 0)) { |
+ offset = 0; |
+ length = 0; |
+ } |
+ analyzer.CompilationUnitElement unitElement = _getUnitElement(element); |
+ analyzer.SourceRange range = new analyzer.SourceRange(offset, length); |
+ return _locationForArgs(unitElement, range); |
+ } |
} |