mirror of
https://github.com/dart-lang/sdk
synced 2024-09-22 00:11:18 +00:00
Merge remote-tracking branch 'origin/master' into analyzer-breaking-0.27
This commit is contained in:
commit
4c28eb6f67
2
DEPS
2
DEPS
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> ');
|
||||
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> ');
|
||||
|
||||
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> ');
|
||||
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);
|
||||
});
|
||||
|
|
1787
pkg/analysis_server/lib/src/status/validator.dart
Normal file
1787
pkg/analysis_server/lib/src/status/validator.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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('''
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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((_) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)]);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
187
tests/compiler/dart2js_extra/consistent_add_error_test.dart
Normal file
187
tests/compiler/dart2js_extra/consistent_add_error_test.dart
Normal 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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
|
|
12
tests/language/const_for_in_variable_test.dart
Normal file
12
tests/language/const_for_in_variable_test.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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 ]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue