mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
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:
parent
c9e9c1d8e9
commit
859632b439
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue