|  | // Copyright (c) 2020, 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/element/element.dart'; | 
|  | import 'package:analyzer/dart/element/type.dart'; | 
|  | import 'package:analyzer/src/dart/ast/ast.dart'; | 
|  | import 'package:analyzer/src/dart/element/element.dart'; | 
|  | import 'package:analyzer/src/dart/element/type.dart'; | 
|  | import 'package:analyzer/src/dart/element/type_provider.dart'; | 
|  | import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart'; | 
|  | import 'package:analyzer/src/dart/resolver/property_element_resolver.dart'; | 
|  | import 'package:analyzer/src/error/codes.dart'; | 
|  | import 'package:analyzer/src/generated/inference_log.dart'; | 
|  | import 'package:analyzer/src/generated/resolver.dart'; | 
|  |  | 
|  | class PrefixedIdentifierResolver { | 
|  | final ResolverVisitor _resolver; | 
|  |  | 
|  | PrefixedIdentifierResolver(this._resolver); | 
|  |  | 
|  | InvocationInferenceHelper get _inferenceHelper => _resolver.inferenceHelper; | 
|  |  | 
|  | TypeProviderImpl get _typeProvider => _resolver.typeProvider; | 
|  |  | 
|  | PropertyAccessImpl? resolve( | 
|  | PrefixedIdentifierImpl node, { | 
|  | required DartType contextType, | 
|  | }) { | 
|  | _resolver.analyzeExpression(node.prefix, _resolver.operations.unknownType); | 
|  | _resolver.popRewrite(); | 
|  | _resolver.checkUnreachableNode(node.identifier); | 
|  |  | 
|  | var prefixElement = node.prefix.element; | 
|  | if (prefixElement is! PrefixElement) { | 
|  | var prefixType = node.prefix.staticType; | 
|  | // TODO(scheglov): It would be nice to rewrite all such cases. | 
|  | if (prefixType != null) { | 
|  | var prefixTypeResolved = _resolver.typeSystem.resolveToBound( | 
|  | prefixType, | 
|  | ); | 
|  | if (prefixTypeResolved is RecordType) { | 
|  | var propertyAccess = PropertyAccessImpl( | 
|  | target: node.prefix, | 
|  | operator: node.period, | 
|  | propertyName: node.identifier, | 
|  | ); | 
|  | _resolver.replaceExpression(node, propertyAccess); | 
|  | return propertyAccess; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | var resolver = PropertyElementResolver(_resolver); | 
|  | var result = resolver.resolvePrefixedIdentifier( | 
|  | node: node, | 
|  | hasRead: true, | 
|  | hasWrite: false, | 
|  | ); | 
|  |  | 
|  | var element = result.readElement2; | 
|  |  | 
|  | var identifier = node.identifier; | 
|  | identifier.element = element; | 
|  |  | 
|  | if (element is ExtensionElement) { | 
|  | _setExtensionIdentifierType(node); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (identical(node.prefix.staticType, NeverTypeImpl.instance)) { | 
|  | identifier.setPseudoExpressionStaticType(NeverTypeImpl.instance); | 
|  | node.recordStaticType(NeverTypeImpl.instance, resolver: _resolver); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | DartType type = InvalidTypeImpl.instance; | 
|  | if (result.readElementRequested2 == null && | 
|  | result.readElementRecovery2 != null) { | 
|  | // Since the element came from error recovery logic, its type isn't | 
|  | // trustworthy; leave it as `dynamic`. | 
|  | } else if (element is InterfaceElement) { | 
|  | if (_isExpressionIdentifier(node)) { | 
|  | var type = _typeProvider.typeType; | 
|  | node.recordStaticType(type, resolver: _resolver); | 
|  | identifier.setPseudoExpressionStaticType(type); | 
|  | } else { | 
|  | inferenceLogWriter?.recordExpressionWithNoType(node); | 
|  | } | 
|  | return null; | 
|  | } else if (element is DynamicElementImpl) { | 
|  | var type = _typeProvider.typeType; | 
|  | node.recordStaticType(type, resolver: _resolver); | 
|  | identifier.setPseudoExpressionStaticType(type); | 
|  | return null; | 
|  | } else if (element is TypeAliasElement) { | 
|  | if (node.parent is NamedType) { | 
|  | // no type | 
|  | } else { | 
|  | var type = _typeProvider.typeType; | 
|  | node.recordStaticType(type, resolver: _resolver); | 
|  | identifier.setPseudoExpressionStaticType(type); | 
|  | } | 
|  | return null; | 
|  | } else if (element is MethodElement) { | 
|  | type = element.type; | 
|  | } else if (element is PropertyAccessorElement) { | 
|  | type = result.getType!; | 
|  | } else if (element is ExecutableElement) { | 
|  | type = element.type; | 
|  | } else if (element is VariableElement) { | 
|  | type = element.type; | 
|  | } else if (result.functionTypeCallType != null) { | 
|  | type = result.functionTypeCallType!; | 
|  | } else if (result.atDynamicTarget) { | 
|  | type = DynamicTypeImpl.instance; | 
|  | } | 
|  |  | 
|  | if (!_resolver.isConstructorTearoffsEnabled) { | 
|  | // Only perform a generic function instantiation on a [PrefixedIdentifier] | 
|  | // in pre-constructor-tearoffs code. In constructor-tearoffs-enabled code, | 
|  | // generic function instantiation is performed at assignability check | 
|  | // sites. | 
|  | // TODO(srawlins): Switch all resolution to use the latter method, in a | 
|  | // breaking change release. | 
|  | type = _inferenceHelper.inferTearOff( | 
|  | node, | 
|  | identifier, | 
|  | type, | 
|  | contextType: contextType, | 
|  | ); | 
|  | } | 
|  | identifier.setPseudoExpressionStaticType(type); | 
|  | node.recordStaticType(type, resolver: _resolver); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /// Return `true` if the given [node] is not a type literal. | 
|  | /// | 
|  | // TODO(scheglov): this is duplicate | 
|  | bool _isExpressionIdentifier(Identifier node) { | 
|  | var parent = node.parent; | 
|  | if (node is SimpleIdentifier && node.inDeclarationContext()) { | 
|  | return false; | 
|  | } | 
|  | if (parent is ConstructorDeclaration) { | 
|  | if (parent.returnType == node) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | if (parent is ConstructorName || | 
|  | parent is MethodInvocation || | 
|  | parent is PrefixedIdentifier && parent.prefix == node || | 
|  | parent is PropertyAccess || | 
|  | parent is NamedType) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // TODO(scheglov): this is duplicate | 
|  | void _setExtensionIdentifierType(IdentifierImpl node) { | 
|  | if (node is SimpleIdentifierImpl && node.inDeclarationContext()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var parent = node.parent; | 
|  |  | 
|  | if (parent is PrefixedIdentifierImpl && parent.identifier == node) { | 
|  | node = parent; | 
|  | parent = node.parent; | 
|  | } | 
|  |  | 
|  | if (parent is CommentReference || | 
|  | parent is MethodInvocation && parent.target == node || | 
|  | parent is PrefixedIdentifierImpl && parent.prefix == node || | 
|  | parent is PropertyAccess && parent.target == node) { | 
|  | inferenceLogWriter?.recordExpressionWithNoType(node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | _resolver.diagnosticReporter.atNode( | 
|  | node, | 
|  | CompileTimeErrorCode.extensionAsExpression, | 
|  | arguments: [node.name], | 
|  | ); | 
|  |  | 
|  | if (node is PrefixedIdentifierImpl) { | 
|  | node.identifier.setPseudoExpressionStaticType(DynamicTypeImpl.instance); | 
|  | node.recordStaticType(DynamicTypeImpl.instance, resolver: _resolver); | 
|  | } else if (node is SimpleIdentifier) { | 
|  | node.recordStaticType(DynamicTypeImpl.instance, resolver: _resolver); | 
|  | } | 
|  | } | 
|  | } |