Merge remote-tracking branch 'origin/master' into analyzer-breaking-0.27

This commit is contained in:
Paul Berry 2015-12-08 10:16:20 -08:00
commit 4c28eb6f67
64 changed files with 4271 additions and 1173 deletions

2
DEPS
View file

@ -67,7 +67,7 @@ vars = {
"intl_rev": "@32047558bd220a53c1f4d93a26d54b83533b1475",
"jinja2_rev": "@2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_tag": "@1.1.1",
"linter_rev": "@e5281475126efdc556c3eba6a8b6683fd814b033",
"linter_rev": "@97a27c37e549510966c1f5256f84586152ba6371",
"logging_rev": "@85d83e002670545e9039ad3985f0018ab640e597",
"markdown_rev": "@4aaadf3d940bb172e1f6285af4d2b1710d309982",
"matcher_tag": "@0.12.0",

View file

@ -539,7 +539,7 @@ class ContextManagerImpl implements ContextManager {
*/
void processOptionsForContext(ContextInfo info, Folder folder,
{bool optionsRemoved: false}) {
Map<String, YamlNode> options;
Map<String, Object> options;
try {
options = analysisOptionsProvider.getOptions(folder);
} catch (_) {
@ -550,6 +550,24 @@ class ContextManagerImpl implements ContextManager {
return;
}
// In case options files are removed, revert to defaults.
if (optionsRemoved) {
// Start with defaults.
info.context.analysisOptions = new AnalysisOptionsImpl();
// Apply inherited options.
options = _getEmbeddedOptions(info.context);
if (options != null) {
configureContextOptions(info.context, options);
}
} else {
// Check for embedded options.
YamlMap embeddedOptions = _getEmbeddedOptions(info.context);
if (embeddedOptions != null) {
options = new Merger().merge(embeddedOptions, options);
}
}
// Notify options processors.
AnalysisEngine.instance.optionsPlugin.optionsProcessors
.forEach((OptionsProcessor p) {
@ -562,34 +580,19 @@ class ContextManagerImpl implements ContextManager {
}
});
// In case options files are removed, revert to defaults.
if (optionsRemoved) {
// Start with defaults.
info.context.analysisOptions = new AnalysisOptionsImpl();
configureContextOptions(info.context, options);
// Apply inherited options.
YamlMap embeddedOptions = _getEmbeddedOptions(info.context);
if (embeddedOptions != null) {
configureContextOptions(info.context, embeddedOptions);
}
// Nothing more to do.
if (options == null) {
return;
}
// Check for embedded options.
YamlMap embeddedOptions = _getEmbeddedOptions(info.context);
if (embeddedOptions != null) {
options = new Merger().merge(embeddedOptions, options);
}
// Analysis options are processed 'in-line'.
var analyzer = options[AnalyzerOptions.analyzer];
if (analyzer is! Map) {
// No options for analyzer.
// Done.
return;
}
configureContextOptions(info.context, options);
// Set ignore patterns.
YamlList exclude = analyzer[AnalyzerOptions.exclude];
if (exclude != null) {

View file

@ -250,15 +250,20 @@ class EditDomainHandler implements RequestHandler {
if (!engine.AnalysisEngine.isDartFileName(file)) {
return new Response.sortMembersInvalidFile(request);
}
// prepare resolved units
List<CompilationUnit> units = server.getResolvedCompilationUnits(file);
if (units.isEmpty) {
// prepare location
ContextSourcePair contextSource = server.getContextSourcePair(file);
engine.AnalysisContext context = contextSource.context;
Source source = contextSource.source;
if (context == null || source == null) {
return new Response.sortMembersInvalidFile(request);
}
// prepare parsed unit
CompilationUnit unit;
try {
unit = context.parseCompilationUnit(source);
} catch (e) {
return new Response.sortMembersInvalidFile(request);
}
// prepare context
CompilationUnit unit = units.first;
engine.AnalysisContext context = unit.element.context;
Source source = unit.element.source;
// check if there are scan/parse errors in the file
engine.AnalysisErrorInfo errors = context.getErrors(source);
int numScanParseErrors = _getNumberOfScanParseErrors(errors.errors);

View file

@ -55,7 +55,7 @@ String getReturnTypeString(engine.Element element) {
if (element.kind == engine.ElementKind.SETTER) {
return null;
} else {
return element.returnType.toString();
return element.returnType?.toString();
}
} else if (element is engine.VariableElement) {
engine.DartType type = element.type;

View file

@ -10,6 +10,7 @@ import 'package:analysis_server/plugin/protocol/protocol.dart';
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
export 'package:analysis_server/src/provisional/completion/completion_core.dart'
show EMPTY_LIST;
@ -59,6 +60,16 @@ abstract class DartCompletionContributor {
* Clients may not extend, implement or mix-in this class.
*/
abstract class DartCompletionRequest extends CompletionRequest {
/**
* Return a [Future] that completes with the library element
* which contains the unit in which the completion is occurring.
* The [Future] may return `null` if the library cannot be determined
* (e.g. unlinked part file).
* Any information obtained from [target] prior to calling this method
* should be discarded as it may have changed.
*/
Future<LibraryElement> get libraryElement;
/**
* Return the completion target. This determines what part of the parse tree
* will receive the newly inserted text.

View file

@ -12,6 +12,8 @@ import 'package:analysis_server/src/services/completion/dart/combinator_contribu
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart/field_formal_contributor.dart';
import 'package:analysis_server/src/services/completion/dart/keyword_contributor.dart';
import 'package:analysis_server/src/services/completion/dart/named_constructor_contributor.dart';
import 'package:analysis_server/src/services/completion/dart/static_member_contributor.dart';
import 'package:analysis_server/src/services/completion/dart/uri_contributor.dart';
import 'package:plugin/plugin.dart';
@ -76,6 +78,10 @@ class DartCompletionPlugin implements Plugin {
() => new FieldFormalContributor());
registerExtension(DART_COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
() => new KeywordContributor());
registerExtension(DART_COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
() => new NamedConstructorContributor());
registerExtension(DART_COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
() => new StaticMemberContributor());
registerExtension(DART_COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
() => new UriContributor());
}

View file

@ -86,6 +86,23 @@ bool _isAppendingToArgList(DartCompletionRequest request) {
return false;
}
/**
* Determine if the completion target is the label for a named argument.
*/
bool _isEditingNamedArgLabel(DartCompletionRequest request) {
AstNode node = request.target.containingNode;
if (node is ArgumentList) {
var entity = request.target.entity;
if (entity is NamedExpression) {
int offset = request.offset;
if (entity.offset <= offset && offset < entity.end) {
return true;
}
}
}
return false;
}
/**
* Determine if the completion target is an emtpy argument list.
*/
@ -233,7 +250,7 @@ class ArgListContributor extends DartCompletionContributor {
_addArgListSuggestion(requiredParam);
return;
}
if (_isAppendingToArgList(request)) {
if (_isEditingNamedArgLabel(request) || _isAppendingToArgList(request)) {
if (requiredCount == 0 || requiredCount < _argCount(request)) {
_addDefaultParamSuggestions(parameters);
}

View file

@ -18,6 +18,7 @@ import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/context.dart'
show AnalysisFutureHelper, AnalysisContextImpl;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/dart.dart';
@ -89,6 +90,19 @@ class DartCompletionRequestImpl extends CompletionRequestImpl
int offset)
: super(context, resourceProvider, searchEngine, source, offset);
@override
Future<LibraryElement> get libraryElement async {
//TODO(danrubel) build the library element rather than all the declarations
CompilationUnit unit = await resolveDeclarationsInScope();
if (unit != null) {
CompilationUnitElement elem = unit.element;
if (elem != null) {
return elem.library;
}
}
return null;
}
@override
CompletionTarget get target {
if (_target == null) {

View file

@ -0,0 +1,85 @@
// 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 services.completion.contributor.dart.named_constructor;
import 'dart:async';
import 'package:analysis_server/plugin/protocol/protocol.dart' hide Element;
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
/**
* A contributor for calculating named constructor suggestions
* such as suggesting `bar` in `new Foo.bar()`.
*/
class NamedConstructorContributor extends DartCompletionContributor {
@override
Future<List<CompletionSuggestion>> computeSuggestions(
DartCompletionRequest request) async {
// Determine if the target looks like a named constructor.
AstNode parsedNode = request.target.containingNode;
SimpleIdentifier targetId;
if (parsedNode is ConstructorName) {
TypeName type = parsedNode.type;
if (type != null) {
targetId = type.name;
}
} else if (parsedNode is PrefixedIdentifier) {
// Some PrefixedIdentifier nodes are transformed into
// ConstructorName nodes during the resolution process.
targetId = parsedNode.prefix;
}
if (targetId == null) {
return EMPTY_LIST;
}
// Resolve the containing library element
LibraryElement libElem = await request.libraryElement;
if (libElem == null) {
return EMPTY_LIST;
}
// Resolve the target to determine the type
await request.resolveIdentifier(targetId);
// Recompute the target since resolution may have changed it
AstNode node = request.target.containingNode;
// Build the list of suggestions
if (node is ConstructorName) {
TypeName typeName = node.type;
if (typeName != null) {
DartType type = typeName.type;
if (type != null) {
Element classElem = type.element;
if (classElem is ClassElement) {
return _buildSuggestions(libElem, classElem);
}
}
}
}
return EMPTY_LIST;
}
List<CompletionSuggestion> _buildSuggestions(
LibraryElement libElem, ClassElement classElem) {
bool isLocalClassDecl = classElem.library == libElem;
List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
for (ConstructorElement elem in classElem.constructors) {
if (isLocalClassDecl || !elem.isPrivate) {
String name = elem.name;
if (name != null) {
CompletionSuggestion s = createSuggestion(elem, completion: name);
if (s != null) {
suggestions.add(s);
}
}
}
}
return suggestions;
}
}

View file

@ -0,0 +1,179 @@
// 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 services.completion.contributor.dart.static_member;
import 'dart:async';
import 'dart:collection';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import '../../../protocol_server.dart'
show CompletionSuggestion, CompletionSuggestionKind;
/**
* A contributor for calculating invocation / access suggestions
* `completion.getSuggestions` request results.
*/
class StaticMemberContributor extends DartCompletionContributor {
@override
Future<List<CompletionSuggestion>> computeSuggestions(
DartCompletionRequest request) async {
// Determine if the target looks like a prefixed identifier,
// a method invocation, or a property access
SimpleIdentifier targetId = _getTargetId(request.target);
if (targetId == null) {
return EMPTY_LIST;
}
// Resolve the expression and the containing library
await request.resolveIdentifier(targetId);
LibraryElement containingLibrary = await request.libraryElement;
// Gracefully degrade if the library could not be determined
// e.g. detached part file or source change
if (containingLibrary == null) {
return EMPTY_LIST;
}
// Recompute the target since resolution may have changed it
targetId = _getTargetId(request.target);
if (targetId == null) {
return EMPTY_LIST;
}
// Build the suggestions
Element elem = targetId.bestElement;
if (elem is ClassElement) {
_SuggestionBuilder builder = new _SuggestionBuilder(containingLibrary);
elem.accept(builder);
return builder.suggestions;
}
return EMPTY_LIST;
}
/**
* Return the identifier to the left of the 'dot' or `null` if none.
*/
SimpleIdentifier _getTargetId(CompletionTarget target) {
AstNode node = target.containingNode;
if (node is MethodInvocation) {
if (identical(node.methodName, target.entity)) {
Expression target = node.realTarget;
if (target is SimpleIdentifier) {
return target;
}
} else {
return null;
}
}
if (node is PrefixedIdentifier) {
if (identical(node.identifier, target.entity)) {
return node.prefix;
} else {
return null;
}
}
return null;
}
}
/**
* This class visits elements in a class and provides suggestions based upon
* the visible static members in that class.
*/
class _SuggestionBuilder extends GeneralizingElementVisitor {
/**
* The library containing the unit in which the completion is requested.
*/
final LibraryElement containingLibrary;
/**
* A collection of completion suggestions.
*/
final List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
_SuggestionBuilder(this.containingLibrary);
@override
visitClassElement(ClassElement element) {
element.visitChildren(this);
element.allSupertypes.forEach((InterfaceType type) {
ClassElement childElem = type.element;
if (childElem != null) {
childElem.visitChildren(this);
}
});
}
@override
visitElement(Element element) {
// ignored
}
@override
visitFieldElement(FieldElement element) {
if (element.isStatic) {
_addSuggestion(element);
}
}
@override
visitMethodElement(MethodElement element) {
if (element.isStatic && !element.isOperator) {
_addSuggestion(element);
}
}
@override
visitPropertyAccessorElement(PropertyAccessorElement element) {
if (element.isStatic) {
_addSuggestion(element);
}
}
/**
* Add a suggestion based upon the given element.
*/
void _addSuggestion(Element element) {
if (element.isPrivate) {
if (element.library != containingLibrary) {
// Do not suggest private members for imported libraries
return;
}
}
if (element.isSynthetic) {
if ((element is PropertyAccessorElement) ||
element is FieldElement && !_isSpecialEnumField(element)) {
return;
}
}
String completion = element.displayName;
if (completion == null || completion.length <= 0) {
return;
}
CompletionSuggestion suggestion =
createSuggestion(element, completion: completion);
if (suggestion != null) {
suggestions.add(suggestion);
}
}
/**
* Determine if the given element is one of the synthetic enum accessors
* for which we should generate a suggestion.
*/
bool _isSpecialEnumField(FieldElement element) {
Element parent = element.enclosingElement;
if (parent is ClassElement && parent.isEnum) {
if (element.name == 'values') {
return true;
}
}
return false;
}
}

View file

@ -129,6 +129,14 @@ class _OpTypeAstVisitor extends GeneralizingAstVisitor {
}
}
@override
void visitAssertStatement(AssertStatement node) {
if (identical(entity, node.condition)) {
optype.includeReturnValueSuggestions = true;
optype.includeTypeNameSuggestions = true;
}
}
void visitAssignmentExpression(AssignmentExpression node) {
if (identical(entity, node.rightHandSide)) {
optype.includeReturnValueSuggestions = true;

View file

@ -264,11 +264,6 @@ class _PrefixedIdentifierSuggestionBuilder
@override
Future<bool> computeFull(AstNode node) {
if (node is ConstructorName) {
// some PrefixedIdentifier nodes are transformed into
// ConstructorName nodes during the resolution process.
return new NamedConstructorSuggestionBuilder(request).computeFull(node);
}
if (node is PrefixedIdentifier) {
SimpleIdentifier prefix = node.prefix;
if (prefix != null) {
@ -306,8 +301,9 @@ class _PrefixedIdentifierSuggestionBuilder
if (element != null) {
InterfaceType type = element.type;
if (type != null) {
StaticClassElementSuggestionBuilder.suggestionsFor(
request, type.element);
// Suggested by StaticMemberContributor
// StaticClassElementSuggestionBuilder.suggestionsFor(
// request, type.element);
}
}
return new Future.value(false);

View file

@ -466,61 +466,6 @@ class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor
}
}
/**
* This class visits elements in a class and provides suggestions based upon
* the visible named constructors in that class.
*/
class NamedConstructorSuggestionBuilder extends GeneralizingElementVisitor
with ElementSuggestionBuilder
implements SuggestionBuilder {
final DartCompletionRequest request;
NamedConstructorSuggestionBuilder(this.request);
@override
CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION;
@override
bool computeFast(AstNode node) {
return false;
}
@override
Future<bool> computeFull(AstNode node) {
if (node is SimpleIdentifier) {
node = node.parent;
}
if (node is ConstructorName) {
TypeName typeName = node.type;
if (typeName != null) {
DartType type = typeName.type;
if (type != null) {
if (type.element is ClassElement) {
type.element.accept(this);
}
return new Future.value(true);
}
}
}
return new Future.value(false);
}
@override
visitClassElement(ClassElement element) {
element.visitChildren(this);
}
@override
visitConstructorElement(ConstructorElement element) {
addSuggestion(element);
}
@override
visitElement(Element element) {
// ignored
}
}
/**
* This class visits elements in a class and provides suggestions based upon
* the visible static members in that class. Clients should call

View file

@ -24,6 +24,7 @@ import 'package:analysis_server/src/services/index/store/split_store.dart';
import 'package:analysis_server/src/socket_server.dart';
import 'package:analysis_server/src/status/ast_writer.dart';
import 'package:analysis_server/src/status/element_writer.dart';
import 'package:analysis_server/src/status/validator.dart';
import 'package:analysis_server/src/utilities/average.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/cache.dart';
@ -91,6 +92,17 @@ class GetHandler {
*/
static const String COMMUNICATION_PERFORMANCE_PATH = '/perf/communication';
/**
* The path used to request diagnostic information for a single context.
*/
static const String CONTEXT_DIAGNOSTICS_PATH = '/diagnostic/context';
/**
* The path used to request running a validation report for a single context.
*/
static const String CONTEXT_VALIDATION_DIAGNOSTICS_PATH =
'/diagnostic/contextValidation';
/**
* The path used to request information about a specific context.
*/
@ -220,7 +232,7 @@ class GetHandler {
*/
void handleGetRequest(HttpRequest request) {
String path = request.uri.path;
if (path == STATUS_PATH) {
if (path == '/' || path == STATUS_PATH) {
_returnServerStatus(request);
} else if (path == ANALYSIS_PERFORMANCE_PATH) {
_returnAnalysisPerformance(request);
@ -234,6 +246,10 @@ class GetHandler {
_returnCompletionInfo(request);
} else if (path == COMMUNICATION_PERFORMANCE_PATH) {
_returnCommunicationPerformance(request);
} else if (path == CONTEXT_DIAGNOSTICS_PATH) {
_returnContextDiagnostics(request);
} else if (path == CONTEXT_VALIDATION_DIAGNOSTICS_PATH) {
_returnContextValidationDiagnostics(request);
} else if (path == CONTEXT_PATH) {
_returnContextInfo(request);
} else if (path == DIAGNOSTIC_PATH) {
@ -971,6 +987,34 @@ class GetHandler {
});
}
/**
* Return a response displaying diagnostic information for a single context.
*/
void _returnContextDiagnostics(HttpRequest request) {
AnalysisServer analysisServer = _server.analysisServer;
if (analysisServer == null) {
return _returnFailure(request, 'Analysis server is not running');
}
String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
if (contextFilter == null) {
return _returnFailure(
request, 'Query parameter $CONTEXT_QUERY_PARAM required');
}
Folder folder = _findFolder(analysisServer, contextFilter);
if (folder == null) {
return _returnFailure(request, 'Invalid context: $contextFilter');
}
InternalAnalysisContext context = analysisServer.folderMap[folder];
_writeResponse(request, (StringBuffer buffer) {
_writePage(buffer, 'Analysis Server - Context Diagnostics',
['Context: $contextFilter'], (StringBuffer buffer) {
_writeContextDiagnostics(buffer, context);
});
});
}
/**
* Return a response containing information about a single source file in the
* cache.
@ -1031,9 +1075,6 @@ class GetHandler {
explicitNames.sort();
implicitNames.sort();
AnalysisDriver driver = (context as AnalysisContextImpl).driver;
List<WorkItem> workItems = driver.currentWorkOrder?.workItems;
_overlayContents.clear();
context.visitContentCache((String fullName, int stamp, String contents) {
_overlayContents[fullName] = contents;
@ -1064,69 +1105,54 @@ class GetHandler {
_writePage(
buffer, 'Analysis Server - Context', ['Context: $contextFilter'],
(StringBuffer buffer) {
buffer.write('<h3>Most Recently Perfomed Tasks</h3>');
AnalysisTask.LAST_TASKS.forEach((String description) {
buffer.write('<p>$description</p>');
});
String _describe(WorkItem item) {
if (item == null) {
return 'None';
}
return '${item.descriptor?.name} computing ${item.spawningResult?.name} for ${item.target?.toString()}';
}
buffer.write('<h3>Work Items</h3>');
buffer.write(
'<p><b>Current:</b> ${_describe(driver.currentWorkOrder?.current)}</p>');
if (workItems != null) {
buffer.writeAll(workItems.reversed
.map((item) => '<p>${_describe(item)}</p>')
?.toList());
}
buffer.write('<h3>Configuration</h3>');
AnalysisOptionsImpl options = context.analysisOptions;
buffer.write('<p><b>Options</b></p>');
buffer.write('<p>');
_writeOption(
buffer, 'Analyze functon bodies', options.analyzeFunctionBodies);
_writeOption(buffer, 'Cache size', options.cacheSize);
_writeOption(
buffer, 'Enable generic methods', options.enableGenericMethods);
_writeOption(buffer, 'Enable strict call checks',
options.enableStrictCallChecks);
_writeOption(buffer, 'Enable super mixins', options.enableSuperMixins);
_writeOption(buffer, 'Generate dart2js hints', options.dart2jsHint);
_writeOption(buffer, 'Generate errors in implicit files',
options.generateImplicitErrors);
_writeOption(
buffer, 'Generate errors in SDK files', options.generateSdkErrors);
_writeOption(buffer, 'Generate hints', options.hint);
_writeOption(buffer, 'Incremental resolution', options.incremental);
_writeOption(buffer, 'Incremental resolution with API changes',
options.incrementalApi);
_writeOption(buffer, 'Preserve comments', options.preserveComments);
_writeOption(buffer, 'Strong mode', options.strongMode);
_writeOption(buffer, 'Strong mode hints', options.strongModeHints,
last: true);
buffer.write('</p>');
List<Linter> lints = context.getConfigurationData(CONFIGURED_LINTS_KEY);
buffer.write('<p><b>Lints</b></p>');
if (lints.isEmpty) {
buffer.write('<p><none></p>');
} else {
for (Linter lint in lints) {
buffer.write('<p> ${lint.runtimeType}</p>');
_writeTwoColumns(buffer, (StringBuffer buffer) {
AnalysisOptionsImpl options = context.analysisOptions;
buffer.write('<p><b>Options</b></p>');
buffer.write('<p>');
_writeOption(
buffer, 'Analyze functon bodies', options.analyzeFunctionBodies);
_writeOption(buffer, 'Cache size', options.cacheSize);
_writeOption(
buffer, 'Enable generic methods', options.enableGenericMethods);
_writeOption(buffer, 'Enable strict call checks',
options.enableStrictCallChecks);
_writeOption(
buffer, 'Enable super mixins', options.enableSuperMixins);
_writeOption(buffer, 'Generate dart2js hints', options.dart2jsHint);
_writeOption(buffer, 'Generate errors in implicit files',
options.generateImplicitErrors);
_writeOption(buffer, 'Generate errors in SDK files',
options.generateSdkErrors);
_writeOption(buffer, 'Generate hints', options.hint);
_writeOption(buffer, 'Incremental resolution', options.incremental);
_writeOption(buffer, 'Incremental resolution with API changes',
options.incrementalApi);
_writeOption(buffer, 'Preserve comments', options.preserveComments);
_writeOption(buffer, 'Strong mode', options.strongMode);
_writeOption(buffer, 'Strong mode hints', options.strongModeHints,
last: true);
buffer.write('</p>');
}, (StringBuffer buffer) {
List<Linter> lints =
context.getConfigurationData(CONFIGURED_LINTS_KEY);
buffer.write('<p><b>Lints</b></p>');
if (lints.isEmpty) {
buffer.write('<p>none</p>');
} else {
for (Linter lint in lints) {
buffer.write('<p>');
buffer.write(lint.runtimeType);
buffer.write('</p>');
}
}
}
List<ErrorFilter> errorFilters =
context.getConfigurationData(CONFIGURED_ERROR_FILTERS);
int filterCount = errorFilters?.length ?? 0;
buffer.write('<p><b>Error Filter count</b>: $filterCount</p>');
List<ErrorFilter> errorFilters =
context.getConfigurationData(CONFIGURED_ERROR_FILTERS);
int filterCount = errorFilters?.length ?? 0;
buffer.write('<p><b>Error Filter count</b>: $filterCount</p>');
});
_writeFiles(buffer, 'Priority Files', priorityNames);
_writeFiles(buffer, 'Explicitly Analyzed Files', explicitNames);
@ -1134,12 +1160,59 @@ class GetHandler {
buffer.write('<h3>Exceptions</h3>');
if (exceptions.isEmpty) {
buffer.write('<p>None</p>');
buffer.write('<p>none</p>');
} else {
exceptions.forEach((CaughtException exception) {
_writeException(buffer, exception);
});
}
buffer.write('<h3>Targets Without Entries</h3>');
bool foundEntry = false;
MapIterator<AnalysisTarget, CacheEntry> iterator =
context.analysisCache.iterator(context: context);
while (iterator.moveNext()) {
if (iterator.value == null) {
foundEntry = true;
buffer.write('<p>');
buffer.write(iterator.key.toString());
buffer.write(' (');
buffer.write(iterator.key.runtimeType.toString());
buffer.write(')</p>');
}
}
if (!foundEntry) {
buffer.write('<p>none</p>');
}
});
});
}
/**
* Return a response displaying the results of running a validation report on
* a single context.
*/
void _returnContextValidationDiagnostics(HttpRequest request) {
AnalysisServer analysisServer = _server.analysisServer;
if (analysisServer == null) {
return _returnFailure(request, 'Analysis server is not running');
}
String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM];
if (contextFilter == null) {
return _returnFailure(
request, 'Query parameter $CONTEXT_QUERY_PARAM required');
}
Folder folder = _findFolder(analysisServer, contextFilter);
if (folder == null) {
return _returnFailure(request, 'Invalid context: $contextFilter');
}
InternalAnalysisContext context = analysisServer.folderMap[folder];
_writeResponse(request, (StringBuffer buffer) {
_writePage(buffer, 'Analysis Server - Context Validation Diagnostics',
['Context: $contextFilter'], (StringBuffer buffer) {
_writeContextValidationDiagnostics(buffer, context);
});
});
}
@ -1329,23 +1402,19 @@ class GetHandler {
void _returnUnknownRequest(HttpRequest request) {
_writeResponse(request, (StringBuffer buffer) {
_writePage(buffer, 'Analysis Server', [], (StringBuffer buffer) {
buffer.write('<h3>Pages</h3>');
buffer.write('<p>');
buffer.write(makeLink(COMPLETION_PATH, {}, 'Completion data'));
buffer.write('</p>');
buffer.write('<p>');
buffer
.write(makeLink(COMMUNICATION_PERFORMANCE_PATH, {}, 'Performance'));
buffer.write('</p>');
buffer.write('<p>');
buffer.write(makeLink(STATUS_PATH, {}, 'Server status'));
buffer.write('</p>');
buffer.write('<p>');
buffer.write(makeLink(OVERLAYS_PATH, {}, 'File overlays'));
buffer.write('</p>');
buffer.write('<p>');
buffer.write(makeLink(DIAGNOSTIC_PATH, {}, 'Diagnostic info'));
buffer.write('</p>');
buffer.write('<h3>Unknown page: ');
buffer.write(request.uri.path);
buffer.write('</h3>');
buffer.write('''
<p>
You have reached an un-recognized page. If you reached this page by
following a link from a status page, please report the broken link to
the Dart analyzer team:
<a>https://github.com/dart-lang/sdk/issues/new</a>.
</p><p>
If you mistyped the URL, you can correct it or return to
${makeLink(STATUS_PATH, {}, 'the main status page')}.
</p>''');
});
});
}
@ -1371,7 +1440,6 @@ class GetHandler {
List<Folder> folders = folderMap.keys.toList();
folders.sort((Folder first, Folder second) =>
first.shortName.compareTo(second.shortName));
AnalysisOptionsImpl options = analysisServer.defaultContextOptions;
ServerOperationQueue operationQueue = analysisServer.operationQueue;
buffer.write('<h3>Analysis Domain</h3>');
@ -1391,6 +1459,9 @@ class GetHandler {
buffer.write('<p>Status: Analyzing</p>');
}
}
buffer.write('<p>');
buffer.write(makeLink(OVERLAYS_PATH, {}, 'All overlay information'));
buffer.write('</p>');
buffer.write('<p><b>Analysis Contexts</b></p>');
buffer.write('<p>');
@ -1404,8 +1475,12 @@ class GetHandler {
String key = folder.shortName;
buffer.write(makeLink(CONTEXT_PATH, {CONTEXT_QUERY_PARAM: folder.path},
key, _hasException(folderMap[folder])));
buffer.write(' <small><b>[');
buffer.write(makeLink(CONTEXT_DIAGNOSTICS_PATH,
{CONTEXT_QUERY_PARAM: folder.path}, 'diagnostics'));
buffer.write(']</b></small>');
if (!folder.getChild('.packages').exists) {
buffer.write(' <b>[No .packages file]</b>');
buffer.write(' <small>[no .packages file]</small>');
}
});
// TODO(brianwilkerson) Add items for the SDK contexts (currently only one).
@ -1413,9 +1488,9 @@ class GetHandler {
int freq = AnalysisServer.performOperationDelayFreqency;
String delay = freq > 0 ? '1 ms every $freq ms' : 'off';
buffer.write('<p><b>perform operation delay:</b> $delay</p>');
buffer.write('<p><b>Performance Data</b></p>');
buffer.write('<p>Perform operation delay: $delay</p>');
buffer.write('<p>');
buffer.write(makeLink(ANALYSIS_PERFORMANCE_PATH, {}, 'Task data'));
buffer.write('</p>');
@ -1527,6 +1602,70 @@ class GetHandler {
then there will be only one number in this column.''');
}
/**
* Write diagnostic information about the given [context] to the given
* [buffer].
*/
void _writeContextDiagnostics(
StringBuffer buffer, InternalAnalysisContext context) {
AnalysisDriver driver = (context as AnalysisContextImpl).driver;
List<WorkItem> workItems = driver.currentWorkOrder?.workItems;
buffer.write('<p>');
buffer.write(makeLink(CONTEXT_VALIDATION_DIAGNOSTICS_PATH,
{CONTEXT_QUERY_PARAM: context.name}, 'Run validation'));
buffer.write('</p>');
buffer.write('<h3>Most Recently Perfomed Tasks</h3>');
AnalysisTask.LAST_TASKS.forEach((String description) {
buffer.write('<p>');
buffer.write(description);
buffer.write('</p>');
});
void writeWorkItem(StringBuffer buffer, WorkItem item) {
if (item == null) {
buffer.write('none');
} else {
buffer.write(item.descriptor?.name);
buffer.write(' computing ');
buffer.write(item.spawningResult?.name);
buffer.write(' for ');
buffer.write(item.target);
}
}
buffer.write('<h3>Work Items</h3>');
buffer.write('<p><b>Current:</b> ');
writeWorkItem(buffer, driver.currentWorkOrder?.current);
buffer.write('</p>');
if (workItems != null) {
workItems.reversed.forEach((WorkItem item) {
buffer.write('<p>');
writeWorkItem(buffer, item);
buffer.write('</p>');
});
}
}
/**
* Write diagnostic information about the given [context] to the given
* [buffer].
*/
void _writeContextValidationDiagnostics(
StringBuffer buffer, InternalAnalysisContext context) {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
ValidationResults results = new ValidationResults(context);
stopwatch.stop();
buffer.write('<h3>Validation Results</h3>');
buffer.write('<p>Re-analysis took ');
buffer.write(stopwatch.elapsedMilliseconds);
buffer.write(' ms</p>');
results.writeOn(buffer);
}
/**
* Write the status of the diagnostic domain to the given [buffer].
*/
@ -1543,28 +1682,32 @@ class GetHandler {
buffer.write('<h3>Timing</h3>');
buffer.write('<p>');
buffer.write('getDiagnostic (last call): $elapsedMs (ms)');
buffer.write('<p>');
buffer.write('getDiagnostic (rolling average): '
'${_diagnosticCallAverage.value} (ms)');
buffer.write('<p>&nbsp;');
buffer.write('<p>getDiagnostic (last call): ');
buffer.write(elapsedMs);
buffer.write(' (ms)</p>');
buffer.write('<p>getDiagnostic (rolling average): ');
buffer.write(_diagnosticCallAverage.value);
buffer.write(' (ms)</p>&nbsp;');
var json = response.toJson()[Response.RESULT];
List contexts = json['contexts'];
contexts.sort((first, second) => first['name'].compareTo(second['name']));
for (var context in contexts) {
buffer.write('<p>');
buffer.write('<h3>${context["name"]}</h3>');
buffer.write('<p>');
buffer.write('explicitFileCount: ${context["explicitFileCount"]}');
buffer.write('<p>');
buffer.write('implicitFileCount: ${context["implicitFileCount"]}');
buffer.write('<p>');
buffer.write('workItemQueueLength: ${context["workItemQueueLength"]}');
buffer.write('<p>');
buffer.write('workItemQueueLengthAverage: '
'${context["workItemQueueLengthAverage"]}');
buffer.write('<p>&nbsp;');
buffer.write('<p><h3>');
buffer.write(context['name']);
buffer.write('</h3></p>');
buffer.write('<p>explicitFileCount: ');
buffer.write(context['explicitFileCount']);
buffer.write('</p>');
buffer.write('<p>implicitFileCount: ');
buffer.write(context['implicitFileCount']);
buffer.write('</p>');
buffer.write('<p>workItemQueueLength: ');
buffer.write(context['workItemQueueLength']);
buffer.write('</p>');
buffer.write('<p>workItemQueueLengthAverage: ');
buffer.write(context['workItemQueueLengthAverage']);
buffer.write('</p>');
}
}
@ -1705,6 +1848,8 @@ class GetHandler {
buffer.write(
'h3 {background-color: #DDDDDD; margin-top: 0em; margin-bottom: 0em;}');
buffer.write('p {margin-top: 0.5em; margin-bottom: 0.5em;}');
buffer.write(
'p.commentary {margin-top: 1em; margin-bottom: 1em; margin-left: 2em; font-style: italic;}');
// response.write('span.error {text-decoration-line: underline; text-decoration-color: red; text-decoration-style: wavy;}');
buffer.write(
'table.column {border: 0px solid black; width: 100%; table-layout: fixed;}');
@ -1865,6 +2010,9 @@ class GetHandler {
buffer.write(makeLink(
COMMUNICATION_PERFORMANCE_PATH, {}, 'Communication performance'));
buffer.write('</p>');
buffer.write('<p>');
buffer.write(makeLink(DIAGNOSTIC_PATH, {}, 'General diagnostics'));
buffer.write('</p>');
}, (StringBuffer buffer) {
_writeSubscriptionList(buffer, ServerService.VALUES, services);
});

File diff suppressed because it is too large Load diff

View file

@ -15,9 +15,13 @@ import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/services/lint.dart';
import 'package:linter/src/plugin/linter_plugin.dart';
import 'package:linter/src/rules/avoid_as.dart';
import 'package:package_config/packages.dart';
import 'package:path/path.dart';
import 'package:plugin/manager.dart';
import 'package:plugin/plugin.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:unittest/unittest.dart';
@ -68,6 +72,34 @@ class AbstractContextManagerTest {
String projPath = '/my/proj';
AnalysisError missing_return =
new AnalysisError(new TestSource(), 0, 1, HintCode.MISSING_RETURN, [
['x']
]);
AnalysisError invalid_assignment_error =
new AnalysisError(new TestSource(), 0, 1, HintCode.INVALID_ASSIGNMENT, [
['x'],
['y']
]);
AnalysisError unused_local_variable = new AnalysisError(
new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
['x']
]);
List<ErrorFilter> get errorFilters =>
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
List<Linter> get lints => getLints(callbacks.currentContext);
AnalysisOptions get options => callbacks.currentContext.analysisOptions;
void deleteFile(List<String> pathComponents) {
String filePath = posix.joinAll(pathComponents);
resourceProvider.deleteFile(filePath);
}
String newFile(List<String> pathComponents, [String content = '']) {
String filePath = posix.joinAll(pathComponents);
resourceProvider.newFile(filePath, content);
@ -81,8 +113,13 @@ class AbstractContextManagerTest {
}
void processRequiredPlugins() {
List<Plugin> plugins = <Plugin>[];
plugins.addAll(AnalysisEngine.instance.requiredPlugins);
plugins.add(AnalysisEngine.instance.commandLinePlugin);
plugins.add(AnalysisEngine.instance.optionsPlugin);
plugins.add(linterPlugin);
ExtensionManager manager = new ExtensionManager();
manager.processPlugins(AnalysisEngine.instance.requiredPlugins);
manager.processPlugins(plugins);
}
UriResolver providePackageResolver(Folder folder) {
@ -100,6 +137,99 @@ class AbstractContextManagerTest {
resourceProvider.newFolder(projPath);
}
test_analysis_options_file_delete() async {
// Setup .analysis_options
newFile(
[projPath, AnalysisEngine.ANALYSIS_OPTIONS_FILE],
r'''
embedder_libs:
"dart:foobar": "../sdk_ext/entry.dart"
analyzer:
language:
enableGenericMethods: true
errors:
unused_local_variable: false
linter:
rules:
- camel_case_types
''');
// Setup context.
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
await pumpEventQueue();
// Verify options were set.
expect(errorFilters, hasLength(1));
expect(lints, hasLength(1));
expect(options.enableGenericMethods, isTrue);
// Remove options.
deleteFile([projPath, AnalysisEngine.ANALYSIS_OPTIONS_FILE]);
await pumpEventQueue();
// Verify defaults restored.
expect(errorFilters, isEmpty);
expect(lints, isEmpty);
expect(options.enableGenericMethods, isFalse);
}
test_analysis_options_file_delete_with_embedder() async {
// Setup _embedder.yaml.
String libPath = newFolder([projPath, LIB_NAME]);
newFile(
[libPath, '_embedder.yaml'],
r'''
analyzer:
strong-mode: true
errors:
missing_return: false
linter:
rules:
- avoid_as
''');
// Setup .packages file
newFile(
[projPath, '.packages'],
r'''
test_pack:lib/''');
// Setup .analysis_options
newFile(
[projPath, AnalysisEngine.ANALYSIS_OPTIONS_FILE],
r'''
analyzer:
language:
enableGenericMethods: true
errors:
unused_local_variable: false
linter:
rules:
- camel_case_types
''');
// Setup context.
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
await pumpEventQueue();
// Verify options were set.
expect(options.enableGenericMethods, isTrue);
expect(options.strongMode, isTrue);
expect(errorFilters, hasLength(2));
expect(lints, hasLength(2));
// Remove options.
deleteFile([projPath, AnalysisEngine.ANALYSIS_OPTIONS_FILE]);
await pumpEventQueue();
// Verify defaults restored.
expect(options.enableGenericMethods, isFalse);
expect(lints, hasLength(1));
expect(lints.first, new isInstanceOf<AvoidAs>());
expect(errorFilters, hasLength(1));
expect(errorFilters.first(missing_return), isTrue);
}
test_analysis_options_parse_failure() async {
// Create files.
String libPath = newFolder([projPath, LIB_NAME]);
@ -161,6 +291,11 @@ analyzer:
strong-mode: true
language:
enableSuperMixins: true
errors:
missing_return: false
linter:
rules:
- avoid_as
''');
// Setup .packages file
newFile(
@ -179,6 +314,9 @@ analyzer:
enableGenericMethods: true
errors:
unused_local_variable: false
linter:
rules:
- camel_case_types
''');
// Setup context.
@ -193,25 +331,31 @@ analyzer:
var context = contexts[0];
// Verify options.
// * from `_embedder.yaml`:
// * from `_embedder.yaml`:
expect(context.analysisOptions.strongMode, isTrue);
expect(context.analysisOptions.enableSuperMixins, isTrue);
// * from `.analysis_options`:
// * from `.analysis_options`:
expect(context.analysisOptions.enableGenericMethods, isTrue);
// verify tests are excluded
// * verify tests are excluded
expect(callbacks.currentContextFilePaths[projPath].keys,
['/my/proj/sdk_ext/entry.dart']);
// Verify filter setup.
List<ErrorFilter> filters =
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
expect(filters, hasLength(1));
expect(errorFilters, hasLength(2));
// * (embedder.)
expect(errorFilters.any((f) => f(missing_return)), isTrue);
// * (options.)
expect(errorFilters.any((f) => f(unused_local_variable)), isTrue);
// Verify lints.
var lintNames = lints.map((lint) => lint.name);
expect(
filters.first(new AnalysisError(
new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
['x']
])),
isTrue);
lintNames,
unorderedEquals(
['avoid_as' /* embedder */, 'camel_case_types' /* options */]));
// Sanity check embedder libs.
var source = context.sourceFactory.forUri('dart:foobar');
@ -275,16 +419,9 @@ analyzer:
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
// Verify filter setup.
List<ErrorFilter> filters =
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
expect(filters, isNotNull);
expect(filters, hasLength(1));
expect(
filters.first(new AnalysisError(
new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
['x']
])),
isTrue);
expect(errorFilters, isNotNull);
expect(errorFilters, hasLength(1));
expect(errorFilters.first(unused_local_variable), isTrue);
}
test_error_filter_analysis_option_multiple_filters() async {
@ -301,24 +438,12 @@ analyzer:
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
// Verify filter setup.
List<ErrorFilter> filters =
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
expect(filters, isNotNull);
expect(filters, hasLength(2));
expect(errorFilters, isNotNull);
expect(errorFilters, hasLength(2));
var unused_error = new AnalysisError(
new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
['x']
]);
var invalid_assignment_error =
new AnalysisError(new TestSource(), 0, 1, HintCode.INVALID_ASSIGNMENT, [
['x'],
['y']
]);
expect(filters.any((filter) => filter(unused_error)), isTrue);
expect(filters.any((filter) => filter(invalid_assignment_error)), isTrue);
expect(errorFilters.any((filter) => filter(unused_local_variable)), isTrue);
expect(
errorFilters.any((filter) => filter(invalid_assignment_error)), isTrue);
}
test_error_filter_analysis_option_synonyms() async {
@ -335,10 +460,8 @@ analyzer:
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
// Verify filter setup.
List<ErrorFilter> filters =
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
expect(filters, isNotNull);
expect(filters, hasLength(2));
expect(errorFilters, isNotNull);
expect(errorFilters, hasLength(2));
}
test_error_filter_analysis_option_unpsecified() async {
@ -354,9 +477,7 @@ analyzer:
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
// Verify filter setup.
List<ErrorFilter> filters =
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
expect(filters, isEmpty);
expect(errorFilters, isEmpty);
}
test_ignoreFilesInPackagesFolder() {

View file

@ -34,8 +34,7 @@ class SortMembersTest extends AbstractAnalysisTest {
handler = new EditDomainHandler(server);
}
Future test_BAD_doesNotExist() async {
await waitForTasksFinished();
test_BAD_doesNotExist() async {
Request request =
new EditSortMembersParams('/no/such/file.dart').toRequest('0');
Response response = handler.handleRequest(request);
@ -43,21 +42,19 @@ class SortMembersTest extends AbstractAnalysisTest {
isResponseFailure('0', RequestErrorCode.SORT_MEMBERS_INVALID_FILE));
}
Future test_BAD_hasParseError() async {
test_BAD_hasParseError() async {
addTestFile('''
main() {
print()
}
''');
await waitForTasksFinished();
Request request = new EditSortMembersParams(testFile).toRequest('0');
Response response = handler.handleRequest(request);
expect(response,
isResponseFailure('0', RequestErrorCode.SORT_MEMBERS_PARSE_ERRORS));
}
Future test_BAD_notDartFile() async {
await waitForTasksFinished();
test_BAD_notDartFile() async {
Request request =
new EditSortMembersParams('/not-a-Dart-file.txt').toRequest('0');
Response response = handler.handleRequest(request);
@ -65,7 +62,21 @@ main() {
isResponseFailure('0', RequestErrorCode.SORT_MEMBERS_INVALID_FILE));
}
Future test_OK_classMembers_method() {
test_OK_afterWaitForAnalysis() async {
addTestFile('''
class C {}
class A {}
class B {}
''');
await waitForTasksFinished();
return _assertSorted(r'''
class A {}
class B {}
class C {}
''');
}
test_OK_classMembers_method() async {
addTestFile('''
class A {
c() {}
@ -82,7 +93,7 @@ class A {
''');
}
Future test_OK_directives() {
test_OK_directives() async {
addTestFile('''
library lib;
@ -133,7 +144,7 @@ main() {
''');
}
Future test_OK_unitMembers_class() {
test_OK_unitMembers_class() async {
addTestFile('''
class C {}
class A {}
@ -147,7 +158,6 @@ class C {}
}
Future _assertSorted(String expectedCode) async {
await waitForTasksFinished();
_requestSort();
String resultCode = SourceEdit.applySequence(testCode, fileEdit.edits);
expect(resultCode, expectedCode);

View file

@ -356,29 +356,6 @@ abstract class AbstractCompletionTest extends AbstractContextTest {
return cs;
}
CompletionSuggestion assertSuggestNamedConstructor(
String name, String returnType,
[int relevance = DART_RELEVANCE_DEFAULT,
CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION]) {
if (contributor is PrefixedElementContributor) {
CompletionSuggestion cs =
assertSuggest(name, csKind: kind, relevance: relevance);
protocol.Element element = cs.element;
expect(element, isNotNull);
expect(element.kind, equals(protocol.ElementKind.CONSTRUCTOR));
expect(element.name, equals(name));
String param = element.parameters;
expect(param, isNotNull);
expect(param[0], equals('('));
expect(param[param.length - 1], equals(')'));
expect(element.returnType, equals(returnType));
assertHasParameterInfo(cs);
return cs;
} else {
return assertNotSuggested(name);
}
}
CompletionSuggestion assertSuggestParameter(String name, String returnType,
{int relevance: DART_RELEVANCE_PARAMETER}) {
return assertNotSuggested(name);
@ -2452,7 +2429,8 @@ abstract class AbstractSelectorSuggestionTest extends AbstractCompletionTest {
return computeFull((bool result) {
expect(request.replacementOffset, completionOffset);
expect(request.replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
// Suggested by NamedConstructorContributor
assertNotSuggested('c');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('_d');
@ -2479,7 +2457,8 @@ abstract class AbstractSelectorSuggestionTest extends AbstractCompletionTest {
return computeFull((bool result) {
expect(request.replacementOffset, completionOffset);
expect(request.replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
// Suggested by NamedConstructorContributor
assertNotSuggested('c');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('_d');
@ -2497,7 +2476,8 @@ abstract class AbstractSelectorSuggestionTest extends AbstractCompletionTest {
return computeFull((bool result) {
expect(request.replacementOffset, completionOffset - 2);
expect(request.replacementLength, 13);
assertSuggestNamedConstructor('fromCharCodes', 'String');
// Suggested by NamedConstructorContributor
assertNotSuggested('fromCharCodes');
assertNotSuggested('isEmpty');
assertNotSuggested('isNotEmpty');
assertNotSuggested('length');
@ -2518,8 +2498,9 @@ abstract class AbstractSelectorSuggestionTest extends AbstractCompletionTest {
return computeFull((bool result) {
expect(request.replacementOffset, completionOffset);
expect(request.replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
assertSuggestNamedConstructor('_d', 'X');
// Suggested by NamedConstructorContributor
assertNotSuggested('c');
assertNotSuggested('_d');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('z');
@ -2539,8 +2520,9 @@ abstract class AbstractSelectorSuggestionTest extends AbstractCompletionTest {
return computeFull((bool result) {
expect(request.replacementOffset, completionOffset);
expect(request.replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
assertSuggestNamedConstructor('_d', 'X');
// Suggested by NamedConstructorContributor
assertNotSuggested('c');
assertNotSuggested('_d');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('z');
@ -3878,9 +3860,10 @@ abstract class AbstractSelectorSuggestionTest extends AbstractCompletionTest {
return computeFull((bool result) {
expect(request.replacementOffset, completionOffset);
expect(request.replacementLength, 0);
assertSuggestInvocationField('scA', 'String');
assertSuggestInvocationField('scB', 'int');
assertSuggestInvocationField('scI', null);
// Suggested by StaticMemberContributor
assertNotSuggested('scA');
assertNotSuggested('scB');
assertNotSuggested('scI');
assertNotSuggested('b');
assertNotSuggested('_c');
assertNotSuggested('d');

View file

@ -302,6 +302,27 @@ library libA; class A { A({int one, String two: 'defaultValue'}) { } }''');
assertNoSuggestions();
}
test_ArgumentList_imported_function_named_param_label1() async {
//
addTestSource('main() { int.parse("16", r^: 16);}');
await computeSuggestions();
assertSuggestArguments(namedArguments: ['radix', 'onError']);
}
test_ArgumentList_imported_function_named_param_label2() async {
//
addTestSource('main() { int.parse("16", ^r: 16);}');
await computeSuggestions();
assertSuggestArguments(namedArguments: ['radix', 'onError']);
}
test_ArgumentList_imported_function_named_param_label3() async {
//
addTestSource('main() { int.parse("16", ^: 16);}');
await computeSuggestions();
assertSuggestArguments(namedArguments: ['radix', 'onError']);
}
test_ArgumentList_local_constructor_named_param() async {
//
addTestSource('''

View file

@ -0,0 +1,182 @@
// Copyright (c) 2014, 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 test.services.completion.contributor.dart.named_constructor;
import 'package:analysis_server/plugin/protocol/protocol.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/services/completion/dart/named_constructor_contributor.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:unittest/unittest.dart';
import '../../../utils.dart';
import 'completion_contributor_util.dart';
import 'package:analyzer/src/generated/source.dart';
main() {
initializeTestEnvironment();
defineReflectiveTests(NamedConstructorContributorTest);
}
@reflectiveTest
class NamedConstructorContributorTest extends DartCompletionContributorTest {
CompletionSuggestion assertSuggestNamedConstructor(
String name, String returnType,
[int relevance = DART_RELEVANCE_DEFAULT,
CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION]) {
CompletionSuggestion cs =
assertSuggest(name, csKind: kind, relevance: relevance);
Element element = cs.element;
expect(element, isNotNull);
expect(element.kind, equals(ElementKind.CONSTRUCTOR));
expect(element.name, equals(name));
String param = element.parameters;
expect(param, isNotNull);
expect(param[0], equals('('));
expect(param[param.length - 1], equals(')'));
expect(element.returnType, equals(returnType));
assertHasParameterInfo(cs);
return cs;
}
@override
DartCompletionContributor createContributor() {
return new NamedConstructorContributor();
}
test_ConstructorName_importedClass() async {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
// InstanceCreationExpression
Source libSource = addSource(
'/testB.dart',
'''
lib B;
int T1;
F1() { }
class X {X.c(); X._d(); z() {}}''');
addTestSource('''
import "/testB.dart";
var m;
main() {new X.^}''');
// Assume that imported libraries are resolved
await resolveLibraryUnit(libSource);
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('_d');
assertNotSuggested('z');
assertNotSuggested('m');
}
test_ConstructorName_importedClass_unresolved() async {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
// InstanceCreationExpression
addSource(
'/testB.dart',
'''
lib B;
int T1;
F1() { }
class X {X.c(); X._d(); z() {}}''');
addTestSource('''
import "/testB.dart";
var m;
main() {new X.^}''');
// Assume that imported libraries are NOT resolved
//await resolveLibraryUnit(libSource);
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestNamedConstructor('c', null);
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('_d');
assertNotSuggested('z');
assertNotSuggested('m');
}
test_ConstructorName_importedFactory() async {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
// InstanceCreationExpression
Source libSource = addSource(
'/testB.dart',
'''
lib B;
int T1;
F1() { }
class X {factory X.c(); factory X._d(); z() {}}''');
addTestSource('''
import "/testB.dart";
var m;
main() {new X.^}''');
// Assume that imported libraries are resolved
await resolveLibraryUnit(libSource);
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('_d');
assertNotSuggested('z');
assertNotSuggested('m');
}
test_ConstructorName_importedFactory2() async {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
// InstanceCreationExpression
addTestSource('''
main() {new String.fr^omCharCodes([]);}''');
await computeSuggestions();
expect(replacementOffset, completionOffset - 2);
expect(replacementLength, 13);
assertSuggestNamedConstructor('fromCharCodes', 'String');
assertNotSuggested('isEmpty');
assertNotSuggested('isNotEmpty');
assertNotSuggested('length');
assertNotSuggested('Object');
assertNotSuggested('String');
}
test_ConstructorName_localClass() async {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
// InstanceCreationExpression
addTestSource('''
int T1;
F1() { }
class X {X.c(); X._d(); z() {}}
main() {new X.^}''');
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
assertSuggestNamedConstructor('_d', 'X');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('z');
assertNotSuggested('m');
}
test_ConstructorName_localFactory() async {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
// InstanceCreationExpression
addTestSource('''
int T1;
F1() { }
class X {factory X.c(); factory X._d(); z() {}}
main() {new X.^}''');
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestNamedConstructor('c', 'X');
assertSuggestNamedConstructor('_d', 'X');
assertNotSuggested('F1');
assertNotSuggested('T1');
assertNotSuggested('z');
assertNotSuggested('m');
}
}

View file

@ -0,0 +1,157 @@
// Copyright (c) 2014, 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 test.services.completion.dart.static_member;
import 'dart:async';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:unittest/unittest.dart';
import '../../../utils.dart';
import 'completion_contributor_util.dart';
import 'package:analysis_server/src/services/completion/dart/static_member_contributor.dart';
main() {
initializeTestEnvironment();
defineReflectiveTests(StaticMemberContributorTest);
}
@reflectiveTest
class StaticMemberContributorTest extends DartCompletionContributorTest {
@override
DartCompletionContributor createContributor() {
return new StaticMemberContributor();
}
fail_enumConst_deprecated() async {
addTestSource('@deprecated enum E { one, two } main() {E.^}');
await computeSuggestions();
assertNotSuggested('E');
// TODO(danrubel) Investigate why enum suggestion is not marked
// as deprecated if enum ast element is deprecated
assertSuggestEnumConst('one', isDeprecated: true);
assertSuggestEnumConst('two', isDeprecated: true);
assertNotSuggested('index');
assertSuggestField('values', 'List<E>', isDeprecated: true);
}
test_PrefixedIdentifier_class_const() async {
// SimpleIdentifier PrefixedIdentifier ExpressionStatement Block
addSource(
'/testB.dart',
'''
lib B;
class I {
static const scI = 'boo';
X get f => new A();
get _g => new A();}
class B implements I {
static const int scB = 12;
var b; X _c;
X get d => new A();get _e => new A();
set s1(I x) {} set _s2(I x) {}
m(X x) {} I _n(X x) {}}
class X{}''');
addTestSource('''
import "/testB.dart";
class A extends B {
static const String scA = 'foo';
w() { }}
main() {A.^}''');
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestField('scA', 'String');
assertSuggestField('scB', 'int');
assertSuggestField('scI', null);
assertNotSuggested('b');
assertNotSuggested('_c');
assertNotSuggested('d');
assertNotSuggested('_e');
assertNotSuggested('f');
assertNotSuggested('_g');
assertNotSuggested('s1');
assertNotSuggested('_s2');
assertNotSuggested('m');
assertNotSuggested('_n');
assertNotSuggested('a');
assertNotSuggested('A');
assertNotSuggested('X');
assertNotSuggested('w');
assertNotSuggested('Object');
assertNotSuggested('==');
}
test_enumConst() async {
addTestSource('enum E { one, two } main() {E.^}');
await computeSuggestions();
assertNotSuggested('E');
assertSuggestEnumConst('one');
assertSuggestEnumConst('two');
assertNotSuggested('index');
assertSuggestField('values', 'List<E>');
}
test_enumConst2() async {
addTestSource('enum E { one, two } main() {E.o^}');
await computeSuggestions();
assertNotSuggested('E');
assertSuggestEnumConst('one');
assertSuggestEnumConst('two');
assertNotSuggested('index');
assertSuggestField('values', 'List<E>');
}
test_enumConst3() async {
addTestSource('enum E { one, two } main() {E.^ int g;}');
await computeSuggestions();
assertNotSuggested('E');
assertSuggestEnumConst('one');
assertSuggestEnumConst('two');
assertNotSuggested('index');
assertSuggestField('values', 'List<E>');
}
test_keyword() async {
addTestSource('class C { static C get instance => null; } main() {C.in^}');
await computeSuggestions();
assertSuggestGetter('instance', 'C');
}
test_only_static() async {
// SimpleIdentifier PrefixedIdentifier ExpressionStatement
addTestSource('''
class C {
int f1;
static int f2;
m1() {}
static m2() {}
}
void main() {C.^}''');
await computeSuggestions();
assertNotSuggested('f1');
assertSuggestField('f2', 'int');
assertNotSuggested('m1');
assertSuggestMethod('m2', 'C', null);
}
test_only_static2() async {
// SimpleIdentifier MethodInvocation ExpressionStatement
addTestSource('''
class C {
int f1;
static int f2;
m1() {}
static m2() {}
}
void main() {C.^ print("something");}''');
await computeSuggestions();
assertNotSuggested('f1');
assertSuggestField('f2', 'int');
assertNotSuggested('m1');
assertSuggestMethod('m2', 'C', null);
}
}

View file

@ -7,12 +7,14 @@ library test.services.completion.dart;
import 'package:unittest/unittest.dart';
import '../../../utils.dart';
import 'combinator_contributor_test.dart' as combinator_test;
import 'arglist_contributor_test.dart' as arglist_test;
import 'combinator_contributor_test.dart' as combinator_test;
import 'common_usage_sorter_test.dart' as common_usage_test;
import 'field_formal_contributor_test.dart' as field_formal_contributor_test;
import 'inherited_contributor_test.dart' as inherited_contributor_test;
import 'keyword_contributor_test.dart' as keyword_test;
import 'named_constructor_contributor_test.dart' as named_contributor_test;
import 'static_member_contributor_test.dart' as static_contributor_test;
import 'uri_contributor_test.dart' as uri_contributor_test;
/// Utility for manually running all tests.
@ -25,6 +27,8 @@ main() {
field_formal_contributor_test.main();
inherited_contributor_test.main();
keyword_test.main();
named_contributor_test.main();
static_contributor_test.main();
uri_contributor_test.main();
});
}

View file

@ -257,6 +257,13 @@ class ImportedReferenceContributorTest extends AbstractSelectorSuggestionTest {
});
}
test_Assert() {
addTestSource('main() {assert(^)}');
return computeFull((bool result) {
assertSuggestClass('String');
});
}
@override
test_AssignmentExpression_RHS() {
return super.test_AssignmentExpression_RHS().then((_) {

View file

@ -108,6 +108,11 @@ class OpTypeTest {
assertOpType(typeNames: true);
}
test_Assert() {
addTestSource('main() {assert(^)}');
assertOpType(returnValue: true, typeNames: true);
}
test_AssignmentExpression_name() {
// SimpleIdentifier VariableDeclaration VariableDeclarationList
// VariableDeclarationStatement Block

View file

@ -61,19 +61,6 @@ void f(Derived d) {
});
}
fail_enumConst_deprecated() {
addTestSource('@deprecated enum E { one, two } main() {E.^}');
return computeFull((bool result) {
assertNotSuggested('E');
// TODO(danrubel) Investigate why enum suggestion is not marked
// as deprecated if enum ast element is deprecated
assertSuggestEnumConst('one', isDeprecated: true);
assertSuggestEnumConst('two', isDeprecated: true);
assertNotSuggested('index');
assertSuggestField('values', 'List<E>', isDeprecated: true);
});
}
fail_test_PrefixedIdentifier_trailingStmt_const_untyped() {
// SimpleIdentifier PrefixedIdentifier ExpressionStatement
addTestSource('const g = "hello"; f() {g.^ int y = 0;}');
@ -92,10 +79,11 @@ void f(Derived d) {
addTestSource('enum E { one, two } main() {E.^}');
return computeFull((bool result) {
assertNotSuggested('E');
assertSuggestEnumConst('one');
assertSuggestEnumConst('two');
// Suggested by StaticMemberContributor
assertNotSuggested('one');
assertNotSuggested('two');
assertNotSuggested('index');
assertSuggestField('values', 'List<E>');
assertNotSuggested('values');
});
}
@ -103,10 +91,11 @@ void f(Derived d) {
addTestSource('enum E { one, two } main() {E.o^}');
return computeFull((bool result) {
assertNotSuggested('E');
assertSuggestEnumConst('one');
assertSuggestEnumConst('two');
// Suggested by StaticMemberContributor
assertNotSuggested('one');
assertNotSuggested('two');
assertNotSuggested('index');
assertSuggestField('values', 'List<E>');
assertNotSuggested('values');
});
}
@ -114,10 +103,11 @@ void f(Derived d) {
addTestSource('enum E { one, two } main() {E.^ int g;}');
return computeFull((bool result) {
assertNotSuggested('E');
assertSuggestEnumConst('one');
assertSuggestEnumConst('two');
// Suggested by StaticMemberContributor
assertNotSuggested('one');
assertNotSuggested('two');
assertNotSuggested('index');
assertSuggestField('values', 'List<E>');
assertNotSuggested('values');
});
}
@ -220,7 +210,8 @@ void f(C<int> c) {
test_keyword() {
addTestSource('class C { static C get instance => null; } main() {C.in^}');
return computeFull((bool result) {
assertSuggestGetter('instance', 'C');
// Suggested by StaticMemberContributor
assertNotSuggested('instance');
});
}
@ -481,9 +472,10 @@ class C {
void main() {C.^}''');
return computeFull((bool result) {
assertNotSuggested('f1');
assertSuggestInvocationField('f2', 'int');
// Suggested by StaticMemberContributor
assertNotSuggested('f2');
assertNotSuggested('m1');
assertSuggestMethod('m2', 'C', null);
assertNotSuggested('m2');
});
}
@ -499,9 +491,10 @@ class C {
void main() {C.^ print("something");}''');
return computeFull((bool result) {
assertNotSuggested('f1');
assertSuggestInvocationField('f2', 'int');
// Suggested by StaticMemberContributor
assertNotSuggested('f2');
assertNotSuggested('m1');
assertSuggestMethod('m2', 'C', null);
assertNotSuggested('m2');
});
}

View file

@ -8,6 +8,9 @@
might be in use anyway.
* Removed previously deprecated API's (marked with the @deprecated annotation).
## 0.26.4
* Options processing API updated to accept untyped options maps (#25126).
## 0.26.3
* (Internal) Support for `_embedder.yaml` discovery and processing.

View file

@ -69,7 +69,7 @@ abstract class OptionsProcessor {
/// options have changed and to handle those changes appropriately. In
/// addition to the [options] map, the associated analysis [context] is
/// provided as well to allow for context-specific configuration.
void optionsProcessed(AnalysisContext context, Map<String, YamlNode> options);
void optionsProcessed(AnalysisContext context, Map<String, Object> options);
}
/// Validates options as defined in an analysis options file.

View file

@ -1945,10 +1945,13 @@ class ConstructorElementImpl extends ExecutableElementImpl
class ConstructorMember extends ExecutableMember implements ConstructorElement {
/**
* Initialize a newly created element to represent a constructor, based on the
* [baseElement], defined by the [definingType].
* [baseElement], defined by the [definingType]. If [type] is passed, it
* represents the full type of the member, and will take precedence over
* the [definingType].
*/
ConstructorMember(ConstructorElement baseElement, InterfaceType definingType)
: super(baseElement, definingType);
ConstructorMember(ConstructorElement baseElement, InterfaceType definingType,
[FunctionType type])
: super(baseElement, definingType, type);
@override
ConstructorElement get baseElement => super.baseElement as ConstructorElement;
@ -2036,10 +2039,7 @@ class ConstructorMember extends ExecutableMember implements ConstructorElement {
if (baseType == substitutedType) {
return constructor;
}
// TODO(brianwilkerson) Consider caching the substituted type in the
// instance. It would use more memory but speed up some operations.
// We need to see how often the type is being re-computed.
return new ConstructorMember(constructor, definingType);
return new ConstructorMember(constructor, definingType, substitutedType);
}
}
@ -3430,7 +3430,7 @@ abstract class ElementVisitor<R> {
* An element representing an executable object, including functions, methods,
* constructors, getters, and setters.
*/
abstract class ExecutableElement implements TypeParameterizedElement {
abstract class ExecutableElement implements FunctionTypedElement {
/**
* An empty list of executable elements.
*/
@ -3506,24 +3506,6 @@ abstract class ExecutableElement implements TypeParameterizedElement {
* executable element.
*/
List<LocalVariableElement> get localVariables;
/**
* Return a list containing all of the parameters defined by this executable
* element.
*/
List<ParameterElement> get parameters;
/**
* Return the return type defined by this executable element. If the element
* model is fully populated, then the [returnType] will not be `null`, even
* if no return type was explicitly specified.
*/
DartType get returnType;
/**
* Return the type of function defined by this executable element.
*/
FunctionType get type;
}
/**
@ -3791,12 +3773,21 @@ abstract class ExecutableElementImpl extends ElementImpl
* type parameters are known.
*/
abstract class ExecutableMember extends Member implements ExecutableElement {
@override
final FunctionType type;
/**
* Initialize a newly created element to represent a constructor, based on the
* [baseElement], defined by the [definingType].
* Initialize a newly created element to represent a callable element (like a
* method or function or property), based on the [baseElement], defined by the
* [definingType]. If [type] is passed, it represents the full type of the
* member, and will take precedence over the [definingType].
*/
ExecutableMember(ExecutableElement baseElement, InterfaceType definingType)
: super(baseElement, definingType);
ExecutableMember(ExecutableElement baseElement, InterfaceType definingType,
[FunctionType type])
: type = type ??
baseElement.type.substitute2(definingType.typeArguments,
TypeParameterTypeImpl.getTypes(definingType.typeParameters)),
super(baseElement, definingType);
@override
ExecutableElement get baseElement => super.baseElement as ExecutableElement;
@ -3849,26 +3840,10 @@ abstract class ExecutableMember extends Member implements ExecutableElement {
}
@override
List<ParameterElement> get parameters {
List<ParameterElement> baseParameters = baseElement.parameters;
int parameterCount = baseParameters.length;
if (parameterCount == 0) {
return baseParameters;
}
List<ParameterElement> parameterizedParameters =
new List<ParameterElement>(parameterCount);
for (int i = 0; i < parameterCount; i++) {
parameterizedParameters[i] =
ParameterMember.from(baseParameters[i], definingType);
}
return parameterizedParameters;
}
List<ParameterElement> get parameters => type.parameters;
@override
DartType get returnType => substituteFor(baseElement.returnType);
@override
FunctionType get type => substituteFor(baseElement.type);
DartType get returnType => type.returnType;
@override
List<TypeParameterElement> get typeParameters => baseElement.typeParameters;
@ -4058,18 +4033,21 @@ class FieldFormalParameterElementImpl extends ParameterElementImpl
class FieldFormalParameterMember extends ParameterMember
implements FieldFormalParameterElement {
/**
* Initialize a newly created element to represent a constructor, based on the
* [baseElement], defined by the [definingType].
* Initialize a newly created element to represent a field formal parameter,
* based on the [baseElement], defined by the [definingType]. If [type]
* is passed it will be used as the substituted type for this member.
*/
FieldFormalParameterMember(
FieldFormalParameterElement baseElement, ParameterizedType definingType)
: super(baseElement, definingType);
FieldFormalParameterElement baseElement, ParameterizedType definingType,
[DartType type])
: super(baseElement, definingType, type);
@override
FieldElement get field {
FieldElement field = (baseElement as FieldFormalParameterElement).field;
if (field is FieldElement) {
return FieldMember.from(field, definingType);
return FieldMember.from(
field, substituteFor(field.enclosingElement.type));
}
return field;
}
@ -4085,7 +4063,7 @@ class FieldFormalParameterMember extends ParameterMember
*/
class FieldMember extends VariableMember implements FieldElement {
/**
* Initialize a newly created element to represent a constructor, based on the
* Initialize a newly created element to represent a field, based on the
* [baseElement], defined by the [definingType].
*/
FieldMember(FieldElement baseElement, InterfaceType definingType)
@ -4094,9 +4072,6 @@ class FieldMember extends VariableMember implements FieldElement {
@override
FieldElement get baseElement => super.baseElement as FieldElement;
@override
InterfaceType get definingType => super.definingType as InterfaceType;
@override
ClassElement get enclosingElement => baseElement.enclosingElement;
@ -4130,7 +4105,7 @@ class FieldMember extends VariableMember implements FieldElement {
* field. Return the member that was created, or the base field if no member
* was created.
*/
static FieldElement from(FieldElement field, InterfaceType definingType) {
static FieldElement from(FieldElement field, ParameterizedType definingType) {
if (!_isChangedByTypeSubstitution(field, definingType)) {
return field;
}
@ -4146,11 +4121,12 @@ class FieldMember extends VariableMember implements FieldElement {
* arguments from the defining type.
*/
static bool _isChangedByTypeSubstitution(
FieldElement field, InterfaceType definingType) {
FieldElement field, ParameterizedType definingType) {
List<DartType> argumentTypes = definingType.typeArguments;
if (field != null && argumentTypes.length != 0) {
DartType baseType = field.type;
List<DartType> parameterTypes = definingType.element.type.typeArguments;
List<DartType> parameterTypes =
TypeParameterTypeImpl.getTypes(definingType.typeParameters);
if (baseType != null) {
DartType substitutedType =
baseType.substitute2(argumentTypes, parameterTypes);
@ -4318,6 +4294,70 @@ class FunctionElementImpl extends ExecutableElementImpl
}
}
/**
* An element of a generic function, where the type parameters are known.
*/
// TODO(jmesserly): the term "function member" is a bit weird, but it allows
// a certain consistency.
class FunctionMember extends ExecutableMember implements FunctionElement {
/**
* Initialize a newly created element to represent a function, based on the
* [baseElement], with the corresponding function [type].
*/
FunctionMember(FunctionElement baseElement, [DartType type])
: super(baseElement, null, type);
@override
FunctionElement get baseElement => super.baseElement as FunctionElement;
@override
Element get enclosingElement => baseElement.enclosingElement;
@override
bool get isEntryPoint => baseElement.isEntryPoint;
@override
SourceRange get visibleRange => baseElement.visibleRange;
@override
accept(ElementVisitor visitor) => visitor.visitFunctionElement(this);
@override
FunctionDeclaration computeNode() => baseElement.computeNode();
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write(baseElement.displayName);
(type as FunctionTypeImpl).appendTo(buffer);
return buffer.toString();
}
/**
* If the given [method]'s type is different when any type parameters from the
* defining type's declaration are replaced with the actual type arguments
* from the [definingType], create a method member representing the given
* method. Return the member that was created, or the base method if no member
* was created.
*/
static MethodElement from(
MethodElement method, ParameterizedType definingType) {
if (method == null || definingType.typeArguments.length == 0) {
return method;
}
FunctionType baseType = method.type;
List<DartType> argumentTypes = definingType.typeArguments;
List<DartType> parameterTypes =
TypeParameterTypeImpl.getTypes(definingType.typeParameters);
FunctionType substitutedType =
baseType.substitute2(argumentTypes, parameterTypes);
if (baseType == substitutedType) {
return method;
}
return new MethodMember(method, definingType, substitutedType);
}
}
/**
* The type of a function, method, constructor, getter, or setter. Function
* types come in three variations:
@ -4463,7 +4503,7 @@ abstract class FunctionType implements ParameterizedType {
* A function type alias (`typedef`).
*/
abstract class FunctionTypeAliasElement
implements TypeDefiningElement, TypeParameterizedElement {
implements TypeDefiningElement, FunctionTypedElement {
/**
* An empty array of type alias elements.
*/
@ -4476,19 +4516,6 @@ abstract class FunctionTypeAliasElement
@override
CompilationUnitElement get enclosingElement;
/**
* Return a list containing all of the parameters defined by this type alias.
*/
List<ParameterElement> get parameters;
/**
* Return the return type defined by this type alias.
*/
DartType get returnType;
@override
FunctionType get type;
/**
* Return the resolved function type alias node that declares this element.
*
@ -4639,6 +4666,31 @@ class FunctionTypeAliasElementImpl extends ElementImpl
}
}
/**
* An element that has a [FunctionType] as its [type].
*
* This also provides convenient access to the parameters and return type.
*/
abstract class FunctionTypedElement implements TypeParameterizedElement {
/**
* Return a list containing all of the parameters defined by this executable
* element.
*/
List<ParameterElement> get parameters;
/**
* Return the return type defined by this element. If the element model is
* fully populated, then the [returnType] will not be `null`, even
* if no return type was explicitly specified.
*/
DartType get returnType;
/**
* Return the type of function defined by this element.
*/
FunctionType get type;
}
/**
* The type of a function, method, constructor, getter, or setter.
*/
@ -4729,26 +4781,12 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
/**
* Return the base parameter elements of this function element.
*/
List<ParameterElement> get baseParameters {
Element element = this.element;
if (element is ExecutableElement) {
return element.parameters;
} else {
return (element as FunctionTypeAliasElement).parameters;
}
}
List<ParameterElement> get baseParameters => element.parameters;
/**
* Return the return type defined by this function's element.
*/
DartType get baseReturnType {
Element element = this.element;
if (element is ExecutableElement) {
return element.returnType;
} else {
return (element as FunctionTypeAliasElement).returnType;
}
}
DartType get baseReturnType => element.returnType;
@override
List<TypeParameterElement> get boundTypeParameters => _boundTypeParameters;
@ -4824,6 +4862,9 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
return name;
}
@override
FunctionTypedElement get element => super.element;
@override
int get hashCode {
if (element == null) {
@ -4847,6 +4888,26 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
return code;
}
/**
* The type arguments that were used to instantiate this function type, if
* any, otherwise this will return an empty list.
*
* Given a function type `f`:
*
* f == f.originalFunction.instantiate(f.instantiatedTypeArguments)
*
* Will always hold.
*/
List<DartType> get instantiatedTypeArguments {
int typeParameterCount = element.type.boundTypeParameters.length;
if (typeParameterCount == 0) {
return DartType.EMPTY_LIST;
}
// The substituted types at the end should be our bound type parameters.
int skipCount = typeArguments.length - typeParameterCount;
return new List<DartType>.from(typeArguments.skip(skipCount));
}
@override
Map<String, DartType> get namedParameterTypes {
LinkedHashMap<String, DartType> namedParameterTypes =
@ -4944,6 +5005,20 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
return types;
}
/**
* If this is an instantiation of a generic function type, this will get
* the original function from which it was instantiated.
*
* Otherwise, this will return `this`.
*/
FunctionTypeImpl get originalFunction {
if (element.type.boundTypeParameters.isEmpty) {
return this;
}
return (element.type as FunctionTypeImpl).substitute2(typeArguments,
TypeParameterTypeImpl.getTypes(typeParameters), prunedTypedefs);
}
@override
List<ParameterElement> get parameters {
List<ParameterElement> baseParameters = this.baseParameters;
@ -7903,7 +7978,7 @@ abstract class Member implements Element {
final ParameterizedType _definingType;
/**
* Initialize a newly created element to represent a constructor, based on the
* Initialize a newly created element to represent a member, based on the
* [baseElement], defined by the [definingType].
*/
Member(this._baseElement, this._definingType);
@ -8016,6 +8091,7 @@ abstract class Member implements Element {
* Return the type that results from replacing the type parameters in the
* given [type] with the type arguments associated with this member.
*/
@deprecated
DartType substituteFor(DartType type) {
if (type == null) {
return null;
@ -8143,11 +8219,14 @@ class MethodElementImpl extends ExecutableElementImpl implements MethodElement {
*/
class MethodMember extends ExecutableMember implements MethodElement {
/**
* Initialize a newly created element to represent a constructor, based on the
* [baseElement], defined by the [definingType].
* Initialize a newly created element to represent a method, based on the
* [baseElement], defined by the [definingType]. If [type] is passed, it
* represents the full type of the member, and will take precedence over
* the [definingType].
*/
MethodMember(MethodElement baseElement, InterfaceType definingType)
: super(baseElement, definingType);
MethodMember(MethodElement baseElement, InterfaceType definingType,
[DartType type])
: super(baseElement, definingType, type);
@override
MethodElement get baseElement => super.baseElement as MethodElement;
@ -8205,10 +8284,7 @@ class MethodMember extends ExecutableMember implements MethodElement {
if (baseType == substitutedType) {
return method;
}
// TODO(brianwilkerson) Consider caching the substituted type in the
// instance. It would use more memory but speed up some operations.
// We need to see how often the type is being re-computed.
return new MethodMember(method, definingType);
return new MethodMember(method, definingType, substitutedType);
}
}
@ -8918,11 +8994,13 @@ class ParameterMember extends VariableMember
with ParameterElementMixin
implements ParameterElement {
/**
* Initialize a newly created element to represent a constructor, based on the
* [baseElement], defined by the [definingType].
* Initialize a newly created element to represent a parameter, based on the
* [baseElement], defined by the [definingType]. If [type] is passed it will
* represent the already substituted type.
*/
ParameterMember(ParameterElement baseElement, ParameterizedType definingType)
: super(baseElement, definingType);
ParameterMember(ParameterElement baseElement, ParameterizedType definingType,
[DartType type])
: super._(baseElement, definingType, type);
@override
ParameterElement get baseElement => super.baseElement as ParameterElement;
@ -8944,18 +9022,11 @@ class ParameterMember extends VariableMember
@override
List<ParameterElement> get parameters {
List<ParameterElement> baseParameters = baseElement.parameters;
int parameterCount = baseParameters.length;
if (parameterCount == 0) {
return baseParameters;
DartType type = this.type;
if (type is FunctionType) {
return type.parameters;
}
List<ParameterElement> parameterizedParameters =
new List<ParameterElement>(parameterCount);
for (int i = 0; i < parameterCount; i++) {
parameterizedParameters[i] =
ParameterMember.from(baseParameters[i], definingType);
}
return parameterizedParameters;
return ParameterElement.EMPTY_LIST;
}
@override
@ -8964,6 +9035,8 @@ class ParameterMember extends VariableMember
@override
SourceRange get visibleRange => baseElement.visibleRange;
// TODO(jmesserly): this equality is broken. It should consider the defining
// type as well, otherwise we're dropping the substitution.
@override
bool operator ==(Object object) =>
object is ParameterMember && baseElement == object.baseElement;
@ -9030,8 +9103,9 @@ class ParameterMember extends VariableMember
// Check if parameter type depends on defining type type arguments.
// It is possible that we did not resolve field formal parameter yet,
// so skip this check for it.
bool isFieldFormal = parameter is FieldFormalParameterElement;
if (!isFieldFormal) {
if (parameter is FieldFormalParameterElement) {
return new FieldFormalParameterMember(parameter, definingType);
} else {
DartType baseType = parameter.type;
List<DartType> argumentTypes = definingType.typeArguments;
List<DartType> parameterTypes =
@ -9041,15 +9115,8 @@ class ParameterMember extends VariableMember
if (baseType == substitutedType) {
return parameter;
}
return new ParameterMember(parameter, definingType, substitutedType);
}
// TODO(brianwilkerson) Consider caching the substituted type in the
// instance. It would use more memory but speed up some operations.
// We need to see how often the type is being re-computed.
if (isFieldFormal) {
return new FieldFormalParameterMember(
parameter as FieldFormalParameterElement, definingType);
}
return new ParameterMember(parameter, definingType);
}
}
@ -9324,7 +9391,7 @@ class PropertyAccessorElementImpl extends ExecutableElementImpl
class PropertyAccessorMember extends ExecutableMember
implements PropertyAccessorElement {
/**
* Initialize a newly created element to represent a constructor, based on the
* Initialize a newly created element to represent a property, based on the
* [baseElement], defined by the [definingType].
*/
PropertyAccessorMember(
@ -10587,12 +10654,25 @@ abstract class VariableElementImpl extends ElementImpl
* type parameters are known.
*/
abstract class VariableMember extends Member implements VariableElement {
@override
final DartType type;
/**
* Initialize a newly created element to represent a constructor, based on the
* Initialize a newly created element to represent a variable, based on the
* [baseElement], defined by the [definingType].
*/
VariableMember(VariableElement baseElement, ParameterizedType definingType)
: super(baseElement, definingType);
VariableMember(VariableElement baseElement, ParameterizedType definingType,
[DartType type])
: type = type ??
baseElement.type.substitute2(definingType.typeArguments,
TypeParameterTypeImpl.getTypes(definingType.typeParameters)),
super(baseElement, definingType);
// TODO(jmesserly): this is temporary to allow the ParameterMember subclass.
// Apparently mixins don't work with optional params.
VariableMember._(VariableElement baseElement, ParameterizedType definingType,
DartType type)
: this(baseElement, definingType, type);
@override
VariableElement get baseElement => super.baseElement as VariableElement;
@ -10630,9 +10710,6 @@ abstract class VariableMember extends Member implements VariableElement {
@override
bool get isStatic => baseElement.isStatic;
@override
DartType get type => substituteFor(baseElement.type);
@override
void visitChildren(ElementVisitor visitor) {
// TODO(brianwilkerson) We need to finish implementing the accessors used

View file

@ -635,6 +635,46 @@ class ElementResolver extends SimpleAstVisitor<Object> {
}
}
}
//
// Check for a generic method & apply type arguments if any were passed.
//
if (staticElement is MethodElement || staticElement is FunctionElement) {
FunctionType type = (staticElement as ExecutableElement).type;
List<TypeParameterElement> parameters = type.boundTypeParameters;
NodeList<TypeName> arguments = node.typeArguments?.arguments;
if (arguments != null && arguments.length != parameters.length) {
// Wrong number of type arguments. Ignore them
arguments = null;
_resolver.reportErrorForNode(
StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
methodName,
[type, parameters.length, arguments.length]);
}
if (parameters.isNotEmpty) {
List<DartType> typeArgs;
if (arguments == null) {
typeArgs = new List<DartType>.filled(
parameters.length, DynamicTypeImpl.instance);
} else {
typeArgs = new List<DartType>.from(arguments.map((n) => n.type));
}
type = type.instantiate(typeArgs);
if (staticElement is MethodMember) {
MethodMember member = staticElement;
staticElement =
new MethodMember(member.baseElement, member.definingType, type);
} else if (staticElement is MethodElement) {
ClassElement clazz = staticElement.enclosingElement;
staticElement = new MethodMember(staticElement, clazz.type, type);
} else {
staticElement =
new FunctionMember(staticElement as FunctionElement, type);
}
}
}
staticElement = _convertSetterToGetter(staticElement);
propagatedElement = _convertSetterToGetter(propagatedElement);
//

View file

@ -4940,11 +4940,12 @@ class StaticWarningCode extends ErrorCode {
*
* Parameters:
* 0: the number of named parameters in the overridden member
* 1: the name of the class from the overridden method
* 1: the signature of the overridden member
* 2: the name of the class from the overridden method
*/
static const StaticWarningCode INVALID_OVERRIDE_NAMED = const StaticWarningCode(
'INVALID_OVERRIDE_NAMED',
"Missing the named parameter '{0}' to match the overridden method from '{1}'");
"Missing the named parameter '{0}' to match the overridden method from '{1}' from '{2}'");
/**
* 7.1 Instance Methods: It is a static warning if an instance method
@ -4953,11 +4954,12 @@ class StaticWarningCode extends ErrorCode {
*
* Parameters:
* 0: the number of positional parameters in the overridden member
* 1: the name of the class from the overridden method
* 1: the signature of the overridden member
* 2: the name of the class from the overridden method
*/
static const StaticWarningCode INVALID_OVERRIDE_POSITIONAL =
const StaticWarningCode('INVALID_OVERRIDE_POSITIONAL',
"Must have at least {0} parameters to match the overridden method from '{1}'");
"Must have at least {0} parameters to match the overridden method '{1}' from '{2}'");
/**
* 7.1 Instance Methods: It is a static warning if an instance method
@ -4966,11 +4968,12 @@ class StaticWarningCode extends ErrorCode {
*
* Parameters:
* 0: the number of required parameters in the overridden member
* 1: the name of the class from the overridden method
* 1: the signature of the overridden member
* 2: the name of the class from the overridden method
*/
static const StaticWarningCode INVALID_OVERRIDE_REQUIRED =
const StaticWarningCode('INVALID_OVERRIDE_REQUIRED',
"Must have {0} required parameters or less to match the overridden method from '{1}'");
"Must have {0} required parameters or less to match the overridden method '{1}' from '{2}'");
/**
* 7.3 Setters: It is a static warning if a setter <i>m1</i> overrides a

View file

@ -1361,6 +1361,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
_errorReporter.reportErrorForNode(
StaticWarningCode.INVALID_OVERRIDE_REQUIRED, errorNameTarget, [
overriddenNormalPT.length,
overriddenExecutable,
overriddenExecutable.enclosingElement.displayName
]);
return true;
@ -1370,6 +1371,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
_errorReporter.reportErrorForNode(
StaticWarningCode.INVALID_OVERRIDE_POSITIONAL, errorNameTarget, [
overriddenPositionalPT.length + overriddenNormalPT.length,
overriddenExecutable,
overriddenExecutable.enclosingElement.displayName
]);
return true;
@ -1383,6 +1385,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
_errorReporter.reportErrorForNode(
StaticWarningCode.INVALID_OVERRIDE_NAMED, errorNameTarget, [
overriddenParamName,
overriddenExecutable,
overriddenExecutable.enclosingElement.displayName
]);
return true;

View file

@ -7,7 +7,6 @@ library engine.resolver;
import 'dart:collection';
import '../task/strong/info.dart' show InferredType, StaticInfo;
import '../task/strong/rules.dart' show TypeRules;
import 'ast.dart';
import 'constant.dart';
import 'element.dart';
@ -5335,11 +5334,6 @@ class InferenceContext {
*/
final TypeSystem _typeSystem;
/**
* The DDC type rules, used to create the inference info nodes.
*/
final TypeRules _rules;
/**
* A stack of return types for all of the enclosing
* functions and methods.
@ -5348,8 +5342,7 @@ class InferenceContext {
InferenceContext._(this._errorListener, TypeProvider typeProvider,
this._typeSystem, this._inferenceHints)
: _typeProvider = typeProvider,
_rules = new TypeRules(typeProvider);
: _typeProvider = typeProvider;
/**
* Get the return type of the current enclosing function, if any.
@ -5390,7 +5383,7 @@ class InferenceContext {
* [type] has been inferred as the type of [node].
*/
void recordInference(Expression node, DartType type) {
StaticInfo info = InferredType.create(_rules, node, type);
StaticInfo info = InferredType.create(_typeSystem, node, type);
if (!_inferenceHints || info == null) {
return;
}
@ -9030,6 +9023,7 @@ class ResolverVisitor extends ScopedVisitor {
// because it needs to be visited in the context of the invocation.
//
safelyVisit(node.target);
safelyVisit(node.typeArguments);
node.accept(elementResolver);
_inferFunctionExpressionsParametersTypes(node.argumentList);
Element methodElement = node.methodName.staticElement;

View file

@ -661,6 +661,17 @@ class SourceFactory {
return resolver != null ? resolver.packageMap : null;
}
/**
* Return a source factory that will resolve URI's in the same way that this
* source factory does.
*/
SourceFactory clone() {
SourceFactory factory =
new SourceFactory(_resolvers, _packages, _resourceProvider);
factory.localSourcePredicate = _localSourcePredicate;
return factory;
}
/**
* Return a source object representing the given absolute URI, or `null` if the URI is not a
* valid URI or if it is not an absolute URI.

View file

@ -1823,31 +1823,51 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
Element element = node.methodName.staticElement;
DartType fnType = node.methodName.staticType;
TypeSystem ts = _typeSystem;
// TODO(jmesserly): once we allow explicitly passed typeArguments, we need
// to only do this if node.typeArguments == null.
if (element is ExecutableElement &&
if (node.typeArguments == null &&
element is ExecutableElement &&
fnType is FunctionTypeImpl &&
ts is StrongTypeSystemImpl) {
// We may have too many (or too few) arguments. Only use arguments
// which have been matched up with a static parameter.
Iterable<Expression> arguments = node.argumentList.arguments
.where((e) => e.staticParameterElement != null);
List<DartType> argTypes = arguments.map((e) => e.staticType).toList();
List<DartType> paramTypes =
arguments.map((e) => e.staticParameterElement.type).toList();
FunctionTypeImpl genericFunction = fnType.originalFunction;
if (element is PropertyAccessorElement) {
genericFunction = element.type.returnType;
}
if (genericFunction.boundTypeParameters.isEmpty) {
return false;
}
for (DartType typeArg in fnType.instantiatedTypeArguments) {
if (!typeArg.isDynamic) {
return false;
}
}
List<ParameterElement> genericParameters = genericFunction.parameters;
List<DartType> argTypes = new List<DartType>();
List<DartType> paramTypes = new List<DartType>();
for (Expression arg in node.argumentList.arguments) {
// We may have too many (or too few) arguments. Only use arguments
// which have been matched up with a static parameter.
ParameterElement p = arg.staticParameterElement;
if (p != null) {
int i = element.parameters.indexOf(p);
argTypes.add(arg.staticType);
paramTypes.add(genericParameters[i].type);
}
}
FunctionType inferred = ts.inferCallFromArguments(
_typeProvider, fnType, paramTypes, argTypes);
_typeProvider, genericFunction, paramTypes, argTypes);
if (inferred != fnType) {
// TODO(jmesserly): inference should be happening earlier, which would
// allow these parameters to be correct from the get-go.
// TODO(jmesserly): we need to fix up the parameter elements based on
// inferred method.
List<ParameterElement> inferredParameters = inferred.parameters;
List<ParameterElement> correspondingParams =
new List<ParameterElement>();
for (Expression arg in arguments) {
int i = element.parameters.indexOf(arg.staticParameterElement);
correspondingParams.add(inferredParameters[i]);
for (Expression arg in node.argumentList.arguments) {
ParameterElement p = arg.staticParameterElement;
if (p != null) {
int i = element.parameters.indexOf(p);
correspondingParams.add(inferredParameters[i]);
}
}
node.argumentList.correspondingStaticParameters = correspondingParams;
_recordStaticType(node.methodName, inferred);

View file

@ -22,6 +22,22 @@ class StrongTypeSystemImpl implements TypeSystem {
StrongTypeSystemImpl();
bool anyParameterType(FunctionType ft, bool predicate(DartType t)) {
return ft.parameters.any((p) => predicate(p.type));
}
/**
* Given a type t, if t is an interface type with a call method
* defined, return the function type for the call method, otherwise
* return null.
*/
FunctionType getCallMethodType(DartType t) {
if (t is InterfaceType) {
return t.lookUpInheritedMethod("call")?.type;
}
return null;
}
@override
DartType getLeastUpperBound(
TypeProvider typeProvider, DartType type1, DartType type2) {
@ -128,9 +144,10 @@ class StrongTypeSystemImpl implements TypeSystem {
return fnType.instantiate(inferredTypes);
}
// TODO(leafp): Document the rules in play here
@override
bool isAssignableTo(DartType fromType, DartType toType) {
// TODO(leafp): Document the rules in play here
// An actual subtype
if (isSubtypeOf(fromType, toType)) {
return true;
@ -138,8 +155,8 @@ class StrongTypeSystemImpl implements TypeSystem {
// Don't allow implicit downcasts between function types
// and call method objects, as these will almost always fail.
if ((fromType is FunctionType && _getCallMethodType(toType) != null) ||
(toType is FunctionType && _getCallMethodType(fromType) != null)) {
if ((fromType is FunctionType && getCallMethodType(toType) != null) ||
(toType is FunctionType && getCallMethodType(fromType) != null)) {
return false;
}
@ -160,21 +177,37 @@ class StrongTypeSystemImpl implements TypeSystem {
return false;
}
bool isGroundType(DartType t) {
// TODO(leafp): Revisit this.
if (t is TypeParameterType) return false;
if (_isTop(t)) return true;
if (t is FunctionType) {
if (!_isTop(t.returnType) ||
anyParameterType(t, (pt) => !_isBottom(pt, dynamicIsBottom: true))) {
return false;
} else {
return true;
}
}
if (t is InterfaceType) {
var typeArguments = t.typeArguments;
for (var typeArgument in typeArguments) {
if (!_isTop(typeArgument)) return false;
}
return true;
}
// We should not see any other type aside from malformed code.
return false;
}
@override
bool isSubtypeOf(DartType leftType, DartType rightType) {
return _isSubtypeOf(leftType, rightType, null);
}
// Given a type t, if t is an interface type with a call method
// defined, return the function type for the call method, otherwise
// return null.
FunctionType _getCallMethodType(DartType t) {
if (t is InterfaceType) {
return t.lookUpInheritedMethod("call")?.type;
}
return null;
}
_GuardedSubtypeChecker<DartType> _guard(
_GuardedSubtypeChecker<DartType> check) {
return (DartType t1, DartType t2, Set<Element> visited) {
@ -407,7 +440,7 @@ class StrongTypeSystemImpl implements TypeSystem {
// the interface type declares a call method with a type
// which is a super type of the function type.
if (t1 is InterfaceType && t2 is FunctionType) {
var callType = _getCallMethodType(t1);
var callType = getCallMethodType(t1);
return (callType != null) && _isFunctionSubtypeOf(callType, t2);
}

View file

@ -84,10 +84,11 @@ class PluginConfig {
PluginConfig(this.plugins);
/// Create a plugin configuration from an options map.
factory PluginConfig.fromOptions(Map<String, YamlNode> options) {
factory PluginConfig.fromOptions(Map<String, Object> options) {
List<PluginInfo> plugins = [];
var analyzerOptions = options[_analyzerOptionScope];
if (analyzerOptions != null) {
//TODO(pq): handle "raw" maps (https://github.com/dart-lang/sdk/issues/25126)
if (analyzerOptions is YamlMap) {
var pluginConfig = analyzerOptions[_pluginOptionScope];
if (pluginConfig is YamlMap) {
@ -143,7 +144,7 @@ class PluginConfigOptionsProcessor extends OptionsProcessor {
@override
void optionsProcessed(
AnalysisContext context, Map<String, YamlNode> options) {
AnalysisContext context, Map<String, Object> options) {
_config = new PluginConfig.fromOptions(options);
}
}

View file

@ -29,7 +29,6 @@ import 'package:analyzer/src/task/html.dart';
import 'package:analyzer/src/task/inputs.dart';
import 'package:analyzer/src/task/model.dart';
import 'package:analyzer/src/task/strong/checker.dart';
import 'package:analyzer/src/task/strong/rules.dart';
import 'package:analyzer/src/task/strong_mode.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/general.dart';
@ -5000,7 +4999,8 @@ class StrongModeVerifyUnitTask extends SourceBasedAnalysisTask {
TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
if (context.analysisOptions.strongMode) {
unit.accept(new CodeChecker(new TypeRules(typeProvider), errorListener));
unit.accept(new CodeChecker(
typeProvider, new StrongTypeSystemImpl(), errorListener));
}
//

View file

@ -4,6 +4,8 @@
library analyzer.src.task.options;
import 'dart:collection';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/plugin/options.dart';
import 'package:analyzer/source/analysis_options_provider.dart';
@ -28,9 +30,9 @@ final ListResultDescriptor<AnalysisError> ANALYSIS_OPTIONS_ERRORS =
final _OptionsProcessor _processor = new _OptionsProcessor();
/// Configure this [context] based on configuration details specified in
/// the given [options].
/// the given [options]. If [options] is `null`, default values are applied.
void configureContextOptions(
AnalysisContext context, Map<String, YamlNode> options) =>
AnalysisContext context, Map<String, Object> options) =>
_processor.configure(context, options);
/// `analyzer` analysis options constants.
@ -126,9 +128,20 @@ class ErrorFilterOptionValidator extends OptionsValidator {
new List.from(AnalyzerOptions.ignoreSynonyms)
..addAll(AnalyzerOptions.includeSynonyms));
bool recognizedErrorCode(String name) =>
ErrorCode.values.any((ErrorCode code) => code.name == name) ||
StaticInfo.names.contains(name);
/// Lazily populated set of error codes (hashed for speedy lookup).
static HashSet<String> _errorCodes;
/// Legal error code names.
static Set<String> get errorCodes {
if (_errorCodes == null) {
_errorCodes = new HashSet<String>();
// Engine codes.
_errorCodes.addAll(ErrorCode.values.map((ErrorCode code) => code.name));
// Strong-mode codes.
_errorCodes.addAll(StaticInfo.names);
}
return _errorCodes;
}
@override
void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
@ -143,7 +156,7 @@ class ErrorFilterOptionValidator extends OptionsValidator {
filters.nodes.forEach((k, v) {
if (k is YamlScalar) {
value = toUpperCase(k.value);
if (!recognizedErrorCode(value)) {
if (!errorCodes.contains(value)) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNRECOGNIZED_ERROR_CODE,
k.span,
@ -371,9 +384,13 @@ class TrueOrFalseValueErrorBuilder extends ErrorBuilder {
}
class _OptionsProcessor {
void configure(AnalysisContext context, Map<String, YamlNode> options) {
static final Map<String, Object> defaults = {'analyzer': {}};
/// Configure [context] based on the given [options] (which can be `null`
/// to restore [defaults]).
void configure(AnalysisContext context, Map<String, Object> options) {
if (options == null) {
return;
options = defaults;
}
var analyzer = options[AnalyzerOptions.analyzer];
@ -394,19 +411,36 @@ class _OptionsProcessor {
setLanguageOptions(context, language);
}
ErrorFilter parseFilter(String code, Object enable) {
enable = toLowerCase(enable);
if (AnalyzerOptions.ignoreSynonyms.contains(enable)) {
// Case-insensitive.
code = toUpperCase(code);
return ((AnalysisError error) => error.errorCode.name == code);
}
}
void setFilters(AnalysisContext context, Object codes) {
List<ErrorFilter> filters = <ErrorFilter>[];
// If codes are enumerated, collect them as filters; else leave filters
// empty to overwrite previous value.
if (codes is YamlMap) {
String value;
// TODO(pq): stop traversing nodes and unify w/ standard map handling
codes.nodes.forEach((k, v) {
if (k is YamlScalar && v is YamlScalar) {
value = toLowerCase(v.value);
if (AnalyzerOptions.ignoreSynonyms.contains(value)) {
// Case-insensitive.
String code = toUpperCase(k.value);
filters.add((AnalysisError error) => error.errorCode.name == code);
ErrorFilter filter = parseFilter(k.value, v.value);
if (filter != null) {
filters.add(filter);
}
}
});
} else if (codes is Map) {
codes.forEach((k, v) {
if (k is String) {
ErrorFilter filter = parseFilter(k, v);
if (filter != null) {
filters.add(filter);
}
}
});

View file

@ -9,20 +9,21 @@ library analyzer.src.task.strong.checker;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType;
import 'package:analyzer/src/generated/type_system.dart';
import 'info.dart';
import 'rules.dart';
/// Checks for overriding declarations of fields and methods. This is used to
/// check overrides between classes and superclasses, interfaces, and mixin
/// applications.
class _OverrideChecker {
bool _failure = false;
final TypeRules _rules;
final StrongTypeSystemImpl rules;
final AnalysisErrorListener _reporter;
_OverrideChecker(this._rules, this._reporter);
_OverrideChecker(this.rules, this._reporter);
void check(ClassDeclaration node) {
if (node.element.type.isObject) return;
@ -299,7 +300,7 @@ class _OverrideChecker {
AstNode node, AstNode errorLocation, bool isSubclass) {
assert(!element.isStatic);
FunctionType subType = _rules.elementType(element);
FunctionType subType = _elementType(element);
// TODO(vsm): Test for generic
FunctionType baseType = _getMemberType(type, element);
if (baseType == null) return false;
@ -313,8 +314,8 @@ class _OverrideChecker {
errorLocation, element, type, subType, baseType));
}
}
if (!_rules.isAssignable(subType, baseType)) {
// See whether non-assignable cases fit one of our common patterns:
if (!rules.isSubtypeOf(subType, baseType)) {
// See whether non-subtype cases fit one of our common patterns:
//
// Common pattern 1: Inferable return type (on getters and methods)
// class A {
@ -341,7 +342,8 @@ class _OverrideChecker {
/// Checks the body of functions and properties.
class CodeChecker extends RecursiveAstVisitor {
final TypeRules rules;
final StrongTypeSystemImpl rules;
final TypeProvider typeProvider;
final AnalysisErrorListener reporter;
final _OverrideChecker _overrideChecker;
final bool _hints;
@ -354,7 +356,7 @@ class CodeChecker extends RecursiveAstVisitor {
_overrideChecker._failure = false;
}
CodeChecker(TypeRules rules, AnalysisErrorListener reporter,
CodeChecker(this.typeProvider, StrongTypeSystemImpl rules, AnalysisErrorListener reporter,
{bool hints: false})
: rules = rules,
reporter = reporter,
@ -403,7 +405,7 @@ class CodeChecker extends RecursiveAstVisitor {
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
var field = node.fieldName;
var element = field.staticElement;
DartType staticType = rules.elementType(element);
DartType staticType = _elementType(element);
checkAssignment(node.expression, staticType);
node.visitChildren(this);
}
@ -413,8 +415,8 @@ class CodeChecker extends RecursiveAstVisitor {
// Check that the expression is an Iterable.
var expr = node.iterable;
var iterableType = node.awaitKeyword != null
? rules.provider.streamType
: rules.provider.iterableType;
? typeProvider.streamType
: typeProvider.iterableType;
var loopVariable = node.identifier != null
? node.identifier
: node.loopVariable?.identifier;
@ -463,7 +465,7 @@ class CodeChecker extends RecursiveAstVisitor {
@override
void visitListLiteral(ListLiteral node) {
var type = rules.provider.dynamicType;
var type = DynamicTypeImpl.instance;
if (node.typeArguments != null) {
var targs = node.typeArguments.arguments;
if (targs.length > 0) type = targs[0].type;
@ -481,8 +483,8 @@ class CodeChecker extends RecursiveAstVisitor {
@override
void visitMapLiteral(MapLiteral node) {
var ktype = rules.provider.dynamicType;
var vtype = rules.provider.dynamicType;
var ktype = DynamicTypeImpl.instance;
var vtype = DynamicTypeImpl.instance;
if (node.typeArguments != null) {
var targs = node.typeArguments.arguments;
if (targs.length > 0) ktype = targs[0].type;
@ -521,8 +523,8 @@ class CodeChecker extends RecursiveAstVisitor {
// TODO(vsm): When can this happen?
assert(element != null);
}
DartType expectedType = rules.elementType(element);
if (expectedType == null) expectedType = rules.provider.dynamicType;
DartType expectedType = _elementType(element);
if (expectedType == null) expectedType = DynamicTypeImpl.instance;
checkArgument(arg, expectedType);
}
}
@ -538,19 +540,19 @@ class CodeChecker extends RecursiveAstVisitor {
void checkFunctionApplication(
Expression node, Expression f, ArgumentList list) {
if (rules.isDynamicCall(f)) {
if (_isDynamicCall(f)) {
// If f is Function and this is a method invocation, we should have
// gotten an analyzer error, so no need to issue another error.
_recordDynamicInvoke(node, f);
} else {
checkArgumentList(list, rules.getTypeAsCaller(f));
checkArgumentList(list, _getTypeAsCaller(f));
}
}
@override
visitMethodInvocation(MethodInvocation node) {
var target = node.realTarget;
if (rules.isDynamicTarget(target) &&
if (_isDynamicTarget(target) &&
!_isObjectMethod(node, node.methodName)) {
_recordDynamicInvoke(node, target);
@ -606,15 +608,15 @@ class CodeChecker extends RecursiveAstVisitor {
void _checkReturnOrYield(Expression expression, AstNode node,
{bool yieldStar: false}) {
var body = node.getAncestor((n) => n is FunctionBody);
var type = rules.getExpectedReturnType(body, yieldStar: yieldStar);
var type = _getExpectedReturnType(body, yieldStar: yieldStar);
if (type == null) {
// We have a type mismatch: the async/async*/sync* modifier does
// not match the return or yield type. We should have already gotten an
// analyzer error in this case.
return;
}
InterfaceType futureType = rules.provider.futureType;
DartType actualType = expression.staticType;
InterfaceType futureType = typeProvider.futureType;
DartType actualType = expression?.staticType;
if (body.isAsynchronous &&
!body.isGenerator &&
actualType is InterfaceType &&
@ -625,6 +627,59 @@ class CodeChecker extends RecursiveAstVisitor {
if (expression != null) checkAssignment(expression, type);
}
/// Gets the expected return type of the given function [body], either from
/// a normal return/yield, or from a yield*.
DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
FunctionType functionType;
var parent = body.parent;
if (parent is Declaration) {
functionType = _elementType(parent.element);
} else {
assert(parent is FunctionExpression);
functionType = parent.staticType ?? DynamicTypeImpl.instance;
}
var type = functionType.returnType;
InterfaceType expectedType = null;
if (body.isAsynchronous) {
if (body.isGenerator) {
// Stream<T> -> T
expectedType = typeProvider.streamType;
} else {
// Future<T> -> T
// TODO(vsm): Revisit with issue #228.
expectedType = typeProvider.futureType;
}
} else {
if (body.isGenerator) {
// Iterable<T> -> T
expectedType = typeProvider.iterableType;
} else {
// T -> T
return type;
}
}
if (yieldStar) {
if (type.isDynamic) {
// Ensure it's at least a Stream / Iterable.
return expectedType.substitute4([typeProvider.dynamicType]);
} else {
// Analyzer will provide a separate error if expected type
// is not compatible with type.
return type;
}
}
if (type.isDynamic) {
return type;
} else if (type is InterfaceType && type.element == expectedType.element) {
return type.typeArguments[0];
} else {
// Malformed type - fallback on analyzer error.
return null;
}
}
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
_checkReturnOrYield(node.expression, node);
@ -644,7 +699,7 @@ class CodeChecker extends RecursiveAstVisitor {
}
void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) {
if ((rules.isDynamicTarget(target) || field.staticElement == null) &&
if ((_isDynamicTarget(target) || field.staticElement == null) &&
!_isObjectProperty(target, field)) {
_recordDynamicInvoke(node, target);
}
@ -665,7 +720,7 @@ class CodeChecker extends RecursiveAstVisitor {
void visitDefaultFormalParameter(DefaultFormalParameter node) {
// Check that defaults have the proper subtype.
var parameter = node.parameter;
var parameterType = rules.elementType(parameter.element);
var parameterType = _elementType(parameter.element);
assert(parameterType != null);
var defaultValue = node.defaultValue;
if (defaultValue != null) {
@ -680,11 +735,11 @@ class CodeChecker extends RecursiveAstVisitor {
var element = node.element;
var typeName = node.type;
if (typeName != null) {
var type = rules.elementType(element);
var type = _elementType(element);
var fieldElement =
node.identifier.staticElement as FieldFormalParameterElement;
var fieldType = rules.elementType(fieldElement.field);
if (!rules.isSubTypeOf(type, fieldType)) {
var fieldType = _elementType(fieldElement.field);
if (!rules.isSubtypeOf(type, fieldType)) {
var staticInfo =
new InvalidParameterDeclaration(rules, node, fieldType);
_recordMessage(staticInfo);
@ -698,7 +753,7 @@ class CodeChecker extends RecursiveAstVisitor {
var arguments = node.argumentList;
var element = node.staticElement;
if (element != null) {
var type = rules.elementType(node.staticElement);
var type = _elementType(node.staticElement);
checkArgumentList(arguments, type);
}
node.visitChildren(this);
@ -765,7 +820,7 @@ class CodeChecker extends RecursiveAstVisitor {
if (op.isUserDefinableOperator ||
op.type == TokenType.PLUS_PLUS ||
op.type == TokenType.MINUS_MINUS) {
if (rules.isDynamicTarget(node.operand)) {
if (_isDynamicTarget(node.operand)) {
_recordDynamicInvoke(node, node.operand);
}
// For ++ and --, even if it is not dynamic, we still need to check
@ -778,7 +833,7 @@ class CodeChecker extends RecursiveAstVisitor {
void visitBinaryExpression(BinaryExpression node) {
var op = node.operator;
if (op.isUserDefinableOperator) {
if (rules.isDynamicTarget(node.leftOperand)) {
if (_isDynamicTarget(node.leftOperand)) {
// Dynamic invocation
// TODO(vsm): Move this logic to the resolver?
if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
@ -826,7 +881,7 @@ class CodeChecker extends RecursiveAstVisitor {
@override
void visitIndexExpression(IndexExpression node) {
var target = node.realTarget;
if (rules.isDynamicTarget(target)) {
if (_isDynamicTarget(target)) {
_recordDynamicInvoke(node, target);
} else {
var element = node.staticElement;
@ -845,23 +900,94 @@ class CodeChecker extends RecursiveAstVisitor {
}
DartType getType(TypeName name) {
return (name == null) ? rules.provider.dynamicType : name.type;
return (name == null) ? DynamicTypeImpl.instance : name.type;
}
/// Analyzer checks boolean conversions, but we need to check too, because
/// it uses the default assignability rules that allow `dynamic` and `Object`
/// to be assigned to bool with no message.
void checkBoolean(Expression expr) =>
checkAssignment(expr, rules.provider.boolType);
checkAssignment(expr, typeProvider.boolType);
void checkAssignment(Expression expr, DartType type) {
if (expr is ParenthesizedExpression) {
checkAssignment(expr.expression, type);
} else {
_recordMessage(rules.checkAssignment(expr, type));
_recordMessage(_checkAssignment(expr, type));
}
}
StaticInfo _checkAssignment(Expression expr, DartType toT) {
final fromT = expr.staticType ?? DynamicTypeImpl.instance;
final Coercion c = _coerceTo(fromT, toT);
if (c is Identity) return null;
if (c is CoercionError) return new StaticTypeError(rules, expr, toT);
var reason = null;
var errors = <String>[];
var ok = _inferExpression(expr, toT, errors);
if (ok) return InferredType.create(rules, expr, toT);
reason = (errors.isNotEmpty) ? errors.first : null;
if (c is Cast) return DownCast.create(rules, expr, c, reason: reason);
assert(false);
return null;
}
/// Checks if we can perform downwards inference on [e] tp get type [t].
/// If it is not possible, this will add a message to [errors].
bool _inferExpression(Expression e, DartType t, List<String> errors) {
DartType staticType = e.staticType ?? DynamicTypeImpl.instance;
if (rules.isSubtypeOf(staticType, t)) {
return true;
}
errors.add("$e cannot be typed as $t");
return false;
}
// Produce a coercion which coerces something of type fromT
// to something of type toT.
// Returns the error coercion if the types cannot be coerced
// according to our current criteria.
Coercion _coerceTo(DartType fromT, DartType toT) {
// We can use anything as void
if (toT.isVoid) return Coercion.identity(toT);
// fromT <: toT, no coercion needed
if (rules.isSubtypeOf(fromT, toT)) return Coercion.identity(toT);
// TODO(vsm): We can get rid of the second clause if we disallow
// all sideways casts - see TODO below.
// -------
// Note: a function type is never assignable to a class per the Dart
// spec - even if it has a compatible call method. We disallow as
// well for consistency.
if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) ||
(toT is FunctionType && rules.getCallMethodType(fromT) != null)) {
return Coercion.error();
}
// Downcast if toT <: fromT
if (rules.isSubtypeOf(toT, fromT)) return Coercion.cast(fromT, toT);
// TODO(vsm): Once we have generic methods, we should delete this
// workaround. These sideways casts are always ones we warn about
// - i.e., we think they are likely to fail at runtime.
// -------
// Downcast if toT <===> fromT
// The intention here is to allow casts that are sideways in the restricted
// type system, but allowed in the regular dart type system, since these
// are likely to succeed. The canonical example is List<dynamic> and
// Iterable<T> for some concrete T (e.g. Object). These are unrelated
// in the restricted system, but List<dynamic> <: Iterable<T> in dart.
if (fromT.isAssignableTo(toT)) {
return Coercion.cast(fromT, toT);
}
return Coercion.error();
}
DartType _specializedBinaryReturnType(
TokenType op, DartType t1, DartType t2, DartType normalReturnType) {
// This special cases binary return types as per 16.26 and 16.27 of the
@ -877,14 +1003,14 @@ class CodeChecker extends RecursiveAstVisitor {
case TokenType.STAR_EQ:
case TokenType.TILDE_SLASH_EQ:
case TokenType.PERCENT_EQ:
if (t1 == rules.provider.intType &&
t2 == rules.provider.intType) return t1;
if (t1 == rules.provider.doubleType &&
t2 == rules.provider.doubleType) return t1;
if (t1 == typeProvider.intType &&
t2 == typeProvider.intType) return t1;
if (t1 == typeProvider.doubleType &&
t2 == typeProvider.doubleType) return t1;
// This particular combo is not spelled out in the spec, but all
// implementations and analyzer seem to follow this.
if (t1 == rules.provider.doubleType &&
t2 == rules.provider.intType) return t1;
if (t1 == typeProvider.doubleType &&
t2 == typeProvider.intType) return t1;
}
return normalReturnType;
}
@ -912,11 +1038,11 @@ class CodeChecker extends RecursiveAstVisitor {
var returnType = _specializedBinaryReturnType(
op, lhsType, rhsType, functionType.returnType);
if (!rules.isSubTypeOf(returnType, lhsType)) {
final numType = rules.provider.numType;
if (!rules.isSubtypeOf(returnType, lhsType)) {
final numType = typeProvider.numType;
// Try to fix up the numerical case if possible.
if (rules.isSubTypeOf(lhsType, numType) &&
rules.isSubTypeOf(lhsType, rhsType)) {
if (rules.isSubtypeOf(lhsType, numType) &&
rules.isSubtypeOf(lhsType, rhsType)) {
// This is also slightly different from spec, but allows us to keep
// compound operators in the int += num and num += dynamic cases.
staticInfo = DownCast.create(
@ -932,7 +1058,7 @@ class CodeChecker extends RecursiveAstVisitor {
// Check the rhs type
if (staticInfo is! CoercionInfo) {
var paramType = paramTypes.first;
staticInfo = rules.checkAssignment(expr.rightHandSide, paramType);
staticInfo = _checkAssignment(expr.rightHandSide, paramType);
_recordMessage(staticInfo);
}
}
@ -940,13 +1066,13 @@ class CodeChecker extends RecursiveAstVisitor {
bool _isObjectGetter(Expression target, SimpleIdentifier id) {
PropertyAccessorElement element =
rules.provider.objectType.element.getGetter(id.name);
typeProvider.objectType.element.getGetter(id.name);
return (element != null && !element.isStatic);
}
bool _isObjectMethod(Expression target, SimpleIdentifier id) {
MethodElement element =
rules.provider.objectType.element.getMethod(id.name);
typeProvider.objectType.element.getMethod(id.name);
return (element != null && !element.isStatic);
}
@ -955,7 +1081,7 @@ class CodeChecker extends RecursiveAstVisitor {
}
DartType _getStaticType(Expression expr) {
return expr.staticType ?? rules.provider.dynamicType;
return expr.staticType ?? DynamicTypeImpl.instance;
}
void _recordDynamicInvoke(AstNode node, AstNode target) {
@ -986,6 +1112,58 @@ class CodeChecker extends RecursiveAstVisitor {
CoercionInfo.set(info.node, info);
}
}
bool _isLibraryPrefix(Expression node) =>
node is SimpleIdentifier && node.staticElement is PrefixElement;
/// Returns `true` if the target expression is dynamic.
bool _isDynamicTarget(Expression node) {
if (node == null) return false;
if (_isLibraryPrefix(node)) return false;
// Null type happens when we have unknown identifiers, like a dart: import
// that doesn't resolve.
var type = node.staticType;
return type == null || type.isDynamic;
}
/// Returns `true` if the expression is a dynamic function call or method
/// invocation.
bool _isDynamicCall(Expression call) {
var ft = _getTypeAsCaller(call);
// TODO(leafp): This will currently return true if t is Function
// This is probably the most correct thing to do for now, since
// this code is also used by the back end. Maybe revisit at some
// point?
if (ft == null) return true;
// Dynamic as the parameter type is treated as bottom. A function with
// a dynamic parameter type requires a dynamic call in general.
// However, as an optimization, if we have an original definition, we know
// dynamic is reified as Object - in this case a regular call is fine.
if (call is SimpleIdentifier) {
var element = call.staticElement;
if (element is FunctionElement || element is MethodElement) {
// An original declaration.
return false;
}
}
return rules.anyParameterType(ft, (pt) => pt.isDynamic);
}
/// Given an expression, return its type assuming it is
/// in the caller position of a call (that is, accounting
/// for the possibility of a call method). Returns null
/// if expression is not statically callable.
FunctionType _getTypeAsCaller(Expression applicand) {
var t = applicand.staticType ?? DynamicTypeImpl.instance;
if (t is InterfaceType) {
return rules.getCallMethodType(t);
}
if (t is FunctionType) return t;
return null;
}
}
// Return the field on type corresponding to member, or null if none
@ -1054,6 +1232,14 @@ _MemberTypeGetter _memberTypeGetter(ExecutableElement member) {
if (baseMethod == null || baseMethod.isStatic) return null;
return baseMethod.type;
}
;
return f;
}
DartType _elementType(Element e) {
if (e == null) {
// Malformed code - just return dynamic.
return DynamicTypeImpl.instance;
}
return (e as dynamic).type;
}

View file

@ -11,15 +11,14 @@ library analyzer.src.task.strong.info;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/error.dart';
import 'rules.dart';
import 'package:analyzer/src/generated/type_system.dart';
// A down cast due to a variable declaration to a ground type. E.g.,
// T x = expr;
// where T is ground. We exclude non-ground types as these behave differently
// compared to standard Dart.
class AssignmentCast extends DownCast {
AssignmentCast(TypeRules rules, Expression expression, Cast cast)
AssignmentCast(TypeSystem rules, Expression expression, Cast cast)
: super._internal(rules, expression, cast);
@override
@ -56,13 +55,13 @@ class CoercionError extends Coercion {
abstract class CoercionInfo extends StaticInfo {
static const String _propertyName = 'dev_compiler.src.info.CoercionInfo';
final TypeRules rules;
final TypeSystem rules;
final Expression node;
CoercionInfo(this.rules, this.node);
DartType get baseType => rules.getStaticType(node);
DartType get baseType => node.staticType ?? DynamicTypeImpl.instance;
DartType get convertedType;
String get message;
@ -84,7 +83,8 @@ abstract class CoercionInfo extends StaticInfo {
abstract class DownCast extends CoercionInfo {
Cast _cast;
DownCast._internal(TypeRules rules, Expression expression, this._cast)
DownCast._internal(
TypeSystem rules, Expression expression, this._cast)
: super(rules, expression) {
assert(_cast.toType != baseType &&
_cast.fromType == baseType &&
@ -103,7 +103,8 @@ abstract class DownCast extends CoercionInfo {
'to cast to type {2}';
// Factory to create correct DownCast variant.
static StaticInfo create(TypeRules rules, Expression expression, Cast cast,
static StaticInfo create(
StrongTypeSystemImpl rules, Expression expression, Cast cast,
{String reason}) {
final fromT = cast.fromType;
final toT = cast.toType;
@ -138,7 +139,7 @@ abstract class DownCast extends CoercionInfo {
// TODO(vsm): Change this to an assert when we have generic methods and
// fix TypeRules._coerceTo to disallow implicit sideways casts.
if (!rules.isSubTypeOf(toT, fromT)) {
if (!rules.isSubtypeOf(toT, fromT)) {
assert(toT.isSubtypeOf(fromT) || fromT.isAssignableTo(toT));
return new DownCastComposite(rules, expression, cast);
}
@ -182,7 +183,8 @@ abstract class DownCast extends CoercionInfo {
// A down cast to a non-ground type. These behave differently from standard
// Dart and may be more likely to fail at runtime.
class DownCastComposite extends DownCast {
DownCastComposite(TypeRules rules, Expression expression, Cast cast)
DownCastComposite(
TypeSystem rules, Expression expression, Cast cast)
: super._internal(rules, expression, cast);
@override
@ -194,7 +196,7 @@ class DownCastComposite extends DownCast {
// A down cast to a non-ground type. These behave differently from standard
// Dart and may be more likely to fail at runtime.
class DownCastImplicit extends DownCast {
DownCastImplicit(TypeRules rules, Expression expression, Cast cast)
DownCastImplicit(TypeSystem rules, Expression expression, Cast cast)
: super._internal(rules, expression, cast);
@override
@ -205,7 +207,7 @@ class DownCastImplicit extends DownCast {
// A down cast from dynamic to T.
class DynamicCast extends DownCast {
DynamicCast(TypeRules rules, Expression expression, Cast cast)
DynamicCast(TypeSystem rules, Expression expression, Cast cast)
: super._internal(rules, expression, cast);
@override
@ -217,9 +219,9 @@ class DynamicCast extends DownCast {
class DynamicInvoke extends CoercionInfo {
static const String _propertyName = 'dev_compiler.src.info.DynamicInvoke';
DynamicInvoke(TypeRules rules, Expression expression)
DynamicInvoke(TypeSystem rules, Expression expression)
: super(rules, expression);
DartType get convertedType => rules.provider.dynamicType;
DartType get convertedType => DynamicTypeImpl.instance;
String get message => '{0} requires dynamic invoke';
@override
@ -249,7 +251,7 @@ class Identity extends Coercion {
// Standard / unspecialized inferred type
class InferredType extends InferredTypeBase {
InferredType(TypeRules rules, Expression expression, DartType type)
InferredType(TypeSystem rules, Expression expression, DartType type)
: super._internal(rules, expression, type);
@override
@ -257,7 +259,7 @@ class InferredType extends InferredTypeBase {
// Factory to create correct InferredType variant.
static InferredTypeBase create(
TypeRules rules, Expression expression, DartType type) {
TypeSystem rules, Expression expression, DartType type) {
// Specialized inference:
if (expression is Literal) {
return new InferredTypeLiteral(rules, expression, type);
@ -274,7 +276,8 @@ class InferredType extends InferredTypeBase {
// An inferred type for a non-literal allocation site.
class InferredTypeAllocation extends InferredTypeBase {
InferredTypeAllocation(TypeRules rules, Expression expression, DartType type)
InferredTypeAllocation(
TypeSystem rules, Expression expression, DartType type)
: super._internal(rules, expression, type);
@override
@ -286,7 +289,8 @@ class InferredTypeAllocation extends InferredTypeBase {
abstract class InferredTypeBase extends CoercionInfo {
final DartType _type;
InferredTypeBase._internal(TypeRules rules, Expression expression, this._type)
InferredTypeBase._internal(
TypeSystem rules, Expression expression, this._type)
: super(rules, expression);
@override List get arguments => [node, type];
@ -299,7 +303,8 @@ abstract class InferredTypeBase extends CoercionInfo {
// An inferred type for a closure expression
class InferredTypeClosure extends InferredTypeBase {
InferredTypeClosure(TypeRules rules, Expression expression, DartType type)
InferredTypeClosure(
TypeSystem rules, Expression expression, DartType type)
: super._internal(rules, expression, type);
@override
@ -308,7 +313,8 @@ class InferredTypeClosure extends InferredTypeBase {
// An inferred type for a literal expression.
class InferredTypeLiteral extends InferredTypeBase {
InferredTypeLiteral(TypeRules rules, Expression expression, DartType type)
InferredTypeLiteral(
TypeSystem rules, Expression expression, DartType type)
: super._internal(rules, expression, type);
@override
@ -385,8 +391,8 @@ abstract class InvalidOverride extends StaticError {
class InvalidParameterDeclaration extends StaticError {
final DartType expectedType;
InvalidParameterDeclaration(
TypeRules rules, FormalParameter declaration, this.expectedType)
InvalidParameterDeclaration(TypeSystem rules,
FormalParameter declaration, this.expectedType)
: super(declaration);
@override List<Object> get arguments => [node, expectedType];
@ -438,7 +444,7 @@ class InvalidVariableDeclaration extends StaticError {
final DartType expectedType;
InvalidVariableDeclaration(
TypeRules rules, AstNode declaration, this.expectedType)
TypeSystem rules, AstNode declaration, this.expectedType)
: super(declaration);
@override List<Object> get arguments => [expectedType];
@ -533,9 +539,10 @@ class StaticTypeError extends StaticError {
final DartType expectedType;
String reason = null;
StaticTypeError(TypeRules rules, Expression expression, this.expectedType,
StaticTypeError(
TypeSystem rules, Expression expression, this.expectedType,
{this.reason})
: baseType = rules.getStaticType(expression),
: baseType = expression.staticType ?? DynamicTypeImpl.instance,
super(expression);
@override List<Object> get arguments => [node, baseType, expectedType];
@ -558,7 +565,8 @@ class StaticTypeError extends StaticError {
//
// TODO(vsm,leafp): Remove this.
class UninferredClosure extends DownCast {
UninferredClosure(TypeRules rules, FunctionExpression expression, Cast cast)
UninferredClosure(
TypeSystem rules, FunctionExpression expression, Cast cast)
: super._internal(rules, expression, cast);
@override

View file

@ -1,524 +0,0 @@
// 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.
// TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
// refactored to fit into analyzer.
library analyzer.src.task.strong.rules;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'info.dart';
// TODO(jmesserly): this entire file needs to be removed in favor of TypeSystem.
final _objectMap = new Expando('providerToObjectMap');
Map<String, DartType> getObjectMemberMap(TypeProvider typeProvider) {
var map = _objectMap[typeProvider] as Map<String, DartType>;
if (map == null) {
map = <String, DartType>{};
_objectMap[typeProvider] = map;
var objectType = typeProvider.objectType;
var element = objectType.element;
// Only record methods (including getters) with no parameters. As parameters are contravariant wrt
// type, using Object's version may be too strict.
// Add instance methods.
element.methods.where((method) => !method.isStatic).forEach((method) {
map[method.name] = method.type;
});
// Add getters.
element.accessors
.where((member) => !member.isStatic && member.isGetter)
.forEach((member) {
map[member.name] = member.type.returnType;
});
}
return map;
}
class TypeRules {
final TypeProvider provider;
/// Map of fields / properties / methods on Object.
final Map<String, DartType> objectMembers;
DownwardsInference inferrer;
TypeRules(TypeProvider provider)
: provider = provider,
objectMembers = getObjectMemberMap(provider) {
inferrer = new DownwardsInference(this);
}
/// Given a type t, if t is an interface type with a call method
/// defined, return the function type for the call method, otherwise
/// return null.
FunctionType getCallMethodType(DartType t) {
if (t is InterfaceType) {
return t.lookUpMethod("call", null)?.type;
}
return null;
}
/// Given an expression, return its type assuming it is
/// in the caller position of a call (that is, accounting
/// for the possibility of a call method). Returns null
/// if expression is not statically callable.
FunctionType getTypeAsCaller(Expression applicand) {
var t = getStaticType(applicand);
if (t is InterfaceType) {
return getCallMethodType(t);
}
if (t is FunctionType) return t;
return null;
}
/// Gets the expected return type of the given function [body], either from
/// a normal return/yield, or from a yield*.
DartType getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
FunctionType functionType;
var parent = body.parent;
if (parent is Declaration) {
functionType = elementType(parent.element);
} else {
assert(parent is FunctionExpression);
functionType = getStaticType(parent);
}
var type = functionType.returnType;
InterfaceType expectedType = null;
if (body.isAsynchronous) {
if (body.isGenerator) {
// Stream<T> -> T
expectedType = provider.streamType;
} else {
// Future<T> -> T
// TODO(vsm): Revisit with issue #228.
expectedType = provider.futureType;
}
} else {
if (body.isGenerator) {
// Iterable<T> -> T
expectedType = provider.iterableType;
} else {
// T -> T
return type;
}
}
if (yieldStar) {
if (type.isDynamic) {
// Ensure it's at least a Stream / Iterable.
return expectedType.substitute4([provider.dynamicType]);
} else {
// Analyzer will provide a separate error if expected type
// is not compatible with type.
return type;
}
}
if (type.isDynamic) {
return type;
} else if (type is InterfaceType && type.element == expectedType.element) {
return type.typeArguments[0];
} else {
// Malformed type - fallback on analyzer error.
return null;
}
}
DartType getStaticType(Expression expr) {
return expr.staticType ?? provider.dynamicType;
}
bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
if (t.isDynamic && dynamicIsBottom) return true;
// TODO(vsm): We need direct support for non-nullability in DartType.
// This should check on "true/nonnullable" Bottom
if (t.isBottom) return true;
return false;
}
bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
if (t.isDynamic && !dynamicIsBottom) return true;
if (t.isObject) return true;
return false;
}
bool _anyParameterType(FunctionType ft, bool predicate(DartType t)) {
return ft.normalParameterTypes.any(predicate) ||
ft.optionalParameterTypes.any(predicate) ||
ft.namedParameterTypes.values.any(predicate);
}
// TODO(leafp): Revisit this.
bool isGroundType(DartType t) {
if (t is TypeParameterType) return false;
if (_isTop(t)) return true;
if (t is FunctionType) {
if (!_isTop(t.returnType) ||
_anyParameterType(t, (pt) => !_isBottom(pt, dynamicIsBottom: true))) {
return false;
} else {
return true;
}
}
if (t is InterfaceType) {
var typeArguments = t.typeArguments;
for (var typeArgument in typeArguments) {
if (!_isTop(typeArgument)) return false;
}
return true;
}
// We should not see any other type aside from malformed code.
return false;
}
/// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC
/// checker to determine whether f1 would be a subtype of f2 if the return
/// type of f1 is set to match f2's return type.
// [fuzzyArrows] indicates whether or not the f1 and f2 should be
// treated as fuzzy arrow types (and hence dynamic parameters to f2 treated as
// bottom).
bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2,
{bool fuzzyArrows: true, bool ignoreReturn: false}) {
final r1s = f1.normalParameterTypes;
final o1s = f1.optionalParameterTypes;
final n1s = f1.namedParameterTypes;
final r2s = f2.normalParameterTypes;
final o2s = f2.optionalParameterTypes;
final n2s = f2.namedParameterTypes;
final ret1 = ignoreReturn ? f2.returnType : f1.returnType;
final ret2 = f2.returnType;
// A -> B <: C -> D if C <: A and
// either D is void or B <: D
if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false;
// Reject if one has named and the other has optional
if (n1s.length > 0 && o2s.length > 0) return false;
if (n2s.length > 0 && o1s.length > 0) return false;
// f2 has named parameters
if (n2s.length > 0) {
// Check that every named parameter in f2 has a match in f1
for (String k2 in n2s.keys) {
if (!n1s.containsKey(k2)) return false;
if (!isSubTypeOf(n2s[k2], n1s[k2],
dynamicIsBottom: fuzzyArrows)) return false;
}
}
// If we get here, we either have no named parameters,
// or else the named parameters match and we have no optional
// parameters
// If f1 has more required parameters, reject
if (r1s.length > r2s.length) return false;
// If f2 has more required + optional parameters, reject
if (r2s.length + o2s.length > r1s.length + o1s.length) return false;
// The parameter lists must look like the following at this point
// where rrr is a region of required, and ooo is a region of optionals.
// f1: rrr ooo ooo ooo
// f2: rrr rrr ooo
int rr = r1s.length; // required in both
int or = r2s.length - r1s.length; // optional in f1, required in f2
int oo = o2s.length; // optional in both
for (int i = 0; i < rr; ++i) {
if (!isSubTypeOf(r2s[i], r1s[i],
dynamicIsBottom: fuzzyArrows)) return false;
}
for (int i = 0, j = rr; i < or; ++i, ++j) {
if (!isSubTypeOf(r2s[j], o1s[i],
dynamicIsBottom: fuzzyArrows)) return false;
}
for (int i = or, j = 0; i < oo; ++i, ++j) {
if (!isSubTypeOf(o2s[j], o1s[i],
dynamicIsBottom: fuzzyArrows)) return false;
}
return true;
}
bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) {
if (i1 == i2) return true;
if (i1.element == i2.element) {
List<DartType> tArgs1 = i1.typeArguments;
List<DartType> tArgs2 = i2.typeArguments;
// TODO(leafp): Verify that this is always true
// Do raw types get filled in?
assert(tArgs1.length == tArgs2.length);
for (int i = 0; i < tArgs1.length; i++) {
DartType t1 = tArgs1[i];
DartType t2 = tArgs2[i];
if (!isSubTypeOf(t1, t2)) return false;
}
return true;
}
if (i2.isDartCoreFunction) {
if (i1.element.getMethod("call") != null) return true;
}
if (i1 == provider.objectType) return false;
if (_isInterfaceSubTypeOf(i1.superclass, i2)) return true;
for (final parent in i1.interfaces) {
if (_isInterfaceSubTypeOf(parent, i2)) return true;
}
for (final parent in i1.mixins) {
if (_isInterfaceSubTypeOf(parent, i2)) return true;
}
return false;
}
bool isSubTypeOf(DartType t1, DartType t2, {bool dynamicIsBottom: false}) {
if (t1 == t2) return true;
// Trivially true.
if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
_isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
return true;
}
// Trivially false.
if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
_isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
return false;
}
// The null type is a subtype of any nullable type, which is all Dart types.
// TODO(vsm): Note, t1.isBottom still allows for null confusingly.
// _isBottom(t1) does not necessarily imply t1.isBottom if there are
// nonnullable types in the system.
if (t1.isBottom) {
return true;
}
// S <: T where S is a type variable
// T is not dynamic or object (handled above)
// S != T (handled above)
// So only true if bound of S is S' and
// S' <: T
if (t1 is TypeParameterType) {
DartType bound = t1.element.bound;
if (bound == null) return false;
return isSubTypeOf(bound, t2);
}
if (t2 is TypeParameterType) {
return false;
}
if (t1.isVoid || t2.isVoid) {
return false;
}
if (t2.isDartCoreFunction) {
if (t1 is FunctionType) return true;
if (t1.element is ClassElement) {
if ((t1.element as ClassElement).getMethod("call") != null) return true;
}
}
// "Traditional" name-based subtype check.
if (t1 is InterfaceType && t2 is InterfaceType) {
return _isInterfaceSubTypeOf(t1, t2);
}
if (t1 is! FunctionType && t2 is! FunctionType) return false;
if (t1 is InterfaceType && t2 is FunctionType) {
var callType = getCallMethodType(t1);
if (callType == null) return false;
return isFunctionSubTypeOf(callType, t2);
}
if (t1 is FunctionType && t2 is InterfaceType) {
return false;
}
// Functions
// Note: it appears under the hood all Dart functions map to a class /
// hidden type that:
// (a) subtypes Object (an internal _FunctionImpl in the VM)
// (b) implements Function
// (c) provides standard Object members (hashCode, toString)
// (d) contains private members (corresponding to _FunctionImpl?)
// (e) provides a call method to handle the actual function invocation
//
// The standard Dart subtyping rules are structural in nature. I.e.,
// bivariant on arguments and return type.
//
// The below tries for a more traditional subtyping rule:
// - covariant on return type
// - contravariant on parameters
// - 'sensible' (?) rules on optional and/or named params
// but doesn't properly mix with class subtyping. I suspect Java 8 lambdas
// essentially map to dynamic (and rely on invokedynamic) due to similar
// issues.
return isFunctionSubTypeOf(t1 as FunctionType, t2 as FunctionType);
}
bool isAssignable(DartType t1, DartType t2) {
return isSubTypeOf(t1, t2);
}
// Produce a coercion which coerces something of type fromT
// to something of type toT.
// Returns the error coercion if the types cannot be coerced
// according to our current criteria.
Coercion _coerceTo(DartType fromT, DartType toT) {
// We can use anything as void
if (toT.isVoid) return Coercion.identity(toT);
// fromT <: toT, no coercion needed
if (isSubTypeOf(fromT, toT)) return Coercion.identity(toT);
// TODO(vsm): We can get rid of the second clause if we disallow
// all sideways casts - see TODO below.
// -------
// Note: a function type is never assignable to a class per the Dart
// spec - even if it has a compatible call method. We disallow as
// well for consistency.
if ((fromT is FunctionType && getCallMethodType(toT) != null) ||
(toT is FunctionType && getCallMethodType(fromT) != null)) {
return Coercion.error();
}
// Downcast if toT <: fromT
if (isSubTypeOf(toT, fromT)) return Coercion.cast(fromT, toT);
// TODO(vsm): Once we have generic methods, we should delete this
// workaround. These sideways casts are always ones we warn about
// - i.e., we think they are likely to fail at runtime.
// -------
// Downcast if toT <===> fromT
// The intention here is to allow casts that are sideways in the restricted
// type system, but allowed in the regular dart type system, since these
// are likely to succeed. The canonical example is List<dynamic> and
// Iterable<T> for some concrete T (e.g. Object). These are unrelated
// in the restricted system, but List<dynamic> <: Iterable<T> in dart.
if (fromT.isAssignableTo(toT)) {
return Coercion.cast(fromT, toT);
}
return Coercion.error();
}
StaticInfo checkAssignment(Expression expr, DartType toT) {
final fromT = getStaticType(expr);
final Coercion c = _coerceTo(fromT, toT);
if (c is Identity) return null;
if (c is CoercionError) return new StaticTypeError(this, expr, toT);
var reason = null;
var errors = <String>[];
var ok = inferrer.inferExpression(expr, toT, errors);
if (ok) return InferredType.create(this, expr, toT);
reason = (errors.isNotEmpty) ? errors.first : null;
if (c is Cast) return DownCast.create(this, expr, c, reason: reason);
assert(false);
return null;
}
DartType elementType(Element e) {
if (e == null) {
// Malformed code - just return dynamic.
return provider.dynamicType;
}
return (e as dynamic).type;
}
bool _isLibraryPrefix(Expression node) =>
node is SimpleIdentifier && node.staticElement is PrefixElement;
/// Returns `true` if the target expression is dynamic.
bool isDynamicTarget(Expression node) {
if (node == null) return false;
if (_isLibraryPrefix(node)) return false;
// Null type happens when we have unknown identifiers, like a dart: import
// that doesn't resolve.
var type = node.staticType;
return type == null || type.isDynamic;
}
/// Returns `true` if the expression is a dynamic function call or method
/// invocation.
bool isDynamicCall(Expression call) {
var ft = getTypeAsCaller(call);
// TODO(leafp): This will currently return true if t is Function
// This is probably the most correct thing to do for now, since
// this code is also used by the back end. Maybe revisit at some
// point?
if (ft == null) return true;
// Dynamic as the parameter type is treated as bottom. A function with
// a dynamic parameter type requires a dynamic call in general.
// However, as an optimization, if we have an original definition, we know
// dynamic is reified as Object - in this case a regular call is fine.
if (call is SimpleIdentifier) {
var element = call.staticElement;
if (element is FunctionElement || element is MethodElement) {
// An original declaration.
return false;
}
}
return _anyParameterType(ft, (pt) => pt.isDynamic);
}
}
class DownwardsInference {
final TypeRules rules;
DownwardsInference(this.rules);
/// Called for each list literal which gets inferred
void annotateListLiteral(ListLiteral e, List<DartType> targs) {}
/// Called for each map literal which gets inferred
void annotateMapLiteral(MapLiteral e, List<DartType> targs) {}
/// Called for each new/const which gets inferred
void annotateInstanceCreationExpression(
InstanceCreationExpression e, List<DartType> targs) {}
/// Called for cast from dynamic required for inference to succeed
void annotateCastFromDynamic(Expression e, DartType t) {}
/// Called for each function expression return type inferred
void annotateFunctionExpression(FunctionExpression e, DartType returnType) {}
/// Downward inference
bool inferExpression(Expression e, DartType t, List<String> errors) {
// Don't cast top level expressions, only sub-expressions
return _inferExpression(e, t, errors, cast: false);
}
/// Downward inference
bool _inferExpression(Expression e, DartType t, List<String> errors,
{cast: true}) {
if (rules.isSubTypeOf(rules.getStaticType(e), t)) return true;
if (cast && rules.getStaticType(e).isDynamic) {
annotateCastFromDynamic(e, t);
return true;
}
errors.add("$e cannot be typed as $t");
return false;
}
}

View file

@ -12639,6 +12639,81 @@ main() {
expect(declaration.initializer.propagatedType, isNull);
}
void test_genericFunction_typedef() {
String code = r'''
typedef T F<T>(T x);
F f0;
class C {
static F f1;
F f2;
void g(F f3) {
F f4;
f0(3);
f1(3);
f2(3);
f3(3);
f4(3);
}
}
class D<S> {
static F f1;
F f2;
void g(F f3) {
F f4;
f0(3);
f1(3);
f2(3);
f3(3);
f4(3);
}
}
''';
_resolveTestUnit(code);
{
List<Statement> statements =
AstFinder.getStatementsInMethod(testUnit, "C", "g");
ExpressionStatement exps0 = statements[1];
ExpressionStatement exps1 = statements[2];
ExpressionStatement exps2 = statements[3];
ExpressionStatement exps3 = statements[4];
ExpressionStatement exps4 = statements[5];
Expression exp0 = exps0.expression;
Expression exp1 = exps1.expression;
Expression exp2 = exps2.expression;
Expression exp3 = exps3.expression;
Expression exp4 = exps4.expression;
expect(exp0.staticType, typeProvider.dynamicType);
expect(exp1.staticType, typeProvider.dynamicType);
expect(exp2.staticType, typeProvider.dynamicType);
expect(exp3.staticType, typeProvider.dynamicType);
expect(exp4.staticType, typeProvider.dynamicType);
}
{
List<Statement> statements =
AstFinder.getStatementsInMethod(testUnit, "D", "g");
ExpressionStatement exps0 = statements[1];
ExpressionStatement exps1 = statements[2];
ExpressionStatement exps2 = statements[3];
ExpressionStatement exps3 = statements[4];
ExpressionStatement exps4 = statements[5];
Expression exp0 = exps0.expression;
Expression exp1 = exps1.expression;
Expression exp2 = exps2.expression;
Expression exp3 = exps3.expression;
Expression exp4 = exps4.expression;
expect(exp0.staticType, typeProvider.dynamicType);
expect(exp1.staticType, typeProvider.dynamicType);
expect(exp2.staticType, typeProvider.dynamicType);
expect(exp3.staticType, typeProvider.dynamicType);
expect(exp4.staticType, typeProvider.dynamicType);
}
}
void test_genericFunction() {
_resolveTestUnit(r'/*=T*/ f/*<T>*/(/*=T*/ x) => null;');
SimpleIdentifier f = _findIdentifier('f');
@ -12677,6 +12752,26 @@ main() {
expect('${ft.typeArguments}/${ft.typeParameters}', '[String, int]/[E, T]');
}
void test_genericMethod_explicitTypeParams() {
_resolveTestUnit(r'''
class C<E> {
List/*<T>*/ f/*<T>*/(E e) => null;
}
main() {
C<String> cOfString;
var x = cOfString.f/*<int>*/('hi');
}
''');
SimpleIdentifier f = _findIdentifier('f/*<int>*/');
FunctionType ft = f.staticType;
expect(ft.toString(), '(String) → List<int>');
expect('${ft.typeArguments}/${ft.typeParameters}', '[String, int]/[E, T]');
SimpleIdentifier x = _findIdentifier('x');
expect(x.staticType,
typeProvider.listType.substitute4([typeProvider.intType]));
}
void test_genericMethod_functionTypedParameter() {
_resolveTestUnit(r'''
class C<E> {
@ -12701,6 +12796,28 @@ main() {
expect(ft.toString(), '((String) → int) → List<int>');
}
void test_genericMethod_implicitDynamic() {
// Regression test for:
// https://github.com/dart-lang/sdk/issues/25100#issuecomment-162047588
// These should not cause any hints or warnings.
_resolveTestUnit(r'''
class List<E> {
/*=T*/ map/*<T>*/(/*=T*/ f(E e)) => null;
}
void foo() {
List list = null;
list.map((e) => e);
list.map((e) => 3);
}''');
SimpleIdentifier map1 = _findIdentifier('map((e) => e);');
expect(map1.staticType.toString(), '((dynamic) → dynamic) → dynamic');
expect(map1.propagatedType, isNull);
SimpleIdentifier map2 = _findIdentifier('map((e) => 3);');
expect(map2.staticType.toString(), '((dynamic) → int) → int');
expect(map2.propagatedType, isNull);
}
void test_genericMethod_nestedCapture() {
_resolveTestUnit(r'''
class C<T> {

View file

@ -247,7 +247,7 @@ void main() {
class B extends A {
B() : super(/*severe:STATIC_TYPE_ERROR*/"hello");
B.c2(int x, String y) : super.c2(/*severe:STATIC_TYPE_ERROR*/y,
B.c2(int x, String y) : super.c2(/*severe:STATIC_TYPE_ERROR*/y,
/*severe:STATIC_TYPE_ERROR*/x);
B.c3(num x, Object y) : super.c3(x, /*info:DOWN_CAST_IMPLICIT*/y);
@ -1351,6 +1351,51 @@ void main() {
'''
});
testChecker('generic class method override', {
'/main.dart': '''
class A {}
class B extends A {}
class Base<T extends B> {
T foo() => null;
}
class Derived<S extends A> extends Base<B> {
/*severe:INVALID_METHOD_OVERRIDE*/S foo() => null;
}
class Derived2<S extends B> extends Base<B> {
S foo() => null;
}
'''
});
testChecker('generic method override', {
'/main.dart': '''
class Future<T> {
/*=S*/ then/*<S>*/(/*=S*/ onValue(T t)) => null;
}
// These work because they're exactly equal FunctionTypes
class DerivedFuture<T> extends Future<T> {
/*=S*/ then/*<S>*/(/*=S*/ onValue(T t)) => null;
}
class DerivedFuture2<A> extends Future<A> {
/*=B*/ then/*<B>*/(/*=B*/ onValue(A a)) => null;
}
// These don't work but should.
class DerivedFuture3<T> extends Future<T> {
/*=/*severe:INVALID_METHOD_OVERRIDE should be pass*/S*/ then/*<S>*/(Object onValue(T t)) => null;
}
class DerivedFuture4<A> extends Future<A> {
/*=/*severe:INVALID_METHOD_OVERRIDE should be pass*/B*/ then/*<B>*/(Object onValue(A a)) => null;
}
'''
});
testChecker('unary operators', {
'/main.dart': '''
class A {

View file

@ -15,8 +15,8 @@ import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/type_system.dart';
import 'package:analyzer/src/task/strong/checker.dart';
import 'package:analyzer/src/task/strong/rules.dart';
import 'package:logging/logging.dart';
import 'package:source_span/source_span.dart';
import 'package:unittest/unittest.dart';
@ -240,8 +240,8 @@ void testChecker(String name, Map<String, String> testFiles) {
context.resolveCompilationUnit2(mainSource, mainSource);
var collector = new _ErrorCollector();
var checker = new CodeChecker(
new TypeRules(context.typeProvider), collector,
var checker = new CodeChecker(context.typeProvider,
new StrongTypeSystemImpl(), collector,
hints: true);
// Extract expectations from the comments in the test files, and
@ -426,13 +426,18 @@ class _ExpectedErrorVisitor extends UnifyingAstVisitor {
var commentText = '$comment';
var start = commentText.lastIndexOf('/*');
var end = commentText.lastIndexOf('*/');
if (start != -1 && end != -1) {
if (start != -1 &&
end != -1 &&
!commentText.startsWith('/*<', start) &&
!commentText.startsWith('/*=', start)) {
expect(start, lessThan(end));
var errors = commentText.substring(start + 2, end).split(',');
var expectations =
errors.map(_ErrorExpectation.parse).where((x) => x != null);
for (var e in expectations) _expectError(node, e);
for (var e in expectations) {
_expectError(node, e);
}
}
}
}

View file

@ -2155,6 +2155,7 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
InterfaceType type,
ast.NodeList arguments,
CallStructure callStructure, _) {
for (ast.Node argument in arguments) visit(argument);
return buildAbstractClassInstantiationError(element.enclosingClass);
}

View file

@ -146,6 +146,7 @@ enum MessageKind {
CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS,
CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_CONSTRUCTOR,
CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_FIELD,
CONST_LOOP_VARIABLE,
CONST_MAP_KEY_OVERRIDES_EQUALS,
CONST_WITHOUT_INITIALIZER,
CONSTRUCTOR_CALL_EXPECTED,
@ -2057,6 +2058,16 @@ void main() {
examples: const [
"class C { static final field; } main() => C.field;"]),
MessageKind.CONST_LOOP_VARIABLE:
const MessageTemplate(MessageKind.CONST_LOOP_VARIABLE,
"A loop variable cannot be constant.",
howToFix: "Try remove the 'const' modifier or "
"replacing it with a 'final' modifier.",
examples: const ["""
void main() {
for (const c in []) {}
}"""]),
MessageKind.MEMBER_USES_CLASS_NAME:
const MessageTemplate(MessageKind.MEMBER_USES_CLASS_NAME,
"Member variable can't have the same name as the class it is "

View file

@ -658,6 +658,18 @@ class Parser {
return identical(value, token.stringValue);
}
/**
* Returns true if the stringValue of the [token] is either [value1],
* [value2], or [value3].
*/
bool isOneOf3(Token token, String value1, String value2, String value3) {
String stringValue = token.stringValue;
return
value1 == stringValue ||
value2 == stringValue ||
value3 == stringValue;
}
/**
* Returns true if the stringValue of the [token] is either [value1],
* [value2], [value3], or [value4].
@ -665,10 +677,10 @@ class Parser {
bool isOneOf4(Token token,
String value1, String value2, String value3, String value4) {
String stringValue = token.stringValue;
return identical(value1, stringValue) ||
identical(value2, stringValue) ||
identical(value3, stringValue) ||
identical(value4, stringValue);
return value1 == stringValue ||
value2 == stringValue ||
value3 == stringValue ||
value4 == stringValue;
}
bool notEofOrValue(String value, Token token) {
@ -2427,7 +2439,7 @@ class Parser {
if (identical(value, ';')) {
listener.handleNoExpression(token);
return token;
} else if ((identical(value, 'var')) || (identical(value, 'final'))) {
} else if (isOneOf3(token, 'var', 'final', 'const')) {
return parseVariablesDeclarationNoSemicolon(token);
}
Token identifier = peekIdentifierAfterType(token);

View file

@ -126,7 +126,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
/// When visiting the type declaration of the variable in a [ForIn] loop,
/// the initializer of the variable is implicit and we should not emit an
/// error when verifying that all final variables are initialized.
bool allowFinalWithoutInitializer = false;
bool inLoopVariable = false;
/// The nodes for which variable access and mutation must be registered in
/// order to determine when the static type of variables types is promoted.
@ -4387,10 +4387,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
Scope blockScope) {
LibraryElement library = enclosingElement.library;
bool oldAllowFinalWithoutInitializer = allowFinalWithoutInitializer;
allowFinalWithoutInitializer = true;
bool oldAllowFinalWithoutInitializer = inLoopVariable;
inLoopVariable = true;
visitIn(declaration, blockScope);
allowFinalWithoutInitializer = oldAllowFinalWithoutInitializer;
inLoopVariable = oldAllowFinalWithoutInitializer;
Send send = declaration.asSend();
VariableDefinitions variableDefinitions =

View file

@ -60,11 +60,16 @@ class VariableDefinitionsVisitor extends CommonResolverVisitor<Identifier> {
registry.registerTypeUse(
new TypeUse.instantiation(compiler.coreTypes.nullType));
if (definitions.modifiers.isConst) {
reporter.reportErrorMessage(
node, MessageKind.CONST_WITHOUT_INITIALIZER);
if (resolver.inLoopVariable) {
reporter.reportErrorMessage(
node, MessageKind.CONST_LOOP_VARIABLE);
} else {
reporter.reportErrorMessage(
node, MessageKind.CONST_WITHOUT_INITIALIZER);
}
}
if (definitions.modifiers.isFinal &&
!resolver.allowFinalWithoutInitializer) {
!resolver.inLoopVariable) {
reporter.reportErrorMessage(
node, MessageKind.FINAL_WITHOUT_INITIALIZER);
}

View file

@ -19,6 +19,8 @@ def makeArchive(tar_path, client_root, files):
for input_file_name in files:
# Chop off client_root.
archive_file_name = input_file_name[ len(client_root) : ]
# Replace back slash with forward slash. So we do not have Windows paths.
archive_file_name = archive_file_name.replace("\\", "/")
# Open input file and add it to the archive.
with open(input_file_name, 'rb') as input_file:
tarInfo = tarfile.TarInfo(name=archive_file_name)

View file

@ -3967,7 +3967,7 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
if (library_.is_dart_scheme() && library_.IsPrivate(*method->name)) {
func.set_is_reflectable(false);
}
if (method->metadata_pos > 0) {
if (FLAG_enable_mirrors && (method->metadata_pos > 0)) {
library_.AddFunctionMetadata(func, method->metadata_pos);
}
if (method->has_native) {
@ -4067,7 +4067,7 @@ void Parser::ParseFieldDefinition(ClassDesc* members, MemberDesc* field) {
class_field.set_has_initializer(has_initializer);
members->AddField(class_field);
field->field_ = &class_field;
if (field->metadata_pos >= 0) {
if (FLAG_enable_mirrors && (field->metadata_pos >= 0)) {
library_.AddFieldMetadata(class_field, field->metadata_pos);
}
@ -4464,7 +4464,7 @@ void Parser::ParseEnumDeclaration(const GrowableObjectArray& pending_classes,
library_.AddClass(cls);
cls.set_is_synthesized_class();
cls.set_is_enum_class();
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddClassMetadata(cls, tl_owner, metadata_pos);
}
cls.set_super_type(Type::Handle(Z, Type::ObjectType()));
@ -4587,7 +4587,7 @@ void Parser::ParseClassDeclaration(const GrowableObjectArray& pending_classes,
if (is_abstract) {
cls.set_is_abstract();
}
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddClassMetadata(cls, tl_owner, metadata_pos);
}
@ -5020,7 +5020,7 @@ void Parser::ParseMixinAppAlias(
}
ExpectSemicolon();
pending_classes.Add(mixin_application, Heap::kOld);
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddClassMetadata(mixin_application, tl_owner, metadata_pos);
}
}
@ -5186,7 +5186,7 @@ void Parser::ParseTypedef(const GrowableObjectArray& pending_classes,
ASSERT(!function_type_alias.IsCanonicalSignatureClass());
ASSERT(!function_type_alias.is_finalized());
pending_classes.Add(function_type_alias, Heap::kOld);
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddClassMetadata(function_type_alias,
tl_owner,
metadata_pos);
@ -5309,7 +5309,7 @@ void Parser::ParseTypeParameters(const Class& cls) {
declaration_pos);
type_parameters_array.Add(
&AbstractType::ZoneHandle(Z, type_parameter.raw()));
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddTypeParameterMetadata(type_parameter, metadata_pos);
}
index++;
@ -5468,7 +5468,7 @@ void Parser::ParseTopLevelVariable(TopLevel* top_level,
field.SetStaticValue(Object::null_instance(), true);
top_level->AddField(field);
library_.AddObject(field, var_name);
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddFieldMetadata(field, metadata_pos);
}
if (CurrentToken() == Token::kASSIGN) {
@ -5659,7 +5659,7 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
toplevel_cls.RemoveFunction(replaced_func);
library_.ReplaceObject(func, func_name);
}
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddFunctionMetadata(func, metadata_pos);
}
}
@ -5824,7 +5824,7 @@ void Parser::ParseTopLevelAccessor(TopLevel* top_level,
toplevel_cls.RemoveFunction(replaced_func);
library_.ReplaceObject(func, accessor_name);
}
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddFunctionMetadata(func, metadata_pos);
}
}
@ -5986,7 +5986,7 @@ void Parser::ParseLibraryImportExport(const Object& tl_owner,
Namespace& ns = Namespace::Handle(Z,
Namespace::New(library, show_names, hide_names));
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
ns.AddMetadata(tl_owner, metadata_pos);
}
@ -6072,7 +6072,7 @@ void Parser::ParseLibraryDefinition(const Object& tl_owner) {
ReportError("patch cannot override library name");
}
ParseLibraryName();
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddLibraryMetadata(tl_owner, metadata_pos);
}
rewind_pos = TokenPos();
@ -7661,7 +7661,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
innermost_function(),
function_pos);
function.set_result_type(result_type);
if (metadata_pos >= 0) {
if (FLAG_enable_mirrors && (metadata_pos >= 0)) {
library_.AddFunctionMetadata(function, metadata_pos);
}
}

View file

@ -580,7 +580,13 @@ class DateTime implements Comparable {
* Returns a new [DateTime] instance with [duration] subtracted from [this].
*
* DateTime today = new DateTime.now();
* DateTime sixtyDaysAgo = today.subtract(new Duration(days: 60));
* DateTime sixtyDaysAgo = today.subtract(new Duration(days: 30));
*
* Notice that duration being subtracted is actually 30 * 24 * 60 * 60 seconds
* and if that crosses a daylight saving time change, the resulting `DateTime`
* won't have the same time of day as `today`, and may not actually hit the
* calendar date 30 days earlier. Be careful when working with dates in local
* time.
*/
DateTime subtract(Duration duration) {
int ms = millisecondsSinceEpoch;
@ -591,13 +597,29 @@ class DateTime implements Comparable {
/**
* Returns a [Duration] with the difference between [this] and [other].
*
* DateTime berlinWallFell = new DateTime(1989, DateTime.NOVEMBER, 9);
* DateTime dDay = new DateTime(1944, DateTime.JUNE, 6);
* DateTime berlinWallFell = new DateTime.utc(1989, DateTime.NOVEMBER, 9);
* DateTime dDay = new DateTime.utc(1944, DateTime.JUNE, 6);
*
* Duration difference = berlinWallFell.difference(dDay);
* assert(difference.inDays == 16592);
*
* The difference is measured in seconds and fractions of seconds.
* The difference above counts the number of fractional seconds between
* midnight at the beginning of those dates.
* If the dates above had been in local time, not UTC, then the difference
* between two midnights may not be a multiple of 24 hours due to daylight
* saving differences.
*
* For example, in Australia, similar code using local time instead of UTC:
*
* DateTime berlinWallFell = new DateTime(1989, DateTime.NOVEMBER, 9);
* DateTime dDay = new DateTime(1944, DateTime.JUNE, 6);
* Duration difference = berlinWallFell.difference(dDay);
* assert(difference.inDays == 16592);
*
* will fail because the difference is actually 16591 days and 23 hours, and
* [Duration.inDays] only returns the number of whole days.
*/
Duration difference(DateTime other) {
int ms = millisecondsSinceEpoch;
int otherMs = other.millisecondsSinceEpoch;

View file

@ -170,7 +170,7 @@ abstract class int extends num {
* non-negative number (i.e. unsigned representation). The returned value has
* zeros in all bit positions higher than [width].
*
* (-1).toUnsigned(5) == 32 // 11111111 -> 00011111
* (-1).toUnsigned(5) == 31 // 11111111 -> 00011111
*
* This operation can be used to simulate arithmetic from low level languages.
* For example, to increment an 8 bit quantity:

View file

@ -192,17 +192,26 @@ abstract class List<E> implements Iterable<E>, EfficientLength {
*
* The [compare] function must act as a [Comparator].
*
* List<String> numbers = ['one', 'two', 'three', 'four'];
* List<String> numbers = ['two', 'three', 'four'];
* // Sort from shortest to longest.
* numbers.sort((x, y) => x.length.compareTo(y.length));
* numbers.join(', '); // 'one, two, four, three'
* numbers.sort((a, b) => a.length.compareTo(b.length));
* print(numbers); // [two, four, three]
*
* The default List implementations use [Comparable.compare] if
* [compare] is omitted.
*
* List<int> nums = [13, 2, -11];
* nums.sort();
* nums.join(', '); // '-11, 2, 13'
* print(nums); // [-11, 2, 13]
*
* A [Comparator] may compare objects as equal (return zero), even if they
* are distinct objects.
* The sort function is not guaranteed to be stable, so distinct objects
* that compare as equal may occur in any order in the result:
*
* List<String> numbers = ['one', 'two', 'three', 'four'];
* numbers.sort((a, b) => a.length.compareTo(b.length));
* print(numbers); // [one, two, four, three] OR [two, one, four, three]
*/
void sort([int compare(E a, E b)]);

View file

@ -631,6 +631,7 @@ LayoutTests/fast/backgrounds/repeat/parsing-background-repeat_t01: RuntimeError
LayoutTests/fast/backgrounds/background-shorthand-with-backgroundSize-style_t01: RuntimeError # co19 issue 14
LayoutTests/fast/backgrounds/multiple-backgrounds-computed-style_t01: RuntimeError # co19 issue 14
LayoutTests/fast/borders/border-radius-child_t01: RuntimeError # Please triage this failure
LayoutTests/fast/borders/border-width-percent_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/canvas/2d.fillText.gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/2d.text.draw.fill.maxWidth.gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/2d.text.draw.fill.maxWidth.negative_t01: RuntimeError # Please triage this failure
@ -750,6 +751,7 @@ LayoutTests/fast/css/aspect-ratio-parsing-tests_t01: Pass, RuntimeError # Please
LayoutTests/fast/css/auto-min-size_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/background-position-serialize_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/background-serialize_t01: RuntimeError # https://github.com/dart-lang/co19/issues/14
LayoutTests/fast/css/css-selector-text_t01: RuntimeError # co19 Issue 15
LayoutTests/fast/css/css-escaped-identifier_t01: RuntimeError # co19 issue 14
LayoutTests/fast/css/checked-pseudo-selector_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/collapsed-whitespace-reattach-in-style-recalc_t01: RuntimeError # Please triage this failure
@ -842,6 +844,7 @@ LayoutTests/fast/css3-text/css3-text-indent/getComputedStyle/getComputedStyle-te
LayoutTests/fast/css3-text/css3-text-indent/getComputedStyle/getComputedStyle-text-indent_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css3-text/css3-text-justify/getComputedStyle/getComputedStyle-text-justify_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/52776_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/click-method-on-html-element_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/dom/DOMException/XPathException_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/DOMImplementation/createDocument-namespace-err_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/Document/CaretRangeFromPoint/basic_t01: RuntimeError # Please triage this failure
@ -881,6 +884,7 @@ LayoutTests/fast/dom/HTMLLinkElement/resolve-url-on-insertion_t01: RuntimeError
LayoutTests/fast/dom/HTMLObjectElement/beforeload-set-text-crash_t01: Skip # Times out. Please triage this failure
LayoutTests/fast/dom/HTMLOptionElement/collection-setter-getter_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLOutputElement/dom-settable-token-list_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLObjectElement/form/test1_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/dom/HTMLScriptElement/async-false-inside-async-false-load_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLScriptElement/async-inline-script_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLScriptElement/async-onbeforeload_t01: RuntimeError # Please triage this failure
@ -934,6 +938,7 @@ LayoutTests/fast/dom/focus-contenteditable_t01: Pass, RuntimeError # Please tria
LayoutTests/fast/dom/fragment-activation-focuses-target_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/dom/getElementsByClassName/011_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/dom/getElementsByClassName/dumpNodeList_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/getelementsbyname-invalidation-cache_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/dom/horizontal-scrollbar-in-rtl-doesnt-fire-onscroll_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/horizontal-scrollbar-in-rtl_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/horizontal-scrollbar-when-dir-change_t01: RuntimeError # Please triage this failure
@ -941,6 +946,7 @@ LayoutTests/fast/dom/location-hash_t01: Pass, RuntimeError # Please triage this
LayoutTests/fast/dom/navigatorcontentutils/is-protocol-handler-registered_t01: Skip # API not supported.
LayoutTests/fast/dom/navigatorcontentutils/register-protocol-handler_t01: Skip # API not supported.
LayoutTests/fast/dom/navigatorcontentutils/unregister-protocol-handler_t01: Skip # API not supported.
LayoutTests/fast/dom/object-plugin-hides-properties_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/dom/option-properties_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/partial-layout-overlay-scrollbars_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/set-innerHTML_t01: RuntimeError # Please triage this failure
@ -954,6 +960,7 @@ LayoutTests/fast/dom/shadow/pseudoclass-update-enabled-optgroup_t01: RuntimeErro
LayoutTests/fast/dom/shadow/pseudoclass-update-enabled-option_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/shadow/shadow-content-crash_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/shadow/shadow-disable_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/dom/shadow/shadow-element-inactive_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/dom/shadow/shadow-removechild-and-blur-event_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/dom/shadow/shadow-root-js-api_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/shadow/shadowhost-keyframes_t01: Pass, RuntimeError # Please triage this failure
@ -963,6 +970,7 @@ LayoutTests/fast/dynamic/crash-generated-image_t01: RuntimeError # Please triage
LayoutTests/fast/dynamic/crash-generated-quote_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dynamic/crash-generated-text_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dynamic/insertAdjacentElement_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dynamic/insertAdjacentHTML-allowed-parents_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/dynamic/recursive-layout_t01: RuntimeError # Please triage this failure
LayoutTests/fast/encoding/css-charset-dom_t01: RuntimeError # Please triage this failure
LayoutTests/fast/events/add-event-without-document_t01: RuntimeError # Please triage this failure
@ -1010,13 +1018,16 @@ LayoutTests/fast/forms/autofocus-opera-007_t01: Skip # Times out. Please triage
LayoutTests/fast/forms/autofocus-readonly-attribute_t01: Skip # Times out. Please triage this failure
LayoutTests/fast/forms/button-baseline-and-collapsing_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/button/button-disabled-blur_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/checkValidity-001_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/forms/clone-input-with-dirty-value_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/date-multiple-fields/date-multiple-fields-onblur-setvalue-onfocusremoved_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/date/input-valueasdate-date_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/datetimelocal/input-valueasdate-datetimelocal_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/fieldset/fieldset-elements_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/forms/file/file-input-capture_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/focus-style-pending_t01: Skip # Times out. Please triage this failure
LayoutTests/fast/forms/focus_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/form-attribute_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/forms/input-appearance-elementFromPoint_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/input-hit-test-border_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/input-inputmode_t01: RuntimeError # Please triage this failure
@ -1036,11 +1047,14 @@ LayoutTests/fast/forms/select-clientheight-large-size_t01: RuntimeError # Please
LayoutTests/fast/forms/select-clientheight-with-multiple-attr_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/select-list-box-mouse-focus_t01: Pass, RuntimeError # Please triage this failure
LayoutTests/fast/forms/select-max-length_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/setCustomValidity-existence_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/forms/setrangetext_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/submit-form-attributes_t01: RuntimeError # co19 issue 14
LayoutTests/fast/forms/textarea-paste-newline_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/textfield-focus-out_t01: Skip # Times out. Please triage this failure
LayoutTests/fast/forms/validationMessage_t01: RuntimeError # Please triage this failure
LayoutTests/fast/forms/validity-property_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/forms/willvalidate_t01: Pass, RuntimeError # Issue 25155
LayoutTests/fast/html/hidden-attr_t01: RuntimeError # Please triage this failure
LayoutTests/fast/html/imports/import-element-removed-flag_t01: RuntimeError # Please triage this failure
LayoutTests/fast/html/imports/import-events_t01: RuntimeError # Please triage this failure
@ -1063,6 +1077,7 @@ LayoutTests/fast/loader/scroll-position-restored-on-back_t01: RuntimeError # Ple
LayoutTests/fast/loader/scroll-position-restored-on-reload-at-load-event_t01: RuntimeError # Please triage this failure
LayoutTests/fast/loader/stateobjects/replacestate-in-onunload_t01: RuntimeError # Please triage this failure
LayoutTests/fast/innerHTML/innerHTML-uri-resolution_t01: RuntimeError # co19 issue 14
LayoutTests/fast/masking/parsing-clip-path-iri_t01: RuntimeError # co19 issue 14
LayoutTests/fast/masking/parsing-clip-path-shape_t01: RuntimeError # Please triage this failure
LayoutTests/fast/masking/parsing-mask-source-type_t01: RuntimeError # Please triage this failure
LayoutTests/fast/masking/parsing-mask_t01: RuntimeError # Please triage this failure
@ -1104,6 +1119,7 @@ LayoutTests/fast/replaced/table-percent-height_t01: RuntimeError # Please triage
LayoutTests/fast/replaced/table-percent-width_t01: RuntimeError # Please triage this failure
LayoutTests/fast/ruby/ruby-line-height_t01: RuntimeError # Please triage this failure
LayoutTests/fast/shapes/parsing/parsing-shape-lengths_t01: RuntimeError # Please triage this failure
LayoutTests/fast/shapes/parsing/parsing-shape-outside_t01: RuntimeError # co19 issue 14
LayoutTests/fast/shapes/shape-outside-floats/shape-outside-big-box-border-radius_t01: RuntimeError # Please triage this failure
LayoutTests/fast/shapes/shape-outside-floats/shape-outside-floats-diamond-margin-polygon_t01: RuntimeError # Please triage this failure
LayoutTests/fast/shapes/shape-outside-floats/shape-outside-floats-ellipse-margin-left_t01: RuntimeError # Please triage this failure
@ -1236,6 +1252,7 @@ LibTest/html/Element/getAttributeNS_A01_t01: RuntimeError # Issue 16395
LibTest/html/Element/getNamespacedAttributes_A01_t01: RuntimeError # Issue 16395
LibTest/html/Element/isContentEditable_A01_t01: RuntimeError # Please triage this failure
LibTest/html/Element/isContentEditable_A02_t01: RuntimeError # Please triage this failure
LibTest/html/Element/isTagSupported_A01_t01: Pass, RuntimeError # Issue 25155
LibTest/html/Element/marginEdge_A01_t01: RuntimeError # Issue 16574
LibTest/html/Element/mouseWheelEvent_A01_t01: Skip # Times out. Please triage this failure
LibTest/html/Element/onMouseWheel_A01_t01: Skip # Times out. Please triage this failure
@ -1243,6 +1260,7 @@ LibTest/html/Element/onTransitionEnd_A01_t01: RuntimeError # Please triage this
LibTest/html/Element/paddingEdge_A01_t01: RuntimeError # Issue 16574
LibTest/html/Element/querySelectorAll_A01_t02: RuntimeError # Please triage this failure
LibTest/html/Element/replaceWith_A01_t02: RuntimeError # Please triage this failure
LibTest/html/Element/tagName_A01_t01: Pass, RuntimeError # Issue 25155
LibTest/html/Element/transitionEndEvent_A01_t01: RuntimeError # Please triage this failure
LibTest/html/HttpRequest/getAllResponseHeaders_A01_t01: RuntimeError # Please triage this failure
LibTest/html/HttpRequest/getResponseHeader_A01_t01: RuntimeError # Please triage this failure
@ -1313,6 +1331,12 @@ WebPlatformTest/Utils/test/asyncTestTimeout_t01: Skip # Times out. Please triage
WebPlatformTest/custom-elements/concepts/type_A03_t01: RuntimeError # Please triage this failure
WebPlatformTest/custom-elements/concepts/type_A05_t01: RuntimeError # Please triage this failure
WebPlatformTest/custom-elements/concepts/type_A06_t01: RuntimeError # Please triage this failure
WebPlatformTest/custom-elements/instantiating/createElement_A02_t01: Pass, RuntimeError # Issue 25155
WebPlatformTest/custom-elements/instantiating/createElement_A03_t01: Pass, RuntimeError # Issue 25155
WebPlatformTest/custom-elements/instantiating/createElementNS_A02_t01: Pass, RuntimeError # Issue 25155
WebPlatformTest/custom-elements/instantiating/createElementNS_A03_t01: Pass, RuntimeError # Issue 25155
WebPlatformTest/custom-elements/instantiating/isAttribute_A01_t01: Pass, RuntimeError # Issue 25155
WebPlatformTest/custom-elements/instantiating/isAttribute_A01_t02: Pass, RuntimeError # Issue 25155
WebPlatformTest/dom/EventTarget/dispatchEvent_A02_t01: RuntimeError # Please triage this failure
WebPlatformTest/dom/EventTarget/dispatchEvent_A03_t01: Skip # Times out. Please triage this failure
WebPlatformTest/dom/events/type_A01_t01: RuntimeError # Please triage this failure
@ -1360,7 +1384,6 @@ WebPlatformTest/html/semantics/embedded-content/media-elements/interfaces/HTMLEl
WebPlatformTest/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/src_t01: RuntimeError # Please triage this failure
WebPlatformTest/html/semantics/embedded-content/media-elements/interfaces/TextTrack/cues_t01: Skip # Times out. Please triage this failure
WebPlatformTest/html/semantics/embedded-content/media-elements/interfaces/TextTrack/mode_t01: RuntimeError # Please triage this failure
WebPlatformTest/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address_t01: RuntimeError # Please triage this failure
WebPlatformTest/html/semantics/forms/attributes-common-to-form-controls/formaction_t01: RuntimeError # Please triage this failure
WebPlatformTest/html/semantics/forms/textfieldselection/selection_t01: RuntimeError # Please triage this failure
WebPlatformTest/html/semantics/forms/textfieldselection/textfieldselection-setRangeText_t01: RuntimeError # Please triage this failure
@ -1453,6 +1476,7 @@ WebPlatformTest/shadow-dom/shadow-trees/lower-boundary-encapsulation/test-004_t0
WebPlatformTest/shadow-dom/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-002_t01: RuntimeError # Please triage this failure
WebPlatformTest/shadow-dom/shadow-trees/upper-boundary-encapsulation/ownerdocument-002_t01: RuntimeError # Please triage this failure
WebPlatformTest/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-009_t01: RuntimeError # Please triage this failure
WebPlatformTest/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-005_t01.dart: Pass, RuntimeError # Issue 25155
WebPlatformTest/webstorage/event_constructor_t01: RuntimeError # Please triage this failure
WebPlatformTest/webstorage/event_constructor_t02: RuntimeError # Please triage this failure
@ -1625,25 +1649,6 @@ LayoutTests/fast/text/line-break-after-question-mark_t01: RuntimeError # Please
LayoutTests/fast/text/regional-indicator-symobls_t01: RuntimeError # Please triage this failure
LayoutTests/fast/text/text-combine-shrink-to-fit_t01: RuntimeError # Please triage this failure
[ $compiler == dart2js && $runtime == chrome && $system == windows ]
LayoutTests/fast/canvas/canvas-blending-color-over-gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-color-over-image_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-color-over-pattern_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-fill-style_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-global-alpha_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-gradient-over-color_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-gradient-over-gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-gradient-over-image_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-image-over-image_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-pattern-over-color_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-pattern-over-gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-pattern-over-image_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-pattern-over-pattern_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-shadow_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-blending-transforms_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-composite-image_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/canvas-composite-stroke-alpha_t01: RuntimeError # Please triage this failure
[ $compiler == dart2js && $runtime == chrome && $system != linux ]
LayoutTests/fast/xpath/py-dom-xpath/abbreviations_t01: RuntimeError # Issue 24398
@ -3167,6 +3172,9 @@ LayoutTests/fast/dom/HTMLAnchorElement/set-href-attribute-pathname_t01: RuntimeE
LayoutTests/fast/xpath/4XPath/Core/test_parser_t01: RuntimeError # Dartium JSInterop failure
LayoutTests/fast/xpath/py-dom-xpath/abbreviations_t01: RuntimeError # Dartium JSInterop failure
[ $compiler == dart2js && $runtime == ff && $system == linux]
LayoutTests/fast/canvas/canvas-composite-text-alpha_t01: RuntimeError # co19 issue 16
[ $compiler == dart2js && $runtime == safari ]
LayoutTests/fast/alignment/parse-align-items_t01: RuntimeError # Please triage this failure
LayoutTests/fast/alignment/parse-align-self_t01: RuntimeError # Please triage this failure
@ -9615,7 +9623,6 @@ WebPlatformTest/webstorage/storage_builtins_t01: RuntimeError # Please triage th
WebPlatformTest/webstorage/storage_local_setitem_quotaexceedederr_t01: Skip # Times out. Please triage this failure
[ $compiler == dart2js && $cps_ir ]
Language/Expressions/Instance_Creation/New/evaluation_t12: RuntimeError # Please triage this failure.
Language/Expressions/Method_Invocation/Super_Invocation/syntax_t01: RuntimeError # Cannot read property 'call' of undefined
Language/Expressions/Identifier_Reference/evaluation_variable_or_parameter_t03.dart: Crash # (i=0): For-loop variable captured in loop header
Language/Statements/For/syntax_t07: Crash # unsupported operation on erroneous element

View file

@ -0,0 +1,187 @@
// 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.
import "package:expect/expect.dart";
// Test that optimized '+' and slow path '+' produce the same error.
@NoInline()
@AssumeDynamic()
confuse(x) => x;
void check2(String name, name1, f1, name2, f2) {
Error trap(part, f) {
try {
f();
} catch (e) {
return e;
}
Expect.fail('should throw: $name.$part');
}
var e1 = trap(name1, f1);
var e2 = trap(name2, f2);
var s1 = '$e1';
var s2 = '$e2';
Expect.equals(s1, s2, '\n $name.$name1: "$s1"\n $name.$name2: "$s2"\n');
}
void check(String name, f1, f2, [f3, f4, f5, f6, f7]) {
check2(name, 'f1', f1, 'f2', f2);
if (f3 != null) check2(name, 'f1', f1, 'f3', f3);
if (f4 != null) check2(name, 'f1', f1, 'f4', f4);
if (f5 != null) check2(name, 'f1', f1, 'f5', f5);
if (f6 != null) check2(name, 'f1', f1, 'f6', f6);
if (f7 != null) check2(name, 'f1', f1, 'f7', f7);
}
class IntPlusNull {
static f1() {
return confuse(1) + confuse(null);
}
static f2() {
return confuse(1) + null;
}
static f3() {
return (confuse(1) as int) + confuse(null);
}
static f4() {
return (confuse(1) as int) + null;
}
static f5() {
var a = confuse(true) ? 1 : 2; // Small int with unknown value.
return a + confuse(null);
}
static f6() {
var a = confuse(true) ? 1 : 2; // Small int with unknown value.
return a + null;
}
static f7() {
return 1 + null;
}
static test() {
check('IntPlusNull', f1, f2, f3, f4, f5, f6, f7);
}
}
class StringPlusNull {
static f1() {
return confuse('a') + confuse(null);
}
static f2() {
return confuse('a') + null;
}
static f3() {
return (confuse('a') as String) + confuse(null);
}
static f4() {
return (confuse('a') as String) + null;
}
static f5() {
var a = confuse(true) ? 'a' : 'bc';
return a + confuse(null);
}
static f6() {
var a = confuse(true) ? 'a' : 'bc';
return a + null;
}
static f7() {
return 'a' + null;
}
static test() {
check('StringPlusNull', f1, f2, f3, f4, f5, f6, f7);
}
}
class IntPlusString {
static f1() {
return confuse(1) + confuse('a');
}
static f2() {
return confuse(1) + 'a';
}
static f3() {
var a = confuse(true) ? 1 : 2; // Small int with unknown value.
return a + confuse('a');
}
static f4() {
return (confuse(1) as int) + confuse('a');
}
static f5() {
return (confuse(1) as int) + 'a';
}
static f6() {
var a = confuse(true) ? 1 : 2; // Small int with unknown value.
return a + 'a';
}
static f7() {
return 1 + 'a';
}
static test() {
check('IntPlusString', f1, f2, f3, f4, f5, f6, f7);
}
}
class StringPlusInt {
static f1() {
return confuse('a') + confuse(1);
}
static f2() {
return confuse('a') + 1;
}
static f3() {
return (confuse('a') as String) + confuse(1);
}
static f4() {
return (confuse('a') as String) + 1;
}
static f5() {
var a = confuse(true) ? 'a' : 'bc';
return a + confuse(1);
}
static f6() {
var a = confuse(true) ? 'a' : 'bc';
return a + 1;
}
static f7() {
return 'a' + 1;
}
static test() {
check('StringPlusInt', f1, f2, f3, f4, f5, f6, f7);
}
}
main() {
IntPlusNull.test();
StringPlusNull.test();
IntPlusString.test();
StringPlusInt.test();
}

View file

@ -0,0 +1,82 @@
// 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.
import "package:expect/expect.dart";
// Test that optimized 'null + x' and slow path '+' produce the same error.
//
// They don't, sometimes we generate null.$add, sometimes JSNull_methods.$add.
@NoInline()
@AssumeDynamic()
confuse(x) => x;
void check2(String name, name1, f1, name2, f2) {
Error trap(part, f) {
try {
f();
} catch (e) {
return e;
}
Expect.fail('should throw: $name.$part');
}
var e1 = trap(name1, f1);
var e2 = trap(name2, f2);
var s1 = '$e1';
var s2 = '$e2';
Expect.equals(s1, s2, '\n $name.$name1: "$s1"\n $name.$name2: "$s2"\n');
}
void check(String name, f1, f2, [f3, f4, f5, f6]) {
check2(name, 'f1', f1, 'f2', f2);
if (f3 != null) check2(name, 'f1', f1, 'f3', f3);
if (f4 != null) check2(name, 'f1', f1, 'f4', f4);
if (f5 != null) check2(name, 'f1', f1, 'f5', f5);
if (f6 != null) check2(name, 'f1', f1, 'f6', f6);
}
class NullPlusInt {
static f1() {
return confuse(null) + confuse(1);
}
static f2() {
return confuse(null) + 1;
}
static f3() {
return (confuse(null) as int) + confuse(1);
}
static f4() {
return (confuse(null) as int) + 1;
}
static f5() {
var a = null;
return a + confuse(1);
}
static f6() {
var a = null;
return a + 1;
}
static test() {
// Sometimes we generate null.$add, sometimes JSNull_methods.$add. The best
// we can do is check there is an error.
check('NullPlusInt', f1, f1);
check('NullPlusInt', f2, f2);
check('NullPlusInt', f3, f3);
check('NullPlusInt', f4, f4);
check('NullPlusInt', f5, f5);
check('NullPlusInt', f6, f6);
}
}
main() {
NullPlusInt.test();
}

View file

@ -106,6 +106,10 @@ touchevent_test/supported: Fail # Touch events are only supported on touch devic
element_animate_test/omit_timing: RuntimeError # Also timing out on MacOS. Issue 23507
element_animate_test/timing_dict: RuntimeError # Also timing out on MacOS. Issue 23507
element_animate_test/simple_timing: RuntimeError # Please triage this failure
element_types_test/supported_object: Pass, RuntimeError # Issue 25155
element_types_test/supported_embed: Pass, RuntimeError # Issue 25155
event_test: Pass, RuntimeError # Issue 25158 Passes on 46, fails on 47
history_test/history: Pass, RuntimeError # Issue 25158, passes on 46 fails on 47
[ $runtime == chrome && $system == macos ]
canvasrenderingcontext2d_test/drawImage_video_element: Skip # Times out. Please triage this failure.

View file

@ -0,0 +1,12 @@
// 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.
main() {
for(
const /// 01: compile-time error
final /// 02: ok
int x in const [1, 2, 3]) {
break;
}
}

View file

@ -85,6 +85,8 @@ multiline_newline_test/04: MissingCompileTimeError # Issue 23888
multiline_newline_test/05: MissingCompileTimeError # Issue 23888
multiline_newline_test/06: MissingCompileTimeError # Issue 23888
const_for_in_variable_test/01: MissingCompileTimeError # Issue 25161
# Please add new failing tests before this line.
# Section below is for invalid tests.
#

View file

@ -37,6 +37,8 @@ multiline_newline_test/04: MissingCompileTimeError # Issue 23888
multiline_newline_test/05: MissingCompileTimeError # Issue 23888
multiline_newline_test/06: MissingCompileTimeError # Issue 23888
const_for_in_variable_test/01: MissingCompileTimeError # Issue 25161
# Please add new failing tests before this line.
# Section below is for invalid tests.
#

View file

@ -224,7 +224,7 @@ generic_field_mixin4_test: Crash # Issue 18651
generic_field_mixin5_test: Crash # Issue 18651
many_method_calls_test: Crash # Stack overflow in HGraphVisitor.visitPostDominatorTree.visitBasicBlockAndSuccessors
[ $compiler == dart2js ]
[ $compiler == dart2js && $runtime != drt && $runtime != dartium ]
issue23244_test: RuntimeError # 23244
[ $compiler == dart2js && $cps_ir ]

View file

@ -518,15 +518,15 @@ final coreImports = [
"name": "Pattern",
"kind": "class"
},
{
"name": "Match",
"kind": "class"
},
{
"name": "print",
"kind": "function",
"type": "(Object) -> void"
},
{
"name": "Match",
"kind": "class"
},
{
"name": "RegExp",
"kind": "class"

View file

@ -39,9 +39,6 @@ poi/serialize_test: SkipByDesign # Uses dart:io.
[ $compiler == dart2js ]
poi/*: Skip # Issue 20031
[ $compiler == dart2js && $runtime == none ]
poi/serialize_test: RuntimeError # Issue 25125
[ $compiler == dart2js ]
# Compilation is slow, test fails at runtime due to time out, but
# unpredictably.