Skip to content

Commit

Permalink
Version 3.7.0-207.0.dev
Browse files Browse the repository at this point in the history
Merge 91c05b2 into dev
  • Loading branch information
Dart CI committed Dec 2, 2024
2 parents c579d19 + 91c05b2 commit 61bfa9b
Show file tree
Hide file tree
Showing 27 changed files with 1,008 additions and 395 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ class EditableArgumentsHandler
};
}

/// Checks whether [argument] is editable and if not, returns a human-readable
/// description why.
String? _getNotEditableReason(Expression argument) {
return switch (argument) {
AdjacentStrings() => "Adjacent strings can't be edited",
StringInterpolation() => "Interpolated strings can't be edited",
SimpleStringLiteral() when argument.value.contains('\n') =>
"Strings containing newlines can't be edited",
_ => null,
};
}

/// Computes the values for a parameter and argument and returns them along
/// with a flag indicating if the default parameter value is being used.
_Values _getValues(
Expand Down Expand Up @@ -194,6 +206,11 @@ class EditableArgumentsHandler
Object? value;
List<String>? options;

// Check whether this value may be editable (for example is not an
// interpolated string).
var notEditableReason =
valueExpression != null ? _getNotEditableReason(valueExpression) : null;

if (parameter.type.isDartCoreDouble) {
type = 'double';
value =
Expand Down Expand Up @@ -232,11 +249,22 @@ class EditableArgumentsHandler
return null;
}

// If the value is not a literal, include the source as displayValue.
var displayValue =
valueExpression is! Literal ? valueExpression?.toSource() : null;
// Unless it turns out to match the value (converted to string).
if (displayValue != null && displayValue == value?.toString()) {
var isEditable = notEditableReason == null;

// Compute a displayValue.
String? displayValue;
if (!isEditable) {
// Not editable, so show the value or source in displayValue.
displayValue = value?.toString() ?? valueExpression?.toSource();
// And remove the value.
value = null;
} else if (valueExpression is! Literal) {
// Also provide the source if it was not a literal.
displayValue = valueExpression?.toSource();
}

// Never provide a displayValue if it's the same as value.
if (displayValue == value) {
displayValue = null;
}

Expand All @@ -251,6 +279,8 @@ class EditableArgumentsHandler
isRequired: parameter.isRequired,
isNullable:
parameter.type.nullabilitySuffix == NullabilitySuffix.question,
isEditable: notEditableReason == null,
notEditableReason: notEditableReason,
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,14 @@ final class ExtractMethodRefactoringImpl extends RefactoringImpl
sb.write(' ');
}
}
// name
sb.write(parameter.name);
// optional function-typed parameter parameters
if (parameter.parameters != null) {
sb.write('Function');
sb.write(parameter.parameters);
sb.write(' ');
}
// name
sb.write(parameter.name);
}
sb.write(')');
}
Expand Down Expand Up @@ -1849,8 +1851,10 @@ extension on LibraryElement {
parametersBuffer.write(', ');
}
parametersBuffer.write(parameterType);
parametersBuffer.write(' ');
parametersBuffer.write(parameter.name);
if (parameter.name.isNotEmpty) {
parametersBuffer.write(' ');
parametersBuffer.write(parameter.name);
}
}
parametersBuffer.write(')');
return getTypeSource(type.returnType, librariesToImport);
Expand Down
178 changes: 178 additions & 0 deletions pkg/analysis_server/test/lsp/editable_arguments_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ $content
Object? isDefault = anything,
Object? isRequired = anything,
Object? isNullable = anything,
Object? isEditable = anything,
Object? notEditableReason = anything,
Object? options = anything,
}) {
return isA<EditableArgument>()
Expand All @@ -75,6 +77,12 @@ $content
.having((arg) => arg.isDefault, 'isDefault', isDefault)
.having((arg) => arg.isRequired, 'isRequired', isRequired)
.having((arg) => arg.isNullable, 'isNullable', isNullable)
.having((arg) => arg.isEditable, 'isEditable', isEditable)
.having(
(arg) => arg.notEditableReason,
'notEditableReason',
notEditableReason,
)
.having((arg) => arg.options, 'options', options)
// Some extra checks that should be true for all.
.having(
Expand All @@ -84,6 +92,16 @@ $content
'different value and displayValues',
isTrue,
)
.having(
(arg) => (arg.notEditableReason == null) == arg.isEditable,
'notEditableReason must be supplied if isEditable=false',
isTrue,
)
.having(
(arg) => arg.value == null || arg.isEditable,
'isEditable must be true if there is a value',
isTrue,
)
.having(
(arg) =>
arg.type == 'enum'
Expand Down Expand Up @@ -129,6 +147,166 @@ class MyWidget extends StatelessWidget {
);
}

test_isEditable_false_string_adjacent() async {
var result = await getEditableArgumentsFor(r'''
class MyWidget extends StatelessWidget {
const MyWidget(String s);
@override
Widget build(BuildContext context) => MyW^idget('a' 'b');
}
''');
expect(
result,
hasArg(
isArg(
's',
type: 'string',
value: isNull,
displayValue: 'ab',
isDefault: false,
isEditable: false,
notEditableReason: "Adjacent strings can't be edited",
),
),
);
}

test_isEditable_false_string_interpolated() async {
var result = await getEditableArgumentsFor(r'''
class MyWidget extends StatelessWidget {
const MyWidget(String s);
@override
Widget build(BuildContext context) => MyW^idget('${context.runtimeType}');
}
''');
expect(
result,
hasArgs(
orderedEquals([
isArg(
's',
type: 'string',
value: isNull,
displayValue: r"'${context.runtimeType}'",
isEditable: false,
notEditableReason: "Interpolated strings can't be edited",
),
]),
),
);
}

test_isEditable_false_string_withNewlines() async {
var result = await getEditableArgumentsFor(r'''
class MyWidget extends StatelessWidget {
const MyWidget(String sEscaped, String sLiteral);
@override
Widget build(BuildContext context) => MyW^idget(
'a\nb',
"""
a
b
""",
);
}
''');
expect(
result,
hasArgs(
orderedEquals([
isArg(
'sEscaped',
type: 'string',
value: isNull,
displayValue: 'a\nb',
isEditable: false,
notEditableReason: "Strings containing newlines can't be edited",
),
isArg(
'sLiteral',
type: 'string',
value: isNull,
displayValue: 'a\nb\n',
isEditable: false,
notEditableReason: "Strings containing newlines can't be edited",
),
]),
),
);
}

test_isEditable_true_string_dollar_escaped() async {
var result = await getEditableArgumentsFor(r'''
class MyWidget extends StatelessWidget {
const MyWidget(String s);
@override
Widget build(BuildContext context) => MyW^idget('\${1}');
}
''');
expect(
result,
hasArg(
isArg(
's',
type: 'string',
value: r'${1}',
displayValue: isNull,
isEditable: true,
),
),
);
}

test_isEditable_true_string_dollar_raw() async {
var result = await getEditableArgumentsFor(r'''
class MyWidget extends StatelessWidget {
const MyWidget(String s);
@override
Widget build(BuildContext context) => MyW^idget(r'${1}');
}
''');
expect(
result,
hasArg(
isArg(
's',
type: 'string',
value: r'${1}',
displayValue: isNull,
isEditable: true,
),
),
);
}

test_isEditable_true_string_tripleQuoted_withoutNewlines() async {
var result = await getEditableArgumentsFor(r'''
class MyWidget extends StatelessWidget {
const MyWidget(String s);
@override
Widget build(BuildContext context) => MyW^idget("""string_value""");
}
''');
expect(
result,
hasArg(
isArg(
's',
type: 'string',
value: 'string_value',
displayValue: isNull,
isEditable: true,
),
),
);
}

test_isNullable() async {
failTestOnErrorDiagnostic = false;
var result = await getEditableArgumentsFor('''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3377,7 +3377,7 @@ class C {
return d;
}
int res(int callback(int x, int y), int b) {
int res(int Function(int x, int y) callback, int b) {
int c = callback(b, 2);
return c;
}
Expand Down Expand Up @@ -3604,6 +3604,53 @@ int res() {
''');
}

Future<void> test_statements_topFunction_parameters_function() async {
await indexTestUnit('''
Future<void> f(void f(String x), String a) async {
// start
f(a);
// end
}
''');
_createRefactoringForStartEndComments();
// apply refactoring
return _assertSuccessfulRefactoring('''
Future<void> f(void f(String x), String a) async {
// start
res(f, a);
// end
}
void res(void Function(String x) f, String a) {
f(a);
}
''');
}

Future<void>
test_statements_topFunction_parameters_function_functionSyntax() async {
await indexTestUnit('''
Future<void> f(void Function(String) f, String a) async {
// start
f(a);
// end
}
''');
_createRefactoringForStartEndComments();
// apply refactoring
return _assertSuccessfulRefactoring('''
Future<void> f(void Function(String) f, String a) async {
// start
res(f, a);
// end
}
void res(void Function(String) f, String a) {
f(a);
}
''');
}

Future<void> test_statements_topFunction_parameters_recordType() async {
await indexTestUnit('''
void f((int, String) r) {
Expand Down
15 changes: 15 additions & 0 deletions pkg/analysis_server/tool/lsp_spec/generate_all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,21 @@ List<LspEntity> getCustomClasses() {
'Whether this argument can be `null`. It is possible for an '
'argument to be required, but still allow an explicit `null`.',
),
field(
'isEditable',
type: 'boolean',
comment:
'Whether this argument can be add/edited. If not, '
'notEditableReason will contain an explanation for why.',
),
field(
'notEditableReason',
type: 'String',
canBeUndefined: true,
comment:
'If isEditable is false, contains a human-readable '
'description of why.',
),
field(
'options',
type: 'string',
Expand Down
1 change: 0 additions & 1 deletion pkg/linter/analyzer_use_new_elements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ lib/src/rules/avoid_setters_without_getters.dart
lib/src/rules/deprecated_member_use_from_same_package.dart
lib/src/rules/invalid_runtime_check_with_js_interop_types.dart
lib/src/rules/prefer_asserts_in_initializer_lists.dart
lib/src/rules/prefer_final_fields.dart
lib/src/rules/prefer_initializing_formals.dart
lib/src/rules/unnecessary_overrides.dart
lib/src/rules/use_build_context_synchronously.dart
Expand Down
Loading

0 comments on commit 61bfa9b

Please sign in to comment.