diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html index dd02b959fa2..b8f17eb6d13 100644 --- a/pkg/analysis_server/doc/api.html +++ b/pkg/analysis_server/doc/api.html @@ -548,6 +548,7 @@ a:focus, a:hover { +

Requests

analysis.getErrors
request: {
@@ -4242,6 +4243,19 @@ a:focus, a:hover {
           which does not match a file currently subject to
           analysis.
         

+
GET_SIGNATURE_INVALID_FILE
+ +

+ An "analysis.getSignature" request specified a FilePath + which does not match a file currently subject to + analysis. +

+
GET_SIGNATURE_UNKNOWN_FUNCTION
+ +

+ An "analysis.getSignature" request specified an offset + that could not be matched to a function call. +

IMPORT_ELEMENTS_INVALID_FILE

diff --git a/pkg/analysis_server/lib/protocol/protocol.dart b/pkg/analysis_server/lib/protocol/protocol.dart index 730887e5180..697fa5695b0 100644 --- a/pkg/analysis_server/lib/protocol/protocol.dart +++ b/pkg/analysis_server/lib/protocol/protocol.dart @@ -488,6 +488,25 @@ class Response { RequestErrorCode.GET_REACHABLE_SOURCES_INVALID_FILE, 'Error during `analysis.getReachableSources`: invalid file.')); + /** + * Initialize a newly created instance to represent the + * GET_SIGNATURE_INVALID_FILE error condition. + */ + Response.getSignatureInvalidFile(Request request) + : this(request.id, + error: new RequestError(RequestErrorCode.GET_SIGNATURE_INVALID_FILE, + 'Error during `analysis.getSignature`: invalid file.')); + + /** + * Initialize a newly created instance to represent the + * GET_SIGNATURE_UNKNOWN_FUNCTION error condition. + */ + Response.getSignatureUnknownFunction(Request request) + : this(request.id, + error: new RequestError( + RequestErrorCode.GET_SIGNATURE_UNKNOWN_FUNCTION, + 'Error during `analysis.getSignature`: unknown function.')); + /** * Initialize a newly created instance to represent the * IMPORT_ELEMENTS_INVALID_FILE error condition. diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart index fcd43003aed..ffcb4467c95 100644 --- a/pkg/analysis_server/lib/protocol/protocol_constants.dart +++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart @@ -66,6 +66,9 @@ const String ANALYSIS_REQUEST_GET_NAVIGATION_OFFSET = 'offset'; const String ANALYSIS_REQUEST_GET_REACHABLE_SOURCES = 'analysis.getReachableSources'; const String ANALYSIS_REQUEST_GET_REACHABLE_SOURCES_FILE = 'file'; +const String ANALYSIS_REQUEST_GET_SIGNATURE = 'analysis.getSignature'; +const String ANALYSIS_REQUEST_GET_SIGNATURE_FILE = 'file'; +const String ANALYSIS_REQUEST_GET_SIGNATURE_OFFSET = 'offset'; const String ANALYSIS_REQUEST_REANALYZE = 'analysis.reanalyze'; const String ANALYSIS_REQUEST_REANALYZE_ROOTS = 'roots'; const String ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS = 'analysis.setAnalysisRoots'; @@ -94,6 +97,11 @@ const String ANALYSIS_RESPONSE_GET_NAVIGATION_FILES = 'files'; const String ANALYSIS_RESPONSE_GET_NAVIGATION_REGIONS = 'regions'; const String ANALYSIS_RESPONSE_GET_NAVIGATION_TARGETS = 'targets'; const String ANALYSIS_RESPONSE_GET_REACHABLE_SOURCES_SOURCES = 'sources'; +const String ANALYSIS_RESPONSE_GET_SIGNATURE_DARTDOC = 'dartdoc'; +const String ANALYSIS_RESPONSE_GET_SIGNATURE_NAME = 'name'; +const String ANALYSIS_RESPONSE_GET_SIGNATURE_PARAMETERS = 'parameters'; +const String ANALYSIS_RESPONSE_GET_SIGNATURE_SELECTED_PARAMETER_INDEX = + 'selectedParameterIndex'; const String ANALYTICS_REQUEST_ENABLE = 'analytics.enable'; const String ANALYTICS_REQUEST_ENABLE_VALUE = 'value'; const String ANALYTICS_REQUEST_IS_ENABLED = 'analytics.isEnabled'; diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart index 7175c14044c..1887aacac99 100644 --- a/pkg/analysis_server/lib/protocol/protocol_generated.dart +++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart @@ -1903,6 +1903,302 @@ class AnalysisGetReachableSourcesResult implements ResponseResult { } } +/** + * analysis.getSignature params + * + * { + * "file": FilePath + * "offset": int + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class AnalysisGetSignatureParams implements RequestParams { + String _file; + + int _offset; + + /** + * The file in which signature information is being requested. + */ + String get file => _file; + + /** + * The file in which signature information is being requested. + */ + void set file(String value) { + assert(value != null); + this._file = value; + } + + /** + * The location for which signature information is being requested. + */ + int get offset => _offset; + + /** + * The location for which signature information is being requested. + */ + void set offset(int value) { + assert(value != null); + this._offset = value; + } + + AnalysisGetSignatureParams(String file, int offset) { + this.file = file; + this.offset = offset; + } + + factory AnalysisGetSignatureParams.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String file; + if (json.containsKey("file")) { + file = jsonDecoder.decodeString(jsonPath + ".file", json["file"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "file"); + } + int offset; + if (json.containsKey("offset")) { + offset = jsonDecoder.decodeInt(jsonPath + ".offset", json["offset"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "offset"); + } + return new AnalysisGetSignatureParams(file, offset); + } else { + throw jsonDecoder.mismatch( + jsonPath, "analysis.getSignature params", json); + } + } + + factory AnalysisGetSignatureParams.fromRequest(Request request) { + return new AnalysisGetSignatureParams.fromJson( + new RequestDecoder(request), "params", request.params); + } + + @override + Map toJson() { + Map result = {}; + result["file"] = file; + result["offset"] = offset; + return result; + } + + @override + Request toRequest(String id) { + return new Request(id, "analysis.getSignature", toJson()); + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is AnalysisGetSignatureParams) { + return file == other.file && offset == other.offset; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, file.hashCode); + hash = JenkinsSmiHash.combine(hash, offset.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + +/** + * analysis.getSignature result + * + * { + * "name": String + * "dartdoc": String + * "parameters": List + * "selectedParameterIndex": int + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class AnalysisGetSignatureResult implements ResponseResult { + String _name; + + String _dartdoc; + + List _parameters; + + int _selectedParameterIndex; + + /** + * The name of the function being invoked at the given offset. + */ + String get name => _name; + + /** + * The name of the function being invoked at the given offset. + */ + void set name(String value) { + assert(value != null); + this._name = value; + } + + /** + * The dartdoc associated with the function being invoked. Other than the + * removal of the comment delimiters, including leading asterisks in the case + * of a block comment, the dartdoc is unprocessed markdown. This data is + * omitted if there is no referenced element, or if the element has no + * dartdoc. + */ + String get dartdoc => _dartdoc; + + /** + * The dartdoc associated with the function being invoked. Other than the + * removal of the comment delimiters, including leading asterisks in the case + * of a block comment, the dartdoc is unprocessed markdown. This data is + * omitted if there is no referenced element, or if the element has no + * dartdoc. + */ + void set dartdoc(String value) { + assert(value != null); + this._dartdoc = value; + } + + /** + * A list of information about each of the parameters of the function being + * invoked. + */ + List get parameters => _parameters; + + /** + * A list of information about each of the parameters of the function being + * invoked. + */ + void set parameters(List value) { + assert(value != null); + this._parameters = value; + } + + /** + * The index of the paramter in the parameters collection at the specified + * offset. + */ + int get selectedParameterIndex => _selectedParameterIndex; + + /** + * The index of the paramter in the parameters collection at the specified + * offset. + */ + void set selectedParameterIndex(int value) { + assert(value != null); + this._selectedParameterIndex = value; + } + + AnalysisGetSignatureResult(String name, String dartdoc, + List parameters, int selectedParameterIndex) { + this.name = name; + this.dartdoc = dartdoc; + this.parameters = parameters; + this.selectedParameterIndex = selectedParameterIndex; + } + + factory AnalysisGetSignatureResult.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String name; + if (json.containsKey("name")) { + name = jsonDecoder.decodeString(jsonPath + ".name", json["name"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "name"); + } + String dartdoc; + if (json.containsKey("dartdoc")) { + dartdoc = + jsonDecoder.decodeString(jsonPath + ".dartdoc", json["dartdoc"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "dartdoc"); + } + List parameters; + if (json.containsKey("parameters")) { + parameters = jsonDecoder.decodeList( + jsonPath + ".parameters", + json["parameters"], + (String jsonPath, Object json) => + new ParameterInfo.fromJson(jsonDecoder, jsonPath, json)); + } else { + throw jsonDecoder.mismatch(jsonPath, "parameters"); + } + int selectedParameterIndex; + if (json.containsKey("selectedParameterIndex")) { + selectedParameterIndex = jsonDecoder.decodeInt( + jsonPath + ".selectedParameterIndex", + json["selectedParameterIndex"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "selectedParameterIndex"); + } + return new AnalysisGetSignatureResult( + name, dartdoc, parameters, selectedParameterIndex); + } else { + throw jsonDecoder.mismatch( + jsonPath, "analysis.getSignature result", json); + } + } + + factory AnalysisGetSignatureResult.fromResponse(Response response) { + return new AnalysisGetSignatureResult.fromJson( + new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)), + "result", + response.result); + } + + @override + Map toJson() { + Map result = {}; + result["name"] = name; + result["dartdoc"] = dartdoc; + result["parameters"] = + parameters.map((ParameterInfo value) => value.toJson()).toList(); + result["selectedParameterIndex"] = selectedParameterIndex; + return result; + } + + @override + Response toResponse(String id) { + return new Response(id, result: toJson()); + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is AnalysisGetSignatureResult) { + return name == other.name && + dartdoc == other.dartdoc && + listEqual(parameters, other.parameters, + (ParameterInfo a, ParameterInfo b) => a == b) && + selectedParameterIndex == other.selectedParameterIndex; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, name.hashCode); + hash = JenkinsSmiHash.combine(hash, dartdoc.hashCode); + hash = JenkinsSmiHash.combine(hash, parameters.hashCode); + hash = JenkinsSmiHash.combine(hash, selectedParameterIndex.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + /** * analysis.highlights params * @@ -15309,6 +15605,8 @@ class RequestError implements HasToJson { * GET_KYTHE_ENTRIES_INVALID_FILE * GET_NAVIGATION_INVALID_FILE * GET_REACHABLE_SOURCES_INVALID_FILE + * GET_SIGNATURE_INVALID_FILE + * GET_SIGNATURE_UNKNOWN_FUNCTION * IMPORT_ELEMENTS_INVALID_FILE * INVALID_ANALYSIS_ROOT * INVALID_EXECUTION_CONTEXT @@ -15400,6 +15698,20 @@ class RequestErrorCode implements Enum { static const RequestErrorCode GET_REACHABLE_SOURCES_INVALID_FILE = const RequestErrorCode._("GET_REACHABLE_SOURCES_INVALID_FILE"); + /** + * An "analysis.getSignature" request specified a FilePath which does not + * match a file currently subject to analysis. + */ + static const RequestErrorCode GET_SIGNATURE_INVALID_FILE = + const RequestErrorCode._("GET_SIGNATURE_INVALID_FILE"); + + /** + * An "analysis.getSignature" request specified an offset that could not be + * matched to a function call. + */ + static const RequestErrorCode GET_SIGNATURE_UNKNOWN_FUNCTION = + const RequestErrorCode._("GET_SIGNATURE_UNKNOWN_FUNCTION"); + /** * An "edit.importElements" request specified a FilePath that does not match * a file currently subject to analysis. @@ -15540,6 +15852,8 @@ class RequestErrorCode implements Enum { GET_KYTHE_ENTRIES_INVALID_FILE, GET_NAVIGATION_INVALID_FILE, GET_REACHABLE_SOURCES_INVALID_FILE, + GET_SIGNATURE_INVALID_FILE, + GET_SIGNATURE_UNKNOWN_FUNCTION, IMPORT_ELEMENTS_INVALID_FILE, INVALID_ANALYSIS_ROOT, INVALID_EXECUTION_CONTEXT, @@ -15586,6 +15900,10 @@ class RequestErrorCode implements Enum { return GET_NAVIGATION_INVALID_FILE; case "GET_REACHABLE_SOURCES_INVALID_FILE": return GET_REACHABLE_SOURCES_INVALID_FILE; + case "GET_SIGNATURE_INVALID_FILE": + return GET_SIGNATURE_INVALID_FILE; + case "GET_SIGNATURE_UNKNOWN_FUNCTION": + return GET_SIGNATURE_UNKNOWN_FUNCTION; case "IMPORT_ELEMENTS_INVALID_FILE": return IMPORT_ELEMENTS_INVALID_FILE; case "INVALID_ANALYSIS_ROOT": diff --git a/pkg/analysis_server/lib/src/computer/computer_hover.dart b/pkg/analysis_server/lib/src/computer/computer_hover.dart index b33ff14fa58..0905fe781d5 100644 --- a/pkg/analysis_server/lib/src/computer/computer_hover.dart +++ b/pkg/analysis_server/lib/src/computer/computer_hover.dart @@ -75,7 +75,7 @@ class DartUnitHoverComputer { } } // documentation - hover.dartdoc = _computeDocumentation(element); + hover.dartdoc = computeDocumentation(element); } // parameter hover.parameter = _safeToString(expression.staticParameterElement); @@ -103,7 +103,8 @@ class DartUnitHoverComputer { return null; } - String _computeDocumentation(Element element) { + static String computeDocumentation(Element element) { + // TODO(dantup) We're reusing this in parameter information - move it somewhere shared? if (element is FieldFormalParameterElement) { element = (element as FieldFormalParameterElement).field; } diff --git a/pkg/analysis_server/lib/src/computer/computer_signature.dart b/pkg/analysis_server/lib/src/computer/computer_signature.dart new file mode 100644 index 00000000000..a1d734bc240 --- /dev/null +++ b/pkg/analysis_server/lib/src/computer/computer_signature.dart @@ -0,0 +1,68 @@ +// 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/protocol/protocol_generated.dart' + show AnalysisGetSignatureResult; +import 'package:analysis_server/src/computer/computer_hover.dart'; +import 'package:analysis_server/src/protocol_server.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/src/dart/ast/utilities.dart'; + +/** + * A computer for the signature at the specified offset of a Dart [CompilationUnit]. + */ +class DartUnitSignatureComputer { + final CompilationUnit _unit; + final int _offset; + + DartUnitSignatureComputer(this._unit, this._offset); + + /** + * Returns the computed signature information, maybe `null`. + */ + AnalysisGetSignatureResult compute() { + AstNode node = new NodeLocator(_offset).searchWithin(_unit); + if (node == null) { + return null; + } + + // Find the closest argument list. + while (node != null && !(node is ArgumentList)) { + node = node.parent; + } + + if (node == null) { + return null; + } + + final args = node; + String name; + ExecutableElement execElement; + if (args.parent is MethodInvocation) { + MethodInvocation method = args.parent; + name = method.methodName.name; + execElement = ElementLocator.locate(method) as ExecutableElement; + } + + if (execElement == null) { + return null; + } + + final parameters = + execElement.parameters.map((p) => _convertParam(p)).toList(); + + return new AnalysisGetSignatureResult(name, + DartUnitHoverComputer.computeDocumentation(execElement), parameters, 0); + } + + ParameterInfo _convertParam(ParameterElement param) { + return new ParameterInfo( + param.isOptionalPositional + ? ParameterKind.OPTIONAL + : param.isPositional ? ParameterKind.REQUIRED : ParameterKind.NAMED, + param.displayName, + param.type.displayName); + } +} diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart index a5d51998edd..26b8c39bf3f 100644 --- a/pkg/analysis_server/lib/src/domain_analysis.dart +++ b/pkg/analysis_server/lib/src/domain_analysis.dart @@ -8,6 +8,7 @@ import 'dart:core'; import 'package:analysis_server/protocol/protocol_constants.dart'; import 'package:analysis_server/src/analysis_server.dart'; import 'package:analysis_server/src/computer/computer_hover.dart'; +import 'package:analysis_server/src/computer/computer_signature.dart'; import 'package:analysis_server/src/computer/imported_elements_computer.dart'; import 'package:analysis_server/src/domain_abstract.dart'; import 'package:analysis_server/src/domains/analysis/navigation_dart.dart'; @@ -247,6 +248,32 @@ class AnalysisDomainHandler extends AbstractRequestHandler { // .toResponse(request.id); } + /** + * Implement the `analysis.getSignature` request. + */ + Future getSignature(Request request) async { + var params = new AnalysisGetSignatureParams.fromRequest(request); + + // Prepare the resolved units. + AnalysisResult result = await server.getAnalysisResult(params.file); + CompilationUnit unit = result?.unit; + if (unit == null) { + server.sendResponse(Response.getSignatureInvalidFile(request)); + return; + } + + // Prepare the signature. + var signature = + new DartUnitSignatureComputer(unit, params.offset).compute(); + + // Send the response. + if (signature != null) { + server.sendResponse(signature.toResponse(request.id)); + } else { + server.sendResponse(Response.getSignatureUnknownFunction(request)); + } + } + @override Response handleRequest(Request request) { try { @@ -267,6 +294,9 @@ class AnalysisDomainHandler extends AbstractRequestHandler { return Response.DELAYED_RESPONSE; } else if (requestName == ANALYSIS_REQUEST_GET_REACHABLE_SOURCES) { return getReachableSources(request); + } else if (requestName == ANALYSIS_REQUEST_GET_SIGNATURE) { + getSignature(request); + return Response.DELAYED_RESPONSE; } else if (requestName == ANALYSIS_REQUEST_REANALYZE) { return reanalyze(request); } else if (requestName == ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS) { diff --git a/pkg/analysis_server/test/analysis/get_signature_test.dart b/pkg/analysis_server/test/analysis/get_signature_test.dart new file mode 100644 index 00000000000..110e6b80c03 --- /dev/null +++ b/pkg/analysis_server/test/analysis/get_signature_test.dart @@ -0,0 +1,226 @@ +// 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 'dart:async'; + +import 'package:analysis_server/protocol/protocol.dart'; +import 'package:analysis_server/protocol/protocol_generated.dart'; +import 'package:analysis_server/src/protocol_server.dart'; +import 'package:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../analysis_abstract.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(AnalysisSignatureTest); + }); +} + +@reflectiveTest +class AnalysisSignatureTest extends AbstractAnalysisTest { + Future prepareSignature(String search) { + int offset = findOffset(search); + return prepareSignatureAt(offset); + } + + Future prepareSignatureAt(int offset) async { + await waitForTasksFinished(); + Request request = + new AnalysisGetSignatureParams(testFile, offset).toRequest('0'); + Response response = await waitResponse(request); + return new AnalysisGetSignatureResult.fromResponse(response); + } + + @override + void setUp() { + super.setUp(); + createProject(); + } + + test_function_required() async { + addTestFile(''' +/// one doc +one(String name, int length) {} +main() { + one("Danny", /*^*/); +} +'''); + var result = await prepareSignature('/*^*/'); + expect(result.name, equals("one")); + expect(result.dartdoc, equals("one doc")); + expect(result.parameters, hasLength(2)); + expect(result.parameters[0], + equals(new ParameterInfo(ParameterKind.REQUIRED, "name", "String"))); + expect(result.parameters[1], + equals(new ParameterInfo(ParameterKind.REQUIRED, "length", "int"))); + } + + test_function_optional() async { + addTestFile(''' +/// one doc +one(String name, [int length]) {} +main() { + one("Danny", /*^*/); +} +'''); + var result = await prepareSignature('/*^*/'); + expect(result.name, equals("one")); + expect(result.dartdoc, equals("one doc")); + expect(result.parameters, hasLength(2)); + expect(result.parameters[0], + equals(new ParameterInfo(ParameterKind.REQUIRED, "name", "String"))); + expect(result.parameters[1], + equals(new ParameterInfo(ParameterKind.OPTIONAL, "length", "int"))); + } + + test_function_named() async { + addTestFile(''' +/// one doc +one(String name, {int length}) {} +main() { + one("Danny", /*^*/); +} +'''); + var result = await prepareSignature('/*^*/'); + expect(result.name, equals("one")); + expect(result.dartdoc, equals("one doc")); + expect(result.parameters, hasLength(2)); + expect(result.parameters[0], + equals(new ParameterInfo(ParameterKind.REQUIRED, "name", "String"))); + expect(result.parameters[1], + equals(new ParameterInfo(ParameterKind.NAMED, "length", "int"))); + } + + @failingTest + test_function_select_param_first() { + // Placeholder for supporting telling the user which param is where the cursor is + // https://github.com/dart-lang/sdk/issues/27034#issuecomment-238280507 + // eg. foo(1^, 2); + fail('TODO'); + } + + @failingTest + test_function_select_param_second() { + // Placeholder for supporting telling the user which param is where the cursor is + // https://github.com/dart-lang/sdk/issues/27034#issuecomment-238280507 + // eg. foo(1, ^2); + fail('TODO'); + } + + @failingTest + test_function_select_param_next() { + // Placeholder for supporting telling the user which param is where the cursor is + // https://github.com/dart-lang/sdk/issues/27034#issuecomment-238280507 + // eg. foo(1, ^); + fail('TODO'); + } + + @failingTest + test_constructor() async { + fail('Requires implementation'); + addTestFile(''' +/// MyClass doc +class MyClass { + MyClass(String name, [int length], { int max }) {} +} +main() { + var a = new MyClass("Danny", /*^*/); +} +'''); + var result = await prepareSignature('/*^*/'); + expect(result.name, equals("MyClass")); + expect(result.dartdoc, equals("MyClass doc")); + expect(result.parameters, hasLength(3)); + expect(result.parameters[0], + equals(new ParameterInfo(ParameterKind.REQUIRED, "name", "String"))); + expect(result.parameters[1], + equals(new ParameterInfo(ParameterKind.OPTIONAL, "length", "int"))); + expect(result.parameters[2], + equals(new ParameterInfo(ParameterKind.NAMED, "max", "int"))); + } + + @failingTest + test_function_nested_call() { + // eg. foo(bar(1, 2)); + fail('TODO'); + } + + @failingTest + test_function_zero_arguments() { + fail('TODO'); + } + + @failingTest + test_function_no_dart_doc() { + fail('TODO'); + } + + @failingTest + test_function_expression() { + // eg. + // int Function(int) f(String s) => (int i) => int.parse(s) + i; + // main() { + // print(f('3')(2)); + // } + fail('TODO'); + } + + @failingTest + test_constructor_named() { + fail('TODO'); + } + + @failingTest + test_constructor_factory() { + fail('TODO'); + } + + @failingTest + test_method_instance() { + fail('TODO'); + } + + @failingTest + test_method_static() { + fail('TODO'); + } + + @failingTest + test_irrelevant_parens() { + // eg. method(something, (((1 * 2)))); + fail('TODO'); + } + + @failingTest + test_error_content_modified() { + // Content changed during response (CONTENT_MODIFIED) + fail('TODO'); + } + + @failingTest + test_error_file_not_analyzed() { + // (valid path, but ) + // invalid file (GET_SIGNATURE_INVALID_FILE) + fail('TODO'); + } + + @failingTest + test_error_file_invalid_path() { + // invalid file (GET_SIGNATURE_INVALID_FILE) + fail('TODO'); + } + + @failingTest + test_error_offset_invalid() { + // invalid offset (GET_SIGNATURE_INVALID_OFFSET) + fail('TODO'); + } + + @failingTest + test_error_function_unknown() { + // invalid function (GET_SIGNATURE_UNKNOWN_FUNCTION) + fail('TODO'); + } +} diff --git a/pkg/analysis_server/test/analysis/test_all.dart b/pkg/analysis_server/test/analysis/test_all.dart index fa72b86e753..a0217b68131 100644 --- a/pkg/analysis_server/test/analysis/test_all.dart +++ b/pkg/analysis_server/test/analysis/test_all.dart @@ -7,6 +7,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart'; import 'get_errors_test.dart' as get_errors_test; import 'get_hover_test.dart' as get_hover_test; import 'get_navigation_test.dart' as get_navigation_test; +import 'get_signature_test.dart' as get_signature_information_test; import 'notification_analysis_options_test.dart' as notification_analysis_options_test; import 'notification_analyzedFiles_test.dart' @@ -34,6 +35,7 @@ main() { get_errors_test.main(); get_hover_test.main(); get_navigation_test.main(); + get_signature_information_test.main(); notification_analysis_options_test.main(); notification_analyzedFiles_test.main(); notification_closingLabels_test.main(); diff --git a/pkg/analysis_server/test/integration/coverage.md b/pkg/analysis_server/test/integration/coverage.md index 466c66e3d5d..2dbaa431535 100644 --- a/pkg/analysis_server/test/integration/coverage.md +++ b/pkg/analysis_server/test/integration/coverage.md @@ -8,6 +8,7 @@ server calls. This file is validated by `coverage_test.dart`. - [x] analysis.getLibraryDependencies (failing - see #29310) - [x] analysis.getNavigation (failing - see #28799) - [x] analysis.getReachableSources (failing - see #29311) +- [ ] analysis.getSignature - [x] analysis.reanalyze - [x] analysis.setAnalysisRoots - [x] analysis.setGeneralSubscriptions diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart index 41880e3d8fe..bfd2f18b5e5 100644 --- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart +++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart @@ -401,6 +401,67 @@ abstract class IntegrationTestMixin { decoder, 'result', result); } + /** + * Return the signature information associated with the given location in the + * given file. If the signature information for the given file has not yet + * been computed, or the most recently computed signature information for the + * given file is out of date, then the response for this request will be + * delayed until it has been computed. If the content of the file changes + * after this request was received but before a response could be sent, then + * an error of type CONTENT_MODIFIED will be generated. If a request is made + * for a file which does not exist, or which is not currently subject to + * analysis (e.g. because it is not associated with any analysis root + * specified to analysis.setAnalysisRoots), an error of type + * GET_SIGNATURE_INVALID_FILE will be generated. If the location given is not + * inside the argument list for a function (including method and constructor) + * invocation, then an error of type GET_SIGNATURE_INVALID_OFFSET will be + * generated. If the location is inside an argument list but the function is + * not defined or cannot be determined (such as a method invocation where the + * target has type 'dynamic') then an error of type + * GET_SIGNATURE_UNKNOWN_FUNCTION will be generated. + * + * Parameters + * + * file: FilePath + * + * The file in which signature information is being requested. + * + * offset: int + * + * The location for which signature information is being requested. + * + * Returns + * + * name: String + * + * The name of the function being invoked at the given offset. + * + * dartdoc: String + * + * The dartdoc associated with the function being invoked. Other than the + * removal of the comment delimiters, including leading asterisks in the + * case of a block comment, the dartdoc is unprocessed markdown. This data + * is omitted if there is no referenced element, or if the element has no + * dartdoc. + * + * parameters: List + * + * A list of information about each of the parameters of the function being + * invoked. + * + * selectedParameterIndex: int + * + * The index of the paramter in the parameters collection at the specified + * offset. + */ + Future sendAnalysisGetSignature( + String file, int offset) async { + var params = new AnalysisGetSignatureParams(file, offset).toJson(); + var result = await server.send("analysis.getSignature", params); + ResponseDecoder decoder = new ResponseDecoder(null); + return new AnalysisGetSignatureResult.fromJson(decoder, 'result', result); + } + /** * Force the re-analysis of everything contained in the specified analysis * roots. This will cause all previously computed analysis results to be diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart index 445d9fde0f5..0c35f99ad24 100644 --- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart +++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart @@ -1057,6 +1057,31 @@ final Matcher isOverride = "interfaceMembers": isListOf(isOverriddenMember) })); +/** + * ParameterInfo + * + * { + * "kind": ParameterKind + * "name": String + * "type": String + * } + */ +final Matcher isParameterInfo = new LazyMatcher(() => new MatchesJsonObject( + "ParameterInfo", + {"kind": isParameterKind, "name": isString, "type": isString})); + +/** + * ParameterKind + * + * enum { + * NAMED + * OPTIONAL + * REQUIRED + * } + */ +final Matcher isParameterKind = + new MatchesEnum("ParameterKind", ["NAMED", "OPTIONAL", "REQUIRED"]); + /** * Position * @@ -1233,6 +1258,8 @@ final Matcher isRequestError = new LazyMatcher(() => new MatchesJsonObject( * GET_KYTHE_ENTRIES_INVALID_FILE * GET_NAVIGATION_INVALID_FILE * GET_REACHABLE_SOURCES_INVALID_FILE + * GET_SIGNATURE_INVALID_FILE + * GET_SIGNATURE_UNKNOWN_FUNCTION * IMPORT_ELEMENTS_INVALID_FILE * INVALID_ANALYSIS_ROOT * INVALID_EXECUTION_CONTEXT @@ -1263,6 +1290,8 @@ final Matcher isRequestErrorCode = new MatchesEnum("RequestErrorCode", [ "GET_KYTHE_ENTRIES_INVALID_FILE", "GET_NAVIGATION_INVALID_FILE", "GET_REACHABLE_SOURCES_INVALID_FILE", + "GET_SIGNATURE_INVALID_FILE", + "GET_SIGNATURE_UNKNOWN_FUNCTION", "IMPORT_ELEMENTS_INVALID_FILE", "INVALID_ANALYSIS_ROOT", "INVALID_EXECUTION_CONTEXT", @@ -1673,6 +1702,36 @@ final Matcher isAnalysisGetReachableSourcesResult = new LazyMatcher(() => new MatchesJsonObject("analysis.getReachableSources result", {"sources": isMapOf(isString, isListOf(isString))})); +/** + * analysis.getSignature params + * + * { + * "file": FilePath + * "offset": int + * } + */ +final Matcher isAnalysisGetSignatureParams = new LazyMatcher(() => + new MatchesJsonObject( + "analysis.getSignature params", {"file": isFilePath, "offset": isInt})); + +/** + * analysis.getSignature result + * + * { + * "name": String + * "dartdoc": String + * "parameters": List + * "selectedParameterIndex": int + * } + */ +final Matcher isAnalysisGetSignatureResult = new LazyMatcher( + () => new MatchesJsonObject("analysis.getSignature result", { + "name": isString, + "dartdoc": isString, + "parameters": isListOf(isParameterInfo), + "selectedParameterIndex": isInt + })); + /** * analysis.highlights params * diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java index f8991b5304c..a8a46aac679 100644 --- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java +++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java @@ -141,6 +141,28 @@ public interface AnalysisServer { */ public void analysis_getReachableSources(String file, GetReachableSourcesConsumer consumer); + /** + * {@code analysis.getSignature} + * + * Return the signature information associated with the given location in the given file. If the + * signature information for the given file has not yet been computed, or the most recently + * computed signature information for the given file is out of date, then the response for this + * request will be delayed until it has been computed. If the content of the file changes after + * this request was received but before a response could be sent, then an error of type + * CONTENT_MODIFIED will be generated. If a request is made for a file which does not exist, or + * which is not currently subject to analysis (e.g. because it is not associated with any analysis + * root specified to analysis.setAnalysisRoots), an error of type GET_SIGNATURE_INVALID_FILE will + * be generated. If the location given is not inside the argument list for a function (including + * method and constructor) invocation, then an error of type GET_SIGNATURE_INVALID_OFFSET will be + * generated. If the location is inside an argument list but the function is not defined or cannot + * be determined (such as a method invocation where the target has type 'dynamic') then an error of + * type GET_SIGNATURE_UNKNOWN_FUNCTION will be generated. + * + * @param file The file in which signature information is being requested. + * @param offset The location for which signature information is being requested. + */ + public void analysis_getSignature(String file, int offset, GetSignatureConsumer consumer); + /** * {@code analysis.reanalyze} * diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ParameterInfo.java b/pkg/analysis_server/tool/spec/generated/java/types/ParameterInfo.java new file mode 100644 index 00000000000..ff39446ef9c --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/ParameterInfo.java @@ -0,0 +1,145 @@ +/* + * 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. + * + * This file has been automatically generated. Please do not edit it manually. + * To regenerate the file, use the script "pkg/analysis_server/tool/spec/generate_files". + */ +package org.dartlang.analysis.server.protocol; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import com.google.common.collect.Lists; +import com.google.dart.server.utilities.general.JsonUtilities; +import com.google.dart.server.utilities.general.ObjectUtilities; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import java.util.ArrayList; +import java.util.Iterator; +import org.apache.commons.lang3.StringUtils; + +/** + * A description of a member that is being overridden. + * + * @coverage dart.server.generated.types + */ +@SuppressWarnings("unused") +public class ParameterInfo { + + public static final ParameterInfo[] EMPTY_ARRAY = new ParameterInfo[0]; + + public static final List EMPTY_LIST = Lists.newArrayList(); + + /** + * The kind of the parameter. + */ + private final String kind; + + /** + * The name of the parameter. + */ + private final String name; + + /** + * The type of the parameter. + */ + private final String type; + + /** + * Constructor for {@link ParameterInfo}. + */ + public ParameterInfo(String kind, String name, String type) { + this.kind = kind; + this.name = name; + this.type = type; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ParameterInfo) { + ParameterInfo other = (ParameterInfo) obj; + return + ObjectUtilities.equals(other.kind, kind) && + ObjectUtilities.equals(other.name, name) && + ObjectUtilities.equals(other.type, type); + } + return false; + } + + public static ParameterInfo fromJson(JsonObject jsonObject) { + String kind = jsonObject.get("kind").getAsString(); + String name = jsonObject.get("name").getAsString(); + String type = jsonObject.get("type").getAsString(); + return new ParameterInfo(kind, name, type); + } + + public static List fromJsonArray(JsonArray jsonArray) { + if (jsonArray == null) { + return EMPTY_LIST; + } + ArrayList list = new ArrayList(jsonArray.size()); + Iterator iterator = jsonArray.iterator(); + while (iterator.hasNext()) { + list.add(fromJson(iterator.next().getAsJsonObject())); + } + return list; + } + + /** + * The kind of the parameter. + */ + public String getKind() { + return kind; + } + + /** + * The name of the parameter. + */ + public String getName() { + return name; + } + + /** + * The type of the parameter. + */ + public String getType() { + return type; + } + + @Override + public int hashCode() { + HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(kind); + builder.append(name); + builder.append(type); + return builder.toHashCode(); + } + + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("kind", kind); + jsonObject.addProperty("name", name); + jsonObject.addProperty("type", type); + return jsonObject; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append("kind="); + builder.append(kind + ", "); + builder.append("name="); + builder.append(name + ", "); + builder.append("type="); + builder.append(type); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ParameterKind.java b/pkg/analysis_server/tool/spec/generated/java/types/ParameterKind.java new file mode 100644 index 00000000000..34404460f2a --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/ParameterKind.java @@ -0,0 +1,33 @@ +/* + * 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. + * + * This file has been automatically generated. Please do not edit it manually. + * To regenerate the file, use the script "pkg/analysis_server/tool/spec/generate_files". + */ +package org.dartlang.analysis.server.protocol; + +/** + * An enumeration of the types of parameters. + * + * @coverage dart.server.generated.types + */ +public class ParameterKind { + + /** + * A named parameter. + */ + public static final String NAMED = "NAMED"; + + /** + * An optional parameter. + */ + public static final String OPTIONAL = "OPTIONAL"; + + /** + * A required parameter. + */ + public static final String REQUIRED = "REQUIRED"; + +} diff --git a/pkg/analysis_server/tool/spec/generated/java/types/RequestErrorCode.java b/pkg/analysis_server/tool/spec/generated/java/types/RequestErrorCode.java index 2d25c3469f0..59059ee8fdd 100644 --- a/pkg/analysis_server/tool/spec/generated/java/types/RequestErrorCode.java +++ b/pkg/analysis_server/tool/spec/generated/java/types/RequestErrorCode.java @@ -73,6 +73,18 @@ public class RequestErrorCode { */ public static final String GET_REACHABLE_SOURCES_INVALID_FILE = "GET_REACHABLE_SOURCES_INVALID_FILE"; + /** + * An "analysis.getSignature" request specified a FilePath which does not match a file currently + * subject to analysis. + */ + public static final String GET_SIGNATURE_INVALID_FILE = "GET_SIGNATURE_INVALID_FILE"; + + /** + * An "analysis.getSignature" request specified an offset that could not be matched to a function + * call. + */ + public static final String GET_SIGNATURE_UNKNOWN_FUNCTION = "GET_SIGNATURE_UNKNOWN_FUNCTION"; + /** * An "edit.importElements" request specified a FilePath that does not match a file currently * subject to analysis. diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html index 0173f12fa3e..22b560269c3 100644 --- a/pkg/analysis_server/tool/spec/spec_input.html +++ b/pkg/analysis_server/tool/spec/spec_input.html @@ -647,6 +647,74 @@ + +

+ Return the signature information associated with the given + location in the given file. If the signature information + for the given file has not yet been computed, or the most + recently computed signature information for the given file + is out of date, then the response for this request will be + delayed until it has been computed. If the content of the + file changes after this request was received but before a + response could be sent, then an error of type + CONTENT_MODIFIED will be generated. + + If a request is made for a file which does not exist, or + which is not currently subject to analysis (e.g. because it + is not associated with any analysis root specified to + analysis.setAnalysisRoots), an error of type + GET_SIGNATURE_INVALID_FILE will be generated. + + If the location given is not inside the argument list for a + function (including method and constructor) invocation, then + an error of type GET_SIGNATURE_INVALID_OFFSET will + be generated. If the location is inside an argument list but + the function is not defined or cannot be determined (such as + a method invocation where the target has type 'dynamic') + then an error of type GET_SIGNATURE_UNKNOWN_FUNCTION + will be generated. +

+ + + FilePath +

+ The file in which signature information is being requested. +

+
+ + int +

+ The location for which signature information is being requested. +

+
+
+ + + String +

The name of the function being invoked at the given offset.

+
+ + String +

+ The dartdoc associated with the function being invoked. Other + than the removal of the comment delimiters, including leading + asterisks in the case of a block comment, the dartdoc is + unprocessed markdown. This data is omitted if there is no + referenced element, or if the element has no dartdoc. +

+
+ + + ParameterInfo + +

A list of information about each of the parameters of the function being invoked.

+
+ + int +

The index of the paramter in the parameters collection at the specified offset.

+
+
+

Force the re-analysis of everything contained in the specified @@ -3953,6 +4021,21 @@ analysis.

+ + GET_SIGNATURE_INVALID_FILE +

+ An "analysis.getSignature" request specified a FilePath + which does not match a file currently subject to + analysis. +

+
+ + GET_SIGNATURE_UNKNOWN_FUNCTION +

+ An "analysis.getSignature" request specified an offset + that could not be matched to a function call. +

+
IMPORT_ELEMENTS_INVALID_FILE

diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart index a6b6812094b..cd2c13a34a7 100644 --- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart +++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart @@ -4578,6 +4578,201 @@ class Outline implements HasToJson { } } +/** + * ParameterInfo + * + * { + * "kind": ParameterKind + * "name": String + * "type": String + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class ParameterInfo implements HasToJson { + ParameterKind _kind; + + String _name; + + String _type; + + /** + * The kind of the parameter. + */ + ParameterKind get kind => _kind; + + /** + * The kind of the parameter. + */ + void set kind(ParameterKind value) { + assert(value != null); + this._kind = value; + } + + /** + * The name of the parameter. + */ + String get name => _name; + + /** + * The name of the parameter. + */ + void set name(String value) { + assert(value != null); + this._name = value; + } + + /** + * The type of the parameter. + */ + String get type => _type; + + /** + * The type of the parameter. + */ + void set type(String value) { + assert(value != null); + this._type = value; + } + + ParameterInfo(ParameterKind kind, String name, String type) { + this.kind = kind; + this.name = name; + this.type = type; + } + + factory ParameterInfo.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + ParameterKind kind; + if (json.containsKey("kind")) { + kind = new ParameterKind.fromJson( + jsonDecoder, jsonPath + ".kind", json["kind"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "kind"); + } + String name; + if (json.containsKey("name")) { + name = jsonDecoder.decodeString(jsonPath + ".name", json["name"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "name"); + } + String type; + if (json.containsKey("type")) { + type = jsonDecoder.decodeString(jsonPath + ".type", json["type"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "type"); + } + return new ParameterInfo(kind, name, type); + } else { + throw jsonDecoder.mismatch(jsonPath, "ParameterInfo", json); + } + } + + @override + Map toJson() { + Map result = {}; + result["kind"] = kind.toJson(); + result["name"] = name; + result["type"] = type; + return result; + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is ParameterInfo) { + return kind == other.kind && name == other.name && type == other.type; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, kind.hashCode); + hash = JenkinsSmiHash.combine(hash, name.hashCode); + hash = JenkinsSmiHash.combine(hash, type.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + +/** + * ParameterKind + * + * enum { + * NAMED + * OPTIONAL + * REQUIRED + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class ParameterKind implements Enum { + /** + * A named parameter. + */ + static const ParameterKind NAMED = const ParameterKind._("NAMED"); + + /** + * An optional parameter. + */ + static const ParameterKind OPTIONAL = const ParameterKind._("OPTIONAL"); + + /** + * A required parameter. + */ + static const ParameterKind REQUIRED = const ParameterKind._("REQUIRED"); + + /** + * A list containing all of the enum values that are defined. + */ + static const List VALUES = const [ + NAMED, + OPTIONAL, + REQUIRED + ]; + + @override + final String name; + + const ParameterKind._(this.name); + + factory ParameterKind(String name) { + switch (name) { + case "NAMED": + return NAMED; + case "OPTIONAL": + return OPTIONAL; + case "REQUIRED": + return REQUIRED; + } + throw new Exception('Illegal enum value: $name'); + } + + factory ParameterKind.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json is String) { + try { + return new ParameterKind(json); + } catch (_) { + // Fall through + } + } + throw jsonDecoder.mismatch(jsonPath, "ParameterKind", json); + } + + @override + String toString() => "ParameterKind.$name"; + + String toJson() => name; +} + /** * Position * diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart index cbf5390877e..bc3a5c5df23 100644 --- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart +++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart @@ -692,6 +692,31 @@ final Matcher isOutline = "children": isListOf(isOutline) })); +/** + * ParameterInfo + * + * { + * "kind": ParameterKind + * "name": String + * "type": String + * } + */ +final Matcher isParameterInfo = new LazyMatcher(() => new MatchesJsonObject( + "ParameterInfo", + {"kind": isParameterKind, "name": isString, "type": isString})); + +/** + * ParameterKind + * + * enum { + * NAMED + * OPTIONAL + * REQUIRED + * } + */ +final Matcher isParameterKind = + new MatchesEnum("ParameterKind", ["NAMED", "OPTIONAL", "REQUIRED"]); + /** * Position * diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html index 2b1c9e5ed27..593fed07c07 100644 --- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html +++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html @@ -1168,6 +1168,56 @@ + +

+ A description of a member that is being overridden. +

+ + + ParameterKind +

+ The kind of the parameter. +

+
+ + String +

+ The name of the parameter. +

+
+ + String +

+ The type of the parameter. +

+
+
+ + +

+ An enumeration of the types of parameters. +

+ + + NAMED +

+ A named parameter. +

+
+ + OPTIONAL +

+ An optional parameter. +

+
+ + REQUIRED +

+ A required parameter. +

+
+
+

A position within a file.