Add a new fix for representationFieldModifier
This add a fix to add a type annotation in place of the `var` keyword. The assist to add a type annotation was already being offered, but it didn't understand that it needed to also remove the keyword. The added fix will do both, and, by being a fix, will appear higher in the list of code actions that users see, along side the fix to remove the keyword without adding a type annotation. The fix to remove the keyword can be bulk applied, so I couldn't make the new one also be bulk applicable. We might consider reversing the status so that the default bulk fix behavior includes adding a type annotation, but this CL doesn't do that. Change-Id: I122221ef1e5119a76e1157a9a15cf1193135fc59 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/507161 Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
@@ -21,14 +21,25 @@ class AddTypeAnnotation extends ResolvedCorrectionProducer {
|
||||
@override
|
||||
final CorrectionApplicability applicability;
|
||||
|
||||
final bool forRepresentationField;
|
||||
|
||||
/// Initializes a newly created instance that can't apply bulk and in-file
|
||||
/// fixes.
|
||||
new({required super.context})
|
||||
: applicability = CorrectionApplicability.singleLocation;
|
||||
: applicability = CorrectionApplicability.singleLocation,
|
||||
forRepresentationField = false;
|
||||
|
||||
/// Initializes a newly created instance that can apply bulk and in-file fixes.
|
||||
/// Initializes a newly created instance that can apply bulk and in-file
|
||||
/// fixes.
|
||||
new bulkFixable({required super.context})
|
||||
: applicability = CorrectionApplicability.automatically;
|
||||
: applicability = CorrectionApplicability.automatically,
|
||||
forRepresentationField = false;
|
||||
|
||||
/// Initializes a newly created instance that will replace the keyword with
|
||||
/// the added type.
|
||||
new forRepresentationField({required super.context})
|
||||
: applicability = CorrectionApplicability.singleLocation,
|
||||
forRepresentationField = true;
|
||||
|
||||
@override
|
||||
AssistKind get assistKind => DartAssistKind.addTypeAnnotation;
|
||||
@@ -92,7 +103,8 @@ class AddTypeAnnotation extends ResolvedCorrectionProducer {
|
||||
) async {
|
||||
await builder.addDartFileEdit(file, (builder) {
|
||||
if (builder.canWriteType(type, offset: name.offset)) {
|
||||
if (keyword != null && keyword.keyword == Keyword.VAR) {
|
||||
if (keyword != null &&
|
||||
(forRepresentationField || keyword.keyword == Keyword.VAR)) {
|
||||
builder.addReplacement(range.token(keyword), (builder) {
|
||||
builder.writeType(type);
|
||||
});
|
||||
@@ -137,7 +149,7 @@ class AddTypeAnnotation extends ResolvedCorrectionProducer {
|
||||
if (parameter.type != null) {
|
||||
return;
|
||||
}
|
||||
// Ensure that the parameter is named.
|
||||
// Ensure that the parameter has a named.
|
||||
var name = parameter.name;
|
||||
if (name == null) {
|
||||
return;
|
||||
@@ -145,15 +157,18 @@ class AddTypeAnnotation extends ResolvedCorrectionProducer {
|
||||
// Prepare the type.
|
||||
var type = parameter.declaredFragment!.element.type;
|
||||
// TODO(scheglov): If the parameter is in a method declaration, and if the
|
||||
// method overrides a method that has a type for the corresponding
|
||||
// parameter, it would be nice to copy down the type from the overridden
|
||||
// method.
|
||||
// method overrides a method that has a type for the corresponding
|
||||
// parameter, it would be nice to copy down the type from the overridden
|
||||
// method.
|
||||
if (type is! InterfaceType &&
|
||||
// type is! FunctionType &&
|
||||
// type is! FunctionType &&
|
||||
type is! RecordType) {
|
||||
return;
|
||||
}
|
||||
await _applyChange(builder, null, name, type);
|
||||
var keyword = forRepresentationField
|
||||
? parameter.constFinalOrVarKeyword
|
||||
: null;
|
||||
await _applyChange(builder, keyword, name, type);
|
||||
}
|
||||
|
||||
Future<void> _forVariableDeclaration(
|
||||
|
||||
@@ -738,7 +738,10 @@ final _builtInNonLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
|
||||
ConvertIntoBlockBody.missingBody,
|
||||
],
|
||||
diag.recordLiteralOnePositionalNoTrailingCommaByType: [AddTrailingComma.new],
|
||||
diag.representationFieldModifier: [RemoveKeyword.varKeyword],
|
||||
diag.representationFieldModifier: [
|
||||
AddTypeAnnotation.forRepresentationField,
|
||||
RemoveKeyword.varKeyword,
|
||||
],
|
||||
diag.returnOfInvalidTypeFromClosure: [AddAsync.wrongReturnType],
|
||||
diag.returnOfInvalidTypeFromFunction: [
|
||||
AddAsync.wrongReturnType,
|
||||
|
||||
@@ -18,6 +18,7 @@ void main() {
|
||||
defineReflectiveTests(PreferTypingUninitializedVariablesBulkTest);
|
||||
defineReflectiveTests(PreferTypingUninitializedVariablesInFileTest);
|
||||
defineReflectiveTests(PreferTypingUninitializedVariablesLintTest);
|
||||
defineReflectiveTests(RepresentationFieldModifierTest);
|
||||
defineReflectiveTests(SpecifyNonObviousLocalVariableTypesBulkTest);
|
||||
defineReflectiveTests(SpecifyNonObviousLocalVariableTypesInFileTest);
|
||||
defineReflectiveTests(SpecifyNonObviousLocalVariableTypesLintTest);
|
||||
@@ -453,6 +454,21 @@ void f() {
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RepresentationFieldModifierTest extends FixProcessorTest {
|
||||
@override
|
||||
FixKind get kind => DartFixKind.addTypeAnnotation;
|
||||
|
||||
Future<void> test_var() async {
|
||||
await resolveTestCode('''
|
||||
extension type E(var v);
|
||||
''');
|
||||
await assertHasFix('''
|
||||
extension type E(Object? v);
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class SpecifyNonObviousLocalVariableTypesBulkTest extends BulkFixProcessorTest {
|
||||
@override
|
||||
|
||||
@@ -154,8 +154,12 @@ abstract class BulkFixProcessorTest extends AbstractSingleUnitTest {
|
||||
expect(resultCode, normalizeSource(expectedCode));
|
||||
}
|
||||
|
||||
Future<void> assertHasFix(String expected, {bool isParse = false}) async {
|
||||
change = await _computeSourceChange(isParse: isParse);
|
||||
Future<void> assertHasFix(
|
||||
String expected, {
|
||||
List<String>? codes,
|
||||
bool isParse = false,
|
||||
}) async {
|
||||
change = await _computeSourceChange(codes: codes, isParse: isParse);
|
||||
|
||||
// apply to "file"
|
||||
var fileEdits = change.edits;
|
||||
@@ -184,11 +188,15 @@ abstract class BulkFixProcessorTest extends AbstractSingleUnitTest {
|
||||
}
|
||||
|
||||
/// Computes fixes for the specified [testUnit].
|
||||
Future<BulkFixProcessor> computeFixes({bool isParse = false}) async {
|
||||
Future<BulkFixProcessor> computeFixes({
|
||||
List<String>? codes,
|
||||
bool isParse = false,
|
||||
}) async {
|
||||
var analysisContext = contextFor(testFile);
|
||||
var processor = BulkFixProcessor(
|
||||
TestInstrumentationService(),
|
||||
await workspace,
|
||||
codes: codes,
|
||||
);
|
||||
if (isParse) {
|
||||
await processor.fixErrorsUsingParsedResult([analysisContext]);
|
||||
@@ -214,8 +222,11 @@ abstract class BulkFixProcessorTest extends AbstractSingleUnitTest {
|
||||
}
|
||||
|
||||
/// Returns the source change for computed fixes in the specified [testUnit].
|
||||
Future<SourceChange> _computeSourceChange({bool isParse = false}) async {
|
||||
processor = await computeFixes(isParse: isParse);
|
||||
Future<SourceChange> _computeSourceChange({
|
||||
List<String>? codes,
|
||||
bool isParse = false,
|
||||
}) async {
|
||||
processor = await computeFixes(codes: codes, isParse: isParse);
|
||||
return processor.builder.sourceChange;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user