Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9e3b028

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Convert several quick fixes to use CorrectionProducer
Change-Id: I125c21f56277904885399b3b7316eec92c77c8bb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/148960 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 3fb5f27 commit 9e3b028

16 files changed

+1140
-714
lines changed

pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import 'package:analysis_server/src/utilities/flutter.dart';
99
import 'package:analyzer/dart/analysis/results.dart';
1010
import 'package:analyzer/dart/analysis/session.dart';
1111
import 'package:analyzer/dart/ast/ast.dart';
12+
import 'package:analyzer/dart/ast/token.dart';
1213
import 'package:analyzer/dart/element/element.dart';
14+
import 'package:analyzer/dart/element/type.dart';
1315
import 'package:analyzer/diagnostic/diagnostic.dart';
1416
import 'package:analyzer/source/source_range.dart';
1517
import 'package:analyzer/src/dart/analysis/session_helper.dart';
1618
import 'package:analyzer/src/dart/ast/utilities.dart';
19+
import 'package:analyzer/src/dart/element/type.dart';
1720
import 'package:analyzer/src/generated/resolver.dart';
1821
import 'package:analyzer_plugin/utilities/assist/assist.dart';
1922
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
@@ -32,6 +35,9 @@ abstract class CorrectionProducer extends _AbstractCorrectionProducer {
3235
/// if this producer doesn't support assists.
3336
AssistKind get assistKind => null;
3437

38+
/// Return the type for the class `bool` from `dart:core`.
39+
DartType get coreTypeBool => resolvedResult.typeProvider.boolType;
40+
3541
/// Return the arguments that should be used when composing the message for a
3642
/// fix, or `null` if the fix message has no parameters or if this producer
3743
/// doesn't support fixes.
@@ -41,8 +47,147 @@ abstract class CorrectionProducer extends _AbstractCorrectionProducer {
4147
/// producer doesn't support fixes.
4248
FixKind get fixKind => null;
4349

50+
/// Returns `true` if [node] is in a static context.
51+
bool get inStaticContext {
52+
// constructor initializer cannot reference "this"
53+
if (node.thisOrAncestorOfType<ConstructorInitializer>() != null) {
54+
return true;
55+
}
56+
// field initializer cannot reference "this"
57+
if (node.thisOrAncestorOfType<FieldDeclaration>() != null) {
58+
return true;
59+
}
60+
// static method
61+
var method = node.thisOrAncestorOfType<MethodDeclaration>();
62+
return method != null && method.isStatic;
63+
}
64+
4465
Future<void> compute(DartChangeBuilder builder);
4566

67+
/// Returns an expected [DartType] of [expression], may be `null` if cannot be
68+
/// inferred.
69+
DartType inferUndefinedExpressionType(Expression expression) {
70+
var parent = expression.parent;
71+
// myFunction();
72+
if (parent is ExpressionStatement) {
73+
if (expression is MethodInvocation) {
74+
return VoidTypeImpl.instance;
75+
}
76+
}
77+
// return myFunction();
78+
if (parent is ReturnStatement) {
79+
var executable = getEnclosingExecutableElement(expression);
80+
return executable?.returnType;
81+
}
82+
// int v = myFunction();
83+
if (parent is VariableDeclaration) {
84+
var variableDeclaration = parent;
85+
if (variableDeclaration.initializer == expression) {
86+
var variableElement = variableDeclaration.declaredElement;
87+
if (variableElement != null) {
88+
return variableElement.type;
89+
}
90+
}
91+
}
92+
// myField = 42;
93+
if (parent is AssignmentExpression) {
94+
var assignment = parent;
95+
if (assignment.leftHandSide == expression) {
96+
var rhs = assignment.rightHandSide;
97+
if (rhs != null) {
98+
return rhs.staticType;
99+
}
100+
}
101+
}
102+
// v = myFunction();
103+
if (parent is AssignmentExpression) {
104+
var assignment = parent;
105+
if (assignment.rightHandSide == expression) {
106+
if (assignment.operator.type == TokenType.EQ) {
107+
// v = myFunction();
108+
var lhs = assignment.leftHandSide;
109+
if (lhs != null) {
110+
return lhs.staticType;
111+
}
112+
} else {
113+
// v += myFunction();
114+
var method = assignment.staticElement;
115+
if (method != null) {
116+
var parameters = method.parameters;
117+
if (parameters.length == 1) {
118+
return parameters[0].type;
119+
}
120+
}
121+
}
122+
}
123+
}
124+
// v + myFunction();
125+
if (parent is BinaryExpression) {
126+
var binary = parent;
127+
var method = binary.staticElement;
128+
if (method != null) {
129+
if (binary.rightOperand == expression) {
130+
var parameters = method.parameters;
131+
return parameters.length == 1 ? parameters[0].type : null;
132+
}
133+
}
134+
}
135+
// foo( myFunction() );
136+
if (parent is ArgumentList) {
137+
var parameter = expression.staticParameterElement;
138+
return parameter?.type;
139+
}
140+
// bool
141+
{
142+
// assert( myFunction() );
143+
if (parent is AssertStatement) {
144+
var statement = parent;
145+
if (statement.condition == expression) {
146+
return coreTypeBool;
147+
}
148+
}
149+
// if ( myFunction() ) {}
150+
if (parent is IfStatement) {
151+
var statement = parent;
152+
if (statement.condition == expression) {
153+
return coreTypeBool;
154+
}
155+
}
156+
// while ( myFunction() ) {}
157+
if (parent is WhileStatement) {
158+
var statement = parent;
159+
if (statement.condition == expression) {
160+
return coreTypeBool;
161+
}
162+
}
163+
// do {} while ( myFunction() );
164+
if (parent is DoStatement) {
165+
var statement = parent;
166+
if (statement.condition == expression) {
167+
return coreTypeBool;
168+
}
169+
}
170+
// !myFunction()
171+
if (parent is PrefixExpression) {
172+
var prefixExpression = parent;
173+
if (prefixExpression.operator.type == TokenType.BANG) {
174+
return coreTypeBool;
175+
}
176+
}
177+
// binary expression '&&' or '||'
178+
if (parent is BinaryExpression) {
179+
var binaryExpression = parent;
180+
var operatorType = binaryExpression.operator.type;
181+
if (operatorType == TokenType.AMPERSAND_AMPERSAND ||
182+
operatorType == TokenType.BAR_BAR) {
183+
return coreTypeBool;
184+
}
185+
}
186+
}
187+
// we don't know
188+
return null;
189+
}
190+
46191
/// Return `true` if the [node] might be a type name.
47192
bool mightBeTypeIdentifier(AstNode node) {
48193
if (node is SimpleIdentifier) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
6+
import 'package:analysis_server/src/services/correction/fix.dart';
7+
import 'package:analyzer/dart/analysis/features.dart';
8+
import 'package:analyzer/dart/ast/ast.dart';
9+
import 'package:analyzer/dart/element/nullability_suffix.dart';
10+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
11+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
12+
13+
class CreateConstructorForFinalFields extends CorrectionProducer {
14+
@override
15+
FixKind get fixKind => DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS;
16+
17+
bool get _isNonNullable => unit.featureSet.isEnabled(Feature.non_nullable);
18+
19+
@override
20+
Future<void> compute(DartChangeBuilder builder) async {
21+
if (node is! SimpleIdentifier || node.parent is! VariableDeclaration) {
22+
return;
23+
}
24+
25+
var classDeclaration = node.thisOrAncestorOfType<ClassDeclaration>();
26+
if (classDeclaration == null) {
27+
return;
28+
}
29+
var className = classDeclaration.name.name;
30+
var superType = classDeclaration.declaredElement.supertype;
31+
32+
// prepare names of uninitialized final fields
33+
var fieldNames = <String>[];
34+
for (var member in classDeclaration.members) {
35+
if (member is FieldDeclaration) {
36+
var variableList = member.fields;
37+
if (variableList.isFinal) {
38+
fieldNames.addAll(variableList.variables
39+
.where((v) => v.initializer == null)
40+
.map((v) => v.name.name));
41+
}
42+
}
43+
}
44+
// prepare location for a new constructor
45+
var targetLocation = utils.prepareNewConstructorLocation(classDeclaration);
46+
47+
if (flutter.isExactlyStatelessWidgetType(superType) ||
48+
flutter.isExactlyStatefulWidgetType(superType)) {
49+
// Specialize for Flutter widgets.
50+
var keyClass = await sessionHelper.getClass(flutter.widgetsUri, 'Key');
51+
await builder.addFileEdit(file, (DartFileEditBuilder builder) {
52+
builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) {
53+
builder.write(targetLocation.prefix);
54+
builder.write('const ');
55+
builder.write(className);
56+
builder.write('({');
57+
builder.writeType(
58+
keyClass.instantiate(
59+
typeArguments: const [],
60+
nullabilitySuffix: _isNonNullable
61+
? NullabilitySuffix.question
62+
: NullabilitySuffix.star,
63+
),
64+
);
65+
builder.write(' key');
66+
67+
var childrenFields = <String>[];
68+
for (var fieldName in fieldNames) {
69+
if (fieldName == 'child' || fieldName == 'children') {
70+
childrenFields.add(fieldName);
71+
continue;
72+
}
73+
builder.write(', this.');
74+
builder.write(fieldName);
75+
}
76+
for (var fieldName in childrenFields) {
77+
builder.write(', this.');
78+
builder.write(fieldName);
79+
}
80+
81+
builder.write('}) : super(key: key);');
82+
builder.write(targetLocation.suffix);
83+
});
84+
});
85+
} else {
86+
await builder.addFileEdit(file, (DartFileEditBuilder builder) {
87+
builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) {
88+
builder.write(targetLocation.prefix);
89+
builder.writeConstructorDeclaration(className,
90+
fieldNames: fieldNames);
91+
builder.write(targetLocation.suffix);
92+
});
93+
});
94+
}
95+
}
96+
97+
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
98+
static CreateConstructorForFinalFields newInstance() =>
99+
CreateConstructorForFinalFields();
100+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
6+
import 'package:analysis_server/src/services/correction/fix.dart';
7+
import 'package:analysis_server/src/services/correction/util.dart';
8+
import 'package:analyzer/dart/ast/ast.dart';
9+
import 'package:analyzer/dart/element/element.dart';
10+
import 'package:analyzer/dart/element/type.dart';
11+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
12+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
13+
14+
class CreateGetter extends CorrectionProducer {
15+
String getterName;
16+
17+
@override
18+
List<Object> get fixArguments => [getterName];
19+
20+
@override
21+
FixKind get fixKind => DartFixKind.CREATE_GETTER;
22+
23+
@override
24+
Future<void> compute(DartChangeBuilder builder) async {
25+
if (node is! SimpleIdentifier) {
26+
return;
27+
}
28+
SimpleIdentifier nameNode = node;
29+
getterName = nameNode.name;
30+
if (!nameNode.inGetterContext()) {
31+
return;
32+
}
33+
// prepare target
34+
Expression target;
35+
{
36+
var nameParent = nameNode.parent;
37+
if (nameParent is PrefixedIdentifier) {
38+
target = nameParent.prefix;
39+
} else if (nameParent is PropertyAccess) {
40+
target = nameParent.realTarget;
41+
}
42+
}
43+
// prepare target element
44+
var staticModifier = false;
45+
Element targetElement;
46+
if (target is ExtensionOverride) {
47+
targetElement = target.staticElement;
48+
} else if (target is Identifier &&
49+
target.staticElement is ExtensionElement) {
50+
targetElement = target.staticElement;
51+
staticModifier = true;
52+
} else if (target != null) {
53+
// prepare target interface type
54+
var targetType = target.staticType;
55+
if (targetType is! InterfaceType) {
56+
return;
57+
}
58+
targetElement = targetType.element;
59+
// maybe static
60+
if (target is Identifier) {
61+
var targetIdentifier = target;
62+
var targetElement = targetIdentifier.staticElement;
63+
staticModifier = targetElement?.kind == ElementKind.CLASS;
64+
}
65+
} else {
66+
targetElement =
67+
getEnclosingClassElement(node) ?? getEnclosingExtensionElement(node);
68+
if (targetElement == null) {
69+
return;
70+
}
71+
staticModifier = inStaticContext;
72+
}
73+
if (targetElement.librarySource.isInSystemLibrary) {
74+
return;
75+
}
76+
// prepare target declaration
77+
var targetDeclarationResult =
78+
await sessionHelper.getElementDeclaration(targetElement);
79+
if (targetDeclarationResult == null) {
80+
return;
81+
}
82+
if (targetDeclarationResult.node is! ClassOrMixinDeclaration &&
83+
targetDeclarationResult.node is! ExtensionDeclaration) {
84+
return;
85+
}
86+
CompilationUnitMember targetNode = targetDeclarationResult.node;
87+
// prepare location
88+
var targetLocation = CorrectionUtils(targetDeclarationResult.resolvedUnit)
89+
.prepareNewGetterLocation(targetNode);
90+
// build method source
91+
var targetSource = targetElement.source;
92+
var targetFile = targetSource.fullName;
93+
await builder.addFileEdit(targetFile, (DartFileEditBuilder builder) {
94+
builder.addInsertion(targetLocation.offset, (DartEditBuilder builder) {
95+
var fieldTypeNode = climbPropertyAccess(nameNode);
96+
var fieldType = inferUndefinedExpressionType(fieldTypeNode);
97+
builder.write(targetLocation.prefix);
98+
builder.writeGetterDeclaration(getterName,
99+
isStatic: staticModifier,
100+
nameGroupName: 'NAME',
101+
returnType: fieldType,
102+
returnTypeGroupName: 'TYPE');
103+
builder.write(targetLocation.suffix);
104+
});
105+
});
106+
}
107+
108+
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
109+
static CreateGetter newInstance() => CreateGetter();
110+
}

0 commit comments

Comments
 (0)