blob: 9c741a64b2d74c783b0c248d1e0ec085bab14d3c [file] [log] [blame]
// Copyright (c) 2015, 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.
library analyzer.src.task.dart;
import 'dart:collection';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
import 'package:analyzer/src/generated/error.dart';
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/source.dart';
import 'package:analyzer/src/task/general.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/general.dart';
import 'package:analyzer/task/model.dart';
/**
* A task that builds a compilation unit element for a single compilation unit.
*/
class BuildCompilationUnitElementTask extends SourceBasedAnalysisTask {
/**
* The name of the input whose value is the line information for the
* compilation unit.
*/
static const String LINE_INFO_INPUT_NAME = "lineInfo";
/**
* The name of the input whose value is the AST for the compilation unit.
*/
static const String PARSED_UNIT_INPUT_NAME = "parsedUnit";
/**
* The task descriptor describing this kind of task.
*/
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'BUILD_COMPILATION_UNIT_ELEMENT',
createTask,
buildInputs,
<ResultDescriptor>[COMPILATION_UNIT_ELEMENT, BUILT_UNIT]);
/**
* Initialize a newly created task to build a compilation unit element for
* the given [target] in the given [context].
*/
BuildCompilationUnitElementTask(InternalAnalysisContext context,
AnalysisTarget target)
: super(context, target);
@override
TaskDescriptor get descriptor => DESCRIPTOR;
@override
void internalPerform() {
Source source = getRequiredSource();
CompilationUnit unit = getRequiredInput(PARSED_UNIT_INPUT_NAME);
CompilationUnitBuilder builder = new CompilationUnitBuilder();
CompilationUnitElement element = builder.buildCompilationUnit(source, unit);
outputs[COMPILATION_UNIT_ELEMENT] = element;
outputs[BUILT_UNIT] = unit;
}
/**
* 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>{
PARSED_UNIT_INPUT_NAME: PARSED_UNIT.inputFor(target)
};
}
/**
* Create a [BuildCompilationUnitElementTask] based on the given [target] in
* the given [context].
*/
static BuildCompilationUnitElementTask createTask(AnalysisContext context,
AnalysisTarget target) {
return new BuildCompilationUnitElementTask(context, target);
}
}
/**
* A task that parses the content of a Dart file, producing an AST structure.
*/
class ParseDartTask extends SourceBasedAnalysisTask {
/**
* The name of the input whose value is the line information produced for the
* file.
*/
static const String LINE_INFO_INPUT_NAME = "lineInfo";
/**
* The name of the input whose value is the token stream produced for the file.
*/
static const String TOKEN_STREAM_INPUT_NAME = "tokenStream";
/**
* The task descriptor describing this kind of task.
*/
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'PARSE_DART',
createTask,
buildInputs,
<ResultDescriptor>[
EXPORTED_LIBRARIES,
IMPORTED_LIBRARIES,
INCLUDED_PARTS,
PARSE_ERRORS,
PARSED_UNIT,
SOURCE_KIND]);
/**
* Initialize a newly created task to parse the content of the Dart file
* associated with the given [target] in the given [context].
*/
ParseDartTask(InternalAnalysisContext context, AnalysisTarget target)
: super(context, target);
@override
TaskDescriptor get descriptor => DESCRIPTOR;
@override
void internalPerform() {
Source source = getRequiredSource();
LineInfo lineInfo = getRequiredInput(LINE_INFO_INPUT_NAME);
Token tokenStream = getRequiredInput(TOKEN_STREAM_INPUT_NAME);
RecordingErrorListener errorListener = new RecordingErrorListener();
Parser parser = new Parser(source, errorListener);
AnalysisOptions options = context.analysisOptions;
parser.parseFunctionBodies = options.analyzeFunctionBodies;
CompilationUnit unit = parser.parseCompilationUnit(tokenStream);
unit.lineInfo = lineInfo;
bool hasNonPartOfDirective = false;
bool hasPartOfDirective = false;
HashSet<Source> exportedSources = new HashSet<Source>();
HashSet<Source> importedSources = new HashSet<Source>();
HashSet<Source> includedSources = new HashSet<Source>();
for (Directive directive in unit.directives) {
if (directive is PartOfDirective) {
hasPartOfDirective = true;
} else {
hasNonPartOfDirective = true;
if (directive is UriBasedDirective) {
Source referencedSource =
resolveDirective(context, source, directive, errorListener);
if (referencedSource != null) {
if (directive is ExportDirective) {
exportedSources.add(referencedSource);
} else if (directive is ImportDirective) {
importedSources.add(referencedSource);
} else if (directive is PartDirective) {
if (referencedSource != source) {
includedSources.add(referencedSource);
}
} else {
throw new AnalysisException(
"$runtimeType failed to handle a ${directive.runtimeType}");
}
}
}
}
}
SourceKind sourceKind = SourceKind.LIBRARY;
if (!hasNonPartOfDirective && hasPartOfDirective) {
sourceKind = SourceKind.PART;
}
outputs[EXPORTED_LIBRARIES] = exportedSources.toList();
outputs[IMPORTED_LIBRARIES] = importedSources.toList();
outputs[INCLUDED_PARTS] = includedSources.toList();
outputs[PARSE_ERRORS] = errorListener.getErrorsForSource(source);
outputs[PARSED_UNIT] = unit;
outputs[SOURCE_KIND] = sourceKind;
}
/**
* 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>{
LINE_INFO_INPUT_NAME: LINE_INFO.inputFor(target),
TOKEN_STREAM_INPUT_NAME: TOKEN_STREAM.inputFor(target)
};
}
/**
* Create a [ParseDartTask] based on the given [target] in the given
* [context].
*/
static ParseDartTask createTask(AnalysisContext context,
AnalysisTarget target) {
return new ParseDartTask(context, target);
}
/**
* Return the result of resolving the URI of the given URI-based [directive]
* against the URI of the given library, or `null` if the URI is not valid.
*
* Resolution is to be performed in the given [context]. Errors should be
* reported to the [errorListener].
*/
static Source resolveDirective(AnalysisContext context, Source librarySource,
UriBasedDirective directive, AnalysisErrorListener errorListener) {
StringLiteral uriLiteral = directive.uri;
String uriContent = uriLiteral.stringValue;
if (uriContent != null) {
uriContent = uriContent.trim();
directive.uriContent = uriContent;
}
UriValidationCode code = directive.validate();
if (code == null) {
String encodedUriContent = Uri.encodeFull(uriContent);
Source source =
context.sourceFactory.resolveUri(librarySource, encodedUriContent);
directive.source = source;
return source;
}
if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) {
return null;
}
if (code == UriValidationCode.URI_WITH_INTERPOLATION) {
errorListener.onError(
new AnalysisError.con2(
librarySource,
uriLiteral.offset,
uriLiteral.length,
CompileTimeErrorCode.URI_WITH_INTERPOLATION));
return null;
}
if (code == UriValidationCode.INVALID_URI) {
errorListener.onError(
new AnalysisError.con2(
librarySource,
uriLiteral.offset,
uriLiteral.length,
CompileTimeErrorCode.INVALID_URI,
[uriContent]));
return null;
}
throw new AnalysisException('Failed to handle validation code: $code');
}
}
/**
* A task that scans the content of a file, producing a set of Dart tokens.
*/
class ScanDartTask extends SourceBasedAnalysisTask {
/**
* The name of the input whose value is the content of the file.
*/
static const String CONTENT_INPUT_NAME = "content";
/**
* The task descriptor describing this kind of task.
*/
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'SCAN_DART',
createTask,
buildInputs,
<ResultDescriptor>[LINE_INFO, SCAN_ERRORS, TOKEN_STREAM]);
/**
* Initialize a newly created task to access the content of the source
* associated with the given [target] in the given [context].
*/
ScanDartTask(InternalAnalysisContext context, AnalysisTarget target)
: super(context, target);
@override
TaskDescriptor get descriptor => DESCRIPTOR;
@override
void internalPerform() {
Source source = getRequiredSource();
String content = getRequiredInput(CONTENT_INPUT_NAME);
RecordingErrorListener errorListener = new RecordingErrorListener();
Scanner scanner =
new Scanner(source, new CharSequenceReader(content), errorListener);
scanner.preserveComments = context.analysisOptions.preserveComments;
outputs[TOKEN_STREAM] = scanner.tokenize();
outputs[LINE_INFO] = new LineInfo(scanner.lineStarts);
outputs[SCAN_ERRORS] = errorListener.getErrorsForSource(source);
}
/**
* 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>{
CONTENT_INPUT_NAME: CONTENT.inputFor(target)
};
}
/**
* Create a [ScanDartTask] based on the given [target] in the given [context].
*/
static ScanDartTask createTask(AnalysisContext context,
AnalysisTarget target) {
return new ScanDartTask(context, target);
}
}