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.
+