Skip to content

Commit

Permalink
Version 2.16.0-111.0.dev
Browse files Browse the repository at this point in the history
Merge commit '78b7fc5ac19d6c6550438aa5ecdbd19983c69c91' into 'dev'
  • Loading branch information
Dart CI committed Dec 15, 2021
2 parents cae3737 + 78b7fc5 commit a04f92c
Show file tree
Hide file tree
Showing 63 changed files with 1,492 additions and 1,219 deletions.
10 changes: 8 additions & 2 deletions pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1259,8 +1259,14 @@ class ForwardingListener implements Listener {

@override
void handleCommentReference(
Token? newKeyword, Token? prefix, Token? period, Token token) {
listener?.handleCommentReference(newKeyword, prefix, period, token);
Token? newKeyword,
Token? firstToken,
Token? firstPeriod,
Token? secondToken,
Token? secondPeriod,
Token thirdToken) {
listener?.handleCommentReference(newKeyword, firstToken, firstPeriod,
secondToken, secondPeriod, thirdToken);
}

@override
Expand Down
13 changes: 10 additions & 3 deletions pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1840,13 +1840,20 @@ class Listener implements UnescapeErrorListener {

/// A single comment reference has been parsed.
/// * [newKeyword] may be null.
/// * [prefix] and [period] are either both tokens or both `null`.
/// * [token] can be an identifier or an operator.
/// * [firstToken] and [firstPeriod] are either both tokens or both
/// `null`.
/// * [secondToken] and [secondPeriod] are either both tokens or both `null`.
/// * [thirdToken] can be an identifier or an operator.
///
/// This event is generated by the parser when the parser's
/// `parseOneCommentReference` method is called.
void handleCommentReference(
Token? newKeyword, Token? prefix, Token? period, Token token) {}
Token? newKeyword,
Token? firstToken,
Token? firstPeriod,
Token? secondToken,
Token? secondPeriod,
Token thirdToken) {}

/// This event is generated by the parser when the parser's
/// `parseOneCommentReference` method is called.
Expand Down
41 changes: 25 additions & 16 deletions pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8355,26 +8355,33 @@ class Parser {
newKeyword = token;
token = token.next!;
}
Token? prefix, period;
Token? firstToken, firstPeriod, secondToken, secondPeriod;
if (token.isIdentifier && optional('.', token.next!)) {
prefix = token;
period = token.next!;
Token identifier = period.next!;
secondToken = token;
secondPeriod = token.next!;
if (secondPeriod.next!.isIdentifier &&
optional('.', secondPeriod.next!.next!)) {
firstToken = secondToken;
firstPeriod = secondPeriod;
secondToken = secondPeriod.next!;
secondPeriod = secondToken.next!;
}
Token identifier = secondPeriod.next!;
if (identifier.kind == KEYWORD_TOKEN && optional('new', identifier)) {
// Treat `new` after `.` is as an identifier so that it can represent an
// unnamed constructor. This support is separate from the
// constructor-tearoffs feature.
rewriter.replaceTokenFollowing(
period,
secondPeriod,
new StringToken(TokenType.IDENTIFIER, identifier.lexeme,
identifier.charOffset));
}
token = period.next!;
token = secondPeriod.next!;
}
if (token.isEof) {
// Recovery: Insert a synthetic identifier for code completion
token = rewriter.insertSyntheticIdentifier(
period ?? newKeyword ?? syntheticPreviousToken(token));
secondPeriod ?? newKeyword ?? syntheticPreviousToken(token));
if (begin == token.next!) {
begin = token;
}
Expand All @@ -8386,21 +8393,21 @@ class Parser {
}
if (token.isUserDefinableOperator) {
if (token.next!.isEof) {
parseOneCommentReferenceRest(
begin, referenceOffset, newKeyword, prefix, period, token);
parseOneCommentReferenceRest(begin, referenceOffset, newKeyword,
firstToken, firstPeriod, secondToken, secondPeriod, token);
return true;
}
} else {
token = operatorKeyword ?? token;
if (token.next!.isEof) {
if (token.isIdentifier) {
parseOneCommentReferenceRest(
begin, referenceOffset, newKeyword, prefix, period, token);
parseOneCommentReferenceRest(begin, referenceOffset, newKeyword,
firstToken, firstPeriod, secondToken, secondPeriod, token);
return true;
}
Keyword? keyword = token.keyword;
if (newKeyword == null &&
prefix == null &&
secondToken == null &&
(keyword == Keyword.THIS ||
keyword == Keyword.NULL ||
keyword == Keyword.TRUE ||
Expand All @@ -8421,8 +8428,10 @@ class Parser {
Token begin,
int referenceOffset,
Token? newKeyword,
Token? prefix,
Token? period,
Token? firstToken,
Token? firstPeriod,
Token? secondToken,
Token? secondPeriod,
Token identifierOrOperator) {
// Adjust the token offsets to match the enclosing comment token.
Token token = begin;
Expand All @@ -8431,8 +8440,8 @@ class Parser {
token = token.next!;
} while (!token.isEof);

listener.handleCommentReference(
newKeyword, prefix, period, identifierOrOperator);
listener.handleCommentReference(newKeyword, firstToken, firstPeriod,
secondToken, secondPeriod, identifierOrOperator);
}

/// Given that we have just found bracketed text within the given [comment],
Expand Down
2 changes: 2 additions & 0 deletions pkg/analyzer/lib/src/dart/analysis/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ class _IndexContributor extends GeneralizingAstVisitor {
return;
}
}
} else if (expression is PropertyAccess) {
// Nothing to do?
} else {
throw UnimplementedError('Unhandled CommentReference expression type: '
'${expression.runtimeType}');
Expand Down
198 changes: 198 additions & 0 deletions pkg/analyzer/lib/src/dart/resolver/comment_reference_resolver.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright (c) 2021, 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/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/generated/resolver.dart';

class CommentReferenceResolver {
final TypeProviderImpl _typeProvider;

final ResolverVisitor _resolver;

/// Helper for resolving properties on types.
final TypePropertyResolver _typePropertyResolver;

CommentReferenceResolver(this._typeProvider, this._resolver)
: _typePropertyResolver = _resolver.typePropertyResolver;

/// Resolves [commentReference].
void resolve(CommentReference commentReference) {
var expression = commentReference.expression;
if (expression is SimpleIdentifierImpl) {
_resolveSimpleIdentifierReference(expression,
hasNewKeyword: commentReference.newKeyword != null);
} else if (expression is PrefixedIdentifierImpl) {
_resolvePrefixedIdentifierReference(expression,
hasNewKeyword: commentReference.newKeyword != null);
} else if (expression is PropertyAccessImpl) {
_resolvePropertyAccessReference(expression,
hasNewKeyword: commentReference.newKeyword != null);
}
}

void _resolvePrefixedIdentifierReference(
PrefixedIdentifierImpl expression, {
required bool hasNewKeyword,
}) {
var prefix = expression.prefix;
var prefixElement = _resolveSimpleIdentifier(prefix);
prefix.staticElement = prefixElement;

if (prefixElement == null) {
return;
}

var name = expression.identifier;

if (prefixElement is PrefixElement) {
var prefixScope = prefixElement.scope;
var lookupResult = prefixScope.lookup(name.name);
var element = lookupResult.getter ?? lookupResult.setter;
element = _resolver.toLegacyElement(element);
name.staticElement = element;
return;
}

if (!hasNewKeyword) {
if (prefixElement is ClassElement) {
name.staticElement = prefixElement.getMethod(name.name) ??
prefixElement.getGetter(name.name) ??
prefixElement.getSetter(name.name) ??
prefixElement.getNamedConstructor(name.name);
} else if (prefixElement is ExtensionElement) {
name.staticElement = prefixElement.getMethod(name.name) ??
prefixElement.getGetter(name.name) ??
prefixElement.getSetter(name.name);
} else {
// TODO(brianwilkerson) Report this error.
}
} else if (prefixElement is ClassElement) {
var constructor = prefixElement.getNamedConstructor(name.name);
if (constructor == null) {
// TODO(brianwilkerson) Report this error.
} else {
name.staticElement = constructor;
}
} else {
// TODO(brianwilkerson) Report this error.
}
}

void _resolvePropertyAccessReference(
PropertyAccessImpl expression, {
required bool hasNewKeyword,
}) {
var target = expression.target;
if (target is! PrefixedIdentifierImpl) {
// A PropertyAccess with a target more complex than a
// [PrefixedIdentifier] is not a valid comment reference.
return;
}

var prefix = target.prefix;
var prefixElement = _resolveSimpleIdentifier(prefix);
prefix.staticElement = prefixElement;

if (prefixElement is! PrefixElement) {
// The only valid prefixElement is a PrefixElement; otherwise, this is
// not a comment reference.
return;
}

var name = target.identifier;
var prefixScope = prefixElement.scope;
var lookupResult = prefixScope.lookup(name.name);
var element = lookupResult.getter ?? lookupResult.setter;
element = _resolver.toLegacyElement(element);
name.staticElement = element;

var propertyName = expression.propertyName;
if (element is ClassElement) {
propertyName.staticElement = element.getMethod(propertyName.name) ??
element.getGetter(propertyName.name) ??
element.getSetter(propertyName.name) ??
element.getNamedConstructor(propertyName.name);
} else if (element is ExtensionElement) {
propertyName.staticElement = element.getMethod(propertyName.name) ??
element.getGetter(propertyName.name) ??
element.getSetter(propertyName.name);
}
}

/// Resolves the given simple [identifier] if possible.
///
/// Returns the resolved element, or `null` if the identifier could not be
/// resolved. This does not record the results of the resolution.
Element? _resolveSimpleIdentifier(SimpleIdentifierImpl identifier) {
var lookupResult = identifier.scopeLookupResult!;

var element = _resolver.toLegacyElement(lookupResult.getter) ??
_resolver.toLegacyElement(lookupResult.setter);

if (element == null) {
InterfaceType enclosingType;
var enclosingClass = _resolver.enclosingClass;
if (enclosingClass != null) {
enclosingType = enclosingClass.thisType;
} else {
var enclosingExtension = _resolver.enclosingExtension;
if (enclosingExtension == null) {
return null;
}
var extendedType = enclosingExtension.extendedType
.resolveToBound(_typeProvider.objectType);
if (extendedType is InterfaceType) {
enclosingType = extendedType;
} else if (extendedType is FunctionType) {
enclosingType = _typeProvider.functionType;
} else {
return null;
}
}
var result = _typePropertyResolver.resolve(
receiver: null,
receiverType: enclosingType,
name: identifier.name,
propertyErrorEntity: identifier,
nameErrorEntity: identifier,
);
if (identifier.parent is CommentReference) {
// TODO(srawlins): Why is the setter preferred? This seems very flawed
// as it will only use the setter for a [SimpleIdentifier] comment
// reference, and not a [PrefixedIdentifier] or a [PropertyAccess].
element = result.setter;
}
element ??= result.getter;
}
return element;
}

void _resolveSimpleIdentifierReference(
SimpleIdentifierImpl expression, {
required bool hasNewKeyword,
}) {
var element = _resolveSimpleIdentifier(expression);
if (element == null) {
return;
}
expression.staticElement = element;
if (hasNewKeyword) {
if (element is ClassElement) {
var constructor = element.unnamedConstructor;
if (constructor == null) {
// TODO(brianwilkerson) Report this error.
} else {
expression.staticElement = constructor;
}
} else {
// TODO(brianwilkerson) Report this error.
}
}
}
}
25 changes: 19 additions & 6 deletions pkg/analyzer/lib/src/fasta/ast_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2668,13 +2668,26 @@ class AstBuilder extends StackListener {

@override
void handleCommentReference(
Token? newKeyword, Token? prefix, Token? period, Token token) {
Identifier identifier = ast.simpleIdentifier(token);
if (prefix != null) {
identifier = ast.prefixedIdentifier(ast.simpleIdentifier(prefix), period!,
identifier as SimpleIdentifier);
Token? newKeyword,
Token? firstToken,
Token? firstPeriod,
Token? secondToken,
Token? secondPeriod,
Token thirdToken,
) {
var identifier = ast.simpleIdentifier(thirdToken);
if (firstToken != null) {
var target = ast.prefixedIdentifier(ast.simpleIdentifier(firstToken),
firstPeriod!, ast.simpleIdentifier(secondToken!));
var expression = ast.propertyAccess(target, secondPeriod!, identifier);
push(ast.commentReference(newKeyword, expression));
} else if (secondToken != null) {
var expression = ast.prefixedIdentifier(
ast.simpleIdentifier(secondToken), secondPeriod!, identifier);
push(ast.commentReference(newKeyword, expression));
} else {
push(ast.commentReference(newKeyword, identifier));
}
push(ast.commentReference(newKeyword, identifier));
}

@override
Expand Down
Loading

0 comments on commit a04f92c

Please sign in to comment.