afcfbbeba8
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the packages owned by the developer experience team to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the packages' SDK constraints to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Since this change requires bumping SDK constaints to `3.13.0-0`, it was only performed on packages that are *not* published on pub. (Packages that *are* published on pub should remain on lower language versions until at least after the stable version of 3.13 is released, so that we don't block users on the stable channel from receiving updates to those packages.) Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
196 lines
6.5 KiB
Dart
196 lines
6.5 KiB
Dart
// Copyright (c) 2018, 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:analysis_server/src/computer/computer_documentation.dart';
|
|
import 'package:analysis_server/src/protocol_server.dart' hide Element;
|
|
import 'package:analysis_server/src/utilities/extensions/ast.dart';
|
|
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/element_locator.dart';
|
|
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
|
|
|
|
/// A computer for the signature at the specified offset of a Dart
|
|
/// [CompilationUnit].
|
|
class DartUnitSignatureComputer {
|
|
final AstNode? _node;
|
|
final int _offset;
|
|
final DocumentationPreference documentationPreference;
|
|
final DartDocumentationComputer _documentationComputer;
|
|
|
|
new(
|
|
DartdocDirectiveInfo dartdocInfo,
|
|
CompilationUnit unit,
|
|
this._offset, {
|
|
this.documentationPreference = DocumentationPreference.full,
|
|
}) : _documentationComputer = DartDocumentationComputer(dartdocInfo),
|
|
_node = unit.nodeCovering(offset: _offset);
|
|
|
|
bool get offsetIsValid => _node != null;
|
|
|
|
/// Returns the computed signature information, maybe `null`.
|
|
SignatureInformation? compute() {
|
|
var argumentAndList = _findArgumentAndList();
|
|
if (argumentAndList == null) {
|
|
return null;
|
|
}
|
|
var (argumentList, argument) = argumentAndList;
|
|
String? name;
|
|
Element? element;
|
|
List<FormalParameterElement>? parameters;
|
|
var parent = argumentList.parent;
|
|
if (parent is MethodInvocation) {
|
|
name = parent.methodName.name;
|
|
element = ElementLocator.locate(parent);
|
|
parameters = element is FunctionTypedElement
|
|
? element.formalParameters
|
|
: null;
|
|
} else if (parent is InstanceCreationExpression) {
|
|
name = parent.constructorName.type.qualifiedName;
|
|
var constructorName = parent.constructorName.name;
|
|
if (constructorName != null) {
|
|
name += '.${constructorName.name}';
|
|
}
|
|
element = ElementLocator.locate(parent);
|
|
parameters = element is FunctionTypedElement
|
|
? element.formalParameters
|
|
: null;
|
|
} else if (parent case FunctionExpressionInvocation(
|
|
function: Identifier function,
|
|
)) {
|
|
name = function.name;
|
|
|
|
if (function.staticType case FunctionType functionType) {
|
|
// Standard function expression.
|
|
element = function.element;
|
|
parameters = functionType.formalParameters;
|
|
} else if (parent.element case ExecutableElement executableElement) {
|
|
// Callable class instance (where we'll look at the `call` method).
|
|
element = executableElement;
|
|
parameters = executableElement.formalParameters;
|
|
}
|
|
}
|
|
|
|
if (name == null || element == null || parameters == null) {
|
|
return null;
|
|
}
|
|
|
|
// Try to compute the active parameter so the IDE can highlight it.
|
|
int? activeParameterIndex;
|
|
if (argument case Argument(:var correspondingParameter?)) {
|
|
// If we know the active parameter, use its index.
|
|
activeParameterIndex = parameters.indexOf(correspondingParameter);
|
|
} else if (argument is! NamedArgument) {
|
|
// If we're not a named expression, then we can count how many positional
|
|
// parameters there are before us, and then find the index of the same
|
|
// index positional parameter.
|
|
var positionalArgsToSkip = argumentList.arguments
|
|
.where((argument) => argument is! NamedArgument)
|
|
.takeWhile((argument) => argument.end < _offset)
|
|
.length;
|
|
for (var i = 0; i < parameters.length; i++) {
|
|
if (parameters[i].isPositional) {
|
|
// This is the first positional parameter after our skips, so this is
|
|
// the active parameter.
|
|
if (positionalArgsToSkip == 0) {
|
|
activeParameterIndex = i;
|
|
break;
|
|
}
|
|
positionalArgsToSkip--;
|
|
}
|
|
}
|
|
}
|
|
|
|
var dartdoc = _documentationComputer.computePreferred(
|
|
element,
|
|
documentationPreference,
|
|
);
|
|
|
|
return SignatureInformation(
|
|
name: name,
|
|
parameters: parameters,
|
|
argumentList: argumentList,
|
|
activeParameterIndex: activeParameterIndex,
|
|
dartdoc: dartdoc,
|
|
);
|
|
}
|
|
|
|
/// Return the closest argument list surrounding the [_node] and the node for
|
|
/// the active argument (if there is one).
|
|
(ArgumentList, AstNode?)? _findArgumentAndList() {
|
|
var node = _node;
|
|
while (node != null) {
|
|
// Certain nodes don't make sense to search above for an argument list
|
|
// (for example when inside a function expression).
|
|
if (node is FunctionExpression) {
|
|
return null;
|
|
}
|
|
|
|
if (node is ArgumentList) {
|
|
return (node, null);
|
|
}
|
|
if (node.parent case ArgumentList list) {
|
|
return (list, node);
|
|
}
|
|
|
|
node = node.parent;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Information about a function signature.
|
|
class SignatureInformation {
|
|
/// The name of the function/method.
|
|
final String name;
|
|
|
|
/// The parameters for the function/method.
|
|
final List<FormalParameterElement> parameters;
|
|
|
|
/// The current argument list at the invocation site.
|
|
final ArgumentList argumentList;
|
|
|
|
/// Documentation for the function/method.
|
|
final String? dartdoc;
|
|
|
|
/// The index in [parameters] for the parameter that matches where the offset
|
|
/// was in the invocation list.
|
|
///
|
|
/// This is only supplied when it can be computed. Positional arguments past
|
|
/// the number of positional parameters or named arguments with no matching
|
|
/// name will not be returned.
|
|
final int? activeParameterIndex;
|
|
|
|
new({
|
|
required this.name,
|
|
required this.parameters,
|
|
required this.argumentList,
|
|
required this.activeParameterIndex,
|
|
required this.dartdoc,
|
|
});
|
|
|
|
AnalysisGetSignatureResult toLegacyProtocol() {
|
|
return AnalysisGetSignatureResult(
|
|
name,
|
|
parameters.map(_convertParam).toList(),
|
|
dartdoc: dartdoc,
|
|
);
|
|
}
|
|
|
|
ParameterInfo _convertParam(FormalParameterElement param) {
|
|
return ParameterInfo(
|
|
param.isOptionalNamed
|
|
? ParameterKind.OPTIONAL_NAMED
|
|
: param.isOptionalPositional
|
|
? ParameterKind.OPTIONAL_POSITIONAL
|
|
: param.isRequiredNamed
|
|
? ParameterKind.REQUIRED_NAMED
|
|
: ParameterKind.REQUIRED_POSITIONAL,
|
|
param.displayName,
|
|
param.type.getDisplayString(),
|
|
defaultValue: param.defaultValueCode,
|
|
);
|
|
}
|
|
}
|