Fixes for uninitialized fields of Flutter widgets create constructors in Flutter style.

R=brianwilkerson@google.com, devoncarew@google.com

Bug: https://github.com/flutter/flutter-intellij/issues/2021
Change-Id: Ia5c8989d463ce7071b31069313e4638dea7b13a6
Reviewed-on: https://dart-review.googlesource.com/48700
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-03-28 20:28:28 +00:00 committed by commit-bot@chromium.org
parent c9e9c1d8e9
commit 859632b439
2 changed files with 194 additions and 16 deletions

View file

@ -27,6 +27,7 @@ import 'package:analyzer/error/error.dart';
import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/session_helper.dart';
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
@ -107,6 +108,16 @@ class FixProcessor {
*/
AnalysisDriver driver;
/**
* The analysis session to be used to create the change builder.
*/
AnalysisSession session;
/**
* The helper wrapper around the [session].
*/
AnalysisSessionHelper sessionHelper;
String file;
CompilationUnitElement unitElement;
Source unitSource;
@ -131,7 +142,11 @@ class FixProcessor {
resourceProvider = dartContext.resourceProvider;
astProvider = dartContext.astProvider;
getTopLevelDeclarations = dartContext.getTopLevelDeclarations;
driver = dartContext.analysisDriver;
session = driver.currentSession;
sessionHelper = new AnalysisSessionHelper(session);
// unit
unit = dartContext.unit;
unitElement = unit.element;
@ -154,11 +169,6 @@ class FixProcessor {
*/
String get eol => utils.endOfLine;
/**
* Return the analysis session to be used to create the change builder.
*/
AnalysisSession get session => driver.currentSession;
TypeProvider get typeProvider {
if (_typeProvider == null) {
_typeProvider = unitElement.context.typeProvider;
@ -1110,11 +1120,15 @@ class FixProcessor {
if (node is! SimpleIdentifier || node.parent is! VariableDeclaration) {
return;
}
ClassDeclaration classDeclaration =
node.getAncestor((node) => node is ClassDeclaration);
if (classDeclaration == null) {
return;
}
String className = classDeclaration.name.name;
InterfaceType superType = classDeclaration.element.supertype;
// prepare names of uninitialized final fields
List<String> fieldNames = <String>[];
for (ClassMember member in classDeclaration.members) {
@ -1130,15 +1144,50 @@ class FixProcessor {
// prepare location for a new constructor
ClassMemberLocation targetLocation =
utils.prepareNewConstructorLocation(classDeclaration);
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) {
builder.write(targetLocation.prefix);
builder.writeConstructorDeclaration(classDeclaration.name.name,
fieldNames: fieldNames);
builder.write(targetLocation.suffix);
if (flutter.isExactlyStatelessWidgetType(superType) ||
flutter.isExactlyStatefulWidgetType(superType)) {
// Specialize for Flutter widgets.
ClassElement keyClass =
await sessionHelper.getClass(flutter.WIDGETS_LIBRARY_URI, 'Key');
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) {
builder.write(targetLocation.prefix);
builder.write('const ');
builder.write(className);
builder.write('({');
builder.writeType(keyClass.type);
builder.write(' key');
List<String> childrenFields = [];
for (String fieldName in fieldNames) {
if (fieldName == 'child' || fieldName == 'children') {
childrenFields.add(fieldName);
continue;
}
builder.write(', this.');
builder.write(fieldName);
}
for (String fieldName in childrenFields) {
builder.write(', this.');
builder.write(fieldName);
}
builder.write('}) : super(key: key);');
builder.write(targetLocation.suffix);
});
});
});
} else {
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) {
builder.write(targetLocation.prefix);
builder.writeConstructorDeclaration(className,
fieldNames: fieldNames);
builder.write(targetLocation.suffix);
});
});
}
_addFixFromBuilder(
changeBuilder, DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS);
}
@ -2965,22 +3014,44 @@ class FixProcessor {
return;
}
ConstructorDeclaration constructor = node.parent;
// add these fields
List<FormalParameter> parameters = constructor.parameters.parameters;
ClassDeclaration classNode = constructor.parent;
InterfaceType superType = classNode.element.supertype;
// Compute uninitialized final fields.
List<FieldElement> fields =
ErrorVerifier.computeNotInitializedFields(constructor);
fields.retainWhere((FieldElement field) => field.isFinal);
// prepare new parameters code
// Prepare new parameters code.
fields.sort((a, b) => a.nameOffset - b.nameOffset);
String fieldParametersCode =
fields.map((field) => 'this.${field.name}').join(', ');
// prepare the last required parameter
// Specialize for Flutter widgets.
if (flutter.isExactlyStatelessWidgetType(superType) ||
flutter.isExactlyStatefulWidgetType(superType)) {
if (parameters.isNotEmpty && parameters.last.isNamed) {
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addSimpleInsertion(
parameters.last.end, ', $fieldParametersCode');
});
_addFixFromBuilder(
changeBuilder, DartFixKind.ADD_FIELD_FORMAL_PARAMETERS);
return;
}
}
// Prepare the last required parameter.
FormalParameter lastRequiredParameter;
List<FormalParameter> parameters = constructor.parameters.parameters;
for (FormalParameter parameter in parameters) {
if (parameter.isRequired) {
lastRequiredParameter = parameter;
}
}
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
// append new field formal initializers

View file

@ -596,6 +596,32 @@ class B {}
''');
}
test_addFieldFormalParameters_flutter() async {
addFlutterPackage();
await resolveTestUnit('''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final int b;
final int c;
MyWidget({Key key, this.a}) : super(key: key);
}
''');
await assertHasFix(DartFixKind.ADD_FIELD_FORMAL_PARAMETERS, '''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final int b;
final int c;
MyWidget({Key key, this.a, this.b, this.c}) : super(key: key);
}
''');
}
test_addFieldFormalParameters_hasRequiredParameter() async {
await resolveTestUnit('''
class Test {
@ -1623,6 +1649,87 @@ class Test {
''');
}
test_createConstructor_forFinalFields_flutter() async {
addFlutterPackage();
errorFilter = (AnalysisError error) {
return error.message.contains("'a'");
};
await resolveTestUnit('''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final int b = 2;
final int c;
}
''');
await assertHasFix(DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS, '''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final int b = 2;
final int c;
const MyWidget({Key key, this.a, this.c}) : super(key: key);
}
''');
}
test_createConstructor_forFinalFields_flutter_childLast() async {
addFlutterPackage();
errorFilter = (AnalysisError error) {
return error.message.contains("'a'");
};
await resolveTestUnit('''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final Widget child;
final int b;
}
''');
await assertHasFix(DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS, '''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final Widget child;
final int b;
const MyWidget({Key key, this.a, this.b, this.child}) : super(key: key);
}
''');
}
test_createConstructor_forFinalFields_flutter_childrenLast() async {
addFlutterPackage();
errorFilter = (AnalysisError error) {
return error.message.contains("'a'");
};
await resolveTestUnit('''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final List<Widget> children;
final int b;
}
''');
await assertHasFix(DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS, '''
import 'package:flutter/widgets.dart';
class MyWidget extends StatelessWidget {
final int a;
final List<Widget> children;
final int b;
const MyWidget({Key key, this.a, this.b, this.children}) : super(key: key);
}
''');
}
test_createConstructor_insteadOfSyntheticDefault() async {
await resolveTestUnit('''
class A {