Version 2.10.0-150.0.dev
Merge commit 'a8586206f836417ec67eeedd3a1a003335367a22' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter.dart
index 7ff448e..38ffa68 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter.dart
@@ -4,7 +4,7 @@
import 'package:analysis_server/src/services/correction/dart/data_driven.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
-import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:meta/meta.dart';
@@ -22,8 +22,8 @@
/// type parameter doesn't have a bound.
final String extendedType;
- /// The value extractor used to compute the value of the type argument.
- final ValueExtractor argumentValue;
+ /// The code template used to compute the value of the type argument.
+ final CodeTemplate argumentValue;
/// Initialize a newly created change to describe adding a type parameter to a
/// type or a function.
@@ -53,7 +53,7 @@
if (node is NamedType) {
// wrong_number_of_type_arguments
// wrong_number_of_type_arguments_constructor
- var argument = argumentValue.from(node, fix.utils);
+ var argument = argumentValue.generate(node, fix.utils);
if (argument == null) {
return null;
}
@@ -66,7 +66,7 @@
var parent = node.parent;
if (parent is InvocationExpression) {
// wrong_number_of_type_arguments_method
- var argument = argumentValue.from(parent, fix.utils);
+ var argument = argumentValue.generate(parent, fix.utils);
if (argument == null) {
return null;
}
@@ -85,7 +85,7 @@
return _TypeParameterData(typeParameters, parent.name.end);
} else if (node is TypeArgumentList && parent is ExtensionOverride) {
// wrong_number_of_type_arguments_extension
- var argument = argumentValue.from(node, fix.utils);
+ var argument = argumentValue.generate(node, fix.utils);
if (argument == null) {
return null;
}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart
new file mode 100644
index 0000000..86b797e
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart
@@ -0,0 +1,95 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+
+/// An object used to generate code to be inserted.
+class CodeTemplate {
+ /// The kind of code that will be generated by this template.
+ final CodeTemplateKind kind;
+
+ /// The components of the template.
+ final List<TemplateComponent> components;
+
+ /// Initialize a newly generated code template with the given [kind] and
+ /// [components].
+ CodeTemplate(this.kind, this.components);
+
+ String generate(AstNode node, CorrectionUtils utils) {
+ var context = _TemplateContext(node, utils);
+ var buffer = StringBuffer();
+ for (var component in components) {
+ component.appendTo(buffer, context);
+ }
+ return buffer.toString();
+ }
+}
+
+/// The kinds of code that can be generated by a template.
+enum CodeTemplateKind {
+ expression,
+ statements,
+}
+
+/// An object used to compute some portion of a template.
+abstract class TemplateComponent {
+ /// Append the text contributed by this component to the given [sink], using
+ /// the [context] to access needed information that isn't already known to
+ /// this component.
+ void appendTo(StringSink sink, _TemplateContext context);
+}
+
+/// Literal text within a template.
+class TemplateText extends TemplateComponent {
+ /// The literal text to be included in the resulting code.
+ final String text;
+
+ /// Initialize a newly create template text with the given [text].
+ TemplateText(this.text);
+
+ @override
+ void appendTo(StringSink sink, _TemplateContext context) {
+ sink.write(text);
+ }
+}
+
+/// A reference to a variable within a template.
+class TemplateVariable extends TemplateComponent {
+ /// The extractor used to compute the value of the variable.
+ final ValueExtractor extractor;
+
+ /// Initialize a newly created template variable with the given [name].
+ TemplateVariable(this.extractor);
+
+ @override
+ void appendTo(StringSink sink, _TemplateContext context) {
+ sink.write(context.valueOf(extractor));
+ }
+}
+
+/// The context in which a template is being evaluated.
+class _TemplateContext {
+ /// The node in the AST that is being transformed.
+ final AstNode node;
+
+ /// The utilities used to help extract the code associated with various nodes.
+ final CorrectionUtils utils;
+
+ /// A table mapping variable names to the values of those variables after they
+ /// have been computed. Used to prevent computing the same value multiple
+ /// times.
+ final Map<ValueExtractor, String> variableValues = {};
+
+ /// Initialize a newly created variable support.
+ _TemplateContext(this.node, this.utils);
+
+ /// Return the value of the variable with the given [name].
+ String valueOf(ValueExtractor extractor) {
+ return variableValues.putIfAbsent(extractor, () {
+ return extractor.from(node, utils);
+ });
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart
index 20e8a0c..857a509 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart
@@ -4,8 +4,8 @@
import 'package:analysis_server/src/services/correction/dart/data_driven.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
-import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
@@ -26,12 +26,12 @@
/// A flag indicating whether the parameter is a positional parameter.
final bool isPositional;
- /// The value of the new argument in invocations of the function, or `null` if
- /// the parameter is optional and no argument needs to be added. The only time
- /// an argument needs to be added for an optional parameter is if the
- /// parameter is positional and there are pre-existing optional positional
- /// parameters after the ones being added.
- final ValueExtractor argumentValue;
+ /// The code template used to compute the value of the new argument in
+ /// invocations of the function, or `null` if the parameter is optional and no
+ /// argument needs to be added. The only time an argument needs to be added
+ /// for an optional parameter is if the parameter is positional and there are
+ /// pre-existing optional positional parameters after the ones being added.
+ final CodeTemplate argumentValue;
/// Initialize a newly created parameter modification to represent the
/// addition of a parameter. If provided, the [argumentValue] will be used as
@@ -91,7 +91,7 @@
/// Write to the [builder] the argument associated with a single
/// [parameter].
void writeArgument(DartEditBuilder builder, AddParameter parameter) {
- var value = parameter.argumentValue.from(argumentList, fix.utils);
+ var value = parameter.argumentValue.generate(argumentList, fix.utils);
if (!parameter.isPositional) {
builder.write(parameter.name);
builder.write(': ');
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
index 2cdeb92..260102c 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
@@ -26,6 +26,19 @@
TransformSetErrorCode('missingKey', "Missing the required key '{0}'.");
/**
+ * No parameters.
+ */
+ static const TransformSetErrorCode missingTemplateEnd = TransformSetErrorCode(
+ 'missingTemplateEnd', "Missing the end brace for the template.");
+
+ /**
+ * Parameters:
+ * 0: the missing key
+ */
+ static const TransformSetErrorCode undefinedVariable = TransformSetErrorCode(
+ 'undefinedVariable', "The variable '{0}' is not defined.");
+
+ /**
* Parameters:
* 0: the unsupported key
*/
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index 0175537..cf09db0 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -4,6 +4,7 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/add_type_parameter.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/modify_parameters.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
@@ -41,6 +42,7 @@
static const String _dateKey = 'date';
static const String _elementKey = 'element';
static const String _enumKey = 'enum';
+ static const String _expressionKey = 'expression';
static const String _extensionKey = 'extension';
static const String _fieldKey = 'field';
static const String _functionKey = 'function';
@@ -56,11 +58,13 @@
static const String _nameKey = 'name';
static const String _newNameKey = 'newName';
static const String _setterKey = 'setter';
+ static const String _statementsKey = 'statements';
static const String _styleKey = 'style';
static const String _titleKey = 'title';
static const String _transformsKey = 'transforms';
static const String _typedefKey = 'typedef';
static const String _urisKey = 'uris';
+ static const String _variablesKey = 'variables';
static const String _versionKey = 'version';
/// A table mapping top-level keys for member elements to the list of keys for
@@ -88,6 +92,9 @@
'required_positional'
];
+ static const String _openComponent = '{%';
+ static const String _closeComponent = '%}';
+
/// The highest file version supported by this parser. The version needs to be
/// incremented any time the parser is updated to disallow input that would
/// have been valid in the most recently published version of server. This
@@ -117,6 +124,50 @@
return _translateTransformSet(map);
}
+ /// Convert the given [template] into a list of components. Variable
+ /// references in the template are looked up in the map of [extractors].
+ List<TemplateComponent> _extractTemplateComponents(String template,
+ Map<String, ValueExtractor> extractors, int templateOffset) {
+ var components = <TemplateComponent>[];
+ var textStart = 0;
+ var variableStart = template.indexOf(_openComponent);
+ while (variableStart >= 0) {
+ if (textStart < variableStart) {
+ // TODO(brianwilkerson) Check for an end brace without a start brace.
+ components
+ .add(TemplateText(template.substring(textStart, variableStart)));
+ }
+ var endIndex = template.indexOf(_closeComponent, variableStart + 2);
+ if (endIndex < 0) {
+ errorReporter.reportErrorForOffset(
+ TransformSetErrorCode.missingTemplateEnd,
+ templateOffset + variableStart,
+ 2);
+ return null;
+ } else {
+ var name = template.substring(variableStart + 2, endIndex).trim();
+ var extractor = extractors[name];
+ if (extractor == null) {
+ errorReporter.reportErrorForOffset(
+ TransformSetErrorCode.undefinedVariable,
+ templateOffset + template.indexOf(name, variableStart),
+ name.length,
+ [name]);
+ return null;
+ } else {
+ components.add(TemplateVariable(extractor));
+ }
+ }
+ textStart = endIndex + 2;
+ variableStart = template.indexOf(_openComponent, textStart);
+ }
+ if (textStart < template.length) {
+ // TODO(brianwilkerson) Check for an end brace without a start brace.
+ components.add(TemplateText(template.substring(textStart)));
+ }
+ return components;
+ }
+
/// Return a textual description of the type of value represented by the
/// [node].
String _nodeType(YamlNode node) {
@@ -173,14 +224,9 @@
/// Report any keys in the [map] whose values are not in [validKeys].
void _reportUnsupportedKeys(YamlMap map, Set<String> validKeys) {
for (var keyNode in map.nodes.keys) {
- if (keyNode is YamlScalar && keyNode.value is String) {
- var key = keyNode.value as String;
- if (key != null && !validKeys.contains(key)) {
- _reportError(TransformSetErrorCode.unsupportedKey, keyNode, [key]);
- }
- } else {
- // TODO(brianwilkerson) Report the invalidKey.
- // "Keys must be of type 'String' but found the type '{0}'."
+ var key = _translateKey(keyNode);
+ if (key != null && !validKeys.contains(key)) {
+ _reportError(TransformSetErrorCode.unsupportedKey, keyNode, [key]);
}
}
}
@@ -236,8 +282,7 @@
}
var isRequired = style.startsWith('required_');
var isPositional = style.endsWith('_positional');
- var argumentValue = _translateValueExtractor(
- node.valueAt(_argumentValueKey),
+ var argumentValue = _translateCodeTemplate(node.valueAt(_argumentValueKey),
ErrorContext(key: _argumentValueKey, parentNode: node));
// TODO(brianwilkerson) We really ought to require an argument value for
// optional positional parameters too for the case where the added
@@ -269,8 +314,7 @@
// we might need to introduce a `TypeParameterModification` change, similar
// to `ParameterModification`. That becomes more likely if we add support
// for removing type parameters.
- var argumentValue = _translateValueExtractor(
- node.valueAt(_argumentValueKey),
+ var argumentValue = _translateCodeTemplate(node.valueAt(_argumentValueKey),
ErrorContext(key: _argumentValueKey, parentNode: node));
if (index == null || name == null || argumentValue == null) {
// The error has already been reported.
@@ -336,6 +380,52 @@
}
}
+ /// Translate the [node] into a code template. Return the resulting template,
+ /// or `null` if the [node] does not represent a valid code template. If the
+ /// [node] is not valid, use the [context] to report the error.
+ CodeTemplate _translateCodeTemplate(YamlNode node, ErrorContext context) {
+ if (node is YamlMap) {
+ CodeTemplateKind kind;
+ int templateOffset;
+ String template;
+ var expressionNode = node.valueAt(_expressionKey);
+ if (expressionNode != null) {
+ _reportUnsupportedKeys(node, const {_expressionKey, _variablesKey});
+ kind = CodeTemplateKind.expression;
+ // TODO(brianwilkerson) We add 1 to account for the quotes around the
+ // string, but quotes aren't required, so we need to find out what
+ // style of node [expressionNode] is to get the right offset.
+ templateOffset = expressionNode.span.start.offset + 1;
+ template = _translateString(expressionNode,
+ ErrorContext(key: _expressionKey, parentNode: node));
+ } else {
+ var statementsNode = node.valueAt(_statementsKey);
+ if (statementsNode != null) {
+ _reportUnsupportedKeys(node, const {_statementsKey, _variablesKey});
+ kind = CodeTemplateKind.statements;
+ // TODO(brianwilkerson) We add 1 to account for the quotes around the
+ // string, but quotes aren't required, so we need to find out what
+ // style of node [expressionNode] is to get the right offset.
+ templateOffset = statementsNode.span.start.offset + 1;
+ template = _translateString(statementsNode,
+ ErrorContext(key: _statementsKey, parentNode: node));
+ } else {
+ // TODO(brianwilkerson) Report the missing key.
+ return null;
+ }
+ }
+ var extractors = _translateTemplateVariables(node.valueAt(_variablesKey),
+ ErrorContext(key: _variablesKey, parentNode: node));
+ var components =
+ _extractTemplateComponents(template, extractors, templateOffset);
+ return CodeTemplate(kind, components);
+ } else if (node == null) {
+ return _reportMissingKey(context);
+ } else {
+ return _reportInvalidValue(node, context, 'Map');
+ }
+ }
+
/// Translate the [node] into a date. Return the resulting date, or `null`
/// if the [node] does not represent a valid date. If the [node] is not
/// valid, use the [context] to report the error.
@@ -435,6 +525,16 @@
}
}
+ /// Translate the given [node] as a key.
+ String _translateKey(YamlNode node) {
+ if (node is YamlScalar && node.value is String) {
+ return node.value as String;
+ }
+ // TODO(brianwilkerson) Report the invalidKey.
+ // "Keys must be of type 'String' but found the type '{0}'."
+ return null;
+ }
+
/// Translate the [node] into a list of objects using the [elementTranslator].
/// Return the resulting list, or `null` if the [node] does not represent a
/// valid list. If any of the elements of the list can't be translated, they
@@ -521,6 +621,32 @@
}
}
+ /// Translate the [node] into a list of template components. Return the
+ /// resulting list, or `null` if the [node] does not represent a valid
+ /// variables map. If the [node] is not valid, use the [context] to report the
+ /// error.
+ Map<String, ValueExtractor> _translateTemplateVariables(
+ YamlNode node, ErrorContext context) {
+ if (node is YamlMap) {
+ var extractors = <String, ValueExtractor>{};
+ for (var entry in node.nodes.entries) {
+ var name = _translateKey(entry.key);
+ if (name != null) {
+ var value = _translateValueExtractor(
+ entry.value, ErrorContext(key: name, parentNode: node));
+ if (value != null) {
+ extractors[name] = value;
+ }
+ }
+ }
+ return extractors;
+ } else if (node == null) {
+ return const {};
+ } else {
+ return _reportInvalidValue(node, context, 'Map');
+ }
+ }
+
/// Translate the [node] into a transform. Return the resulting transform, or
/// `null` if the [node] does not represent a valid transform. If the [node]
/// is not valid, use the [context] to report the error.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
index cc9b79f..cec9e04 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
@@ -42,20 +42,6 @@
}
}
-/// A value extractor that returns a pre-computed piece of code.
-class LiteralExtractor extends ValueExtractor {
- /// The code to be returned.
- final String code;
-
- /// Initialize a newly created extractor to return the given [code].
- LiteralExtractor(this.code);
-
- @override
- String from(AstNode node, CorrectionUtils utils) {
- return code;
- }
-}
-
/// An object used to extract an expression from an AST node.
abstract class ValueExtractor {
/// Return code extracted from the given [node], or `null` if no code could be
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
index 3ac8426..b2e939d 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
@@ -5,7 +5,6 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/add_type_parameter.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
-import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'data_driven_test_support.dart';
@@ -468,6 +467,6 @@
extendedType: extendedType,
index: index,
name: 'T',
- argumentValue: LiteralExtractor('String')),
+ argumentValue: codeTemplate('String')),
]);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_template_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_template_test.dart
new file mode 100644
index 0000000..22b1e74
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_template_test.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'data_driven_test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(CodeTemplateTest);
+ });
+}
+
+@reflectiveTest
+class CodeTemplateTest extends DataDrivenFixProcessorTest {
+ Future<void> test_text_variable() async {
+ await _assertTemplateResult('a0', [
+ _text('a'),
+ _variable(0),
+ ]);
+ }
+
+ Future<void> test_text_variable_text() async {
+ await _assertTemplateResult('a0b', [
+ _text('a'),
+ _variable(0),
+ _text('b'),
+ ]);
+ }
+
+ Future<void> test_textOnly() async {
+ await _assertTemplateResult('a', [
+ _text('a'),
+ ]);
+ }
+
+ Future<void> test_variable_text() async {
+ await _assertTemplateResult('0a', [
+ _variable(0),
+ _text('a'),
+ ]);
+ }
+
+ Future<void> test_variable_text_variable() async {
+ await _assertTemplateResult('0a1', [
+ _variable(0),
+ _text('a'),
+ _variable(1),
+ ]);
+ }
+
+ Future<void> test_variableOnly() async {
+ await _assertTemplateResult('0', [
+ _variable(0),
+ ]);
+ }
+
+ Future<void> _assertTemplateResult(
+ String expectedResult, List<TemplateComponent> components) async {
+ await resolveTestUnit('''
+void f() {
+ g(0, 1);
+}
+void g(int x, int y) {}
+''');
+ var unit = testAnalysisResult.unit;
+ var function = unit.declarations[0] as FunctionDeclaration;
+ var body = function.functionExpression.body as BlockFunctionBody;
+ var statement = body.block.statements[0] as ExpressionStatement;
+ var node = statement.expression;
+ var template = CodeTemplate(CodeTemplateKind.expression, components);
+ var result = template.generate(node, CorrectionUtils(testAnalysisResult));
+ expect(result, expectedResult);
+ }
+
+ TemplateText _text(String text) => TemplateText(text);
+
+ TemplateVariable _variable(int index) =>
+ TemplateVariable(ArgumentExtractor(PositionalParameterReference(index)));
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart
index 4e48e50..fe452ec 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart
@@ -4,6 +4,7 @@
import 'package:analysis_server/src/services/correction/dart/data_driven.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
@@ -28,6 +29,11 @@
addPackageFile('p', TransformSetManager.dataFileName, content);
}
+ /// Return a code template that will produce the given [text].
+ CodeTemplate codeTemplate(String text) {
+ return CodeTemplate(CodeTemplateKind.expression, [TemplateText(text)]);
+ }
+
/// A method that can be used as an error filter to ignore any unused_import
/// diagnostics.
bool ignoreUnusedImport(AnalysisError error) =>
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_template_end_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_template_end_test.dart
new file mode 100644
index 0000000..0301220
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_template_end_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../transform_set_parser_test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(MissingTemplateEndTest);
+ });
+}
+
+@reflectiveTest
+class MissingTemplateEndTest extends AbstractTransformSetParserTest {
+ void test_missing() {
+ assertErrors('''
+version: 1
+transforms:
+- date: 2020-09-19
+ element:
+ uris: ['test.dart']
+ function: 'f'
+ title: ''
+ changes:
+ - kind: 'addParameter'
+ index: 0
+ name: 'a'
+ style: optional_positional
+ argumentValue:
+ expression: 'a{% x b'
+''', [
+ error(TransformSetErrorCode.missingTemplateEnd, 252, 2),
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
index dc7a598..76bb9b4 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
@@ -6,6 +6,8 @@
import 'invalid_value_test.dart' as invalid_value;
import 'missing_key_test.dart' as missing_key;
+import 'missing_template_end_test.dart' as missing_template_end;
+import 'undefined_variable_test.dart' as undefined_variable;
import 'unsupported_key_test.dart' as unsupported_key;
import 'yaml_syntax_error_test.dart' as yaml_syntax_error;
@@ -13,6 +15,8 @@
defineReflectiveSuite(() {
invalid_value.main();
missing_key.main();
+ missing_template_end.main();
+ undefined_variable.main();
unsupported_key.main();
yaml_syntax_error.main();
});
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/undefined_variable_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/undefined_variable_test.dart
new file mode 100644
index 0000000..19334c8
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/undefined_variable_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../transform_set_parser_test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(UndefinedVariableTest);
+ });
+}
+
+@reflectiveTest
+class UndefinedVariableTest extends AbstractTransformSetParserTest {
+ void test_missingVariable() {
+ assertErrors('''
+version: 1
+transforms:
+- date: 2020-09-19
+ element:
+ uris: ['test.dart']
+ function: 'f'
+ title: ''
+ changes:
+ - kind: 'addParameter'
+ index: 0
+ name: 'a'
+ style: optional_positional
+ argumentValue:
+ expression: '{%xyz%}'
+ variables:
+ zyx:
+ kind: 'argument'
+ index: 0
+''', [
+ error(TransformSetErrorCode.undefinedVariable, 253, 3),
+ ]);
+ }
+
+ void test_noVariables() {
+ assertErrors('''
+version: 1
+transforms:
+- date: 2020-09-19
+ element:
+ uris: ['test.dart']
+ function: 'f'
+ title: ''
+ changes:
+ - kind: 'addParameter'
+ index: 0
+ name: 'a'
+ style: optional_positional
+ argumentValue:
+ expression: '{%xyz%}'
+''', [
+ error(TransformSetErrorCode.undefinedVariable, 253, 3),
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
index 0608b29..aad57fa 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
@@ -35,8 +35,11 @@
name: 'y'
style: required_positional
argumentValue:
- kind: 'argument'
- index: 0
+ expression: '{% y %}'
+ variables:
+ y:
+ kind: 'argument'
+ index: 0
''');
await resolveTestUnit('''
import '$importUri';
@@ -75,8 +78,11 @@
index: 1
name: 'T'
argumentValue:
- kind: 'argument'
- index: 0
+ expression: '{% t %}'
+ variables:
+ t:
+ kind: 'argument'
+ index: 0
''');
await resolveTestUnit('''
import '$importUri';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/modify_parameters_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/modify_parameters_test.dart
index 4dd1eb4..f0adabd 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/modify_parameters_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/modify_parameters_test.dart
@@ -7,7 +7,6 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
-import 'package:analysis_server/src/services/correction/fix/data_driven/value_extractor.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'data_driven_test_support.dart';
@@ -57,7 +56,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(0, 'a', false, true, LiteralExtractor('0'))],
+ ['C', 'm'], [AddParameter(0, 'a', false, true, codeTemplate('0'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -84,7 +83,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(0, 'a', true, false, LiteralExtractor('0'))],
+ ['C', 'm'], [AddParameter(0, 'a', true, false, codeTemplate('0'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -111,7 +110,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(0, 'a', true, true, LiteralExtractor('0'))],
+ ['C', 'm'], [AddParameter(0, 'a', true, true, codeTemplate('0'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -165,7 +164,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(1, 'b', false, true, LiteralExtractor('1'))],
+ ['C', 'm'], [AddParameter(1, 'b', false, true, codeTemplate('1'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -192,7 +191,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(1, 'b', true, false, LiteralExtractor('1'))],
+ ['C', 'm'], [AddParameter(1, 'b', true, false, codeTemplate('1'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -219,7 +218,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(1, 'b', true, true, LiteralExtractor('1'))],
+ ['C', 'm'], [AddParameter(1, 'b', true, true, codeTemplate('1'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -249,9 +248,9 @@
'C',
'm'
], [
- AddParameter(1, 'b', true, true, LiteralExtractor('1')),
- AddParameter(2, 'c', true, true, LiteralExtractor('2')),
- AddParameter(4, 'e', true, true, LiteralExtractor('4')),
+ AddParameter(1, 'b', true, true, codeTemplate('1')),
+ AddParameter(2, 'c', true, true, codeTemplate('2')),
+ AddParameter(4, 'e', true, true, codeTemplate('4')),
], newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -276,7 +275,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(0, 'a', true, true, LiteralExtractor('0'))],
+ ['C', 'm'], [AddParameter(0, 'a', true, true, codeTemplate('0'))],
newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -301,7 +300,7 @@
}
''');
setPackageData(_modify(
- ['C', 'm'], [AddParameter(0, 'a', true, true, LiteralExtractor('0'))]));
+ ['C', 'm'], [AddParameter(0, 'a', true, true, codeTemplate('0'))]));
await resolveTestUnit('''
import '$importUri';
@@ -331,7 +330,7 @@
'm'
], [
RemoveParameter(PositionalParameterReference(0)),
- AddParameter(2, 'c', true, true, LiteralExtractor('2'))
+ AddParameter(2, 'c', true, true, codeTemplate('2'))
], newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -362,7 +361,7 @@
'm'
], [
RemoveParameter(PositionalParameterReference(1)),
- AddParameter(0, 'a', true, true, LiteralExtractor('0'))
+ AddParameter(0, 'a', true, true, codeTemplate('0'))
], newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -394,7 +393,7 @@
], [
RemoveParameter(PositionalParameterReference(0)),
RemoveParameter(PositionalParameterReference(1)),
- AddParameter(0, 'c', true, true, LiteralExtractor('2')),
+ AddParameter(0, 'c', true, true, codeTemplate('2')),
], newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -426,7 +425,7 @@
], [
RemoveParameter(PositionalParameterReference(1)),
RemoveParameter(PositionalParameterReference(2)),
- AddParameter(1, 'd', true, true, LiteralExtractor('3')),
+ AddParameter(1, 'd', true, true, codeTemplate('3')),
], newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -456,10 +455,10 @@
'C',
'm1'
], [
- AddParameter(0, 'a', true, true, LiteralExtractor('0')),
+ AddParameter(0, 'a', true, true, codeTemplate('0')),
RemoveParameter(PositionalParameterReference(1)),
RemoveParameter(PositionalParameterReference(3)),
- AddParameter(2, 'd', true, true, LiteralExtractor('3')),
+ AddParameter(2, 'd', true, true, codeTemplate('3')),
], newName: 'm2'));
await resolveTestUnit('''
import '$importUri';
@@ -827,7 +826,7 @@
setPackageData(_modify([
'f'
], [
- AddParameter(0, 'a', true, true, LiteralExtractor('0')),
+ AddParameter(0, 'a', true, true, codeTemplate('0')),
], newName: 'g'));
await resolveTestUnit('''
import '$importUri';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
index 6b9b51a..bd85ebe 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
@@ -5,6 +5,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'add_type_parameter_test.dart' as add_type_parameter_change;
+import 'code_template_test.dart' as code_template;
import 'diagnostics/test_all.dart' as diagnostics;
import 'end_to_end_test.dart' as end_to_end;
import 'modify_parameters_test.dart' as modify_parameters;
@@ -15,6 +16,7 @@
void main() {
defineReflectiveSuite(() {
add_type_parameter_change.main();
+ code_template.main();
diagnostics.main();
end_to_end.main();
modify_parameters.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index e944316..d366700 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/fix/data_driven/add_type_parameter.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/modify_parameters.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
@@ -37,8 +38,11 @@
name: 'p'
style: optional_named
argumentValue:
- kind: 'argument'
- index: 1
+ expression: '{% p %}'
+ variables:
+ p:
+ kind: 'argument'
+ index: 1
''');
var transforms = result.transformsFor('f', ['test.dart']);
expect(transforms, hasLength(1));
@@ -53,7 +57,10 @@
expect(modification.name, 'p');
expect(modification.isRequired, false);
expect(modification.isPositional, false);
- var value = modification.argumentValue as ArgumentExtractor;
+ var components = modification.argumentValue.components;
+ expect(components, hasLength(1));
+ var value =
+ (components[0] as TemplateVariable).extractor as ArgumentExtractor;
var parameter = value.parameter as PositionalParameterReference;
expect(parameter.index, 1);
}
@@ -103,8 +110,11 @@
name: 'p'
style: required_named
argumentValue:
- kind: 'argument'
- index: 1
+ expression: '{% p %}'
+ variables:
+ p:
+ kind: 'argument'
+ index: 1
''');
var transforms = result.transformsFor('f', ['test.dart']);
expect(transforms, hasLength(1));
@@ -119,7 +129,10 @@
expect(modification.name, 'p');
expect(modification.isRequired, true);
expect(modification.isPositional, false);
- var value = modification.argumentValue as ArgumentExtractor;
+ var components = modification.argumentValue.components;
+ expect(components, hasLength(1));
+ var value =
+ (components[0] as TemplateVariable).extractor as ArgumentExtractor;
var parameter = value.parameter as PositionalParameterReference;
expect(parameter.index, 1);
}
@@ -139,8 +152,11 @@
name: 'p'
style: required_positional
argumentValue:
- kind: 'argument'
- index: 1
+ expression: '{% p %}'
+ variables:
+ p:
+ kind: 'argument'
+ index: 1
''');
var transforms = result.transformsFor('f', ['test.dart']);
expect(transforms, hasLength(1));
@@ -155,11 +171,65 @@
expect(modification.name, 'p');
expect(modification.isRequired, true);
expect(modification.isPositional, true);
- var value = modification.argumentValue as ArgumentExtractor;
+ var components = modification.argumentValue.components;
+ expect(components, hasLength(1));
+ var value =
+ (components[0] as TemplateVariable).extractor as ArgumentExtractor;
var parameter = value.parameter as PositionalParameterReference;
expect(parameter.index, 1);
}
+ void test_addParameter_requiredPositional_complexTemplate() {
+ parse('''
+version: 1
+transforms:
+- title: 'Add'
+ date: 2020-09-09
+ element:
+ uris: ['test.dart']
+ function: 'f'
+ changes:
+ - kind: 'addParameter'
+ index: 0
+ name: 'p'
+ style: required_positional
+ argumentValue:
+ expression: '{% a %}({% b %})'
+ variables:
+ a:
+ kind: 'argument'
+ index: 1
+ b:
+ kind: 'argument'
+ index: 2
+''');
+ var transforms = result.transformsFor('f', ['test.dart']);
+ expect(transforms, hasLength(1));
+ var transform = transforms[0];
+ expect(transform.title, 'Add');
+ expect(transform.changes, hasLength(1));
+ var change = transform.changes[0] as ModifyParameters;
+ var modifications = change.modifications;
+ expect(modifications, hasLength(1));
+ var modification = modifications[0] as AddParameter;
+ expect(modification.index, 0);
+ expect(modification.name, 'p');
+ expect(modification.isRequired, true);
+ expect(modification.isPositional, true);
+ var components = modification.argumentValue.components;
+ expect(components, hasLength(4));
+ var extractorA =
+ (components[0] as TemplateVariable).extractor as ArgumentExtractor;
+ var parameterA = extractorA.parameter as PositionalParameterReference;
+ expect(parameterA.index, 1);
+ expect((components[1] as TemplateText).text, '(');
+ var extractorB =
+ (components[2] as TemplateVariable).extractor as ArgumentExtractor;
+ var parameterB = extractorB.parameter as PositionalParameterReference;
+ expect(parameterB.index, 2);
+ expect((components[3] as TemplateText).text, ')');
+ }
+
void test_addTypeParameter_fromNamedArgument() {
parse('''
version: 1
@@ -175,8 +245,11 @@
index: 0
name: 'T'
argumentValue:
- kind: 'argument'
- name: 'p'
+ expression: '{% t %}'
+ variables:
+ t:
+ kind: 'argument'
+ name: 'p'
''');
var transforms = result.transformsFor('A', ['test.dart']);
expect(transforms, hasLength(1));
@@ -186,7 +259,10 @@
var change = transform.changes[0] as AddTypeParameter;
expect(change.index, 0);
expect(change.name, 'T');
- var value = change.argumentValue as ArgumentExtractor;
+ var components = change.argumentValue.components;
+ expect(components, hasLength(1));
+ var value =
+ (components[0] as TemplateVariable).extractor as ArgumentExtractor;
var parameter = value.parameter as NamedParameterReference;
expect(parameter.name, 'p');
}
@@ -206,8 +282,11 @@
index: 0
name: 'T'
argumentValue:
- kind: 'argument'
- index: 2
+ expression: '{% t %}'
+ variables:
+ t:
+ kind: 'argument'
+ index: 2
''');
var transforms = result.transformsFor('A', ['test.dart']);
expect(transforms, hasLength(1));
@@ -217,7 +296,10 @@
var change = transform.changes[0] as AddTypeParameter;
expect(change.index, 0);
expect(change.name, 'T');
- var value = change.argumentValue as ArgumentExtractor;
+ var components = change.argumentValue.components;
+ expect(components, hasLength(1));
+ var value =
+ (components[0] as TemplateVariable).extractor as ArgumentExtractor;
var parameter = value.parameter as PositionalParameterReference;
expect(parameter.index, 2);
}
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index c713b70..27eceab 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -439,8 +439,6 @@
);
}
- InterfaceType get _objectType => _typeSystem.typeProvider.objectType;
-
/// Compute the least upper bound of two types.
///
/// https://github.com/dart-lang/language
@@ -808,9 +806,16 @@
return _typeSystem.getGreatestLowerBound(a.type, b.type);
}
+ /// TODO(scheglov) Use greatest closure.
+ /// See https://github.com/dart-lang/language/pull/1195
DartType _typeParameterResolveToObjectBounds(DartType type) {
var element = type.element;
- type = type.resolveToBound(_objectType);
- return Substitution.fromMap({element: _objectType}).substituteType(type);
+
+ var objectType = _typeSystem.isNonNullableByDefault
+ ? _typeSystem.objectQuestion
+ : _typeSystem.objectStar;
+
+ type = type.resolveToBound(objectType);
+ return Substitution.fromMap({element: objectType}).substituteType(type);
}
}
diff --git a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
index 6454cda..cda9e13 100644
--- a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
+++ b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
@@ -2943,7 +2943,7 @@
_checkLeastUpperBound(
S_none,
typeParameterTypeNone(U),
- interfaceTypeNone(A, typeArguments: [objectNone]),
+ interfaceTypeNone(A, typeArguments: [objectQuestion]),
);
}
@@ -2983,14 +2983,22 @@
_checkLeastUpperBound(typeT, C_none, A_none);
}
- void test_typeParameter_interface_noBound() {
+ void test_typeParameter_interface_bounded_objectQuestion() {
var T = typeParameter('T', bound: objectQuestion);
- var A = class_(name: 'A');
-
_checkLeastUpperBound(
typeParameterTypeNone(T),
- interfaceTypeNone(A),
+ intNone,
+ objectQuestion,
+ );
+ }
+
+ void test_typeParameter_interface_noBound() {
+ var T = typeParameter('T');
+
+ _checkLeastUpperBound(
+ typeParameterTypeNone(T),
+ intNone,
objectQuestion,
);
}
diff --git a/tools/VERSION b/tools/VERSION
index 640a299..e4f21fd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 10
PATCH 0
-PRERELEASE 149
+PRERELEASE 150
PRERELEASE_PATCH 0
\ No newline at end of file