Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(905)

Side by Side Diff: pkg/analysis_services/lib/src/correction/fix.dart

Issue 484733003: Import analysis_services.dart into analysis_server.dart. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, 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 library services.src.correction.fix;
6
7 import 'dart:collection';
8
9 import 'package:analysis_services/correction/change.dart';
10 import 'package:analysis_services/correction/fix.dart';
11 import 'package:analysis_services/search/hierarchy.dart';
12 import 'package:analysis_services/search/search_engine.dart';
13 import 'package:analysis_services/src/correction/levenshtein.dart';
14 import 'package:analysis_services/src/correction/name_suggestion.dart';
15 import 'package:analysis_services/src/correction/source_buffer.dart';
16 import 'package:analysis_services/src/correction/source_range.dart' as rf;
17 import 'package:analysis_services/src/correction/strings.dart';
18 import 'package:analysis_services/src/correction/util.dart';
19 import 'package:analyzer/src/generated/ast.dart';
20 import 'package:analyzer/src/generated/element.dart';
21 import 'package:analyzer/src/generated/engine.dart';
22 import 'package:analyzer/src/generated/error.dart';
23 import 'package:analyzer/src/generated/java_core.dart';
24 import 'package:analyzer/src/generated/parser.dart';
25 import 'package:analyzer/src/generated/scanner.dart';
26 import 'package:analyzer/src/generated/sdk.dart';
27 import 'package:analyzer/src/generated/source.dart';
28 import 'package:analyzer/src/generated/utilities_dart.dart';
29 import 'package:path/path.dart';
30
31
32 /**
33 * A predicate is a one-argument function that returns a boolean value.
34 */
35 typedef bool Predicate<E>(E argument);
36
37
38 /**
39 * The computer for Dart fixes.
40 */
41 class FixProcessor {
42 static const int MAX_LEVENSHTEIN_DISTANCE = 3;
43
44 final SearchEngine searchEngine;
45 final Source source;
46 final String file;
47 final CompilationUnit unit;
48 final AnalysisError error;
49 CompilationUnitElement unitElement;
50 Source unitSource;
51 LibraryElement unitLibraryElement;
52 String unitLibraryFile;
53 String unitLibraryFolder;
54
55 final List<Edit> edits = <Edit>[];
56 final Map<String, LinkedEditGroup> linkedPositionGroups = <String,
57 LinkedEditGroup>{};
58 Position exitPosition = null;
59 final List<Fix> fixes = <Fix>[];
60
61 CorrectionUtils utils;
62 int errorOffset;
63 int errorLength;
64 int errorEnd;
65 AstNode node;
66 AstNode coveredNode;
67
68 FixProcessor(this.searchEngine, this.source, this.file, this.unit, this.error)
69 {
70 unitElement = unit.element;
71 unitSource = unitElement.source;
72 unitLibraryElement = unitElement.library;
73 unitLibraryFile = unitLibraryElement.source.fullName;
74 unitLibraryFolder = dirname(unitLibraryFile);
75 }
76
77 DartType get coreTypeBool => _getCoreType("bool");
78
79 /**
80 * Returns the EOL to use for this [CompilationUnit].
81 */
82 String get eol => utils.endOfLine;
83
84 List<Fix> compute() {
85 utils = new CorrectionUtils(unit);
86 errorOffset = error.offset;
87 errorLength = error.length;
88 errorEnd = errorOffset + errorLength;
89 node = new NodeLocator.con1(errorOffset).searchWithin(unit);
90 coveredNode = new NodeLocator.con2(
91 errorOffset,
92 errorOffset + errorLength).searchWithin(unit);
93 // analyze ErrorCode
94 ErrorCode errorCode = error.errorCode;
95 if (errorCode == StaticWarningCode.UNDEFINED_CLASS_BOOLEAN) {
96 _addFix_boolInsteadOfBoolean();
97 }
98 if (errorCode ==
99 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE) {
100 _addFix_replaceWithConstInstanceCreation();
101 }
102 if (errorCode ==
103 CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT) {
104 _addFix_createConstructorSuperExplicit();
105 }
106 if (errorCode ==
107 CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT) {
108 _addFix_createConstructorSuperImplicit();
109 }
110 if (errorCode ==
111 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT) {
112 _addFix_createConstructorSuperExplicit();
113 }
114 if (errorCode == HintCode.DIVISION_OPTIMIZATION) {
115 _addFix_useEffectiveIntegerDivision();
116 }
117 if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) {
118 _addFix_isNotNull();
119 }
120 if (errorCode == HintCode.TYPE_CHECK_IS_NULL) {
121 _addFix_isNull();
122 }
123 if (errorCode == HintCode.UNNECESSARY_CAST) {
124 _addFix_removeUnnecessaryCast();
125 }
126 if (errorCode == HintCode.UNUSED_IMPORT) {
127 _addFix_removeUnusedImport();
128 }
129 if (errorCode == ParserErrorCode.EXPECTED_TOKEN) {
130 _addFix_insertSemicolon();
131 }
132 if (errorCode == ParserErrorCode.GETTER_WITH_PARAMETERS) {
133 _addFix_removeParameters_inGetterDeclaration();
134 }
135 if (errorCode == StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER) {
136 _addFix_makeEnclosingClassAbstract();
137 }
138 if (errorCode == StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS) {
139 _addFix_createConstructor_insteadOfSyntheticDefault();
140 }
141 if (errorCode == StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR) {
142 _addFix_createConstructor_named();
143 }
144 if (errorCode ==
145 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE ||
146 errorCode ==
147 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO ||
148 errorCode ==
149 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE ||
150 errorCode ==
151 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR | |
152 errorCode ==
153 StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_P LUS) {
154 // make class abstract
155 _addFix_makeEnclosingClassAbstract();
156 // implement methods
157 AnalysisErrorWithProperties errorWithProperties =
158 error as AnalysisErrorWithProperties;
159 Object property =
160 errorWithProperties.getProperty(ErrorProperty.UNIMPLEMENTED_METHODS);
161 List<ExecutableElement> missingOverrides =
162 property as List<ExecutableElement>;
163 _addFix_createMissingOverrides(missingOverrides);
164 _addFix_createNoSuchMethod();
165 }
166 if (errorCode == StaticWarningCode.UNDEFINED_CLASS) {
167 _addFix_importLibrary_withType();
168 _addFix_createClass();
169 _addFix_undefinedClass_useSimilar();
170 }
171 if (errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER) {
172 _addFix_createFunction_forFunctionType();
173 _addFix_importLibrary_withType();
174 _addFix_importLibrary_withTopLevelVariable();
175 }
176 if (errorCode == StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER) {
177 _addFix_useStaticAccess_method();
178 _addFix_useStaticAccess_property();
179 }
180 if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) {
181 _addFix_removeParentheses_inGetterInvocation();
182 }
183 if (errorCode == StaticTypeWarningCode.UNDEFINED_FUNCTION) {
184 _addFix_importLibrary_withFunction();
185 _addFix_undefinedFunction_useSimilar();
186 _addFix_undefinedFunction_create();
187 }
188 if (errorCode == StaticTypeWarningCode.UNDEFINED_GETTER) {
189 _addFix_createFunction_forFunctionType();
190 }
191 if (errorCode == HintCode.UNDEFINED_METHOD ||
192 errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) {
193 _addFix_undefinedMethod_useSimilar();
194 _addFix_undefinedMethod_create();
195 _addFix_undefinedFunction_create();
196 }
197 // done
198 return fixes;
199 }
200
201 void _addFix(FixKind kind, List args, {String fixFile}) {
202 if (fixFile == null) {
203 fixFile = file;
204 }
205 FileEdit fileEdit = new FileEdit(file);
206 fileEdit.addAll(edits);
207 // prepare Change
208 String message = formatList(kind.message, args);
209 Change change = new Change(message);
210 change.addFileEdit(fileEdit);
211 linkedPositionGroups.values.forEach(
212 (group) => change.addLinkedEditGroup(group));
213 change.selection = exitPosition;
214 // add Fix
215 Fix fix = new Fix(kind, change);
216 fixes.add(fix);
217 // clear
218 edits.clear();
219 linkedPositionGroups.clear();
220 exitPosition = null;
221 }
222
223 void _addFix_boolInsteadOfBoolean() {
224 SourceRange range = rf.rangeError(error);
225 _addReplaceEdit(range, "bool");
226 _addFix(FixKind.REPLACE_BOOLEAN_WITH_BOOL, []);
227 }
228
229 void _addFix_createClass() {
230 if (_mayBeTypeIdentifier(node)) {
231 String name = (node as SimpleIdentifier).name;
232 // prepare environment
233 CompilationUnitMember enclosingMember =
234 node.getAncestor((node) => node is CompilationUnitMember);
235 int offset = enclosingMember.end;
236 String prefix = "";
237 // prepare source
238 SourceBuilder sb = new SourceBuilder(file, offset);
239 {
240 sb.append("${eol}${eol}");
241 sb.append(prefix);
242 // "class"
243 sb.append("class ");
244 // append name
245 {
246 sb.startPosition("NAME");
247 sb.append(name);
248 sb.endPosition();
249 }
250 // no members
251 sb.append(" {");
252 sb.append(eol);
253 sb.append("}");
254 }
255 // insert source
256 _insertBuilder(sb);
257 _addLinkedPosition("NAME", rf.rangeNode(node));
258 // add proposal
259 _addFix(FixKind.CREATE_CLASS, [name]);
260 }
261 }
262
263 void _addFix_createConstructorSuperExplicit() {
264 ConstructorDeclaration targetConstructor =
265 node.parent as ConstructorDeclaration;
266 ClassDeclaration targetClassNode =
267 targetConstructor.parent as ClassDeclaration;
268 ClassElement targetClassElement = targetClassNode.element;
269 ClassElement superClassElement = targetClassElement.supertype.element;
270 // add proposals for all super constructors
271 List<ConstructorElement> superConstructors = superClassElement.constructors;
272 for (ConstructorElement superConstructor in superConstructors) {
273 String constructorName = superConstructor.name;
274 // skip private
275 if (Identifier.isPrivateName(constructorName)) {
276 continue;
277 }
278 // prepare SourceBuilder
279 SourceBuilder sb;
280 {
281 List<ConstructorInitializer> initializers =
282 targetConstructor.initializers;
283 if (initializers.isEmpty) {
284 int insertOffset = targetConstructor.parameters.end;
285 sb = new SourceBuilder(file, insertOffset);
286 sb.append(" : ");
287 } else {
288 ConstructorInitializer lastInitializer =
289 initializers[initializers.length - 1];
290 int insertOffset = lastInitializer.end;
291 sb = new SourceBuilder(file, insertOffset);
292 sb.append(", ");
293 }
294 }
295 // add super constructor name
296 sb.append("super");
297 if (!StringUtils.isEmpty(constructorName)) {
298 sb.append(".");
299 sb.append(constructorName);
300 }
301 // add arguments
302 sb.append("(");
303 bool firstParameter = true;
304 for (ParameterElement parameter in superConstructor.parameters) {
305 // skip non-required parameters
306 if (parameter.parameterKind != ParameterKind.REQUIRED) {
307 break;
308 }
309 // comma
310 if (firstParameter) {
311 firstParameter = false;
312 } else {
313 sb.append(", ");
314 }
315 // default value
316 DartType parameterType = parameter.type;
317 sb.startPosition(parameter.name);
318 sb.append(getDefaultValueCode(parameterType));
319 sb.endPosition();
320 }
321 sb.append(")");
322 // insert proposal
323 _insertBuilder(sb);
324 // add proposal
325 String proposalName = _getConstructorProposalName(superConstructor);
326 _addFix(FixKind.ADD_SUPER_CONSTRUCTOR_INVOCATION, [proposalName]);
327 }
328 }
329
330 void _addFix_createConstructorSuperImplicit() {
331 ClassDeclaration targetClassNode = node.parent as ClassDeclaration;
332 ClassElement targetClassElement = targetClassNode.element;
333 ClassElement superClassElement = targetClassElement.supertype.element;
334 String targetClassName = targetClassElement.name;
335 // add proposals for all super constructors
336 List<ConstructorElement> superConstructors = superClassElement.constructors;
337 for (ConstructorElement superConstructor in superConstructors) {
338 String constructorName = superConstructor.name;
339 // skip private
340 if (Identifier.isPrivateName(constructorName)) {
341 continue;
342 }
343 // prepare parameters and arguments
344 SourceBuilder parametersBuffer = new SourceBuilder.buffer();
345 SourceBuilder argumentsBuffer = new SourceBuilder.buffer();
346 bool firstParameter = true;
347 for (ParameterElement parameter in superConstructor.parameters) {
348 // skip non-required parameters
349 if (parameter.parameterKind != ParameterKind.REQUIRED) {
350 break;
351 }
352 // comma
353 if (firstParameter) {
354 firstParameter = false;
355 } else {
356 parametersBuffer.append(', ');
357 argumentsBuffer.append(', ');
358 }
359 // name
360 String parameterName = parameter.displayName;
361 if (parameterName.length > 1 && parameterName.startsWith('_')) {
362 parameterName = parameterName.substring(1);
363 }
364 // parameter & argument
365 _appendParameterSource(parametersBuffer, parameter.type, parameterName);
366 argumentsBuffer.append(parameterName);
367 }
368 // add proposal
369 _ConstructorLocation targetLocation =
370 _prepareNewConstructorLocation(targetClassNode);
371 SourceBuilder sb = new SourceBuilder(file, targetLocation._offset);
372 {
373 String indent = utils.getIndent(1);
374 sb.append(targetLocation._prefix);
375 sb.append(indent);
376 sb.append(targetClassName);
377 if (!constructorName.isEmpty) {
378 sb.startPosition('NAME');
379 sb.append('.');
380 sb.append(constructorName);
381 sb.endPosition();
382 }
383 sb.append("(");
384 sb.append(parametersBuffer.toString());
385 sb.append(') : super');
386 if (!constructorName.isEmpty) {
387 sb.append('.');
388 sb.append(constructorName);
389 }
390 sb.append('(');
391 sb.append(argumentsBuffer.toString());
392 sb.append(');');
393 sb.append(targetLocation._suffix);
394 }
395 _insertBuilder(sb);
396 // add proposal
397 String proposalName = _getConstructorProposalName(superConstructor);
398 _addFix(FixKind.CREATE_CONSTRUCTOR_SUPER, [proposalName]);
399 }
400 }
401
402 void _addFix_createConstructor_insteadOfSyntheticDefault() {
403 TypeName typeName = null;
404 ConstructorName constructorName = null;
405 InstanceCreationExpression instanceCreation = null;
406 if (node is SimpleIdentifier) {
407 if (node.parent is TypeName) {
408 typeName = node.parent as TypeName;
409 if (typeName.name == node && typeName.parent is ConstructorName) {
410 constructorName = typeName.parent as ConstructorName;
411 // should be synthetic default constructor
412 {
413 ConstructorElement constructorElement =
414 constructorName.staticElement;
415 if (constructorElement == null ||
416 !constructorElement.isDefaultConstructor ||
417 !constructorElement.isSynthetic) {
418 return;
419 }
420 }
421 // prepare InstanceCreationExpression
422 if (constructorName.parent is InstanceCreationExpression) {
423 instanceCreation = constructorName.parent as
424 InstanceCreationExpression;
425 if (instanceCreation.constructorName != constructorName) {
426 return;
427 }
428 }
429 }
430 }
431 }
432 // do we have enough information?
433 if (instanceCreation == null) {
434 return;
435 }
436 // prepare target
437 DartType targetType = typeName.type;
438 if (targetType is! InterfaceType) {
439 return;
440 }
441 ClassElement targetElement = targetType.element as ClassElement;
442 String targetFile = targetElement.source.fullName;
443 ClassDeclaration targetClass = targetElement.node;
444 _ConstructorLocation targetLocation =
445 _prepareNewConstructorLocation(targetClass);
446 // build method source
447 SourceBuilder sb = new SourceBuilder(targetFile, targetLocation._offset);
448 {
449 String indent = " ";
450 sb.append(targetLocation._prefix);
451 sb.append(indent);
452 sb.append(targetElement.name);
453 _addFix_undefinedMethod_create_parameters(
454 sb,
455 instanceCreation.argumentList);
456 sb.append(") {${eol}${indent}}");
457 sb.append(targetLocation._suffix);
458 }
459 // insert source
460 _insertBuilder(sb);
461 // add proposal
462 _addFix(FixKind.CREATE_CONSTRUCTOR, [constructorName], fixFile: targetFile);
463 }
464
465 void _addFix_createConstructor_named() {
466 SimpleIdentifier name = null;
467 ConstructorName constructorName = null;
468 InstanceCreationExpression instanceCreation = null;
469 if (node is SimpleIdentifier) {
470 // name
471 name = node as SimpleIdentifier;
472 if (name.parent is ConstructorName) {
473 constructorName = name.parent as ConstructorName;
474 if (constructorName.name == name) {
475 // Type.name
476 if (constructorName.parent is InstanceCreationExpression) {
477 instanceCreation = constructorName.parent as
478 InstanceCreationExpression;
479 // new Type.name()
480 if (instanceCreation.constructorName != constructorName) {
481 return;
482 }
483 }
484 }
485 }
486 }
487 // do we have enough information?
488 if (instanceCreation == null) {
489 return;
490 }
491 // prepare target interface type
492 DartType targetType = constructorName.type.type;
493 if (targetType is! InterfaceType) {
494 return;
495 }
496 ClassElement targetElement = targetType.element as ClassElement;
497 String targetFile = targetElement.source.fullName;
498 ClassDeclaration targetClass = targetElement.node;
499 _ConstructorLocation targetLocation =
500 _prepareNewConstructorLocation(targetClass);
501 // build method source
502 SourceBuilder sb = new SourceBuilder(targetFile, targetLocation._offset);
503 {
504 String indent = " ";
505 sb.append(targetLocation._prefix);
506 sb.append(indent);
507 sb.append(targetElement.name);
508 sb.append(".");
509 // append name
510 {
511 sb.startPosition("NAME");
512 sb.append(name.name);
513 sb.endPosition();
514 }
515 _addFix_undefinedMethod_create_parameters(
516 sb,
517 instanceCreation.argumentList);
518 sb.append(") {${eol}${indent}}");
519 sb.append(targetLocation._suffix);
520 }
521 // insert source
522 _insertBuilder(sb);
523 if (targetFile == file) {
524 _addLinkedPosition("NAME", rf.rangeNode(name));
525 }
526 // add proposal
527 _addFix(FixKind.CREATE_CONSTRUCTOR, [constructorName], fixFile: targetFile);
528 }
529
530 void _addFix_createFunction_forFunctionType() {
531 if (node is SimpleIdentifier) {
532 SimpleIdentifier nameNode = node as SimpleIdentifier;
533 // prepare argument expression (to get parameter)
534 ClassElement targetElement;
535 Expression argument;
536 {
537 Expression target = getQualifiedPropertyTarget(node);
538 if (target != null) {
539 DartType targetType = target.bestType;
540 if (targetType != null && targetType.element is ClassElement) {
541 targetElement = targetType.element as ClassElement;
542 argument = target.parent as Expression;
543 } else {
544 return;
545 }
546 } else {
547 ClassDeclaration enclosingClass =
548 node.getAncestor((node) => node is ClassDeclaration);
549 targetElement = enclosingClass != null ?
550 enclosingClass.element :
551 null;
552 argument = nameNode;
553 }
554 }
555 // should be argument of some invocation
556 ParameterElement parameterElement = argument.bestParameterElement;
557 if (parameterElement == null) {
558 return;
559 }
560 // should be parameter of function type
561 DartType parameterType = parameterElement.type;
562 if (parameterType is! FunctionType) {
563 return;
564 }
565 FunctionType functionType = parameterType as FunctionType;
566 // add proposal
567 if (targetElement != null) {
568 _addProposal_createFunction_method(targetElement, functionType);
569 } else {
570 _addProposal_createFunction_function(functionType);
571 }
572 }
573 }
574
575 void
576 _addFix_createMissingOverrides(List<ExecutableElement> missingOverrides) {
577 // sort by name
578 missingOverrides.sort((Element firstElement, Element secondElement) {
579 return compareStrings(
580 firstElement.displayName,
581 secondElement.displayName);
582 });
583 ClassDeclaration targetClass = node.parent as ClassDeclaration;
584 int insertOffset = targetClass.end - 1;
585 SourceBuilder sb = new SourceBuilder(file, insertOffset);
586 // add elements
587 bool isFirst = true;
588 for (ExecutableElement missingOverride in missingOverrides) {
589 if (!isFirst || !targetClass.members.isEmpty) {
590 sb.append(eol);
591 }
592 _addFix_createMissingOverrides_single(sb, targetClass, missingOverride);
593 isFirst = false;
594 }
595 // add proposal
596 exitPosition = new Position(file, insertOffset);
597 _insertBuilder(sb);
598 _addFix(FixKind.CREATE_MISSING_OVERRIDES, [missingOverrides.length]);
599 }
600
601 void _addFix_createMissingOverrides_single(SourceBuilder sb,
602 ClassDeclaration targetClass, ExecutableElement missingOverride) {
603 // prepare environment
604 String prefix = utils.getIndent(1);
605 String prefix2 = utils.getIndent(2);
606 // may be property
607 ElementKind elementKind = missingOverride.kind;
608 bool isGetter = elementKind == ElementKind.GETTER;
609 bool isSetter = elementKind == ElementKind.SETTER;
610 bool isMethod = elementKind == ElementKind.METHOD;
611 bool isOperator = isMethod && (missingOverride as MethodElement).isOperator;
612 sb.append(prefix);
613 if (isGetter) {
614 sb.append('// TODO: implement ${missingOverride.displayName}');
615 sb.append(eol);
616 sb.append(prefix);
617 }
618 // @override
619 {
620 sb.append('@override');
621 sb.append(eol);
622 sb.append(prefix);
623 }
624 // return type
625 _appendType(sb, missingOverride.type.returnType);
626 if (isGetter) {
627 sb.append('get ');
628 } else if (isSetter) {
629 sb.append('set ');
630 } else if (isOperator) {
631 sb.append('operator ');
632 }
633 // name
634 sb.append(missingOverride.displayName);
635 // parameters + body
636 if (isGetter) {
637 sb.append(' => null;');
638 } else {
639 List<ParameterElement> parameters = missingOverride.parameters;
640 _appendParameters(sb, parameters, _getDefaultValueMap(parameters));
641 sb.append(' {');
642 // TO-DO
643 sb.append(eol);
644 sb.append(prefix2);
645 sb.append('// TODO: implement ${missingOverride.displayName}');
646 sb.append(eol);
647 // close method
648 sb.append(prefix);
649 sb.append('}');
650 }
651 sb.append(eol);
652 }
653
654 void _addFix_createNoSuchMethod() {
655 ClassDeclaration targetClass = node.parent as ClassDeclaration;
656 // prepare environment
657 String prefix = utils.getIndent(1);
658 int insertOffset = targetClass.end - 1;
659 // prepare source
660 SourceBuilder sb = new SourceBuilder(file, insertOffset);
661 {
662 // insert empty line before existing member
663 if (!targetClass.members.isEmpty) {
664 sb.append(eol);
665 }
666 // append method
667 sb.append(prefix);
668 sb.append(
669 "noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation) ;");
670 sb.append(eol);
671 }
672 // done
673 _insertBuilder(sb);
674 exitPosition = new Position(file, insertOffset);
675 // add proposal
676 _addFix(FixKind.CREATE_NO_SUCH_METHOD, []);
677 }
678
679 void _addFix_importLibrary(FixKind kind, String importPath) {
680 CompilationUnitElement libraryUnitElement =
681 unitLibraryElement.definingCompilationUnit;
682 CompilationUnit libraryUnit = libraryUnitElement.node;
683 // prepare new import location
684 int offset = 0;
685 String prefix;
686 String suffix;
687 {
688 // if no directives
689 prefix = "";
690 suffix = eol;
691 CorrectionUtils libraryUtils = new CorrectionUtils(libraryUnit);
692 // after last directive in library
693 for (Directive directive in libraryUnit.directives) {
694 if (directive is LibraryDirective || directive is ImportDirective) {
695 offset = directive.end;
696 prefix = eol;
697 suffix = "";
698 }
699 }
700 // if still beginning of file, skip shebang and line comments
701 if (offset == 0) {
702 CorrectionUtils_InsertDesc desc = libraryUtils.getInsertDescTop();
703 offset = desc.offset;
704 prefix = desc.prefix;
705 suffix = "${desc.suffix}${eol}";
706 }
707 }
708 // insert new import
709 String importSource = "${prefix}import '${importPath}';${suffix}";
710 _addInsertEdit(offset, importSource);
711 // add proposal
712 _addFix(kind, [importPath], fixFile: libraryUnitElement.source.fullName);
713 }
714
715 void _addFix_importLibrary_withElement(String name, ElementKind kind) {
716 // ignore if private
717 if (name.startsWith("_")) {
718 return;
719 }
720
721 // may be there is an existing import,
722 // but it is with prefix and we don't use this prefix
723 for (ImportElement imp in unitLibraryElement.imports) {
724 // prepare element
725 LibraryElement libraryElement = imp.importedLibrary;
726 Element element = getExportedElement(libraryElement, name);
727 if (element == null) {
728 continue;
729 }
730 if (element is PropertyAccessorElement) {
731 element = (element as PropertyAccessorElement).variable;
732 }
733 if (element.kind != kind) {
734 continue;
735 }
736 // may be apply prefix
737 PrefixElement prefix = imp.prefix;
738 if (prefix != null) {
739 SourceRange range = rf.rangeStartLength(node, 0);
740 _addReplaceEdit(range, "${prefix.displayName}.");
741 _addFix(
742 FixKind.IMPORT_LIBRARY_PREFIX,
743 [libraryElement.displayName, prefix.displayName]);
744 continue;
745 }
746 // may be update "show" directive
747 List<NamespaceCombinator> combinators = imp.combinators;
748 if (combinators.length == 1 && combinators[0] is ShowElementCombinator) {
749 ShowElementCombinator showCombinator =
750 combinators[0] as ShowElementCombinator;
751 // prepare new set of names to show
752 Set<String> showNames = new SplayTreeSet<String>();
753 showNames.addAll(showCombinator.shownNames);
754 showNames.add(name);
755 // prepare library name - unit name or 'dart:name' for SDK library
756 String libraryName = libraryElement.definingCompilationUnit.displayName;
757 if (libraryElement.isInSdk) {
758 libraryName = imp.uri;
759 }
760 // update library
761 String newShowCode = "show ${StringUtils.join(showNames, ", ")}";
762 _addReplaceEdit(rf.rangeOffsetEnd(showCombinator), newShowCode);
763 _addFix(
764 FixKind.IMPORT_LIBRARY_SHOW,
765 [libraryName],
766 fixFile: unitLibraryFile);
767 // we support only one import without prefix
768 return;
769 }
770 }
771 // check SDK libraries
772 AnalysisContext context = unitLibraryElement.context;
773 {
774 DartSdk sdk = context.sourceFactory.dartSdk;
775 List<SdkLibrary> sdkLibraries = sdk.sdkLibraries;
776 for (SdkLibrary sdkLibrary in sdkLibraries) {
777 SourceFactory sdkSourceFactory = context.sourceFactory;
778 String libraryUri = 'dart:' + sdkLibrary.shortName;
779 Source librarySource =
780 sdkSourceFactory.resolveUri(unitSource, libraryUri);
781 // prepare LibraryElement
782 LibraryElement libraryElement =
783 context.getLibraryElement(librarySource);
784 if (libraryElement == null) {
785 continue;
786 }
787 // prepare exported Element
788 Element element = getExportedElement(libraryElement, name);
789 if (element == null) {
790 continue;
791 }
792 if (element is PropertyAccessorElement) {
793 element = (element as PropertyAccessorElement).variable;
794 }
795 if (element.kind != kind) {
796 continue;
797 }
798 // add import
799 _addFix_importLibrary(FixKind.IMPORT_LIBRARY_SDK, libraryUri);
800 }
801 }
802 // check project libraries
803 {
804 List<Source> librarySources = context.librarySources;
805 for (Source librarySource in librarySources) {
806 // we don't need SDK libraries here
807 if (librarySource.isInSystemLibrary) {
808 continue;
809 }
810 // prepare LibraryElement
811 LibraryElement libraryElement =
812 context.getLibraryElement(librarySource);
813 if (libraryElement == null) {
814 continue;
815 }
816 // prepare exported Element
817 Element element = getExportedElement(libraryElement, name);
818 if (element == null) {
819 continue;
820 }
821 if (element is PropertyAccessorElement) {
822 element = (element as PropertyAccessorElement).variable;
823 }
824 if (element.kind != kind) {
825 continue;
826 }
827 // prepare "library" file
828 String libraryFile = librarySource.fullName;
829 // may be "package:" URI
830 {
831 String libraryPackageUri = _findPackageUri(context, libraryFile);
832 if (libraryPackageUri != null) {
833 _addFix_importLibrary(
834 FixKind.IMPORT_LIBRARY_PROJECT,
835 libraryPackageUri);
836 continue;
837 }
838 }
839 // relative URI
840 String relativeFile = relative(libraryFile, from: unitLibraryFolder);
841 relativeFile = split(relativeFile).join('/');
842 _addFix_importLibrary(FixKind.IMPORT_LIBRARY_PROJECT, relativeFile);
843 }
844 }
845 }
846
847 void _addFix_importLibrary_withFunction() {
848 if (node is SimpleIdentifier && node.parent is MethodInvocation) {
849 MethodInvocation invocation = node.parent as MethodInvocation;
850 if (invocation.realTarget == null && invocation.methodName == node) {
851 String name = (node as SimpleIdentifier).name;
852 _addFix_importLibrary_withElement(name, ElementKind.FUNCTION);
853 }
854 }
855 }
856
857 void _addFix_importLibrary_withTopLevelVariable() {
858 if (node is SimpleIdentifier) {
859 String name = (node as SimpleIdentifier).name;
860 _addFix_importLibrary_withElement(name, ElementKind.TOP_LEVEL_VARIABLE);
861 }
862 }
863
864 void _addFix_importLibrary_withType() {
865 if (_mayBeTypeIdentifier(node)) {
866 String typeName = (node as SimpleIdentifier).name;
867 _addFix_importLibrary_withElement(typeName, ElementKind.CLASS);
868 }
869 }
870
871 void _addFix_insertSemicolon() {
872 if (error.message.contains("';'")) {
873 int insertOffset = error.offset + error.length;
874 _addInsertEdit(insertOffset, ";");
875 _addFix(FixKind.INSERT_SEMICOLON, []);
876 }
877 }
878
879 void _addFix_isNotNull() {
880 if (coveredNode is IsExpression) {
881 IsExpression isExpression = coveredNode as IsExpression;
882 _addReplaceEdit(
883 rf.rangeEndEnd(isExpression.expression, isExpression),
884 " != null");
885 _addFix(FixKind.USE_NOT_EQ_NULL, []);
886 }
887 }
888
889 void _addFix_isNull() {
890 if (coveredNode is IsExpression) {
891 IsExpression isExpression = coveredNode as IsExpression;
892 _addReplaceEdit(
893 rf.rangeEndEnd(isExpression.expression, isExpression),
894 " == null");
895 _addFix(FixKind.USE_EQ_EQ_NULL, []);
896 }
897 }
898
899 void _addFix_makeEnclosingClassAbstract() {
900 ClassDeclaration enclosingClass =
901 node.getAncestor((node) => node is ClassDeclaration);
902 String className = enclosingClass.name.name;
903 _addInsertEdit(enclosingClass.classKeyword.offset, "abstract ");
904 _addFix(FixKind.MAKE_CLASS_ABSTRACT, [className]);
905 }
906
907 void _addFix_removeParameters_inGetterDeclaration() {
908 if (node is SimpleIdentifier && node.parent is MethodDeclaration) {
909 MethodDeclaration method = node.parent as MethodDeclaration;
910 FunctionBody body = method.body;
911 if (method.name == node && body != null) {
912 _addReplaceEdit(rf.rangeEndStart(node, body), " ");
913 _addFix(FixKind.REMOVE_PARAMETERS_IN_GETTER_DECLARATION, []);
914 }
915 }
916 }
917
918 void _addFix_removeParentheses_inGetterInvocation() {
919 if (node is SimpleIdentifier && node.parent is MethodInvocation) {
920 MethodInvocation invocation = node.parent as MethodInvocation;
921 if (invocation.methodName == node && invocation.target != null) {
922 _addRemoveEdit(rf.rangeEndEnd(node, invocation));
923 _addFix(FixKind.REMOVE_PARENTHESIS_IN_GETTER_INVOCATION, []);
924 }
925 }
926 }
927
928 void _addFix_removeUnnecessaryCast() {
929 if (coveredNode is! AsExpression) {
930 return;
931 }
932 AsExpression asExpression = coveredNode as AsExpression;
933 Expression expression = asExpression.expression;
934 int expressionPrecedence = getExpressionPrecedence(expression);
935 // remove 'as T' from 'e as T'
936 _addRemoveEdit(rf.rangeEndEnd(expression, asExpression));
937 _removeEnclosingParentheses(asExpression, expressionPrecedence);
938 // done
939 _addFix(FixKind.REMOVE_UNNECASSARY_CAST, []);
940 }
941
942 void _addFix_removeUnusedImport() {
943 // prepare ImportDirective
944 ImportDirective importDirective =
945 node.getAncestor((node) => node is ImportDirective);
946 if (importDirective == null) {
947 return;
948 }
949 // remove the whole line with import
950 _addRemoveEdit(utils.getLinesRange(rf.rangeNode(importDirective)));
951 // done
952 _addFix(FixKind.REMOVE_UNUSED_IMPORT, []);
953 }
954
955 void _addFix_replaceWithConstInstanceCreation() {
956 if (coveredNode is InstanceCreationExpression) {
957 var instanceCreation = coveredNode as InstanceCreationExpression;
958 _addReplaceEdit(rf.rangeToken(instanceCreation.keyword), "const");
959 _addFix(FixKind.USE_CONST, []);
960 }
961 }
962
963 void _addFix_undefinedClass_useSimilar() {
964 if (_mayBeTypeIdentifier(node)) {
965 String name = (node as SimpleIdentifier).name;
966 _ClosestElementFinder finder =
967 new _ClosestElementFinder(
968 name,
969 (Element element) => element is ClassElement,
970 MAX_LEVENSHTEIN_DISTANCE);
971 // find closest element
972 {
973 // elements of this library
974 for (CompilationUnitElement unit in unitLibraryElement.units) {
975 finder._updateList(unit.types);
976 }
977 // elements from imports
978 for (ImportElement importElement in unitLibraryElement.imports) {
979 if (importElement.prefix == null) {
980 Map<String, Element> namespace = getImportNamespace(importElement);
981 finder._updateList(namespace.values);
982 }
983 }
984 }
985 // if we have close enough element, suggest to use it
986 if (finder._element != null) {
987 String closestName = finder._element.name;
988 _addReplaceEdit(rf.rangeNode(node), closestName);
989 // add proposal
990 if (closestName != null) {
991 _addFix(FixKind.CHANGE_TO, [closestName]);
992 }
993 }
994 }
995 }
996
997 void _addFix_undefinedFunction_create() {
998 // should be the name of the invocation
999 if (node is SimpleIdentifier && node.parent is MethodInvocation) {
1000 } else {
1001 return;
1002 }
1003 String name = (node as SimpleIdentifier).name;
1004 MethodInvocation invocation = node.parent as MethodInvocation;
1005 // function invocation has no target
1006 Expression target = invocation.realTarget;
1007 if (target != null) {
1008 return;
1009 }
1010 // prepare environment
1011 int insertOffset;
1012 String sourcePrefix;
1013 AstNode enclosingMember =
1014 node.getAncestor((node) => node is CompilationUnitMember);
1015 insertOffset = enclosingMember.end;
1016 sourcePrefix = "${eol}${eol}";
1017 // build method source
1018 SourceBuilder sb = new SourceBuilder(file, insertOffset);
1019 {
1020 sb.append(sourcePrefix);
1021 // append return type
1022 {
1023 DartType type = _inferReturnType(invocation);
1024 _appendType(sb, type, 'RETURN_TYPE');
1025 }
1026 // append name
1027 {
1028 sb.startPosition("NAME");
1029 sb.append(name);
1030 sb.endPosition();
1031 }
1032 _addFix_undefinedMethod_create_parameters(sb, invocation.argumentList);
1033 sb.append(") {${eol}}");
1034 }
1035 // insert source
1036 _insertBuilder(sb);
1037 _addLinkedPosition3('NAME', sb, rf.rangeNode(node));
1038 // add proposal
1039 _addFix(FixKind.CREATE_FUNCTION, [name]);
1040 }
1041
1042 void _addFix_undefinedFunction_useSimilar() {
1043 if (node is SimpleIdentifier) {
1044 String name = (node as SimpleIdentifier).name;
1045 _ClosestElementFinder finder =
1046 new _ClosestElementFinder(
1047 name,
1048 (Element element) => element is FunctionElement,
1049 MAX_LEVENSHTEIN_DISTANCE);
1050 // this library
1051 for (CompilationUnitElement unit in unitLibraryElement.units) {
1052 finder._updateList(unit.functions);
1053 }
1054 // imports
1055 for (ImportElement importElement in unitLibraryElement.imports) {
1056 if (importElement.prefix == null) {
1057 Map<String, Element> namespace = getImportNamespace(importElement);
1058 finder._updateList(namespace.values);
1059 }
1060 }
1061 // if we have close enough element, suggest to use it
1062 if (finder._element != null) {
1063 String closestName = finder._element.name;
1064 _addReplaceEdit(rf.rangeNode(node), closestName);
1065 _addFix(FixKind.CHANGE_TO, [closestName]);
1066 }
1067 }
1068 }
1069
1070 void _addFix_undefinedMethod_create() {
1071 if (node is SimpleIdentifier && node.parent is MethodInvocation) {
1072 String name = (node as SimpleIdentifier).name;
1073 MethodInvocation invocation = node.parent as MethodInvocation;
1074 // prepare environment
1075 Source targetSource;
1076 String prefix;
1077 int insertOffset;
1078 String sourcePrefix;
1079 String sourceSuffix;
1080 bool staticModifier = false;
1081 Expression target = invocation.realTarget;
1082 if (target == null) {
1083 targetSource = source;
1084 ClassMember enclosingMember =
1085 node.getAncestor((node) => node is ClassMember);
1086 staticModifier = _inStaticContext();
1087 prefix = utils.getNodePrefix(enclosingMember);
1088 insertOffset = enclosingMember.end;
1089 sourcePrefix = "${eol}${eol}";
1090 sourceSuffix = "";
1091 } else {
1092 // prepare target interface type
1093 DartType targetType = target.bestType;
1094 if (targetType is! InterfaceType) {
1095 return;
1096 }
1097 ClassElement targetElement = targetType.element as ClassElement;
1098 targetSource = targetElement.source;
1099 // may be static
1100 if (target is Identifier) {
1101 staticModifier = target.bestElement.kind == ElementKind.CLASS;
1102 }
1103 // prepare insert offset
1104 ClassDeclaration targetClass = targetElement.node;
1105 prefix = " ";
1106 insertOffset = targetClass.end - 1;
1107 if (targetClass.members.isEmpty) {
1108 sourcePrefix = "";
1109 } else {
1110 sourcePrefix = eol;
1111 }
1112 sourceSuffix = eol;
1113 }
1114 String targetFile = targetSource.fullName;
1115 // build method source
1116 SourceBuilder sb = new SourceBuilder(targetFile, insertOffset);
1117 {
1118 sb.append(sourcePrefix);
1119 sb.append(prefix);
1120 // maybe "static"
1121 if (staticModifier) {
1122 sb.append("static ");
1123 }
1124 // append return type
1125 _appendType(sb, _inferReturnType(invocation), 'RETURN_TYPE');
1126 // append name
1127 {
1128 sb.startPosition("NAME");
1129 sb.append(name);
1130 sb.endPosition();
1131 }
1132 _addFix_undefinedMethod_create_parameters(sb, invocation.argumentList);
1133 sb.append(") {${eol}${prefix}}");
1134 sb.append(sourceSuffix);
1135 }
1136 // insert source
1137 _insertBuilder(sb);
1138 // add linked positions
1139 if (targetSource == source) {
1140 _addLinkedPosition3('NAME', sb, rf.rangeNode(node));
1141 }
1142 // add proposal
1143 _addFix(FixKind.CREATE_METHOD, [name], fixFile: targetFile);
1144 }
1145 }
1146
1147 void _addFix_undefinedMethod_create_parameters(SourceBuilder sb,
1148 ArgumentList argumentList) {
1149 // append parameters
1150 sb.append("(");
1151 Set<String> excluded = new Set();
1152 List<Expression> arguments = argumentList.arguments;
1153 for (int i = 0; i < arguments.length; i++) {
1154 Expression argument = arguments[i];
1155 // append separator
1156 if (i != 0) {
1157 sb.append(", ");
1158 }
1159 // append type name
1160 DartType type = argument.bestType;
1161 String typeSource = utils.getTypeSource(type);
1162 {
1163 sb.startPosition("TYPE${i}");
1164 sb.append(typeSource);
1165 _addSuperTypeProposals(sb, new Set(), type);
1166 sb.endPosition();
1167 }
1168 sb.append(" ");
1169 // append parameter name
1170 {
1171 List<String> suggestions =
1172 _getArgumentNameSuggestions(excluded, type, argument, i);
1173 String favorite = suggestions[0];
1174 excluded.add(favorite);
1175 sb.startPosition("ARG${i}");
1176 sb.append(favorite);
1177 sb.addSuggestions(LinkedEditSuggestionKind.PARAMETER, suggestions);
1178 sb.endPosition();
1179 }
1180 }
1181 }
1182
1183 void _addFix_undefinedMethod_useSimilar() {
1184 if (node is SimpleIdentifier && node.parent is MethodInvocation) {
1185 MethodInvocation invocation = node.parent as MethodInvocation;
1186 String name = (node as SimpleIdentifier).name;
1187 _ClosestElementFinder finder =
1188 new _ClosestElementFinder(
1189 name,
1190 (Element element) => element is MethodElement && !element.isOperat or,
1191 MAX_LEVENSHTEIN_DISTANCE);
1192 // unqualified invocation
1193 Expression target = invocation.realTarget;
1194 if (target == null) {
1195 ClassDeclaration clazz =
1196 invocation.getAncestor((node) => node is ClassDeclaration);
1197 if (clazz != null) {
1198 ClassElement classElement = clazz.element;
1199 _updateFinderWithClassMembers(finder, classElement);
1200 }
1201 } else {
1202 DartType type = target.bestType;
1203 if (type is InterfaceType) {
1204 ClassElement classElement = type.element;
1205 _updateFinderWithClassMembers(finder, classElement);
1206 }
1207 }
1208 // if we have close enough element, suggest to use it
1209 if (finder._element != null) {
1210 String closestName = finder._element.name;
1211 _addReplaceEdit(rf.rangeNode(node), closestName);
1212 _addFix(FixKind.CHANGE_TO, [closestName]);
1213 }
1214 }
1215 }
1216
1217 void _addFix_useEffectiveIntegerDivision() {
1218 for (AstNode n = node; n != null; n = n.parent) {
1219 if (n is MethodInvocation &&
1220 n.offset == errorOffset &&
1221 n.length == errorLength) {
1222 MethodInvocation invocation = n as MethodInvocation;
1223 Expression target = invocation.target;
1224 while (target is ParenthesizedExpression) {
1225 target = (target as ParenthesizedExpression).expression;
1226 }
1227 // replace "/" with "~/"
1228 BinaryExpression binary = target as BinaryExpression;
1229 _addReplaceEdit(rf.rangeToken(binary.operator), "~/");
1230 // remove everything before and after
1231 _addRemoveEdit(rf.rangeStartStart(invocation, binary.leftOperand));
1232 _addRemoveEdit(rf.rangeEndEnd(binary.rightOperand, invocation));
1233 // add proposal
1234 _addFix(FixKind.USE_EFFECTIVE_INTEGER_DIVISION, []);
1235 // done
1236 break;
1237 }
1238 }
1239 }
1240
1241 void _addFix_useStaticAccess_method() {
1242 if (node is SimpleIdentifier && node.parent is MethodInvocation) {
1243 MethodInvocation invocation = node.parent as MethodInvocation;
1244 if (invocation.methodName == node) {
1245 Expression target = invocation.target;
1246 String targetType = utils.getExpressionTypeSource(target);
1247 // replace "target" with class name
1248 SourceRange range = rf.rangeNode(target);
1249 _addReplaceEdit(range, targetType);
1250 // add proposal
1251 _addFix(FixKind.CHANGE_TO_STATIC_ACCESS, [targetType]);
1252 }
1253 }
1254 }
1255
1256 void _addFix_useStaticAccess_property() {
1257 if (node is SimpleIdentifier) {
1258 if (node.parent is PrefixedIdentifier) {
1259 PrefixedIdentifier prefixed = node.parent as PrefixedIdentifier;
1260 if (prefixed.identifier == node) {
1261 Expression target = prefixed.prefix;
1262 String targetType = utils.getExpressionTypeSource(target);
1263 // replace "target" with class name
1264 SourceRange range = rf.rangeNode(target);
1265 _addReplaceEdit(range, targetType);
1266 // add proposal
1267 _addFix(FixKind.CHANGE_TO_STATIC_ACCESS, [targetType]);
1268 }
1269 }
1270 }
1271 }
1272
1273 /**
1274 * Adds a new [Edit] to [edits].
1275 */
1276 void _addInsertEdit(int offset, String text) {
1277 Edit edit = new Edit(offset, 0, text);
1278 edits.add(edit);
1279 }
1280
1281 /**
1282 * Adds a single linked position to [groupId].
1283 */
1284 void _addLinkedPosition(String groupId, SourceRange range) {
1285 Position position = new Position(file, range.offset);
1286 LinkedEditGroup group = _getLinkedPosition(groupId);
1287 group.addPosition(position, range.length);
1288 }
1289
1290 /**
1291 * Adds a single linked position to [groupId].
1292 */
1293 void _addLinkedPosition3(String groupId, SourceBuilder sb,
1294 SourceRange range) {
1295 if (sb.offset < range.offset) {
1296 int delta = sb.length;
1297 range = range.getTranslated(delta);
1298 }
1299 _addLinkedPosition(groupId, range);
1300 }
1301
1302 /**
1303 * Prepares proposal for creating function corresponding to the given [Functio nType].
1304 */
1305 void _addProposal_createFunction(FunctionType functionType, String name,
1306 Source targetSource, int insertOffset, bool isStatic, String prefix,
1307 String sourcePrefix, String sourceSuffix) {
1308 // build method source
1309 String targetFile = targetSource.fullName;
1310 SourceBuilder sb = new SourceBuilder(targetFile, insertOffset);
1311 {
1312 sb.append(sourcePrefix);
1313 sb.append(prefix);
1314 // may be static
1315 if (isStatic) {
1316 sb.append("static ");
1317 }
1318 // append return type
1319 _appendType(sb, functionType.returnType, 'RETURN_TYPE');
1320 // append name
1321 {
1322 sb.startPosition("NAME");
1323 sb.append(name);
1324 sb.endPosition();
1325 }
1326 // append parameters
1327 sb.append("(");
1328 List<ParameterElement> parameters = functionType.parameters;
1329 for (int i = 0; i < parameters.length; i++) {
1330 ParameterElement parameter = parameters[i];
1331 // append separator
1332 if (i != 0) {
1333 sb.append(", ");
1334 }
1335 // append type name
1336 DartType type = parameter.type;
1337 if (!type.isDynamic) {
1338 String typeSource = utils.getTypeSource(type);
1339 {
1340 sb.startPosition("TYPE${i}");
1341 sb.append(typeSource);
1342 _addSuperTypeProposals(sb, new Set(), type);
1343 sb.endPosition();
1344 }
1345 sb.append(" ");
1346 }
1347 // append parameter name
1348 {
1349 sb.startPosition("ARG${i}");
1350 sb.append(parameter.displayName);
1351 sb.endPosition();
1352 }
1353 }
1354 sb.append(")");
1355 // close method
1356 sb.append(" {${eol}${prefix}}");
1357 sb.append(sourceSuffix);
1358 }
1359 // insert source
1360 _insertBuilder(sb);
1361 // add linked positions
1362 if (targetSource == source) {
1363 _addLinkedPosition3("NAME", sb, rf.rangeNode(node));
1364 }
1365 }
1366
1367 /**
1368 * Adds proposal for creating method corresponding to the given [FunctionType] in the given
1369 * [ClassElement].
1370 */
1371 void _addProposal_createFunction_function(FunctionType functionType) {
1372 String name = (node as SimpleIdentifier).name;
1373 // prepare environment
1374 int insertOffset = unit.end;
1375 // prepare prefix
1376 String prefix = "";
1377 String sourcePrefix = "${eol}";
1378 String sourceSuffix = eol;
1379 _addProposal_createFunction(
1380 functionType,
1381 name,
1382 source,
1383 insertOffset,
1384 false,
1385 prefix,
1386 sourcePrefix,
1387 sourceSuffix);
1388 // add proposal
1389 _addFix(FixKind.CREATE_FUNCTION, [name], fixFile: file);
1390 }
1391
1392 /**
1393 * Adds proposal for creating method corresponding to the given [FunctionType] in the given
1394 * [ClassElement].
1395 */
1396 void _addProposal_createFunction_method(ClassElement targetClassElement,
1397 FunctionType functionType) {
1398 String name = (node as SimpleIdentifier).name;
1399 // prepare environment
1400 Source targetSource = targetClassElement.source;
1401 String targetFile = targetSource.fullName;
1402 // prepare insert offset
1403 ClassDeclaration targetClassNode = targetClassElement.node;
1404 int insertOffset = targetClassNode.end - 1;
1405 // prepare prefix
1406 String prefix = " ";
1407 String sourcePrefix;
1408 if (targetClassNode.members.isEmpty) {
1409 sourcePrefix = "";
1410 } else {
1411 sourcePrefix = eol;
1412 }
1413 String sourceSuffix = eol;
1414 _addProposal_createFunction(
1415 functionType,
1416 name,
1417 targetSource,
1418 insertOffset,
1419 _inStaticContext(),
1420 prefix,
1421 sourcePrefix,
1422 sourceSuffix);
1423 // add proposal
1424 _addFix(FixKind.CREATE_METHOD, [name], fixFile: targetFile);
1425 }
1426
1427 /**
1428 * Adds a new [Edit] to [edits].
1429 */
1430 void _addRemoveEdit(SourceRange range) {
1431 _addReplaceEdit(range, '');
1432 }
1433
1434 /**
1435 * Adds a new [Edit] to [edits].
1436 */
1437 void _addReplaceEdit(SourceRange range, String text) {
1438 Edit edit = new Edit(range.offset, range.length, text);
1439 edits.add(edit);
1440 }
1441
1442 void _appendParameterSource(SourceBuilder sb, DartType type, String name) {
1443 String parameterSource = utils.getParameterSource(type, name);
1444 sb.append(parameterSource);
1445 }
1446
1447 void _appendParameters(SourceBuilder sb, List<ParameterElement> parameters,
1448 Map<ParameterElement, String> defaultValueMap) {
1449 sb.append("(");
1450 bool firstParameter = true;
1451 bool sawNamed = false;
1452 bool sawPositional = false;
1453 for (ParameterElement parameter in parameters) {
1454 if (!firstParameter) {
1455 sb.append(", ");
1456 } else {
1457 firstParameter = false;
1458 }
1459 // may be optional
1460 ParameterKind parameterKind = parameter.parameterKind;
1461 if (parameterKind == ParameterKind.NAMED) {
1462 if (!sawNamed) {
1463 sb.append("{");
1464 sawNamed = true;
1465 }
1466 }
1467 if (parameterKind == ParameterKind.POSITIONAL) {
1468 if (!sawPositional) {
1469 sb.append("[");
1470 sawPositional = true;
1471 }
1472 }
1473 // parameter
1474 _appendParameterSource(sb, parameter.type, parameter.name);
1475 // default value
1476 if (defaultValueMap != null) {
1477 String defaultSource = defaultValueMap[parameter];
1478 if (defaultSource != null) {
1479 if (sawPositional) {
1480 sb.append(" = ");
1481 } else {
1482 sb.append(": ");
1483 }
1484 sb.append(defaultSource);
1485 }
1486 }
1487 }
1488 // close parameters
1489 if (sawNamed) {
1490 sb.append("}");
1491 }
1492 if (sawPositional) {
1493 sb.append("]");
1494 }
1495 sb.append(")");
1496 }
1497
1498 void _appendType(SourceBuilder sb, DartType type, [String groupId]) {
1499 if (type != null && !type.isDynamic) {
1500 String typeSource = utils.getTypeSource(type);
1501 if (groupId != null) {
1502 sb.startPosition(groupId);
1503 sb.append(typeSource);
1504 sb.endPosition();
1505 } else {
1506 sb.append(typeSource);
1507 }
1508 sb.append(' ');
1509 }
1510 }
1511
1512 /**
1513 * @return the string to display as the name of the given constructor in a pro posal name.
1514 */
1515 String _getConstructorProposalName(ConstructorElement constructor) {
1516 SourceBuilder proposalNameBuffer = new SourceBuilder.buffer();
1517 proposalNameBuffer.append("super");
1518 // may be named
1519 String constructorName = constructor.displayName;
1520 if (!constructorName.isEmpty) {
1521 proposalNameBuffer.append(".");
1522 proposalNameBuffer.append(constructorName);
1523 }
1524 // parameters
1525 _appendParameters(proposalNameBuffer, constructor.parameters, null);
1526 // done
1527 return proposalNameBuffer.toString();
1528 }
1529
1530 /**
1531 * Returns the [Type] with given name from the `dart:core` library.
1532 */
1533 DartType _getCoreType(String name) {
1534 List<LibraryElement> libraries = unitLibraryElement.importedLibraries;
1535 for (LibraryElement library in libraries) {
1536 if (library.isDartCore) {
1537 ClassElement classElement = library.getType(name);
1538 if (classElement != null) {
1539 return classElement.type;
1540 }
1541 return null;
1542 }
1543 }
1544 return null;
1545 }
1546
1547 Map<ParameterElement, String>
1548 _getDefaultValueMap(List<ParameterElement> parameters) {
1549 Map<ParameterElement, String> defaultSourceMap = {};
1550 Map<Source, String> sourceContentMap = {};
1551 for (ParameterElement parameter in parameters) {
1552 SourceRange valueRange = parameter.defaultValueRange;
1553 if (valueRange != null) {
1554 Source source = parameter.source;
1555 String sourceContent = sourceContentMap[source];
1556 if (sourceContent == null) {
1557 sourceContent = getSourceContent(parameter.context, source);
1558 sourceContentMap[source] = sourceContent;
1559 }
1560 String valueSource =
1561 sourceContent.substring(valueRange.offset, valueRange.end);
1562 defaultSourceMap[parameter] = valueSource;
1563 }
1564 }
1565 return defaultSourceMap;
1566 }
1567
1568 /**
1569 * Returns an existing or just added [LinkedEditGroup] with [groupId].
1570 */
1571 LinkedEditGroup _getLinkedPosition(String groupId) {
1572 LinkedEditGroup group = linkedPositionGroups[groupId];
1573 if (group == null) {
1574 group = new LinkedEditGroup(groupId);
1575 linkedPositionGroups[groupId] = group;
1576 }
1577 return group;
1578 }
1579
1580 /**
1581 * Returns `true` if [node] is in static context.
1582 */
1583 bool _inStaticContext() {
1584 // constructor initializer cannot reference "this"
1585 if (node.getAncestor((node) => node is ConstructorInitializer) != null) {
1586 return true;
1587 }
1588 // field initializer cannot reference "this"
1589 if (node.getAncestor((node) => node is FieldDeclaration) != null) {
1590 return true;
1591 }
1592 // static method
1593 MethodDeclaration method = node.getAncestor((node) {
1594 return node is MethodDeclaration;
1595 });
1596 return method != null && method.isStatic;
1597 }
1598
1599 /**
1600 * Returns a possible return [Type], may be `null` if cannot be inferred.
1601 */
1602 DartType _inferReturnType(MethodInvocation invocation) {
1603 AstNode parent = invocation.parent;
1604 // myFunction();
1605 if (parent is ExpressionStatement) {
1606 return VoidTypeImpl.instance;
1607 }
1608 // return myFunction();
1609 if (parent is ReturnStatement) {
1610 ExecutableElement executable = getEnclosingExecutableElement(invocation);
1611 return executable != null ? executable.returnType : null;
1612 }
1613 // int v = myFunction();
1614 if (parent is VariableDeclaration) {
1615 VariableDeclaration variableDeclaration = parent;
1616 if (variableDeclaration.initializer == invocation) {
1617 VariableElement variableElement = variableDeclaration.element;
1618 if (variableElement != null) {
1619 return variableElement.type;
1620 }
1621 }
1622 }
1623 // v = myFunction();
1624 if (parent is AssignmentExpression) {
1625 AssignmentExpression assignment = parent;
1626 if (assignment.rightHandSide == invocation) {
1627 if (assignment.operator.type == TokenType.EQ) {
1628 // v = myFunction();
1629 Expression lhs = assignment.leftHandSide;
1630 if (lhs != null) {
1631 return lhs.bestType;
1632 }
1633 } else {
1634 // v += myFunction();
1635 MethodElement method = assignment.bestElement;
1636 if (method != null) {
1637 List<ParameterElement> parameters = method.parameters;
1638 if (parameters.length == 1) {
1639 return parameters[0].type;
1640 }
1641 }
1642 }
1643 }
1644 }
1645 // v + myFunction();
1646 if (parent is BinaryExpression) {
1647 BinaryExpression binary = parent;
1648 MethodElement method = binary.bestElement;
1649 if (method != null) {
1650 if (binary.rightOperand == invocation) {
1651 List<ParameterElement> parameters = method.parameters;
1652 return parameters.length == 1 ? parameters[0].type : null;
1653 }
1654 }
1655 }
1656 // foo( myFunction() );
1657 if (parent is ArgumentList) {
1658 ParameterElement parameter = invocation.bestParameterElement;
1659 return parameter != null ? parameter.type : null;
1660 }
1661 // bool
1662 {
1663 // assert( myFunction() );
1664 if (parent is AssertStatement) {
1665 AssertStatement statement = parent;
1666 if (statement.condition == invocation) {
1667 return coreTypeBool;
1668 }
1669 }
1670 // if ( myFunction() ) {}
1671 if (parent is IfStatement) {
1672 IfStatement statement = parent;
1673 if (statement.condition == invocation) {
1674 return coreTypeBool;
1675 }
1676 }
1677 // while ( myFunction() ) {}
1678 if (parent is WhileStatement) {
1679 WhileStatement statement = parent;
1680 if (statement.condition == invocation) {
1681 return coreTypeBool;
1682 }
1683 }
1684 // do {} while ( myFunction() );
1685 if (parent is DoStatement) {
1686 DoStatement statement = parent;
1687 if (statement.condition == invocation) {
1688 return coreTypeBool;
1689 }
1690 }
1691 // !myFunction()
1692 if (parent is PrefixExpression) {
1693 PrefixExpression prefixExpression = parent;
1694 if (prefixExpression.operator.type == TokenType.BANG) {
1695 return coreTypeBool;
1696 }
1697 }
1698 // binary expression '&&' or '||'
1699 if (parent is BinaryExpression) {
1700 BinaryExpression binaryExpression = parent;
1701 TokenType operatorType = binaryExpression.operator.type;
1702 if (operatorType == TokenType.AMPERSAND_AMPERSAND ||
1703 operatorType == TokenType.BAR_BAR) {
1704 return coreTypeBool;
1705 }
1706 }
1707 }
1708 // we don't know
1709 return null;
1710 }
1711
1712 /**
1713 * Inserts the given [SourceBuilder] at its offset.
1714 */
1715 void _insertBuilder(SourceBuilder builder) {
1716 String text = builder.toString();
1717 _addInsertEdit(builder.offset, text);
1718 // add linked positions
1719 builder.linkedPositionGroups.forEach((LinkedEditGroup group) {
1720 LinkedEditGroup fixGroup = _getLinkedPosition(group.id);
1721 group.positions.forEach((Position position) {
1722 fixGroup.addPosition(position, group.length);
1723 });
1724 group.suggestions.forEach((LinkedEditSuggestion suggestion) {
1725 fixGroup.addSuggestion(suggestion);
1726 });
1727 });
1728 }
1729
1730 // void _addLinkedPositionProposal(String group,
1731 // LinkedPositionProposal proposal) {
1732 // List<LinkedPositionProposal> nodeProposals = linkedPositionProposals[group ];
1733 // if (nodeProposals == null) {
1734 // nodeProposals = <LinkedPositionProposal>[];
1735 // linkedPositionProposals[group] = nodeProposals;
1736 // }
1737 // nodeProposals.add(proposal);
1738 // }
1739
1740 // /**
1741 // * Returns `true` if the given [ClassMember] is a part of a static method or
1742 // * a field initializer.
1743 // */
1744 // bool _inStaticMemberContext2(ClassMember member) {
1745 // if (member is MethodDeclaration) {
1746 // return member.isStatic;
1747 // }
1748 // // field initializer cannot reference "this"
1749 // if (member is FieldDeclaration) {
1750 // return true;
1751 // }
1752 // return false;
1753 // }
1754
1755 _ConstructorLocation
1756 _prepareNewConstructorLocation(ClassDeclaration classDeclaration) {
1757 List<ClassMember> members = classDeclaration.members;
1758 // find the last field/constructor
1759 ClassMember lastFieldOrConstructor = null;
1760 for (ClassMember member in members) {
1761 if (member is FieldDeclaration || member is ConstructorDeclaration) {
1762 lastFieldOrConstructor = member;
1763 } else {
1764 break;
1765 }
1766 }
1767 // after the field/constructor
1768 if (lastFieldOrConstructor != null) {
1769 return new _ConstructorLocation(
1770 "${eol}${eol}",
1771 lastFieldOrConstructor.end,
1772 "");
1773 }
1774 // at the beginning of the class
1775 String suffix = members.isEmpty ? "" : eol;
1776 return new _ConstructorLocation(
1777 eol,
1778 classDeclaration.leftBracket.end,
1779 suffix);
1780 }
1781
1782 /**
1783 * Removes any [ParenthesizedExpression] enclosing [expr].
1784 *
1785 * [exprPrecedence] - the effective precedence of [expr].
1786 */
1787 void _removeEnclosingParentheses(Expression expr, int exprPrecedence) {
1788 while (expr.parent is ParenthesizedExpression) {
1789 ParenthesizedExpression parenthesized =
1790 expr.parent as ParenthesizedExpression;
1791 if (getExpressionParentPrecedence(parenthesized) > exprPrecedence) {
1792 break;
1793 }
1794 _addRemoveEdit(rf.rangeToken(parenthesized.leftParenthesis));
1795 _addRemoveEdit(rf.rangeToken(parenthesized.rightParenthesis));
1796 expr = parenthesized;
1797 }
1798 }
1799
1800 void _updateFinderWithClassMembers(_ClosestElementFinder finder,
1801 ClassElement clazz) {
1802 if (clazz != null) {
1803 List<Element> members = getMembers(clazz);
1804 finder._updateList(members);
1805 }
1806 }
1807
1808 static void _addSuperTypeProposals(SourceBuilder sb,
1809 Set<DartType> alreadyAdded, DartType type) {
1810 if (type != null &&
1811 !alreadyAdded.contains(type) &&
1812 type.element is ClassElement) {
1813 alreadyAdded.add(type);
1814 ClassElement element = type.element as ClassElement;
1815 sb.addSuggestion(LinkedEditSuggestionKind.TYPE, element.name);
1816 _addSuperTypeProposals(sb, alreadyAdded, element.supertype);
1817 for (InterfaceType interfaceType in element.interfaces) {
1818 _addSuperTypeProposals(sb, alreadyAdded, interfaceType);
1819 }
1820 }
1821 }
1822
1823 /**
1824 * Attempts to convert the given absolute path into a "package" URI.
1825 *
1826 * [context] - the [AnalysisContext] to work in.
1827 * [path] - the absolute path, not `null`.
1828 *
1829 * Returns the "package" URI, may be `null`.
1830 */
1831 static String _findPackageUri(AnalysisContext context, String path) {
1832 // Source fileSource = new FileBasedSource.con1(path);
1833 Source fileSource = new NonExistingSource(path, UriKind.FILE_URI);
1834 Uri uri = context.sourceFactory.restoreUri(fileSource);
1835 if (uri == null) {
1836 return null;
1837 }
1838 return uri.toString();
1839 }
1840
1841 /**
1842 * @return the suggestions for given [Type] and [DartExpression], not empty.
1843 */
1844 static List<String> _getArgumentNameSuggestions(Set<String> excluded,
1845 DartType type, Expression expression, int index) {
1846 List<String> suggestions =
1847 getVariableNameSuggestionsForExpression(type, expression, excluded);
1848 if (suggestions.length != 0) {
1849 return suggestions;
1850 }
1851 return <String>["arg${index}"];
1852 }
1853
1854 /**
1855 * Returns `true` if [node] is a type name.
1856 */
1857 static bool _mayBeTypeIdentifier(AstNode node) {
1858 if (node is SimpleIdentifier) {
1859 AstNode parent = node.parent;
1860 if (parent is TypeName) {
1861 return true;
1862 }
1863 if (parent is MethodInvocation) {
1864 return parent.realTarget == node;
1865 }
1866 if (parent is PrefixedIdentifier) {
1867 return parent.prefix == node;
1868 }
1869 }
1870 return false;
1871 }
1872 }
1873
1874 /**
1875 * Helper for finding [Element] with name closest to the given.
1876 */
1877 class _ClosestElementFinder {
1878 final String _targetName;
1879 final Predicate<Element> _predicate;
1880
1881 Element _element = null;
1882 int _distance;
1883
1884 _ClosestElementFinder(this._targetName, this._predicate, this._distance);
1885
1886 void _update(Element element) {
1887 if (_predicate(element)) {
1888 int memberDistance = levenshtein(element.name, _targetName, _distance);
1889 if (memberDistance < _distance) {
1890 _element = element;
1891 _distance = memberDistance;
1892 }
1893 }
1894 }
1895
1896 void _updateList(Iterable<Element> elements) {
1897 for (Element element in elements) {
1898 _update(element);
1899 }
1900 }
1901 }
1902
1903 /**
1904 * Describes the location for a newly created [ConstructorDeclaration].
1905 */
1906 class _ConstructorLocation {
1907 final String _prefix;
1908 final int _offset;
1909 final String _suffix;
1910
1911 _ConstructorLocation(this._prefix, this._offset, this._suffix);
1912 }
OLDNEW
« no previous file with comments | « pkg/analysis_services/lib/src/correction/assist.dart ('k') | pkg/analysis_services/lib/src/correction/levenshtein.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698