Chromium Code Reviews| Index: pkg/analyzer/lib/src/task/dart.dart |
| diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart |
| index 5fd3c1dbf3227d3a828005f644ab53b0c30f6aa1..870baf1c80e1d1b20ad3c42b02bc5a701f320546 100644 |
| --- a/pkg/analyzer/lib/src/task/dart.dart |
| +++ b/pkg/analyzer/lib/src/task/dart.dart |
| @@ -14,8 +14,10 @@ import 'package:analyzer/src/generated/java_engine.dart'; |
| import 'package:analyzer/src/generated/parser.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/scanner.dart'; |
| +import 'package:analyzer/src/generated/sdk.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/task/general.dart'; |
| +import 'package:analyzer/src/task/inputs.dart'; |
| import 'package:analyzer/task/dart.dart'; |
| import 'package:analyzer/task/general.dart'; |
| import 'package:analyzer/task/model.dart'; |
| @@ -86,6 +88,260 @@ class BuildCompilationUnitElementTask extends SourceBasedAnalysisTask { |
| } |
| /** |
| + * A task that builds a library element for a Dart library. |
| + */ |
| +class BuildLibraryElementTask extends SourceBasedAnalysisTask { |
| + /** |
| + * The name of the input whose value is the built compilation unit of the |
| + * defining compiltion unit of a library. |
|
Brian Wilkerson
2015/03/11 14:16:01
"compiltion" --> "compilation"
scheglov
2015/03/11 16:55:07
Done.
|
| + */ |
| + static const String DEFINING_BUILT_UNIT_INPUT_NAME = 'definingBuiltUnit'; |
| + |
| + /** |
| + * The name of the input whose value is a list of built compilation units |
| + * of the parts sourced by a library. |
| + */ |
| + static const String PART_BUILT_UNITS_INPUT_NAME = 'partBuiltUnits'; |
| + |
| + /** |
| + * The name of the function used as an entry point. |
| + */ |
| + static String ENTRY_POINT_NAME = "main"; |
|
Brian Wilkerson
2015/03/11 14:16:01
Perhaps this should be moved to FunctionElement so
scheglov
2015/03/11 16:55:06
Will do in the next CL.
|
| + |
| + /** |
| + * The task descriptor describing this kind of task. |
| + */ |
| + static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| + 'BUILD_LIBRARY_ELEMENT', createTask, buildInputs, <ResultDescriptor>[ |
| + BUILD_LIBRARY_ERRORS, |
| + BUILT_LIBRARY_ELEMENT, |
| + IS_LAUNCHABLE, |
| + HAS_HTML_IMPORT |
| + ]); |
| + |
| + /** |
| + * Initialize a newly created task to build a library element for the given |
| + * [target] in the given [context]. |
| + */ |
| + BuildLibraryElementTask( |
| + InternalAnalysisContext context, AnalysisTarget target) |
| + : super(context, target); |
| + |
| + @override |
| + TaskDescriptor get descriptor => DESCRIPTOR; |
| + |
| + @override |
| + void internalPerform() { |
| + List<AnalysisError> errors = <AnalysisError>[]; |
| + // |
| + // Prepare inputs. |
| + // |
| + Source librarySource = getRequiredSource(); |
| + CompilationUnit definingCompilationUnit = |
| + getRequiredInput(DEFINING_BUILT_UNIT_INPUT_NAME); |
| + List<CompilationUnit> partUnits = |
| + getRequiredInput(PART_BUILT_UNITS_INPUT_NAME); |
| + // |
| + // Process inputs. |
| + // |
| + CompilationUnitElementImpl definingCompilationUnitElement = |
| + definingCompilationUnit.element; |
| + Map<Source, CompilationUnit> partUnitMap = |
| + new HashMap<Source, CompilationUnit>(); |
| + for (CompilationUnit partUnit in partUnits) { |
| + Source partSource = partUnit.element.source; |
| + partUnitMap[partSource] = partUnit; |
| + } |
| + Source htmlSource = context.sourceFactory.forUri(DartSdk.DART_HTML); |
| + // |
| + // Update "part" directives. |
| + // |
| + LibraryIdentifier libraryNameNode = null; |
| + bool hasHtmlImport = false; |
| + bool hasPartDirective = false; |
| + FunctionElement entryPoint = |
| + _findEntryPoint(definingCompilationUnitElement); |
| + List<Directive> directivesToResolve = <Directive>[]; |
| + List<CompilationUnitElementImpl> sourcedCompilationUnits = |
| + <CompilationUnitElementImpl>[]; |
| + for (Directive directive in definingCompilationUnit.directives) { |
| + if (directive is ImportDirective) { |
| + hasHtmlImport = hasHtmlImport || directive.source == htmlSource; |
| + } else if (directive is LibraryDirective) { |
| + if (libraryNameNode == null) { |
| + libraryNameNode = directive.name; |
| + directivesToResolve.add(directive); |
| + } |
| + } else if (directive is PartDirective) { |
| + PartDirective partDirective = directive; |
| + StringLiteral partUri = partDirective.uri; |
| + Source partSource = partDirective.source; |
| + if (context.exists(partSource)) { |
| + hasPartDirective = true; |
| + CompilationUnit partUnit = partUnitMap[partSource]; |
| + CompilationUnitElementImpl partElement = partUnit.element; |
| + partElement.uriOffset = partUri.offset; |
| + partElement.uriEnd = partUri.end; |
| + partElement.uri = partDirective.uriContent; |
| + // |
| + // Validate that the part contains a part-of directive with the same |
| + // name as the library. |
| + // |
| + String partLibraryName = |
| + _getPartLibraryName(partSource, partUnit, directivesToResolve); |
| + if (partLibraryName == null) { |
| + errors.add(new AnalysisError.con2(librarySource, partUri.offset, |
| + partUri.length, CompileTimeErrorCode.PART_OF_NON_PART, [ |
| + partUri.toSource() |
| + ])); |
| + } else if (libraryNameNode == null) { |
| + // TODO(brianwilkerson) Collect the names declared by the part. |
| + // If they are all the same then we can use that name as the |
| + // inferred name of the library and present it in a quick-fix. |
| + // partLibraryNames.add(partLibraryName); |
| + } else if (libraryNameNode.name != partLibraryName) { |
| + errors.add(new AnalysisError.con2(librarySource, partUri.offset, |
| + partUri.length, StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [ |
| + libraryNameNode.name, |
| + partLibraryName |
| + ])); |
| + } |
| + if (entryPoint == null) { |
| + entryPoint = _findEntryPoint(partElement); |
| + } |
| + directive.element = partElement; |
| + sourcedCompilationUnits.add(partElement); |
| + } |
| + } |
| + } |
| + if (hasPartDirective && libraryNameNode == null) { |
| + errors.add(new AnalysisError.con1(librarySource, |
| + ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); |
| + } |
| + // |
| + // Create and populate the library element. |
| + // |
| + LibraryElementImpl libraryElement = |
| + new LibraryElementImpl.forNode(context, libraryNameNode); |
| + libraryElement.definingCompilationUnit = definingCompilationUnitElement; |
| + libraryElement.entryPoint = entryPoint; |
| + libraryElement.parts = sourcedCompilationUnits; |
| + for (Directive directive in directivesToResolve) { |
| + directive.element = libraryElement; |
| + } |
| + if (sourcedCompilationUnits.isNotEmpty) { |
| + _patchTopLevelAccessors(libraryElement); |
| + } |
| + // |
| + // Record outputs. |
| + // |
| + outputs[BUILD_LIBRARY_ERRORS] = errors; |
| + outputs[BUILT_LIBRARY_ELEMENT] = libraryElement; |
| + outputs[IS_LAUNCHABLE] = entryPoint != null; |
| + outputs[HAS_HTML_IMPORT] = hasHtmlImport; |
| + } |
| + |
| + /** |
| + * Add all of the non-synthetic [getters] and [setters] defined in the given |
| + * [unit] that have no corresponding accessor to one of the given collections. |
| + */ |
| + void _collectAccessors(Map<String, PropertyAccessorElement> getters, |
| + List<PropertyAccessorElement> setters, CompilationUnitElement unit) { |
| + for (PropertyAccessorElement accessor in unit.accessors) { |
| + if (accessor.isGetter) { |
| + if (!accessor.isSynthetic && accessor.correspondingSetter == null) { |
| + getters[accessor.displayName] = accessor; |
| + } |
| + } else { |
| + if (!accessor.isSynthetic && accessor.correspondingGetter == null) { |
| + setters.add(accessor); |
| + } |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Return the top-level [FunctionElement] entry point, or `null` if the given |
| + * [element] does not define an entry point. |
| + */ |
| + FunctionElement _findEntryPoint(CompilationUnitElementImpl element) { |
| + for (FunctionElement function in element.functions) { |
| + if (function.name == ENTRY_POINT_NAME) { |
| + return function; |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + /** |
| + * Return the name of the library that the given part is declared to be a |
| + * part of, or `null` if the part does not contain a part-of directive. |
| + */ |
| + String _getPartLibraryName(Source partSource, CompilationUnit partUnit, |
| + List<Directive> directivesToResolve) { |
| + for (Directive directive in partUnit.directives) { |
| + if (directive is PartOfDirective) { |
| + directivesToResolve.add(directive); |
| + LibraryIdentifier libraryName = directive.libraryName; |
| + if (libraryName != null) { |
| + return libraryName.name; |
| + } |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + /** |
| + * Look through all of the compilation units defined for the given [library], |
| + * looking for getters and setters that are defined in different compilation |
| + * units but that have the same names. If any are found, make sure that they |
| + * have the same variable element. |
| + */ |
| + void _patchTopLevelAccessors(LibraryElementImpl library) { |
| + HashMap<String, PropertyAccessorElement> getters = |
| + new HashMap<String, PropertyAccessorElement>(); |
| + List<PropertyAccessorElement> setters = <PropertyAccessorElement>[]; |
| + _collectAccessors(getters, setters, library.definingCompilationUnit); |
| + for (CompilationUnitElement unit in library.parts) { |
| + _collectAccessors(getters, setters, unit); |
| + } |
| + for (PropertyAccessorElementImpl setter in setters) { |
| + PropertyAccessorElement getter = getters[setter.displayName]; |
| + if (getter != null) { |
| + PropertyInducingElementImpl variable = getter.variable; |
| + variable.setter = setter; |
| + setter.variable = variable; |
|
Brian Wilkerson
2015/03/11 14:16:01
I think we need to remove the setter's previous va
scheglov
2015/03/11 16:55:06
Done.
|
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Return a map from the names of the inputs of this kind of task to the task |
| + * input descriptors describing those inputs for a task with the given |
| + * [target]. |
| + */ |
| + static Map<String, TaskInput> buildInputs(AnalysisTarget target) { |
| + return <String, TaskInput>{ |
| + DEFINING_BUILT_UNIT_INPUT_NAME: new SimpleTaskInput(target, BUILT_UNIT), |
| + PART_BUILT_UNITS_INPUT_NAME: |
| + new ListBasedTaskInput<List<Source>, CompilationUnit>( |
| + new SimpleTaskInput<List<Source>>(target, INCLUDED_PARTS), |
| + (Source includedSource) => new SimpleTaskInput<CompilationUnit>( |
| + includedSource, BUILT_UNIT)) |
| + }; |
| + } |
| + |
| + /** |
| + * Create a [BuildLibraryElementTask] based on the given [target] in the |
| + * given [context]. |
| + */ |
| + static BuildLibraryElementTask createTask( |
| + AnalysisContext context, AnalysisTarget target) { |
| + return new BuildLibraryElementTask(context, target); |
| + } |
| +} |
| + |
| +/** |
| * A task that parses the content of a Dart file, producing an AST structure. |
| */ |
| class ParseDartTask extends SourceBasedAnalysisTask { |