| // Copyright (c) 2017, 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:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/syntactic_entity.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/type_system.dart'; |
| import 'package:analyzer/src/dart/ast/token.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element; |
| import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart'; |
| import 'package:analyzer_plugin/utilities/completion/relevance.dart'; |
| |
| typedef SuggestionsFilter = int Function(DartType dartType, int relevance); |
| |
| /// An [AstVisitor] for determining whether top level suggestions or invocation |
| /// suggestions should be made based upon the type of node in which the |
| /// suggestions were requested. |
| class OpType { |
| /// The [TypeSystem] used during resolution of the current unit. |
| TypeSystem _typeSystem; |
| |
| /// Indicates whether constructor suggestions should be included. |
| bool includeConstructorSuggestions = false; |
| |
| /// Indicates whether type names should be suggested. |
| bool includeTypeNameSuggestions = false; |
| |
| /// If [includeTypeNameSuggestions] is set to true, then this function may |
| /// be set to a non-default function to filter out potential suggestions |
| /// (null) based on their static [DartType], or change the relative relevance |
| /// by returning a higher or lower relevance. |
| SuggestionsFilter typeNameSuggestionsFilter = |
| (DartType _, int relevance) => relevance; |
| |
| /// Indicates whether setters along with methods and functions that |
| /// have a [void] return type should be suggested. |
| bool includeVoidReturnSuggestions = false; |
| |
| /// Indicates whether fields and getters along with methods and functions that |
| /// have a non-[void] return type should be suggested. |
| bool includeReturnValueSuggestions = false; |
| |
| /// If [includeReturnValueSuggestions] is set to true, then this function may |
| /// be set to a non-default function to filter out potential suggestions |
| /// (null) based on their static [DartType], or change the relative relevance |
| /// by returning a higher or lower relevance. |
| SuggestionsFilter returnValueSuggestionsFilter = |
| (DartType _, int relevance) => relevance; |
| |
| /// Indicates whether named arguments should be suggested. |
| bool includeNamedArgumentSuggestions = false; |
| |
| /// Indicates whether statement labels should be suggested. |
| bool includeStatementLabelSuggestions = false; |
| |
| /// Indicates whether case labels should be suggested. |
| bool includeCaseLabelSuggestions = false; |
| |
| /// Indicates whether variable names should be suggested. |
| bool includeVarNameSuggestions = false; |
| |
| /// Indicates whether the completion location is in a field declaration. |
| bool inFieldDeclaration = false; |
| |
| /// Indicates whether the completion location is in a top-level variable |
| /// declaration. |
| bool inTopLevelVariableDeclaration = false; |
| |
| /// Indicates whether the completion location is in the body of a static |
| /// method. |
| bool inStaticMethodBody = false; |
| |
| /// Indicates whether the completion target is prefixed. |
| bool isPrefixed = false; |
| |
| /// The suggested completion kind. |
| CompletionSuggestionKind suggestKind = CompletionSuggestionKind.INVOCATION; |
| |
| /// An representation of the location at which completion was requested. |
| String completionLocation; |
| |
| /// The type that is required by the context in which the completion was |
| /// activated, or `null` if there is no such type, or it cannot be determined. |
| DartType _requiredType; |
| |
| /// Determine the suggestions that should be made based upon the given |
| /// [CompletionTarget] and [offset]. |
| factory OpType.forCompletion(CompletionTarget target, int offset) { |
| var optype = OpType._(); |
| |
| // Don't suggest anything right after double or integer literals. |
| if (target.isDoubleOrIntLiteral()) { |
| return optype; |
| } |
| |
| optype._typeSystem = target.unit?.declaredElement?.library?.typeSystem; |
| |
| var targetNode = target.containingNode; |
| targetNode.accept(_OpTypeAstVisitor(optype, target.entity, offset)); |
| |
| var functionBody = targetNode.thisOrAncestorOfType<FunctionBody>(); |
| if (functionBody != null) { |
| var parent = functionBody.parent; |
| |
| if (parent is MethodDeclaration) { |
| optype.inStaticMethodBody = parent.isStatic; |
| } |
| } |
| |
| optype.inFieldDeclaration = |
| targetNode.thisOrAncestorOfType<FieldDeclaration>() != null; |
| optype.inTopLevelVariableDeclaration = |
| targetNode.thisOrAncestorOfType<TopLevelVariableDeclaration>() != null; |
| |
| // If a value should be suggested, suggest also constructors. |
| if (optype.includeReturnValueSuggestions) { |
| // Careful: in angular plugin, `target.unit` may be null! |
| var unitElement = target.unit?.declaredElement; |
| if (unitElement != null) { |
| optype.includeConstructorSuggestions = true; |
| } |
| } |
| |
| // Compute the type required by the context and set filters. |
| optype._computeRequiredTypeAndFilters(target); |
| |
| return optype; |
| } |
| |
| OpType._(); |
| |
| /// Return `true` if free standing identifiers should be suggested |
| bool get includeIdentifiers { |
| return !isPrefixed && |
| (includeReturnValueSuggestions || |
| includeTypeNameSuggestions || |
| includeVoidReturnSuggestions || |
| includeConstructorSuggestions); |
| } |
| |
| /// Indicate whether only type names should be suggested |
| bool get includeOnlyNamedArgumentSuggestions => |
| includeNamedArgumentSuggestions && |
| !includeTypeNameSuggestions && |
| !includeReturnValueSuggestions && |
| !includeVoidReturnSuggestions; |
| |
| /// Try to determine the required context type, and configure filters. |
| void _computeRequiredTypeAndFilters(CompletionTarget target) { |
| var entity = target.entity; |
| var node = target.containingNode; |
| |
| if (node is InstanceCreationExpression && |
| node.keyword != null && |
| node.constructorName == entity) { |
| entity = node; |
| node = node.parent; |
| } |
| |
| if (node is AssignmentExpression && |
| node.operator.type == TokenType.EQ && |
| node.rightHandSide == entity) { |
| _requiredType = node.leftHandSide?.staticType; |
| } else if (node is BinaryExpression && |
| node.operator.type == TokenType.EQ_EQ && |
| node.rightOperand == entity) { |
| _requiredType = node.leftOperand?.staticType; |
| } else if (node is NamedExpression && node.expression == entity) { |
| _requiredType = node.staticParameterElement?.type; |
| } else if (node is SwitchCase && node.expression == entity) { |
| var parent = node.parent; |
| if (parent is SwitchStatement) { |
| _requiredType = parent.expression?.staticType; |
| } |
| } else if (node is VariableDeclaration && node.initializer == entity) { |
| _requiredType = node.declaredElement?.type; |
| } else if (entity is Expression && entity.staticParameterElement != null) { |
| _requiredType = entity.staticParameterElement.type; |
| } |
| |
| if (_requiredType == null) { |
| return; |
| } |
| if (_requiredType.isDynamic || _requiredType.isObject) { |
| _requiredType = null; |
| return; |
| } |
| |
| returnValueSuggestionsFilter = (DartType dartType, int relevance) { |
| if (dartType != null) { |
| if (dartType == _requiredType) { |
| return relevance + DART_RELEVANCE_BOOST_TYPE; |
| } else if (_isSubtypeOf(dartType, _requiredType)) { |
| return relevance + DART_RELEVANCE_BOOST_SUBTYPE; |
| } |
| } |
| return relevance; |
| }; |
| } |
| |
| /// Return `true` if the [leftType] is a subtype of the [rightType]. |
| bool _isSubtypeOf(DartType leftType, DartType rightType) { |
| if (_typeSystem == null) { |
| return false; |
| } |
| |
| return _typeSystem.isSubtypeOf(leftType, rightType); |
| } |
| |
| /// Return the statement before [entity] |
| /// where [entity] can be a statement or the `}` closing the given block. |
| static Statement getPreviousStatement(Block node, Object entity) { |
| if (entity == node.rightBracket) { |
| return node.statements.isNotEmpty ? node.statements.last : null; |
| } |
| if (entity is Statement) { |
| var index = node.statements.indexOf(entity); |
| if (index > 0) { |
| return node.statements[index - 1]; |
| } |
| return null; |
| } |
| return null; |
| } |
| } |
| |
| class _OpTypeAstVisitor extends GeneralizingAstVisitor<void> { |
| /// The entity (AstNode or Token) that will be replaced or displaced by the |
| /// added text. |
| final SyntacticEntity entity; |
| |
| /// The offset within the source at which the completion is requested. |
| final int offset; |
| |
| /// The [OpType] being initialized |
| final OpType optype; |
| |
| _OpTypeAstVisitor(this.optype, this.entity, this.offset); |
| |
| @override |
| void visitAnnotation(Annotation node) { |
| if (identical(entity, node.name)) { |
| optype.completionLocation = 'Annotation_name'; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeReturnValueSuggestions = true; |
| } else if (identical(entity, node.constructorName)) { |
| // There is no location for the constructor name because only named |
| // constructors are valid. |
| // TODO(brianwilkerson) The following looks wrong. I think we want to set |
| // includeConstructorSuggestions = true, but not type names or return |
| // values. On the other hand, I can't construct a test case that reaches |
| // this point, so perhaps we should just remove this branch. |
| optype.includeTypeNameSuggestions = true; |
| optype.includeReturnValueSuggestions = true; |
| optype.isPrefixed = true; |
| } |
| } |
| |
| @override |
| void visitArgumentList(ArgumentList node) { |
| var parent = node.parent; |
| List<ParameterElement> parameters; |
| if (parent is InstanceCreationExpression) { |
| Element constructor; |
| var name = parent.constructorName?.name; |
| if (name != null) { |
| constructor = name.staticElement; |
| } else { |
| var classElem = parent.constructorName?.type?.name?.staticElement; |
| if (classElem is ClassElement) { |
| constructor = classElem.unnamedConstructor; |
| } |
| } |
| if (constructor is ConstructorElement) { |
| parameters = constructor.parameters; |
| } else if (constructor == null) { |
| // If unresolved, then include named arguments |
| optype.includeNamedArgumentSuggestions = true; |
| } |
| } else if (parent is InvocationExpression) { |
| var function = parent.function; |
| if (function is SimpleIdentifier) { |
| var elem = function.staticElement; |
| if (elem is FunctionTypedElement) { |
| parameters = elem.parameters; |
| } else if (elem == null) { |
| // If unresolved, then include named arguments |
| optype.includeNamedArgumentSuggestions = true; |
| } |
| } |
| } else if (parent is SuperConstructorInvocation) { |
| parameters = parent.staticElement?.parameters; |
| } else if (parent is RedirectingConstructorInvocation) { |
| parameters = parent.staticElement?.parameters; |
| } else if (parent is Annotation) { |
| var constructor = parent.element; |
| if (constructor is ConstructorElement) { |
| parameters = constructor.parameters; |
| } else if (constructor == null) { |
| // If unresolved, then include named arguments |
| optype.includeNamedArgumentSuggestions = true; |
| } |
| } |
| // Based upon the insertion location and declared parameters |
| // determine whether only named arguments should be suggested |
| if (parameters != null) { |
| int index; |
| if (node.arguments.isEmpty) { |
| index = 0; |
| } else if (entity == node.rightParenthesis) { |
| // Parser ignores trailing commas |
| var previous = node.findPrevious(node.rightParenthesis); |
| if (previous?.lexeme == ',') { |
| index = node.arguments.length; |
| } else { |
| index = node.arguments.length - 1; |
| } |
| } else { |
| index = node.arguments.indexOf(entity as Expression); |
| } |
| if (0 <= index && index < parameters.length) { |
| var param = parameters[index]; |
| if (param?.isNamed == true) { |
| var context = _argumentListContext(node); |
| optype.completionLocation = 'ArgumentList_${context}_named'; |
| optype.includeNamedArgumentSuggestions = true; |
| return; |
| } |
| } |
| } |
| var context = _argumentListContext(node); |
| optype.completionLocation = 'ArgumentList_${context}_unnamed'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitAsExpression(AsExpression node) { |
| if (identical(entity, node.type)) { |
| optype.completionLocation = 'AsExpression_type'; |
| optype.includeTypeNameSuggestions = true; |
| optype.typeNameSuggestionsFilter = (DartType dartType, int relevance) { |
| var staticType = node.expression.staticType; |
| if (staticType != null && |
| (staticType.isDynamic || |
| (optype._isSubtypeOf(dartType, staticType) && |
| dartType != staticType))) { |
| return relevance; |
| } else { |
| return null; |
| } |
| }; |
| } |
| } |
| |
| @override |
| void visitAssertInitializer(AssertInitializer node) { |
| if (identical(entity, node.condition)) { |
| optype.completionLocation = 'AssertInitializer_condition'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.message)) { |
| optype.completionLocation = 'AssertInitializer_message'; |
| // TODO(brianwilkerson) Consider including return value suggestions and |
| // type name suggestions here. |
| } |
| } |
| |
| @override |
| void visitAssertStatement(AssertStatement node) { |
| if (identical(entity, node.condition)) { |
| optype.completionLocation = 'AssertStatement_condition'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.message)) { |
| optype.completionLocation = 'AssertStatement_message'; |
| // TODO(brianwilkerson) Consider including return value suggestions and |
| // type name suggestions here. |
| } |
| } |
| |
| @override |
| void visitAssignmentExpression(AssignmentExpression node) { |
| if (identical(entity, node.rightHandSide)) { |
| optype.completionLocation = 'AssignmentExpression_rightHandSide'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitAwaitExpression(AwaitExpression node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'AwaitExpression_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitBinaryExpression(BinaryExpression node) { |
| if (identical(entity, node.rightOperand)) { |
| optype.completionLocation = |
| 'BinaryExpression_${node.operator}_rightOperand'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitBlock(Block node) { |
| optype.completionLocation = 'Block_statement'; |
| var prevStmt = OpType.getPreviousStatement(node, entity); |
| if (prevStmt is TryStatement) { |
| if (prevStmt.catchClauses.isEmpty && prevStmt.finallyBlock == null) { |
| return; |
| } |
| } |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| |
| @override |
| void visitBreakStatement(BreakStatement node) { |
| if (node.label == null || identical(entity, node.label)) { |
| optype.includeStatementLabelSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitCascadeExpression(CascadeExpression node) { |
| if (node.cascadeSections.contains(entity)) { |
| optype.completionLocation = 'CascadeExpression_cascadeSection'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| optype.isPrefixed = true; |
| } |
| } |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| if (identical(entity, node.exceptionType)) { |
| optype.completionLocation = 'CatchClause_exceptionType'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| // Make suggestions in the body of the class declaration |
| if (node.members.contains(entity) || identical(entity, node.rightBracket)) { |
| optype.completionLocation = 'ClassDeclaration_member'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitClassMember(ClassMember node) {} |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| if (identical(entity, node.superclass)) { |
| optype.completionLocation = 'ClassTypeAlias_superclass'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitCommentReference(CommentReference node) { |
| optype.completionLocation = 'CommentReference_identifier'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| optype.suggestKind = CompletionSuggestionKind.IDENTIFIER; |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| if (entity is! CommentToken) { |
| int declarationStart() { |
| var declarations = node.declarations; |
| if (declarations.isNotEmpty) { |
| return declarations[0].offset; |
| } |
| var directives = node.directives; |
| if (directives.isNotEmpty) { |
| return directives.last.end; |
| } |
| return node.end; |
| } |
| |
| if (entity != null) { |
| if (entity.offset <= declarationStart()) { |
| optype.completionLocation = 'CompilationUnit_declaration'; |
| } else { |
| optype.completionLocation = 'CompilationUnit_declaration'; |
| } |
| } |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitConditionalExpression(ConditionalExpression node) { |
| if (identical(entity, node.thenExpression)) { |
| optype.completionLocation = 'ConditionalExpression_thenExpression'; |
| } else if (identical(entity, node.elseExpression)) { |
| optype.completionLocation = 'ConditionalExpression_elseExpression'; |
| } |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitConstructorDeclaration(ConstructorDeclaration node) { |
| if (identical(entity, node.returnType)) { |
| optype.completionLocation = 'ConstructorDeclaration_returnType'; |
| optype.includeTypeNameSuggestions = true; |
| } else if (node.initializers.contains(entity)) { |
| optype.completionLocation = 'ConstructorDeclaration_initializer'; |
| } |
| } |
| |
| @override |
| void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'ConstructorFieldInitializer_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitConstructorName(ConstructorName node) { |
| // some PrefixedIdentifier nodes are transformed into |
| // ConstructorName nodes during the resolution process. |
| if (identical(entity, node.name)) { |
| var type = node.type; |
| if (type != null) { |
| var prefix = type.name; |
| if (prefix != null) { |
| optype.includeConstructorSuggestions = true; |
| optype.isPrefixed = true; |
| } |
| } |
| } |
| } |
| |
| @override |
| void visitContinueStatement(ContinueStatement node) { |
| if (node.label == null || identical(entity, node.label)) { |
| optype.includeStatementLabelSuggestions = true; |
| optype.includeCaseLabelSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| if (identical(entity, node.defaultValue)) { |
| optype.completionLocation = 'DefaultFormalParameter_defaultValue'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitDoStatement(DoStatement node) { |
| if (identical(entity, node.body)) { |
| optype.completionLocation = 'DoStatement_body'; |
| } else if (identical(entity, node.condition)) { |
| optype.completionLocation = 'DoStatement_condition'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitEmptyStatement(EmptyStatement node) { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| |
| @override |
| void visitExpression(Expression node) { |
| // This should never be called; we should always dispatch to the visitor |
| // for a particular kind of expression. |
| assert(false); |
| } |
| |
| @override |
| void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'ExpressionFunctionBody_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitExpressionStatement(ExpressionStatement node) { |
| optype.completionLocation = 'ExpressionStatement_expression'; |
| // Given f[], the parser drops the [] from the expression statement |
| // but the [] token is the CompletionTarget entity |
| if (entity is Token) { |
| var token = entity as Token; |
| if (token.lexeme == '[]' && offset == token.offset + 1) { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| if ((token.isSynthetic || token.lexeme == ';') && |
| node.expression is Identifier) { |
| optype.includeVarNameSuggestions = true; |
| } |
| } |
| } |
| |
| @override |
| void visitExtendsClause(ExtendsClause node) { |
| if (identical(entity, node.superclass)) { |
| optype.completionLocation = 'ExtendsClause_superclass'; |
| optype.includeTypeNameSuggestions = true; |
| optype.typeNameSuggestionsFilter = _nonMixinClasses; |
| } |
| } |
| |
| @override |
| void visitExtensionDeclaration(ExtensionDeclaration node) { |
| if (identical(entity, node.extendedType)) { |
| optype.completionLocation = 'ExtensionDeclaration_extendedType'; |
| } else if (node.members.contains(entity) || |
| identical(entity, node.rightBracket)) { |
| // Make suggestions in the body of the extension declaration |
| optype.completionLocation = 'ExtensionDeclaration_member'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitFieldDeclaration(FieldDeclaration node) { |
| if (offset <= node.semicolon.offset) { |
| optype.includeVarNameSuggestions = true; |
| } |
| if (offset <= node.fields.offset) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitFieldFormalParameter(FieldFormalParameter node) { |
| if (entity == node.identifier) { |
| optype.isPrefixed = true; |
| } else { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitForEachParts(ForEachParts node) { |
| if (identical(entity, node.inKeyword) && offset <= node.inKeyword.offset) { |
| if (!(node is ForEachPartsWithIdentifier && node.identifier != null || |
| node is ForEachPartsWithDeclaration && node.loopVariable != null)) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| } |
| |
| @override |
| void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) { |
| if (identical(entity, node.loopVariable)) { |
| optype.completionLocation = 'ForEachPartsWithDeclaration_loopVariable'; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.iterable)) { |
| optype.completionLocation = 'ForEachPartsWithDeclaration_iterable'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| visitForEachParts(node); |
| } |
| |
| @override |
| void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) { |
| if (identical(entity, node.identifier)) { |
| optype.completionLocation = 'ForEachPartsWithIdentifier_identifier'; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.iterable)) { |
| optype.completionLocation = 'ForEachPartsWithIdentifier_iterable'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| visitForEachParts(node); |
| } |
| |
| @override |
| void visitForElement(ForElement node) { |
| // for (^) {} |
| // for (Str^ str = null;) {} |
| // In theory it is possible to specify any expression in initializer, |
| // but for any practical use we need only types. |
| if (entity == node.forLoopParts) { |
| optype.includeTypeNameSuggestions = true; |
| } else if (entity == node.body) { |
| optype.completionLocation = 'ForElement_body'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitFormalParameterList(FormalParameterList node) { |
| optype.completionLocation = 'FormalParameterList_parameter'; |
| var entity = this.entity; |
| if (entity is Token) { |
| var previous = node.findPrevious(entity); |
| if (previous != null) { |
| var type = previous.type; |
| if (type == TokenType.OPEN_PAREN || type == TokenType.COMMA) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| } |
| |
| // Find the containing parameter. |
| var parameter = CompletionTarget.findFormalParameter(node, offset); |
| if (parameter == null) return; |
| |
| // Handle default normal parameter just as a normal parameter. |
| if (parameter is DefaultFormalParameter) { |
| parameter = (parameter as DefaultFormalParameter).parameter; |
| } |
| |
| // "(^ this.field)" |
| if (parameter is FieldFormalParameter) { |
| if (offset < parameter.thisKeyword.offset) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| return; |
| } |
| |
| // "(Type name)" |
| if (parameter is SimpleFormalParameter) { |
| visitSimpleFormalParameter(parameter); |
| } |
| } |
| |
| @override |
| void visitForParts(ForParts node) { |
| var entity = this.entity; |
| if (_isEntityPrevTokenSynthetic()) { |
| // Actual: for (var v i^) |
| // Parsed: for (var i; i^;) |
| } else if (entity is Token && |
| entity.isSynthetic && |
| node.leftSeparator == entity) { |
| // Actual: for (String ^) |
| // Parsed: for (String; ;) |
| // ^ |
| optype.includeVarNameSuggestions = true; |
| } else { |
| if (entity == node.condition) { |
| // for (; ^) {} |
| optype.completionLocation = 'ForParts_condition'; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeReturnValueSuggestions = true; |
| } else if (node.updaters.contains(entity)) { |
| // for (; ; ^) {} |
| optype.completionLocation = 'ForParts_updater'; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| } |
| |
| @override |
| void visitForStatement(ForStatement node) { |
| // for (^) {} |
| // for (Str^ str = null;) {} |
| // In theory it is possible to specify any expression in initializer, |
| // but for any practical use we need only types. |
| if (entity == node.forLoopParts) { |
| optype.includeTypeNameSuggestions = true; |
| } else if (entity == node.body) { |
| optype.completionLocation = 'ForElement_body'; |
| } |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| if (identical(entity, node.returnType) || |
| identical(entity, node.name) && node.returnType == null) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitFunctionExpression(FunctionExpression node) {} |
| |
| @override |
| void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {} |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| if (identical(entity, node.returnType) || |
| identical(entity, node.name) && node.returnType == null) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| if (entity == node.functionType) { |
| optype.completionLocation = 'GenericTypeAlias_functionType'; |
| } |
| } |
| |
| @override |
| void visitHideCombinator(HideCombinator node) { |
| if (node.hiddenNames.contains(entity)) { |
| optype.completionLocation = 'HideCombinator_hiddenName'; |
| } |
| } |
| |
| @override |
| void visitIfElement(IfElement node) { |
| if (identical(entity, node.condition)) { |
| optype.completionLocation = 'IfElement_condition'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.thenElement)) { |
| optype.completionLocation = 'IfElement_thenElement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } else if (identical(entity, node.elseElement)) { |
| optype.completionLocation = 'IfElement_elseElement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitIfStatement(IfStatement node) { |
| if (_isEntityPrevTokenSynthetic()) { |
| // Actual: if (var v i^) |
| // Parsed: if (v) i^; |
| } else if (identical(entity, node.condition)) { |
| optype.completionLocation = 'IfStatement_condition'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.thenStatement)) { |
| optype.completionLocation = 'IfStatement_thenElement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } else if (identical(entity, node.elseStatement)) { |
| optype.completionLocation = 'IfStatement_elseElement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitImplementsClause(ImplementsClause node) { |
| optype.completionLocation = 'ImplementsClause_interface'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitIndexExpression(IndexExpression node) { |
| optype.completionLocation = 'IndexExpression_index'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| if (identical(entity, node.constructorName)) { |
| optype.includeConstructorSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitInterpolationExpression(InterpolationExpression node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'InterpolationExpression_expression'; |
| optype.includeReturnValueSuggestions = true; |
| // Only include type names in a ${ } expression |
| optype.includeTypeNameSuggestions = |
| node.leftBracket != null && node.leftBracket.length > 1; |
| } |
| } |
| |
| @override |
| void visitIsExpression(IsExpression node) { |
| if (identical(entity, node.type)) { |
| optype.completionLocation = 'IsExpression_type'; |
| optype.includeTypeNameSuggestions = true; |
| optype.typeNameSuggestionsFilter = (DartType dartType, int relevance) { |
| var staticType = node.expression.staticType; |
| if (staticType != null && |
| (staticType.isDynamic || |
| (optype._isSubtypeOf(dartType, staticType) && |
| dartType != staticType))) { |
| return relevance; |
| } else { |
| return null; |
| } |
| }; |
| } |
| } |
| |
| @override |
| void visitLabeledStatement(LabeledStatement node) { |
| optype.completionLocation = 'LabeledStatement_statement'; |
| } |
| |
| @override |
| void visitLibraryIdentifier(LibraryIdentifier node) { |
| // No suggestions. |
| } |
| |
| @override |
| void visitListLiteral(ListLiteral node) { |
| if (node.elements.contains(entity)) { |
| optype.completionLocation = 'ListLiteral_element'; |
| } |
| visitTypedLiteral(node); |
| } |
| |
| @override |
| void visitMapLiteralEntry(MapLiteralEntry node) { |
| optype.completionLocation = 'MapLiteralEntry_value'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitMethodInvocation(MethodInvocation node) { |
| var isThis = node.target is ThisExpression; |
| if (identical(entity, node.operator) && offset > node.operator.offset) { |
| // The cursor is between the two dots of a ".." token, so we need to |
| // generate the completions we would generate after a "." token. |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = !isThis; |
| optype.includeVoidReturnSuggestions = true; |
| optype.isPrefixed = true; |
| } else if (identical(entity, node.methodName)) { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = !isThis; |
| optype.includeVoidReturnSuggestions = true; |
| optype.isPrefixed = true; |
| } else if (identical(entity, node.argumentList)) { |
| // Note that when the cursor is in a type argument list (f<^>()), the |
| // entity is (surprisingly) the invocation's argumentList (and not it's |
| // typeArgumentList as you'd expect). |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitMixinDeclaration(MixinDeclaration node) { |
| // Make suggestions in the body of the mixin declaration |
| if (node.members.contains(entity) || identical(entity, node.rightBracket)) { |
| optype.completionLocation = 'MixinDeclaration_member'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitNamedExpression(NamedExpression node) { |
| if (identical(entity, node.expression)) { |
| var context = _argumentListContext(node.parent); |
| optype.completionLocation = 'ArgumentList_${context}_named'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| |
| // Check for named parameters in constructor calls. |
| var grandparent = node.parent.parent; |
| if (grandparent is ConstructorReferenceNode) { |
| var element = grandparent.staticElement; |
| if (element != null) { |
| var parameters = element.parameters; |
| var parameterElement = parameters.firstWhere((e) { |
| if (e is DefaultFieldFormalParameterElementImpl) { |
| return e.field?.name == node.name.label?.name; |
| } |
| return e.isNamed && e.name == node.name.label?.name; |
| }, orElse: () => null); |
| // Suggest tear-offs. |
| if (parameterElement?.type is FunctionType) { |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| } |
| } |
| } |
| |
| @override |
| void visitNode(AstNode node) { |
| // no suggestion by default |
| } |
| |
| @override |
| void visitNormalFormalParameter(NormalFormalParameter node) { |
| if (node.identifier != entity) { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitOnClause(OnClause node) { |
| optype.completionLocation = 'OnClause_superclassConstraint'; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitParenthesizedExpression(ParenthesizedExpression node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'ParenthesizedExpression_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitPostfixExpression(PostfixExpression node) { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| if (identical(entity, node.identifier) || |
| // In addition to the standard case, |
| // handle the exceptional case where the parser considers the would-be |
| // identifier to be a keyword and inserts a synthetic identifier |
| (node.identifier != null && |
| node.identifier.isSynthetic && |
| identical(entity, node.findPrevious(node.identifier.beginToken)))) { |
| if (node.prefix.isSynthetic) { |
| // If the access has no target (empty string) |
| // then don't suggest anything |
| return; |
| } |
| optype.isPrefixed = true; |
| if (node.parent is TypeName && node.parent.parent is ConstructorName) { |
| optype.includeConstructorSuggestions = true; |
| } else if (node.parent is Annotation) { |
| optype.includeConstructorSuggestions = true; |
| } else { |
| optype.completionLocation = 'PropertyAccess_propertyName'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = |
| node.parent is ExpressionStatement; |
| } |
| } |
| } |
| |
| @override |
| void visitPrefixExpression(PrefixExpression node) { |
| if (identical(entity, node.operand)) { |
| optype.completionLocation = 'PrefixExpression_${node.operator}_operand'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitPropertyAccess(PropertyAccess node) { |
| if (node.realTarget is SimpleIdentifier && node.realTarget.isSynthetic) { |
| // If the access has no target (empty string) |
| // then don't suggest anything |
| return; |
| } |
| var isThis = node.target is ThisExpression; |
| if (identical(entity, node.operator) && offset > node.operator.offset) { |
| // The cursor is between the two dots of a ".." token, so we need to |
| // generate the completions we would generate after a "." token. |
| optype.completionLocation = 'PropertyAccess_propertyName'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = !isThis; |
| optype.includeVoidReturnSuggestions = true; |
| optype.isPrefixed = true; |
| } else if (identical(entity, node.propertyName)) { |
| optype.completionLocation = 'PropertyAccess_propertyName'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = |
| !isThis && (node.parent is! CascadeExpression); |
| optype.includeVoidReturnSuggestions = true; |
| optype.isPrefixed = true; |
| } |
| } |
| |
| @override |
| void visitReturnStatement(ReturnStatement node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'ReturnStatement_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitSetOrMapLiteral(SetOrMapLiteral node) { |
| if (node.elements.contains(entity)) { |
| optype.completionLocation = 'SetOrMapLiteral_element'; |
| } |
| visitTypedLiteral(node); |
| } |
| |
| @override |
| void visitShowCombinator(ShowCombinator node) { |
| if (node.shownNames.contains(entity)) { |
| optype.completionLocation = 'ShowCombinator_shownName'; |
| } |
| } |
| |
| @override |
| void visitSimpleFormalParameter(SimpleFormalParameter node) { |
| var type = node.type; |
| var name = node.identifier; |
| |
| // "(Type^)" is parsed as a parameter with the _name_ "Type". |
| if (type == null && |
| name != null && |
| name.offset <= offset && |
| offset <= name.end) { |
| optype.includeTypeNameSuggestions = true; |
| return; |
| } |
| |
| // If "(^ Type)", then include types. |
| if (type == null && offset < name.offset) { |
| optype.includeTypeNameSuggestions = true; |
| return; |
| } |
| |
| // If "(Type ^)", then include parameter names. |
| if (type == null && name.end < offset && offset <= name.token.next.offset) { |
| optype.includeVarNameSuggestions = true; |
| return; |
| } |
| |
| // If inside of "Type" in "(Type^ name)", then include types. |
| if (type != null && type.offset <= offset && offset <= type.end) { |
| optype.includeTypeNameSuggestions = true; |
| return; |
| } |
| |
| // If "(Type name^)", then include parameter names. |
| if (type != null && |
| name != null && |
| name.offset <= offset && |
| offset <= name.end) { |
| optype.includeVarNameSuggestions = true; |
| return; |
| } |
| |
| if (_isParameterOfGenericFunctionType(node) && type != null) { |
| // If "Function(^ Type)", then include types. |
| if (offset < type.offset) { |
| optype.includeTypeNameSuggestions = true; |
| return; |
| } |
| // If "Function(Type ^)", then include parameter names. |
| if (name == null && type.end < offset) { |
| optype.includeVarNameSuggestions = true; |
| return; |
| } |
| } |
| } |
| |
| @override |
| void visitSimpleIdentifier(SimpleIdentifier node) { |
| // This should never happen; the containingNode will always be some node |
| // higher up in the parse tree, and the SimpleIdentifier will be the |
| // entity. |
| assert(false); |
| } |
| |
| @override |
| void visitSpreadElement(SpreadElement node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'SpreadElement_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitStringLiteral(StringLiteral node) { |
| // no suggestions |
| } |
| |
| @override |
| void visitSwitchCase(SwitchCase node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'SwitchCase_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } else if (node.statements.contains(entity)) { |
| optype.completionLocation = 'SwitchCase_statement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitSwitchStatement(SwitchStatement node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'SwitchStatement_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| if (identical(entity, node.rightBracket)) { |
| if (node.members.isNotEmpty) { |
| optype.completionLocation = 'SwitchMember_statement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| if (entity is SwitchMember && entity != node.members.first) { |
| var member = entity as SwitchMember; |
| if (offset <= member.offset) { |
| optype.completionLocation = 'SwitchMember_statement'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| optype.includeVoidReturnSuggestions = true; |
| } |
| } |
| } |
| |
| @override |
| void visitThrowExpression(ThrowExpression node) { |
| optype.completionLocation = 'ThrowExpression_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| if (entity is Token) { |
| var token = entity as Token; |
| if (token.isSynthetic || token.lexeme == ';') { |
| optype.includeVarNameSuggestions = true; |
| } |
| } |
| if (offset <= node.variables.offset) { |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitTypeArgumentList(TypeArgumentList node) { |
| var arguments = node.arguments; |
| for (var type in arguments) { |
| if (identical(entity, type)) { |
| optype.completionLocation = 'TypeArgumentList_argument'; |
| optype.includeTypeNameSuggestions = true; |
| break; |
| } |
| } |
| } |
| |
| @override |
| void visitTypedLiteral(TypedLiteral node) { |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitTypeName(TypeName node) { |
| // The entity won't be the first child entity (node.name), since |
| // CompletionTarget would have chosen an edge higher in the parse tree. So |
| // it must be node.typeArguments, meaning that the cursor is between the |
| // type name and the "<" that starts the type arguments. In this case, |
| // we have no completions to offer. |
| assert(identical(entity, node.typeArguments)); |
| } |
| |
| @override |
| void visitTypeParameter(TypeParameter node) { |
| if (entity == node.bound) { |
| optype.completionLocation = 'TypeParameter_bound'; |
| } |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitVariableDeclaration(VariableDeclaration node) { |
| // Make suggestions for the RHS of a variable declaration |
| if (identical(entity, node.initializer)) { |
| optype.completionLocation = 'VariableDeclaration_initializer'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationList(VariableDeclarationList node) { |
| if (node.keyword == null || node.keyword.lexeme != 'var') { |
| if (node.type == null || identical(entity, node.type)) { |
| optype.includeTypeNameSuggestions = true; |
| } else if (node.type != null && entity is VariableDeclaration) { |
| optype.includeVarNameSuggestions = true; |
| } |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationStatement(VariableDeclarationStatement node) {} |
| |
| @override |
| void visitWhileStatement(WhileStatement node) { |
| if (identical(entity, node.condition)) { |
| optype.completionLocation = 'WhileStatement_condition'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } else if (identical(entity, node.body)) { |
| optype.completionLocation = 'WhileStatement_body'; |
| } |
| } |
| |
| @override |
| void visitWithClause(WithClause node) { |
| if (node.mixinTypes.contains(entity)) { |
| optype.completionLocation = 'WithClause_mixinType'; |
| } |
| optype.includeTypeNameSuggestions = true; |
| } |
| |
| @override |
| void visitYieldStatement(YieldStatement node) { |
| if (identical(entity, node.expression)) { |
| optype.completionLocation = 'YieldStatement_expression'; |
| optype.includeReturnValueSuggestions = true; |
| optype.includeTypeNameSuggestions = true; |
| } |
| } |
| |
| /// Return the context in which the [node] occurs. The [node] is expected to |
| /// be the parent of the argument expression. |
| String _argumentListContext(AstNode node) { |
| if (node is ArgumentList) { |
| var parent = node.parent; |
| if (parent is InstanceCreationExpression) { |
| // TODO(brianwilkerson) Enable this case. |
| // if (flutter.isWidgetType(parent.staticType)) { |
| // return 'widgetConstructor'; |
| // } |
| return 'constructor'; |
| } else if (parent is MethodInvocation) { |
| return 'method'; |
| } else if (parent is FunctionExpressionInvocation) { |
| return 'function'; |
| } else if (parent is SuperConstructorInvocation || |
| parent is RedirectingConstructorInvocation) { |
| return 'constructorRedirect'; |
| } else if (parent is Annotation) { |
| return 'annotation'; |
| } |
| } else if (node is IndexExpression) { |
| return 'index'; |
| } else if (node is AssignmentExpression || |
| node is BinaryExpression || |
| node is PrefixExpression || |
| node is PostfixExpression) { |
| return 'operator'; |
| } |
| throw ArgumentError('Unknown parent of ${node.runtimeType}'); |
| } |
| |
| bool _isEntityPrevTokenSynthetic() { |
| var entity = this.entity; |
| if (entity is AstNode) { |
| var previous = entity.findPrevious(entity.beginToken); |
| if (previous?.isSynthetic ?? false) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// A filter used to disable everything except classes (such as functions and |
| /// mixins). |
| int _nonMixinClasses(DartType type, int relevance) { |
| if (type is InterfaceType) { |
| if (type.element.isMixin) { |
| return null; |
| } |
| return relevance; |
| } |
| return null; |
| } |
| |
| static bool _isParameterOfGenericFunctionType(FormalParameter node) { |
| var parameterList = node.parent; |
| if (parameterList is DefaultFormalParameter) { |
| parameterList = parameterList.parent; |
| } |
| return parameterList is FormalParameterList && |
| parameterList.parent is GenericFunctionType; |
| } |
| } |