From cda4f034ed81e641af805dbd6d0f617e88ecfd6e Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Fri, 12 Apr 2024 19:28:54 +0000 Subject: [PATCH] DAS: Extract static code from static class Flutter. Most methods are refactored as extension methods. Some unused ones are deleted. Some are inlined into their call sites. Change-Id: I47826fd3241db86526efa2ee410990a0fc7be702 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362300 Reviewed-by: Konstantin Shcheglov Commit-Queue: Samuel Rawlins --- pkg/analysis_server/lib/src/cider/rename.dart | 6 +- .../lib/src/computer/computer_color.dart | 6 +- .../lib/src/computer/computer_outline.dart | 6 +- .../src/flutter/flutter_outline_computer.dart | 14 +- .../handler_document_color_presentation.dart | 4 +- .../lib/src/lsp/lsp_analysis_server.dart | 8 +- .../dart/in_scope_completion_pass.dart | 4 +- .../completion/dart/suggestion_builder.dart | 10 +- .../add_diagnostic_property_reference.dart | 8 +- .../dart/add_field_formal_parameters.dart | 6 +- .../dart/add_key_to_constructors.dart | 3 +- .../dart/add_missing_required_argument.dart | 10 +- .../dart/convert_flutter_child.dart | 53 +- .../dart/convert_flutter_children.dart | 4 +- .../create_constructor_for_final_fields.dart | 8 +- .../dart/flutter_convert_to_children.dart | 4 +- .../flutter_convert_to_stateful_widget.dart | 15 +- .../flutter_convert_to_stateless_widget.dart | 14 +- .../correction/dart/flutter_move_down.dart | 4 +- .../correction/dart/flutter_move_up.dart | 4 +- .../dart/flutter_remove_widget.dart | 10 +- .../dart/flutter_swap_with_child.dart | 15 +- .../dart/flutter_swap_with_parent.dart | 6 +- .../correction/dart/flutter_wrap.dart | 30 +- .../correction/dart/flutter_wrap_builder.dart | 11 +- .../correction/dart/flutter_wrap_generic.dart | 7 +- .../dart/flutter_wrap_stream_builder.dart | 12 +- .../dart/sort_child_property_last.dart | 8 +- .../correction/dart/surround_with.dart | 9 +- .../correction/dart/wrap_in_text.dart | 4 +- .../services/flutter/widget_descriptions.dart | 42 +- .../refactoring/legacy/extract_widget.dart | 18 +- .../legacy/rename_unit_member.dart | 7 +- .../services/snippets/snippet_producer.dart | 6 +- .../lib/src/utilities/extensions/flutter.dart | 437 ++++++++++++++++ .../lib/src/utilities/flutter.dart | 488 ------------------ .../test/lsp/flutter_outline_test.dart | 6 +- .../test/src/utilities/flutter_test.dart | 294 +++++------ .../tool/code_completion/flutter_metrics.dart | 4 +- .../code_completion/relevance_metrics.dart | 6 +- .../relevance_table_generator.dart | 4 +- .../lib/src/dart/analysis/session_helper.dart | 8 +- 42 files changed, 778 insertions(+), 845 deletions(-) create mode 100644 pkg/analysis_server/lib/src/utilities/extensions/flutter.dart delete mode 100644 pkg/analysis_server/lib/src/utilities/flutter.dart diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart index 04ea3577b65..10895e820b1 100644 --- a/pkg/analysis_server/lib/src/cider/rename.dart +++ b/pkg/analysis_server/lib/src/cider/rename.dart @@ -9,7 +9,7 @@ import 'package:analysis_server/src/services/refactoring/legacy/naming_conventio import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart'; import 'package:analysis_server/src/services/search/hierarchy.dart'; import 'package:analysis_server/src/utilities/change_builder.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/source/line_info.dart'; @@ -81,9 +81,9 @@ class CanRenameResponse { } FlutterWidgetState? _findFlutterStateClass(Element element, String newName) { - if (Flutter.isStatefulWidgetDeclaration(element)) { + if (element is ClassElement && element.isStatefulWidgetDeclaration) { var oldStateName = '${element.displayName}State'; - var library = element.library!; + var library = element.library; var state = library.getClass(oldStateName) ?? library.getClass('_$oldStateName'); if (state != null) { diff --git a/pkg/analysis_server/lib/src/computer/computer_color.dart b/pkg/analysis_server/lib/src/computer/computer_color.dart index eb38ccf0aa3..28f33958c3e 100644 --- a/pkg/analysis_server/lib/src/computer/computer_color.dart +++ b/pkg/analysis_server/lib/src/computer/computer_color.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; @@ -55,7 +55,7 @@ class ColorComputer { String? memberName, int? index, }) { - if (!Flutter.isColor(expression.staticType)) return false; + if (!expression.staticType.isColor) return false; target ??= expression; @@ -80,7 +80,7 @@ class ColorComputer { /// because they are not const) but are simple well-known dart:ui/Flutter /// color constructors that we can manually parse. bool tryAddKnownColorConstructor(InstanceCreationExpression expression) { - if (!Flutter.isColor(expression.staticType)) return false; + if (!expression.staticType.isColor) return false; final constructor = expression.constructorName; final staticElement = constructor.staticElement; diff --git a/pkg/analysis_server/lib/src/computer/computer_outline.dart b/pkg/analysis_server/lib/src/computer/computer_outline.dart index 17d2c74bc98..ba996edfb15 100644 --- a/pkg/analysis_server/lib/src/computer/computer_outline.dart +++ b/pkg/analysis_server/lib/src/computer/computer_outline.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analysis_server/src/collections.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; @@ -482,14 +482,14 @@ class _FunctionBodyOutlinesVisitor extends RecursiveAstVisitor { @override void visitInstanceCreationExpression(InstanceCreationExpression node) { - if (outlineComputer.withBasicFlutter && Flutter.isWidgetCreation(node)) { + if (outlineComputer.withBasicFlutter && node.isWidgetCreation) { var children = []; node.argumentList .accept(_FunctionBodyOutlinesVisitor(outlineComputer, children)); // The method `getWidgetPresentationText` should not return `null` when // `isWidgetCreation` returns `true`. - var text = Flutter.getWidgetPresentationText(node) ?? ''; + var text = node.widgetPresentationText ?? ''; var element = Element(ElementKind.CONSTRUCTOR_INVOCATION, text, 0, location: outlineComputer._getLocationOffsetLength(node.offset, 0)); diff --git a/pkg/analysis_server/lib/src/flutter/flutter_outline_computer.dart b/pkg/analysis_server/lib/src/flutter/flutter_outline_computer.dart index 0584aa3fee4..f83ab374364 100644 --- a/pkg/analysis_server/lib/src/flutter/flutter_outline_computer.dart +++ b/pkg/analysis_server/lib/src/flutter/flutter_outline_computer.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/computer/computer_outline.dart'; import 'package:analysis_server/src/protocol_server.dart' as protocol; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; @@ -129,7 +129,7 @@ class FlutterOutlineComputer { /// a widget reference outline item. protocol.FlutterOutline? _createOutline(Expression node, bool withGeneric) { var type = node.staticType; - if (type is! InterfaceType || !Flutter.isWidgetType(type)) { + if (type is! InterfaceType || !type.isWidgetType) { return null; } var className = type.element.displayName; @@ -138,11 +138,6 @@ class FlutterOutlineComputer { var attributes = []; var children = []; for (var argument in node.argumentList.arguments) { - var argumentType = argument.staticType; - var isWidgetArgument = Flutter.isWidgetType(argumentType); - var isWidgetListArgument = - argumentType != null && Flutter.isListOfWidgetsType(argumentType); - String? parentAssociationLabel; Expression childrenExpression; @@ -174,6 +169,11 @@ class FlutterOutlineComputer { } } + var argumentType = argument.staticType; + var isWidgetArgument = argumentType.isWidgetType; + var isWidgetListArgument = + argumentType != null && argumentType.isListOfWidgetsType; + if (isWidgetArgument && childrenExpression is ConditionalExpression) { addChildrenFrom(childrenExpression.thenExpression); addChildrenFrom(childrenExpression.elseExpression); diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart index 1883edae34a..60d4df40a83 100644 --- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart +++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart @@ -6,7 +6,6 @@ import 'package:analysis_server/lsp_protocol/protocol.dart'; import 'package:analysis_server/src/lsp/error_or.dart'; import 'package:analysis_server/src/lsp/handlers/handlers.dart'; import 'package:analysis_server/src/lsp/mapping.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -135,8 +134,7 @@ class DocumentColorPresentationHandler extends SharedMessageHandler< final editRange = SourceRange(editStart, editEnd - editStart); final sessionHelper = AnalysisSessionHelper(unit.session); - final colorType = - await sessionHelper.getClass(Flutter.widgetsUri, 'Color'); + final colorType = await sessionHelper.getFlutterClass('Color'); if (colorType == null) { // If we can't find the class (perhaps because this isn't a Flutter // project) we will not include any results. In theory the client should diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart index 7ccdc429f0e..53b352b028f 100644 --- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart +++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart @@ -29,7 +29,7 @@ import 'package:analysis_server/src/server/diagnostic_server.dart'; import 'package:analysis_server/src/server/error_notifier.dart'; import 'package:analysis_server/src/server/performance.dart'; import 'package:analysis_server/src/services/user_prompts/dart_fix_prompt_manager.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analysis_server/src/utilities/process.dart'; import 'package:analyzer/dart/analysis/context_locator.dart'; import 'package:analyzer/dart/analysis/results.dart'; @@ -1016,13 +1016,13 @@ class LspAnalysisServer extends AnalysisServer { ); } - /// Checks whether [file] is in a project that can resolve 'package:flutter' - /// libraries. + /// Returns whether [filePath] is in a project that can resolve + /// 'package:flutter' libraries. bool _isInFlutterProject(String filePath) => getAnalysisDriver(filePath) ?.currentSession .uriConverter - .uriToPath(Uri.parse(Flutter.widgetsUri)) != + .uriToPath(Uri.parse(widgetsUri)) != null; void _notifyPluginsOverlayChanged( diff --git a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart index 74fa17a4827..40fb946a970 100644 --- a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart +++ b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart @@ -14,8 +14,8 @@ import 'package:analysis_server/src/services/completion/dart/uri_helper.dart'; import 'package:analysis_server/src/services/completion/dart/visibility_tracker.dart'; import 'package:analysis_server/src/utilities/extensions/ast.dart'; import 'package:analysis_server/src/utilities/extensions/completion_request.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analysis_server/src/utilities/extensions/object.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/syntactic_entity.dart'; @@ -345,7 +345,7 @@ class InScopeCompletionPass extends SimpleAstVisitor { appendComma = true; } } else if (parent is InstanceCreationExpression && - Flutter.isWidgetCreation(parent)) { + parent.isWidgetCreation) { appendComma = true; } int? replacementLength; diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart index 6909f0a2b7b..021a510676f 100644 --- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart +++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart @@ -16,7 +16,7 @@ import 'package:analysis_server/src/services/completion/dart/feature_computer.da import 'package:analysis_server/src/services/completion/dart/utilities.dart'; import 'package:analysis_server/src/utilities/extensions/ast.dart'; import 'package:analysis_server/src/utilities/extensions/element.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; @@ -776,7 +776,7 @@ class SuggestionBuilder { var enclosingElement = method.enclosingElement; if (method.name == 'setState' && enclosingElement is ClassElement && - Flutter.isExactState(enclosingElement)) { + enclosingElement.isExactState) { // TODO(brianwilkerson): Make this more efficient by creating the correct // suggestion in the first place. // Find the line indentation. @@ -844,11 +844,11 @@ class SuggestionBuilder { var selectionOffset = completion.length; // Optionally add Flutter child widget details. - // TODO(pq): revisit this special casing; likely it can be generalized away + // TODO(pq): revisit this special casing; likely it can be generalized away. var element = parameter.enclosingElement; - // If appendColon is false, default values should never be appended. + // If `appendColon` is false, default values should never be appended. if (element is ConstructorElement && appendColon) { - if (Flutter.isWidget(element.enclosingElement.augmented.declaration)) { + if (element.enclosingElement.augmented.declaration.isWidget) { var analysisOptions = request.analysisSession.analysisContext .getAnalysisOptionsForFile( request.resourceProvider.getFile(request.path)); diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_diagnostic_property_reference.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_diagnostic_property_reference.dart index 5aaf7753e2f..5fb82819be7 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/add_diagnostic_property_reference.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/add_diagnostic_property_reference.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; @@ -45,7 +45,7 @@ class AddDiagnosticPropertyReference extends ResolvedCorrectionProducer { final classDeclaration = node.thisOrAncestorOfType(); if (classDeclaration == null || - !Flutter.isDiagnosticable(classDeclaration.declaredElement!.thisType)) { + !classDeclaration.declaredElement!.thisType.isDiagnosticable) { return; } @@ -74,9 +74,9 @@ class AddDiagnosticPropertyReference extends ResolvedCorrectionProducer { } else if (_isIterable(type)) { constructorId = 'IterableProperty'; typeArgs = (type as InterfaceType).typeArguments; - } else if (Flutter.isColor(type)) { + } else if (type.isColor) { constructorId = 'ColorProperty'; - } else if (Flutter.isMatrix4(type)) { + } else if (type.isMatrix4) { constructorId = 'TransformProperty'; } else { constructorId = 'DiagnosticsProperty'; diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_field_formal_parameters.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_field_formal_parameters.dart index 555c8b4c9b2..16eb2cb3fa6 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/add_field_formal_parameters.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/add_field_formal_parameters.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/src/generated/error_verifier.dart'; @@ -39,8 +39,8 @@ class AddFieldFormalParameters extends ResolvedCorrectionProducer { fields.sort((a, b) => a.nameOffset - b.nameOffset); // Specialize for Flutter widgets. - if (Flutter.isExactlyStatelessWidgetType(superType) || - Flutter.isExactlyStatefulWidgetType(superType)) { + if (superType.isExactlyStatelessWidgetType || + superType.isExactlyStatefulWidgetType) { if (parameters.isNotEmpty && parameters.last.isNamed) { String parameterForField(FieldElement field) { var prefix = ''; diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart index 521c9907410..301996d3ae1 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart @@ -4,7 +4,6 @@ import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; @@ -161,7 +160,7 @@ class AddKeyToConstructors extends ResolvedCorrectionProducer { /// Return the type for the class `Key`. Future _getKeyType() async { - var keyClass = await sessionHelper.getClass(Flutter.widgetsUri, 'Key'); + var keyClass = await sessionHelper.getFlutterClass('Key'); if (keyClass == null) { return null; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart index 1d6bee2f643..930a4ed1210 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/add_missing_required_argument.dart @@ -6,7 +6,7 @@ import 'package:_fe_analyzer_shared/src/scanner/token.dart'; import 'package:analysis_server/src/services/completion/dart/utilities.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -85,10 +85,8 @@ class AddMissingRequiredArgument extends ResolvedCorrectionProducer { offset = lastArgument.end; hasTrailingComma = lastArgument.endToken.next!.type == TokenType.COMMA; - if (lastArgument is NamedExpression && - Flutter.isWidgetExpression(creation)) { - if (Flutter.isChildArgument(lastArgument) || - Flutter.isChildrenArgument(lastArgument)) { + if (lastArgument is NamedExpression && creation.isWidgetExpression) { + if (lastArgument.isChildArgument || lastArgument.isChildrenArgument) { offset = lastArgument.offset; hasTrailingComma = true; insertBetweenParams = true; @@ -123,7 +121,7 @@ class AddMissingRequiredArgument extends ResolvedCorrectionProducer { builder.addSimpleLinkedEdit('VALUE', 'null'); } - if (Flutter.isWidgetExpression(creation)) { + if (creation.isWidgetExpression) { // Insert a trailing comma after Flutter instance creation params. if (!hasTrailingComma) { builder.write(','); diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart index 6ad53b6a579..7a168292a1a 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart @@ -4,8 +4,9 @@ import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/source/source_range.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; import 'package:analyzer_plugin/utilities/range_factory.dart'; @@ -16,33 +17,55 @@ class ConvertFlutterChild extends ResolvedCorrectionProducer { @override Future compute(ChangeBuilder builder) async { - var named = Flutter.findNamedExpression(node, 'child'); + var named = node.findArgumentNamed('child'); if (named == null) { return; } // child: widget var expression = named.expression; - if (Flutter.isWidgetExpression(expression)) { + if (expression.isWidgetExpression) { await builder.addDartFileEdit(file, (builder) { - Flutter.convertChildToChildren2( - builder, - expression, - named, - eol, - utils.getNodeText, - utils.getLinePrefix, - utils.oneIndent, - utils.getText, - utils.replaceSourceIndent, - range.node); + var childLoc = named.offset + 'child'.length; + builder.addSimpleInsertion(childLoc, 'ren'); + var listLoc = expression.offset; + var childArgSrc = utils.getNodeText(expression); + if (!childArgSrc.contains(eol)) { + builder.addSimpleInsertion(listLoc, '['); + builder.addSimpleInsertion(listLoc + expression.length, ']'); + } else { + var newlineLoc = childArgSrc.lastIndexOf(eol); + if (newlineLoc == childArgSrc.length) { + newlineLoc -= 1; + } + var indentOld = + utils.getLinePrefix(expression.offset + eol.length + newlineLoc); + var indentNew = '$indentOld${utils.oneIndent}'; + // The separator includes 'child:' but that has no newlines. + var separator = + utils.getText(named.offset, expression.offset - named.offset); + var prefix = separator.contains(eol) ? '' : '$eol$indentNew'; + if (prefix.isEmpty) { + builder.addSimpleInsertion(named.offset + 'child:'.length, ' ['); + builder.addDeletion(SourceRange(expression.offset - 2, 2)); + } else { + builder.addSimpleInsertion(listLoc, '['); + } + var newChildArgSrc = utils.replaceSourceIndent( + childArgSrc, + indentOld, + indentNew, + ); + newChildArgSrc = '$prefix$newChildArgSrc,$eol$indentOld]'; + builder.addSimpleReplacement(range.node(expression), newChildArgSrc); + } }); return; } // child: [widget1, widget2] if (expression is ListLiteral && - expression.elements.every(Flutter.isWidgetExpression)) { + expression.elements.every((e) => e.isWidgetExpression)) { await builder.addDartFileEdit(file, (builder) { builder.addSimpleReplacement(range.node(named.name), 'children:'); }); diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_children.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_children.dart index 49185a3c6e9..75a4c8420ef 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_children.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_children.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; @@ -23,7 +23,7 @@ class ConvertFlutterChildren extends ResolvedCorrectionProducer { var expression = namedExpression.expression; if (expression is ListLiteral && expression.elements.length == 1) { var widget = expression.elements[0]; - if (Flutter.isWidgetExpression(widget)) { + if (widget.isWidgetExpression) { var widgetText = utils.getNodeText(widget); var indentOld = utils.getLinePrefix(widget.offset); var indentNew = utils.getLinePrefix(namedExpression.offset); diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart index 19fa0da4bb5..c4f59a811bc 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart @@ -4,8 +4,8 @@ import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analysis_server/src/utilities/extensions/object.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -77,8 +77,8 @@ class CreateConstructorForFinalFields extends ResolvedCorrectionProducer { return; } - if (Flutter.isExactlyStatelessWidgetType(superType) || - Flutter.isExactlyStatefulWidgetType(superType)) { + if (superType.isExactlyStatelessWidgetType || + superType.isExactlyStatefulWidgetType) { await _forFlutterWidget( fixContext: fixContext, classDeclaration: container); } else { @@ -142,7 +142,7 @@ class CreateConstructorForFinalFields extends ResolvedCorrectionProducer { required _FixContext fixContext, required NamedCompilationUnitMember classDeclaration, }) async { - final keyClass = await sessionHelper.getClass(Flutter.widgetsUri, 'Key'); + final keyClass = await sessionHelper.getFlutterClass('Key'); if (keyClass == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart index f3f6e3fc569..8bb1f5d7674 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -27,7 +27,7 @@ class FlutterConvertToChildren extends ResolvedCorrectionProducer { parent2 is NamedExpression && node.name == 'child' && node.staticElement != null && - Flutter.isWidgetExpression(parent2.expression)) { + parent2.expression.isWidgetExpression) { namedExp = parent2; } else { return; diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart index 67caa5cbac3..9971ea815e9 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart @@ -5,7 +5,7 @@ import 'package:_fe_analyzer_shared/src/scanner/token.dart'; import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -38,7 +38,7 @@ class FlutterConvertToStatefulWidget extends ResolvedCorrectionProducer { // Must be a StatelessWidget subclass. var widgetClassElement = widgetClass.declaredElement!; var superType = widgetClassElement.supertype; - if (superType == null || !Flutter.isExactlyStatelessWidgetType(superType)) { + if (superType == null || !superType.isExactlyStatelessWidgetType) { return; } @@ -104,14 +104,9 @@ class FlutterConvertToStatefulWidget extends ResolvedCorrectionProducer { return SourceEdit.applySequence(text, visitor.edits.reversed); } - var statefulWidgetClass = await sessionHelper.getClass( - Flutter.widgetsUri, - 'StatefulWidget', - ); - var stateClass = await sessionHelper.getClass( - Flutter.widgetsUri, - 'State', - ); + var statefulWidgetClass = + await sessionHelper.getFlutterClass('StatefulWidget'); + var stateClass = await sessionHelper.getFlutterClass('State'); if (statefulWidgetClass == null || stateClass == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateless_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateless_widget.dart index 57eaa84cf90..2ed18e4f1ba 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateless_widget.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateless_widget.dart @@ -5,7 +5,7 @@ import 'package:_fe_analyzer_shared/src/scanner/token.dart'; import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -37,7 +37,7 @@ class FlutterConvertToStatelessWidget extends ResolvedCorrectionProducer { // Must be a StatefulWidget subclass. var widgetClassElement = widgetClass.declaredElement!; var superType = widgetClassElement.supertype; - if (superType == null || !Flutter.isExactlyStatefulWidgetType(superType)) { + if (superType == null || !superType.isExactlyStatefulWidgetType) { return; } @@ -124,10 +124,8 @@ class FlutterConvertToStatelessWidget extends ResolvedCorrectionProducer { return SourceEdit.applySequence(text, visitor.edits.reversed); } - var statelessWidgetClass = await sessionHelper.getClass( - Flutter.widgetsUri, - 'StatelessWidget', - ); + var statelessWidgetClass = + await sessionHelper.getFlutterClass('StatelessWidget'); if (statelessWidgetClass == null) { return; } @@ -259,7 +257,7 @@ class FlutterConvertToStatelessWidget extends ResolvedCorrectionProducer { } var classElement = type.element; - return classElement is ClassElement && Flutter.isExactState(classElement); + return classElement is ClassElement && classElement.isExactState; } } @@ -367,7 +365,7 @@ class _StatelessVerifier extends RecursiveAstVisitor { if (methodElement is ClassMemberElement) { var classElement = methodElement.enclosingElement; if (classElement is ClassElement && - Flutter.isExactState(classElement) && + classElement.isExactState && !FlutterConvertToStatelessWidget._isDefaultOverride( node.thisOrAncestorOfType())) { canBeStateless = false; diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart index ae1570dc63d..0e1fe0c67aa 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_down.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; @@ -17,7 +17,7 @@ class FlutterMoveDown extends ResolvedCorrectionProducer { @override Future compute(ChangeBuilder builder) async { - var widget = Flutter.identifyWidgetExpression(node); + var widget = node.findWidgetExpression; if (widget == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart index 1145aa9a27f..f11e1037a56 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_move_up.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; @@ -17,7 +17,7 @@ class FlutterMoveUp extends ResolvedCorrectionProducer { @override Future compute(ChangeBuilder builder) async { - var widget = Flutter.identifyWidgetExpression(node); + var widget = node.findWidgetExpression; if (widget == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart index 308872789c2..5adb0ce6b2c 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_remove_widget.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -34,13 +34,13 @@ class FlutterRemoveWidget extends ResolvedCorrectionProducer { @override Future compute(ChangeBuilder builder) async { - var widgetCreation = Flutter.identifyNewExpression(node); + var widgetCreation = node.findInstanceCreationExpression; if (widgetCreation == null) { return; } // Prepare the list of our children. - var childrenArgument = Flutter.findChildrenArgument(widgetCreation); + var childrenArgument = widgetCreation.childrenArgument; if (childrenArgument != null) { var childrenExpression = childrenArgument.expression; if (childrenExpression is ListLiteral && @@ -49,11 +49,11 @@ class FlutterRemoveWidget extends ResolvedCorrectionProducer { builder, widgetCreation, childrenExpression.elements); } } else { - var childArgument = Flutter.findChildArgument(widgetCreation); + var childArgument = widgetCreation.childArgument; if (childArgument != null) { await _removeSingle(builder, widgetCreation, childArgument.expression); } else { - var builderArgument = Flutter.findBuilderArgument(widgetCreation); + var builderArgument = widgetCreation.builderArgument; if (builderArgument != null) { await _removeBuilder(builder, widgetCreation, builderArgument); } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart index 3868eb42095..97860975eea 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_child.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -16,7 +16,7 @@ abstract class FlutterParentAndChild extends ResolvedCorrectionProducer { InstanceCreationExpression parent, InstanceCreationExpression child) async { // The child must have its own child. - var stableChild = Flutter.findChildArgument(child); + var stableChild = child.childArgument; if (stableChild == null) { return; } @@ -63,7 +63,7 @@ abstract class FlutterParentAndChild extends ResolvedCorrectionProducer { // Write all arguments of the parent. // Don't write its child. for (var argument in parentArgs.arguments) { - if (!Flutter.isChildArgument(argument)) { + if (!argument.isChildArgument) { var text = utils.getNodeText(argument); text = utils.replaceSourceIndent( text, @@ -104,15 +104,14 @@ class FlutterSwapWithChild extends FlutterParentAndChild { @override Future compute(ChangeBuilder builder) async { - var parent = Flutter.identifyNewExpression(node); - if (parent == null || !Flutter.isWidgetCreation(parent)) { + var parent = node.findInstanceCreationExpression; + if (parent == null || !parent.isWidgetCreation) { return; } - var childArgument = Flutter.findChildArgument(parent); + var childArgument = parent.childArgument; var child = childArgument?.expression; - if (child is! InstanceCreationExpression || - !Flutter.isWidgetCreation(child)) { + if (child is! InstanceCreationExpression || !child.isWidgetCreation) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart index fb2829cf4a1..0a00d4764d6 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_swap_with_parent.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/flutter_swap_with_child.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -15,8 +15,8 @@ class FlutterSwapWithParent extends FlutterParentAndChild { @override Future compute(ChangeBuilder builder) async { - var child = Flutter.identifyNewExpression(node); - if (child == null || !Flutter.isWidgetCreation(child)) { + var child = node.findInstanceCreationExpression; + if (child == null || !child.isWidgetCreation) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart index 1386e2f71b4..766263a067f 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/selection_analyzer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/source/source_range.dart'; @@ -18,20 +18,20 @@ class FlutterWrap extends MultiCorrectionProducer { @override Future> get producers async { var producers = []; - var widgetExpr = Flutter.identifyWidgetExpression(node); + var widgetExpr = node.findWidgetExpression; if (widgetExpr != null) { var widgetType = widgetExpr.typeOrThrow; producers.add(_FlutterWrapGeneric(widgetExpr)); - if (!Flutter.isExactWidgetTypeCenter(widgetType)) { + if (!widgetType.isExactWidgetTypeCenter) { producers.add(_FlutterWrapCenter(widgetExpr)); } - if (!Flutter.isExactWidgetTypeContainer(widgetType)) { + if (!widgetType.isExactWidgetTypeContainer) { producers.add(_FlutterWrapContainer(widgetExpr)); } - if (!Flutter.isExactWidgetTypePadding(widgetType)) { + if (!widgetType.isExactWidgetTypePadding) { producers.add(_FlutterWrapPadding(widgetExpr)); } - if (!Flutter.isExactWidgetTypeSizedBox(widgetType)) { + if (!widgetType.isExactWidgetTypeSizedBox) { producers.add(_FlutterWrapSizedBox(widgetExpr)); } } @@ -58,8 +58,7 @@ class FlutterWrap extends MultiCorrectionProducer { parent is InstanceCreationExpression) { selectedNode = parent; } - if (selectedNode is! Expression || - !Flutter.isWidgetExpression(selectedNode)) { + if (selectedNode is! Expression || !selectedNode.isWidgetExpression) { return; } widgetExpressions.add(selectedNode); @@ -77,7 +76,7 @@ class FlutterWrap extends MultiCorrectionProducer { coveringNode = coveringNode.parent; } - var widget = Flutter.identifyWidgetExpression(coveringNode); + var widget = coveringNode.findWidgetExpression; if (widget != null) { widgetExpressions.add(widget); } @@ -105,7 +104,7 @@ class _FlutterWrapCenter extends _WrapSingleWidget { String get _parentClassName => 'Center'; @override - String get _parentLibraryUri => Flutter.widgetsUri; + String get _parentLibraryUri => widgetsUri; } /// A correction processor that can make one of the possible changes computed by @@ -132,7 +131,7 @@ class _FlutterWrapContainer extends _WrapSingleWidget { String get _parentClassName => 'Container'; @override - String get _parentLibraryUri => Flutter.widgetsUri; + String get _parentLibraryUri => widgetsUri; } /// A correction processor that can make one of the possible changes computed by @@ -162,7 +161,7 @@ class _FlutterWrapPadding extends _WrapSingleWidget { String get _parentClassName => 'Padding'; @override - String get _parentLibraryUri => Flutter.widgetsUri; + String get _parentLibraryUri => widgetsUri; } /// A correction processor that can make one of the possible changes computed by @@ -189,7 +188,7 @@ class _FlutterWrapSizedBox extends _WrapSingleWidget { String get _parentClassName => 'SizedBox'; @override - String get _parentLibraryUri => Flutter.widgetsUri; + String get _parentLibraryUri => widgetsUri; } /// A correction processor that can make one of the possible changes computed by @@ -203,7 +202,7 @@ abstract class _WrapMultipleWidgets extends ResolvedCorrectionProducer { String get _parentClassName; - String get _parentLibraryUri => Flutter.widgetsUri; + String get _parentLibraryUri => widgetsUri; @override Future compute(ChangeBuilder builder) async { @@ -211,8 +210,7 @@ abstract class _WrapMultipleWidgets extends ResolvedCorrectionProducer { var src = utils.getRangeText(selectedRange); var parentClassElement = await sessionHelper.getClass(_parentLibraryUri, _parentClassName); - var widgetClassElement = - await sessionHelper.getClass(Flutter.widgetsUri, 'Widget'); + var widgetClassElement = await sessionHelper.getFlutterClass('Widget'); if (parentClassElement == null || widgetClassElement == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_builder.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_builder.dart index a03f115c7d1..7ca64872ff1 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_builder.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_builder.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -16,19 +16,16 @@ class FlutterWrapBuilder extends ResolvedCorrectionProducer { @override Future compute(ChangeBuilder builder) async { - var widgetExpr = Flutter.identifyWidgetExpression(node); + var widgetExpr = node.findWidgetExpression; if (widgetExpr == null) { return; } - if (Flutter.isExactWidgetTypeBuilder(widgetExpr.typeOrThrow)) { + if (widgetExpr.typeOrThrow.isExactWidgetTypeBuilder) { return; } var widgetSrc = utils.getNodeText(widgetExpr); - var builderElement = await sessionHelper.getClass( - Flutter.widgetsUri, - 'Builder', - ); + var builderElement = await sessionHelper.getFlutterClass('Builder'); if (builderElement == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart index 9f60db1f45e..8ad0b4b5109 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -19,9 +19,8 @@ class FlutterWrapGeneric extends ResolvedCorrectionProducer { if (node is! ListLiteral) { return; } - if ((node as ListLiteral).elements.any((CollectionElement exp) => - !(exp is InstanceCreationExpression && - Flutter.isWidgetCreation(exp)))) { + if ((node as ListLiteral).elements.any((CollectionElement element) => + !(element is InstanceCreationExpression && element.isWidgetCreation))) { return; } var literalSrc = utils.getNodeText(node); diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart index d0991c7a189..36fbcfac087 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_stream_builder.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; @@ -16,19 +16,17 @@ class FlutterWrapStreamBuilder extends ResolvedCorrectionProducer { @override Future compute(ChangeBuilder builder) async { - var widgetExpr = Flutter.identifyWidgetExpression(node); + var widgetExpr = node.findWidgetExpression; if (widgetExpr == null) { return; } - if (Flutter.isExactWidgetTypeStreamBuilder(widgetExpr.typeOrThrow)) { + if (widgetExpr.typeOrThrow.isExactWidgetTypeStreamBuilder) { return; } var widgetSrc = utils.getNodeText(widgetExpr); - var streamBuilderElement = await sessionHelper.getClass( - Flutter.widgetsUri, - 'StreamBuilder', - ); + var streamBuilderElement = + await sessionHelper.getFlutterClass('StreamBuilder'); if (streamBuilderElement == null) { return; } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart b/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart index d215522398d..b81bcc3e601 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; @@ -39,7 +39,7 @@ class SortChildPropertyLast extends ResolvedCorrectionProducer { var creationExpression = childProp.parent?.parent; if (creationExpression is! InstanceCreationExpression || - !Flutter.isWidgetCreation(creationExpression)) { + !creationExpression.isWidgetCreation) { return; } @@ -96,7 +96,7 @@ class SortChildPropertyLast extends ResolvedCorrectionProducer { return node; } } - return Flutter.findNamedExpression(node, 'child') ?? - Flutter.findNamedExpression(node, 'children'); + return node.findArgumentNamed('child') ?? + node.findArgumentNamed('children'); } } diff --git a/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart b/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart index 42fe533cd71..174b340901a 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/statement_analyzer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/source/source_range.dart'; import 'package:analyzer_plugin/utilities/assist/assist.dart'; @@ -238,10 +238,9 @@ class _SurroundWithSetState extends _SurroundWith { @override Future compute(ChangeBuilder builder) async { - var classDeclaration = - node.parent?.thisOrAncestorOfType(); - if (classDeclaration != null && - Flutter.isState(classDeclaration.declaredElement)) { + var classElement = + node.parent?.thisOrAncestorOfType()?.declaredElement; + if (classElement != null && classElement.isState) { await builder.addDartFileEdit(file, (builder) { builder.addReplacement(statementsRange, (builder) { builder.write(indentOld); diff --git a/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart b/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart index e139fc7159b..ba3703452ba 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart @@ -4,7 +4,7 @@ import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; import 'package:analysis_server/src/services/correction/fix.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; @@ -25,7 +25,7 @@ class WrapInText extends ResolvedCorrectionProducer { if (context == null) { return; } - if (!Flutter.isWidgetType(context.parameterElement.type)) { + if (!context.parameterElement.type.isWidgetType) { return; } diff --git a/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart b/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart index a708fc3582e..22eac1fd87a 100644 --- a/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart +++ b/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/protocol_server.dart' as protocol; import 'package:analysis_server/src/services/flutter/class_description.dart'; import 'package:analysis_server/src/services/flutter/property.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -147,7 +147,7 @@ class _WidgetDescriptionComputer { if (node == null) { return null; } - var instanceCreation = Flutter.identifyNewExpression(node); + var instanceCreation = node.findInstanceCreationExpression; if (instanceCreation == null) { return null; } @@ -173,7 +173,7 @@ class _WidgetDescriptionComputer { List properties, InstanceCreationExpression widgetCreation, ) { - if (!Flutter.isWidgetCreation(widgetCreation)) { + if (!widgetCreation.isWidgetCreation) { return; } @@ -190,7 +190,7 @@ class _WidgetDescriptionComputer { } PropertyDescription containerProperty; - if (Flutter.isExactlyContainerCreation(parentCreation)) { + if (parentCreation?.isExactlyContainerCreation ?? false) { containerProperty = PropertyDescription( resolvedUnit: resolvedUnit, instanceCreation: parentCreation, @@ -241,7 +241,7 @@ class _WidgetDescriptionComputer { ); if (parentCreation != null && - Flutter.isExactlyAlignCreation(parentCreation) && + parentCreation.isExactlyAlignCreation && parentCreation.argumentList.byName('widthFactor') == null && parentCreation.argumentList.byName('heightFactor') == null) { _replaceNestedContainerProperty( @@ -251,8 +251,7 @@ class _WidgetDescriptionComputer { ); } - if (parentCreation != null && - Flutter.isExactlyPaddingCreation(parentCreation)) { + if (parentCreation != null && parentCreation.isExactlyPaddingCreation) { _replaceNestedContainerProperty( containerProperty, parentCreation, @@ -373,7 +372,7 @@ class _WidgetDescriptionComputer { var classEdgeInsets = _classEdgeInsets; if (classEdgeInsets != null && - Flutter.isExactEdgeInsetsGeometryType(parameter.type)) { + parameter.type.isExactEdgeInsetsGeometryType) { propertyDescription.addEdgeInsetsNestedProperties(classEdgeInsets); } else if (valueExpression is InstanceCreationExpression) { var type = valueExpression.staticType; @@ -420,22 +419,11 @@ class _WidgetDescriptionComputer { Future _fetchClassElements() async { var sessionHelper = AnalysisSessionHelper(resolvedUnit.session); - _classAlignment = await sessionHelper.getClass( - Flutter.widgetsUri, - 'Alignment', - ); - _classAlignmentDirectional = await sessionHelper.getClass( - Flutter.widgetsUri, - 'AlignmentDirectional', - ); - _classContainer = await sessionHelper.getClass( - Flutter.widgetsUri, - 'Container', - ); - _classEdgeInsets = await sessionHelper.getClass( - Flutter.widgetsUri, - 'EdgeInsets', - ); + _classAlignment = await sessionHelper.getFlutterClass('Alignment'); + _classAlignmentDirectional = + await sessionHelper.getFlutterClass('AlignmentDirectional'); + _classContainer = await sessionHelper.getFlutterClass('Container'); + _classEdgeInsets = await sessionHelper.getFlutterClass('EdgeInsets'); } protocol.FlutterWidgetPropertyEditor? _getEditor(DartType type) { @@ -467,7 +455,7 @@ class _WidgetDescriptionComputer { enumItems: _enumItemsForEnum(classElement), ); } - if (Flutter.isExactAlignmentGeometry(classElement)) { + if (classElement.isExactAlignmentGeometry) { var items = []; var classAlignment = _classAlignment; if (classAlignment != null) { @@ -546,8 +534,8 @@ class _WidgetDescriptionComputer { if (field is FieldElement && field.isStatic) { var enclosingClass = field.enclosingElement as InterfaceElement; if (field.isEnumConstant || - Flutter.isExactAlignment(enclosingClass) || - Flutter.isExactAlignmentDirectional(enclosingClass)) { + enclosingClass.isExactAlignment || + enclosingClass.isExactAlignmentDirectional) { return protocol.FlutterWidgetPropertyValue( enumValue: _toEnumItem(field), ); diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart index 2a629f00522..01ce4dd0844 100644 --- a/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart +++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart @@ -10,7 +10,7 @@ import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart import 'package:analysis_server/src/services/refactoring/legacy/refactoring_internal.dart'; import 'package:analysis_server/src/services/search/element_visitors.dart'; import 'package:analysis_server/src/services/search/search_engine.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; @@ -183,9 +183,9 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl _enclosingClassNode = node?.thisOrAncestorOfType(); _enclosingClassElement = _enclosingClassNode?.declaredElement; - // new MyWidget(...) - var newExpression = Flutter.identifyNewExpression(node); - if (Flutter.isWidgetCreation(newExpression)) { + // `new MyWidget(...)` + var newExpression = node.findInstanceCreationExpression; + if (newExpression?.isWidgetCreation ?? false) { _expression = newExpression; return RefactoringStatus(); } @@ -203,7 +203,7 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl if (statements.isNotEmpty) { var lastStatement = statements.last; if (lastStatement is ReturnStatement && - Flutter.isWidgetExpression(lastStatement.expression)) { + lastStatement.expression.isWidgetExpression) { _statements = statements; _statementsRange = range.startEnd(statements.first, statements.last); return RefactoringStatus(); @@ -221,7 +221,7 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl } if (node is MethodDeclaration) { var returnType = node.returnType?.type; - if (Flutter.isWidgetType(returnType)) { + if (returnType.isWidgetType) { _method = node; return RefactoringStatus(); } @@ -238,11 +238,9 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl var result = RefactoringStatus(); Future getClass(String name) async { - var element = await sessionHelper.getClass(Flutter.widgetsUri, name); + var element = await sessionHelper.getFlutterClass(name); if (element == null) { - result.addFatalError( - "Unable to find '$name' in ${Flutter.widgetsUri}", - ); + result.addFatalError("Unable to find '$name' in $widgetsUri"); } return element; } diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_unit_member.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_unit_member.dart index 06b8ed0ea1a..4bf10c5e125 100644 --- a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_unit_member.dart +++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_unit_member.dart @@ -11,7 +11,7 @@ import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart import 'package:analysis_server/src/services/refactoring/legacy/rename.dart'; import 'package:analysis_server/src/services/search/element_visitors.dart'; import 'package:analysis_server/src/services/search/search_engine.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart' show Identifier; import 'package:analyzer/dart/element/element.dart'; @@ -152,9 +152,10 @@ class RenameUnitMemberRefactoringImpl extends RenameRefactoringImpl { } void _findFlutterStateClass() { - if (Flutter.isStatefulWidgetDeclaration(element)) { + final element = this.element; + if (element is ClassElement && element.isStatefulWidgetDeclaration) { var oldStateName = '${oldName}State'; - var library = element.library!; + var library = element.library; _flutterWidgetState = library.getClass(oldStateName) ?? library.getClass('_$oldStateName'); } diff --git a/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart b/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart index 5e01cbfb404..0e697ad1c0e 100644 --- a/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart +++ b/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart @@ -5,7 +5,7 @@ import 'package:analysis_server/src/services/correction/util.dart'; import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart'; import 'package:analysis_server/src/services/snippets/snippet.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/code_style_options.dart'; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -72,7 +72,7 @@ abstract class FlutterSnippetProducer extends DartSnippetProducer { FlutterSnippetProducer(super.request, {required super.elementImportCache}); Future getClass(String name) async { - final class_ = await sessionHelper.getClass(Flutter.widgetsUri, name); + final class_ = await sessionHelper.getFlutterClass(name); if (class_ != null) { requiredElementImports.add(class_); } @@ -80,7 +80,7 @@ abstract class FlutterSnippetProducer extends DartSnippetProducer { } Future getMixin(String name) async { - final mixin = await sessionHelper.getMixin(Flutter.widgetsUri, name); + final mixin = await sessionHelper.getMixin(widgetsUri, name); if (mixin != null) { requiredElementImports.add(mixin); } diff --git a/pkg/analysis_server/lib/src/utilities/extensions/flutter.dart b/pkg/analysis_server/lib/src/utilities/extensions/flutter.dart new file mode 100644 index 00000000000..bea0d5d5189 --- /dev/null +++ b/pkg/analysis_server/lib/src/utilities/extensions/flutter.dart @@ -0,0 +1,437 @@ +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analysis_server/src/utilities/strings.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:collection/collection.dart'; + +const String widgetsUri = 'package:flutter/widgets.dart'; +const _nameAlign = 'Align'; +const _nameBuilder = 'Builder'; +const _nameCenter = 'Center'; +const _nameContainer = 'Container'; +const _namePadding = 'Padding'; +const _nameSizedBox = 'SizedBox'; +const _nameState = 'State'; +const _nameStatefulWidget = 'StatefulWidget'; +const _nameStatelessWidget = 'StatelessWidget'; +const _nameStreamBuilder = 'StreamBuilder'; + +const _nameWidget = 'Widget'; +final Uri _uriAlignment = Uri.parse( + 'package:flutter/src/painting/alignment.dart', +); +final Uri _uriAsync = Uri.parse( + 'package:flutter/src/widgets/async.dart', +); +final Uri _uriBasic = Uri.parse( + 'package:flutter/src/widgets/basic.dart', +); +final Uri _uriContainer = Uri.parse( + 'package:flutter/src/widgets/container.dart', +); +final Uri _uriDiagnostics = Uri.parse( + 'package:flutter/src/foundation/diagnostics.dart', +); +final Uri _uriEdgeInsets = Uri.parse( + 'package:flutter/src/painting/edge_insets.dart', +); +final Uri _uriFramework = Uri.parse( + 'package:flutter/src/widgets/framework.dart', +); +final Uri _uriWidgetsIcon = Uri.parse( + 'package:flutter/src/widgets/icon.dart', +); + +final Uri _uriWidgetsText = Uri.parse( + 'package:flutter/src/widgets/text.dart', +); + +extension AstNodeExtension on AstNode? { + /// Returns the instance creation expression that surrounds this node, if any, + /// and otherwise `null`. + /// + /// This node may be the instance creation expression itself or an (optionally + /// prefixed) identifier that names the constructor. + InstanceCreationExpression? get findInstanceCreationExpression { + var node = this; + if (node is ImportPrefixReference) { + node = node.parent; + } + if (node is SimpleIdentifier) { + node = node.parent; + } + if (node is PrefixedIdentifier) { + node = node.parent; + } + if (node is NamedType) { + node = node.parent; + } + if (node is ConstructorName) { + node = node.parent; + } + if (node is InstanceCreationExpression) { + return node; + } + return null; + } + + /// Attempts to find and return the closest expression that encloses this + /// and is an independent Flutter `Widget`. + /// + /// Returns `null` if nothing is found. + Expression? get findWidgetExpression { + for (var node = this; node != null; node = node.parent) { + if (!node.isWidgetExpression) { + if (node is ArgumentList || node is Statement || node is FunctionBody) { + return null; + } + continue; + } + + if (node is AssignmentExpression) { + return null; + } + + var parent = node.parent; + + if (parent is AssignmentExpression) { + if (parent.rightHandSide == node) { + return node as Expression; + } + return null; + } + + if (parent is ArgumentList || + parent is ConditionalExpression && parent.thenExpression == node || + parent is ConditionalExpression && parent.elseExpression == node || + parent is ExpressionFunctionBody && parent.expression == node || + parent is ForElement && parent.body == node || + parent is IfElement && parent.thenElement == node || + parent is IfElement && parent.elseElement == node || + parent is ListLiteral || + parent is NamedExpression && parent.expression == node || + parent is Statement || + parent is SwitchExpressionCase && parent.expression == node || + parent is VariableDeclaration) { + return node as Expression; + } + } + return null; + } + + /// Whether the given [node] is the Flutter class `Widget`, or its subtype. + bool get isWidgetExpression { + return switch (this) { + null => false, + AstNode(parent: NamedType()) || + AstNode(parent: AstNode(parent: NamedType())) => + false, + AstNode(parent: ConstructorName()) => false, + NamedExpression() => false, + Expression(:var staticType) => staticType.isWidgetType, + _ => false, + }; + } + + /// Finds the named expression whose name is the given [name] that is an + /// argument to a Flutter instance creation expression. + /// + /// Returns `null` if this is not a [SimpleIdentifier], or if any other + /// condition cannot be satisfied. + NamedExpression? findArgumentNamed(String name) { + var self = this; + if (self is! SimpleIdentifier) { + return null; + } + var parent = self.parent; + var grandParent = parent?.parent; + if (parent is Label && grandParent is NamedExpression) { + if (self.name != name) { + return null; + } + } else { + return null; + } + var invocation = grandParent.parent?.parent; + if (invocation is! InstanceCreationExpression || + !invocation.isWidgetCreation) { + return null; + } + return grandParent; + } +} + +extension ClassElementExtension on ClassElement { + /// Whether this is the Flutter class `State`. + bool get isExactState => _isExactly(_nameState, _uriFramework); + + /// Whether this has the Flutter class `State` as a superclass. + bool get isState => _hasSupertype(_uriFramework, _nameState); + + /// Whether this is a [ClassElement] that extends the Flutter class + /// `StatefulWidget`. + bool get isStatefulWidgetDeclaration => supertype.isExactlyStatefulWidgetType; +} + +extension DartTypeExtension on DartType? { + /// Whether this is the 'dart.ui' class `Color`, or a subtype. + bool get isColor { + var self = this; + if (self is! InterfaceType) { + return false; + } + + return [self, ...self.element.allSupertypes].any((t) => + t.element.name == 'Color' && t.element.library.name == 'dart.ui'); + } + + /// Whether this is the Flutter mixin `Diagnosticable` or a subtype. + bool get isDiagnosticable { + var self = this; + if (self is! InterfaceType) { + return false; + } + + return [self, ...self.element.allSupertypes].any((t) => + t.element.name == 'Diagnosticable' && + t.element.source.uri == _uriDiagnostics); + } + + /// Whether this is the Flutter type `EdgeInsetsGeometry`. + bool get isExactEdgeInsetsGeometryType { + var self = this; + return self is InterfaceType && + self.element._isExactly('EdgeInsetsGeometry', _uriEdgeInsets); + } + + /// Whether this is the Flutter class `StatefulWidget`. + bool get isExactlyStatefulWidgetType { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameStatefulWidget, _uriFramework); + } + + /// Whether this is the Flutter class `StatelessWidget`. + bool get isExactlyStatelessWidgetType { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameStatelessWidget, _uriFramework); + } + + /// Whether this is the Flutter class `Align`. + bool get isExactWidgetTypeAlign { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameAlign, _uriBasic); + } + + /// Whether this is the Flutter class `StreamBuilder`. + bool get isExactWidgetTypeBuilder { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameBuilder, _uriBasic); + } + + /// Whether this is the Flutter class `Center`. + bool get isExactWidgetTypeCenter { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameCenter, _uriBasic); + } + + /// Whether this is the Flutter class `Container`. + bool get isExactWidgetTypeContainer { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameContainer, _uriContainer); + } + + /// Whether this is the Flutter class `Padding`. + bool get isExactWidgetTypePadding { + var self = this; + return self is InterfaceType && + self.element._isExactly(_namePadding, _uriBasic); + } + + /// Whether this is the Flutter class `SizedBox`. + bool get isExactWidgetTypeSizedBox { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameSizedBox, _uriBasic); + } + + /// Whether this is the Flutter class `StreamBuilder`. + bool get isExactWidgetTypeStreamBuilder { + var self = this; + return self is InterfaceType && + self.element._isExactly(_nameStreamBuilder, _uriAsync); + } + + /// Whether this is the Flutter class `Widget`, or its subtype. + bool get isListOfWidgetsType { + var self = this; + return self is InterfaceType && + self.isDartCoreList && + self.typeArguments[0].isWidgetType; + } + + /// Whether this is the vector_math_64 class `Matrix4`, or its + /// subtype. + bool get isMatrix4 { + var self = this; + if (self is! InterfaceType) { + return false; + } + + return [self, ...self.element.allSupertypes].any((t) => + t.element.name == 'Matrix4' && + t.element.library.name == 'vector_math_64'); + } + + /// Whether this is the Flutter class `Widget`, or its subtype. + bool get isWidgetType { + var self = this; + return self is InterfaceType && self.element.isWidget; + } +} + +extension ExpressionExtension on Expression { + /// Whether this is the `builder` argument. + bool get isBuilderArgument { + var self = this; + return self is NamedExpression && self.name.label.name == 'builder'; + } + + /// Whether this is the `child` argument. + bool get isChildArgument { + var self = this; + return self is NamedExpression && self.name.label.name == 'child'; + } + + /// Whether this is the `children` argument. + bool get isChildrenArgument { + var self = this; + return self is NamedExpression && self.name.label.name == 'children'; + } +} + +extension InstanceCreationExpressionExtension on InstanceCreationExpression { + /// The named expression representing the `builder` argument, or `null` if + /// there is none. + NamedExpression? get builderArgument => argumentList.arguments + .whereType() + .firstWhereOrNull((argument) => argument.isBuilderArgument); + + /// The named expression representing the `child` argument, or `null` if there + /// is none. + NamedExpression? get childArgument => argumentList.arguments + .whereType() + .firstWhereOrNull((argument) => argument.isChildArgument); + + /// The named expression representing the `children` argument, or `null` if + /// there is none. + NamedExpression? get childrenArgument => argumentList.arguments + .whereType() + .firstWhereOrNull((argument) => argument.isChildrenArgument); + + bool get isExactlyAlignCreation => staticType.isExactWidgetTypeAlign; + + bool get isExactlyContainerCreation => staticType.isExactWidgetTypeContainer; + + bool get isExactlyPaddingCreation => staticType.isExactWidgetTypePadding; + + /// Whether this is a constructor invocation for a class that has the Flutter + /// class `Widget` as a superclass. + bool get isWidgetCreation { + var element = + constructorName.staticElement?.enclosingElement.augmented.declaration; + return element.isWidget; + } + + /// The presentation for this node. + String? get widgetPresentationText { + var element = + constructorName.staticElement?.enclosingElement.augmented.declaration; + if (!element.isWidget) { + return null; + } + var arguments = argumentList.arguments; + if (element._isExactly('Icon', _uriWidgetsIcon)) { + if (arguments.isNotEmpty) { + var text = arguments[0].toString(); + var arg = shorten(text, 32); + return 'Icon($arg)'; + } else { + return 'Icon'; + } + } + if (element._isExactly('Text', _uriWidgetsText)) { + if (arguments.isNotEmpty) { + var text = arguments[0].toString(); + var arg = shorten(text, 32); + return 'Text($arg)'; + } else { + return 'Text'; + } + } + return element?.name; + } +} + +extension InterfaceElementExtension on InterfaceElement? { + /// Whether this is the Flutter class `Alignment`. + bool get isExactAlignment { + return _isExactly('Alignment', _uriAlignment); + } + + /// Whether this is the Flutter class `AlignmentDirectional`. + bool get isExactAlignmentDirectional { + return _isExactly('AlignmentDirectional', _uriAlignment); + } + + /// Whether this is the Flutter class `AlignmentGeometry`. + bool get isExactAlignmentGeometry { + return _isExactly('AlignmentGeometry', _uriAlignment); + } + + /// Whether this is the Flutter class `Widget`, or a subtype. + bool get isWidget { + var self = this; + if (self is! ClassElement) { + return false; + } + if (_isExactly(_nameWidget, _uriFramework)) { + return true; + } + return self.allSupertypes + .any((type) => type.element._isExactly(_nameWidget, _uriFramework)); + } + + /// Whether this has a supertype with the [requiredName] defined in the file + /// with the [requiredUri]. + bool _hasSupertype(Uri requiredUri, String requiredName) { + var self = this; + if (self == null) { + return false; + } + for (var type in self.allSupertypes) { + if (type.element.name == requiredName) { + var uri = type.element.source.uri; + if (uri == requiredUri) { + return true; + } + } + } + return false; + } + + /// Whether this is the exact [type] defined in the file with the given [uri]. + bool _isExactly(String type, Uri uri) { + var self = this; + return self is ClassElement && self.name == type && self.source.uri == uri; + } +} diff --git a/pkg/analysis_server/lib/src/utilities/flutter.dart b/pkg/analysis_server/lib/src/utilities/flutter.dart deleted file mode 100644 index 49b97609ff0..00000000000 --- a/pkg/analysis_server/lib/src/utilities/flutter.dart +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:analysis_server/src/utilities/strings.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/source/source_range.dart'; -import 'package:analyzer/src/generated/source.dart'; -import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; -import 'package:collection/collection.dart'; - -abstract final class Flutter { - static const _nameAlign = 'Align'; - static const _nameBuilder = 'Builder'; - static const _nameCenter = 'Center'; - static const _nameContainer = 'Container'; - static const _namePadding = 'Padding'; - static const _nameSizedBox = 'SizedBox'; - static const _nameState = 'State'; - static const _nameStatefulWidget = 'StatefulWidget'; - static const _nameStatelessWidget = 'StatelessWidget'; - static const _nameStreamBuilder = 'StreamBuilder'; - static const _nameWidget = 'Widget'; - - static final String widgetsUri = 'package:flutter/widgets.dart'; - - static final Uri _uriAlignment = Uri.parse( - 'package:flutter/src/painting/alignment.dart', - ); - static final Uri _uriAsync = Uri.parse( - 'package:flutter/src/widgets/async.dart', - ); - static final Uri _uriBasic = Uri.parse( - 'package:flutter/src/widgets/basic.dart', - ); - static final Uri _uriContainer = Uri.parse( - 'package:flutter/src/widgets/container.dart', - ); - static final Uri _uriDiagnostics = Uri.parse( - 'package:flutter/src/foundation/diagnostics.dart', - ); - static final Uri _uriEdgeInsets = Uri.parse( - 'package:flutter/src/painting/edge_insets.dart', - ); - static final Uri _uriFramework = Uri.parse( - 'package:flutter/src/widgets/framework.dart', - ); - static final Uri _uriWidgetsIcon = Uri.parse( - 'package:flutter/src/widgets/icon.dart', - ); - static final Uri _uriWidgetsText = Uri.parse( - 'package:flutter/src/widgets/text.dart', - ); - - static void convertChildToChildren2( - DartFileEditBuilder builder, - Expression childArg, - NamedExpression namedExp, - String eol, - String Function(Expression) getNodeText, - String Function(int) getLinePrefix, - String oneIndent, - String Function(int, int) getText, - String Function(String, String, String, - {bool includeLeading, bool ensureTrailingNewline}) - replaceSourceIndent, - SourceRange Function(Expression) rangeNode) { - var childLoc = namedExp.offset + 'child'.length; - builder.addSimpleInsertion(childLoc, 'ren'); - var listLoc = childArg.offset; - var childArgSrc = getNodeText(childArg); - if (!childArgSrc.contains(eol)) { - builder.addSimpleInsertion(listLoc, '['); - builder.addSimpleInsertion(listLoc + childArg.length, ']'); - } else { - var newlineLoc = childArgSrc.lastIndexOf(eol); - if (newlineLoc == childArgSrc.length) { - newlineLoc -= 1; - } - var indentOld = getLinePrefix(childArg.offset + eol.length + newlineLoc); - var indentNew = '$indentOld$oneIndent'; - // The separator includes 'child:' but that has no newlines. - var separator = - getText(namedExp.offset, childArg.offset - namedExp.offset); - var prefix = separator.contains(eol) ? '' : '$eol$indentNew'; - if (prefix.isEmpty) { - builder.addSimpleInsertion(namedExp.offset + 'child:'.length, ' ['); - builder.addDeletion(SourceRange(childArg.offset - 2, 2)); - } else { - builder.addSimpleInsertion(listLoc, '['); - } - var newChildArgSrc = replaceSourceIndent( - childArgSrc, - indentOld, - indentNew, - ); - newChildArgSrc = '$prefix$newChildArgSrc,$eol$indentOld]'; - builder.addSimpleReplacement(rangeNode(childArg), newChildArgSrc); - } - } - - /// Returns the named expression representing the `builder` argument of the - /// given [newExpr], or `null` if none. - static NamedExpression? findBuilderArgument( - InstanceCreationExpression newExpr) => - newExpr.argumentList.arguments - .whereType() - .firstWhereOrNull((argument) => isBuilderArgument(argument)); - - /// Returns the named expression representing the `child` argument of the - /// given [newExpr], or `null` if none. - static NamedExpression? findChildArgument( - InstanceCreationExpression newExpr) => - newExpr.argumentList.arguments - .whereType() - .firstWhereOrNull((argument) => isChildArgument(argument)); - - /// Returns the named expression representing the `children` argument of the - /// given [newExpr], or `null` if none. - static NamedExpression? findChildrenArgument( - InstanceCreationExpression newExpr) => - newExpr.argumentList.arguments - .whereType() - .firstWhereOrNull((argument) => isChildrenArgument(argument)); - - /// Returns the Flutter instance creation expression that is the value of the - /// 'child' argument of the given [newExpr], or `null` if none. - static InstanceCreationExpression? findChildWidget( - InstanceCreationExpression newExpr) { - var child = findChildArgument(newExpr); - var widget = getChildWidget(child); - if (widget is InstanceCreationExpression) { - return widget; - } - return null; - } - - /// If the given [node] is a simple identifier, finds the named expression - /// whose name is the given [name] that is an argument to a Flutter instance - /// creation expression. - /// - /// Returns `null` if any condition cannot be satisfied. - static NamedExpression? findNamedExpression(AstNode node, String name) { - if (node is! SimpleIdentifier) { - return null; - } - var parent = node.parent; - var grandParent = parent?.parent; - if (parent is Label && grandParent is NamedExpression) { - if (node.name != name) { - return null; - } - } else { - return null; - } - var invocation = grandParent.parent?.parent; - if (invocation is! InstanceCreationExpression || - !isWidgetCreation(invocation)) { - return null; - } - return grandParent; - } - - /// Returns the expression that is a Flutter Widget that is the value of the - /// given [child], or `null` if none. - static Expression? getChildWidget(NamedExpression? child) { - var expression = child?.expression; - if (isWidgetExpression(expression)) { - return expression; - } - return null; - } - - /// Returns the presentation for the given Flutter `Widget` creation [node]. - static String? getWidgetPresentationText(InstanceCreationExpression node) { - var element = node - .constructorName.staticElement?.enclosingElement.augmented.declaration; - if (!isWidget(element)) { - return null; - } - var arguments = node.argumentList.arguments; - if (_isExactWidget(element, 'Icon', _uriWidgetsIcon)) { - if (arguments.isNotEmpty) { - var text = arguments[0].toString(); - var arg = shorten(text, 32); - return 'Icon($arg)'; - } else { - return 'Icon'; - } - } - if (_isExactWidget(element, 'Text', _uriWidgetsText)) { - if (arguments.isNotEmpty) { - var text = arguments[0].toString(); - var arg = shorten(text, 32); - return 'Text($arg)'; - } else { - return 'Text'; - } - } - return element?.name; - } - - /// Returns the instance creation expression that surrounds the given - /// [node], if any, else null. - /// - /// The [node] may be the instance creation expression itself or an - /// (optionally prefixed) identifier that names the constructor. - static InstanceCreationExpression? identifyNewExpression(AstNode? node) { - InstanceCreationExpression? newExpr; - if (node is ImportPrefixReference) { - node = node.parent; - } - if (node is SimpleIdentifier) { - node = node.parent; - } - if (node is PrefixedIdentifier) { - node = node.parent; - } - if (node is NamedType) { - node = node.parent; - } - if (node is ConstructorName) { - node = node.parent; - } - if (node is InstanceCreationExpression) { - newExpr = node; - } - return newExpr; - } - - /// Attempts to find and return the closest expression that encloses the [node] - /// and is an independent Flutter `Widget`. - /// - /// Returns `null` if nothing is found. - static Expression? identifyWidgetExpression(AstNode? node) { - for (; node != null; node = node.parent) { - if (isWidgetExpression(node)) { - var parent = node.parent; - - if (node is AssignmentExpression) { - return null; - } - if (parent is AssignmentExpression) { - if (parent.rightHandSide == node) { - return node as Expression; - } - return null; - } - - if (parent is ArgumentList || - parent is ConditionalExpression && parent.thenExpression == node || - parent is ConditionalExpression && parent.elseExpression == node || - parent is ExpressionFunctionBody && parent.expression == node || - parent is ForElement && parent.body == node || - parent is IfElement && parent.thenElement == node || - parent is IfElement && parent.elseElement == node || - parent is ListLiteral || - parent is NamedExpression && parent.expression == node || - parent is Statement || - parent is SwitchExpressionCase && parent.expression == node || - parent is VariableDeclaration) { - return node as Expression; - } - } - if (node is ArgumentList || node is Statement || node is FunctionBody) { - return null; - } - } - return null; - } - - /// Whether the given [argument] is the `builder` argument. - static bool isBuilderArgument(Expression argument) => - argument is NamedExpression && argument.name.label.name == 'builder'; - - /// Whether the given [argument] is the `child` argument. - static bool isChildArgument(Expression argument) => - argument is NamedExpression && argument.name.label.name == 'child'; - - /// Whether the given [argument] is the `child` argument. - static bool isChildrenArgument(Expression argument) => - argument is NamedExpression && argument.name.label.name == 'children'; - - /// Whether the given [type] is the 'dart.ui' class `Color`, or its subtype. - static bool isColor(DartType? type) { - if (type is! InterfaceType) { - return false; - } - - return [type, ...type.element.allSupertypes].any((t) => - t.element.name == 'Color' && t.element.library.name == 'dart.ui'); - } - - /// Whether the given [type] is the flutter mixin `Diagnosticable` or its - /// subtype. - static bool isDiagnosticable(DartType? type) { - if (type is! InterfaceType) { - return false; - } - - return [type, ...type.element.allSupertypes].any((t) => - t.element.name == 'Diagnosticable' && - t.element.source.uri == _uriDiagnostics); - } - - /// Whether the [element] is the Flutter class `Alignment`. - static bool isExactAlignment(InterfaceElement element) { - return _isExactWidget(element, 'Alignment', _uriAlignment); - } - - /// Whether if the [element] is the Flutter class - /// `AlignmentDirectional`. - static bool isExactAlignmentDirectional(InterfaceElement element) { - return _isExactWidget(element, 'AlignmentDirectional', _uriAlignment); - } - - /// Whether if the [element] is the Flutter class `AlignmentGeometry`. - static bool isExactAlignmentGeometry(InterfaceElement element) { - return _isExactWidget(element, 'AlignmentGeometry', _uriAlignment); - } - - /// Whether the [type] is the Flutter type `EdgeInsetsGeometry`. - static bool isExactEdgeInsetsGeometryType(DartType type) { - return type is InterfaceType && - _isExactWidget(type.element, 'EdgeInsetsGeometry', _uriEdgeInsets); - } - - /// Whether the [node] is creation of `Align`. - static bool isExactlyAlignCreation(InstanceCreationExpression node) => - isExactWidgetTypeAlign(node.staticType); - - /// Whether the [node] is creation of `Container`. - static bool isExactlyContainerCreation(InstanceCreationExpression? node) => - isExactWidgetTypeContainer(node?.staticType); - - /// Whether the [node] is creation of `Padding`. - static bool isExactlyPaddingCreation(InstanceCreationExpression node) => - isExactWidgetTypePadding(node.staticType); - - /// Whether the given [type] is the Flutter class `StatefulWidget`. - static bool isExactlyStatefulWidgetType(DartType? type) => - type is InterfaceType && - _isExactWidget(type.element, _nameStatefulWidget, _uriFramework); - - /// Whether the given [type] is the Flutter class `StatelessWidget`. - static bool isExactlyStatelessWidgetType(DartType type) => - type is InterfaceType && - _isExactWidget(type.element, _nameStatelessWidget, _uriFramework); - - /// Whether the given [element] is the Flutter class `State`. - static bool isExactState(ClassElement element) => - _isExactWidget(element, _nameState, _uriFramework); - - /// Whether the given [type] is the Flutter class `Align`. - static bool isExactWidgetTypeAlign(DartType? type) => - type is InterfaceType && - _isExactWidget(type.element, _nameAlign, _uriBasic); - - /// Whether the given [type] is the Flutter class `StreamBuilder`. - static bool isExactWidgetTypeBuilder(DartType type) => - type is InterfaceType && - _isExactWidget(type.element, _nameBuilder, _uriBasic); - - /// Whether the given [type] is the Flutter class `Center`. - static bool isExactWidgetTypeCenter(DartType type) => - type is InterfaceType && - _isExactWidget(type.element, _nameCenter, _uriBasic); - - /// Whether the given [type] is the Flutter class `Container`. - static bool isExactWidgetTypeContainer(DartType? type) => - type is InterfaceType && - _isExactWidget(type.element, _nameContainer, _uriContainer); - - /// Whether the given [type] is the Flutter class `Padding`. - static bool isExactWidgetTypePadding(DartType? type) => - type is InterfaceType && - _isExactWidget(type.element, _namePadding, _uriBasic); - - /// Whether the given [type] is the Flutter class `SizedBox`. - static bool isExactWidgetTypeSizedBox(DartType type) => - type is InterfaceType && - _isExactWidget(type.element, _nameSizedBox, _uriBasic); - - /// Whether the given [type] is the Flutter class `StreamBuilder`. - static bool isExactWidgetTypeStreamBuilder(DartType type) => - type is InterfaceType && - _isExactWidget(type.element, _nameStreamBuilder, _uriAsync); - - /// Whether the given [type] is the Flutter class `Widget`, or its subtype. - static bool isListOfWidgetsType(DartType type) => - type is InterfaceType && - type.isDartCoreList && - isWidgetType(type.typeArguments[0]); - - /// Whether the given [type] is the vector_math_64 class `Matrix4`, or its - /// subtype. - static bool isMatrix4(DartType type) { - if (type is! InterfaceType) { - return false; - } - - return [type, ...type.element.allSupertypes].any((t) => - t.element.name == 'Matrix4' && - t.element.library.name == 'vector_math_64'); - } - - /// Whether the given [element] has the Flutter class `State` as - /// a superclass. - static bool isState(ClassElement? element) => - _hasSupertype(element, _uriFramework, _nameState); - - /// Whether the given [element] is a [ClassElement] that extends the Flutter - /// class `StatefulWidget`. - static bool isStatefulWidgetDeclaration(Element element) => - element is ClassElement && isExactlyStatefulWidgetType(element.supertype); - - /// Whether the given [element] is the Flutter class `Widget`, or its - /// subtype. - static bool isWidget(InterfaceElement? element) { - if (element is! ClassElement) { - return false; - } - if (_isExactWidget(element, _nameWidget, _uriFramework)) { - return true; - } - return element.allSupertypes.any( - (type) => _isExactWidget(type.element, _nameWidget, _uriFramework)); - } - - /// Whether the given [expr] is a constructor invocation for a class that has - /// the Flutter class `Widget` as a superclass. - static bool isWidgetCreation(InstanceCreationExpression? expr) { - var element = expr - ?.constructorName.staticElement?.enclosingElement.augmented.declaration; - return isWidget(element); - } - - /// Whether the given [node] is the Flutter class `Widget`, or its subtype. - static bool isWidgetExpression(AstNode? node) { - if (node == null) { - return false; - } - if (node.parent is NamedType || node.parent?.parent is NamedType) { - return false; - } - if (node.parent is ConstructorName) { - return false; - } - if (node is NamedExpression) { - return false; - } - if (node is Expression) { - return isWidgetType(node.staticType); - } - return false; - } - - /// Whether the given [type] is the Flutter class `Widget`, or its subtype. - static bool isWidgetType(DartType? type) => - type is InterfaceType && isWidget(type.element); - - /// Whether the given [element] has a supertype with the [requiredName] - /// defined in the file with the [requiredUri]. - static bool _hasSupertype( - InterfaceElement? element, Uri requiredUri, String requiredName) { - if (element == null) { - return false; - } - for (var type in element.allSupertypes) { - if (type.element.name == requiredName) { - var uri = type.element.source.uri; - if (uri == requiredUri) { - return true; - } - } - } - return false; - } - - /// Whether the given [element] is the exact [type] defined in the file with - /// the given [uri]. - static bool _isExactWidget(InterfaceElement? element, String type, Uri uri) => - element is ClassElement && - element.name == type && - element.source.uri == uri; -} diff --git a/pkg/analysis_server/test/lsp/flutter_outline_test.dart b/pkg/analysis_server/test/lsp/flutter_outline_test.dart index 569ee28137e..54df67993d0 100644 --- a/pkg/analysis_server/test/lsp/flutter_outline_test.dart +++ b/pkg/analysis_server/test/lsp/flutter_outline_test.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analysis_server/lsp_protocol/protocol.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/src/test_utilities/test_code_format.dart'; import 'package:test/test.dart'; @@ -95,8 +95,8 @@ Widget build(BuildContext context) => Icon(Icons.alarm); // Find the path to our mock 'package:flutter/widgets.dart'. var driver = server.getAnalysisDriver(mainFilePath)!; - var widgetsFilePath = driver.currentSession.uriConverter - .uriToPath(Uri.parse(Flutter.widgetsUri))!; + var widgetsFilePath = + driver.currentSession.uriConverter.uriToPath(Uri.parse(widgetsUri))!; var widgetsFileUri = Uri.file(widgetsFilePath); // We have to provide content to open a file so just read it. diff --git a/pkg/analysis_server/test/src/utilities/flutter_test.dart b/pkg/analysis_server/test/src/utilities/flutter_test.dart index 25f805314a2..978f2cad18f 100644 --- a/pkg/analysis_server/test/src/utilities/flutter_test.dart +++ b/pkg/analysis_server/test/src/utilities/flutter_test.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; @@ -25,76 +25,7 @@ class FlutterTest extends AbstractSingleUnitTest { ); } - Future test_getWidgetPresentationText_icon() async { - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = const Icon(Icons.book); -'''); - var w = _getTopVariableCreation('w'); - expect(Flutter.getWidgetPresentationText(w), 'Icon(Icons.book)'); - } - - Future test_getWidgetPresentationText_icon_withoutArguments() async { - verifyNoTestUnitErrors = false; - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = const Icon(); -'''); - var w = _getTopVariableCreation('w'); - expect(Flutter.getWidgetPresentationText(w), 'Icon'); - } - - Future test_getWidgetPresentationText_notWidget() async { - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = Object(); -'''); - var w = _getTopVariableCreation('w'); - expect(Flutter.getWidgetPresentationText(w), isNull); - } - - Future test_getWidgetPresentationText_text() async { - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = const Text('foo'); -'''); - var w = _getTopVariableCreation('w'); - expect(Flutter.getWidgetPresentationText(w), "Text('foo')"); - } - - Future test_getWidgetPresentationText_text_longText() async { - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = const Text('${'abc' * 100}'); -'''); - var w = _getTopVariableCreation('w'); - expect( - Flutter.getWidgetPresentationText(w), - "Text('abcabcabcabcab...cabcabcabcabc')", - ); - } - - Future test_getWidgetPresentationText_text_withoutArguments() async { - verifyNoTestUnitErrors = false; - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = const Text(); -'''); - var w = _getTopVariableCreation('w'); - expect(Flutter.getWidgetPresentationText(w), 'Text'); - } - - Future test_getWidgetPresentationText_unresolved() async { - verifyNoTestUnitErrors = false; - await resolveTestCode(''' -import 'package:flutter/material.dart'; -var w = new Foo(); -'''); - var w = _getTopVariableCreation('w'); - expect(Flutter.getWidgetPresentationText(w), isNull); - } - - Future test_identifyWidgetExpression_node_instanceCreation() async { + Future test_enclosingWidgetExpression_node_instanceCreation() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -120,14 +51,11 @@ class MyWidget extends StatelessWidget { var constructorName = creation.constructorName; var namedType = constructorName.type; var argumentList = creation.argumentList; - expect(Flutter.identifyWidgetExpression(creation), creation); - expect(Flutter.identifyWidgetExpression(constructorName), creation); - expect(Flutter.identifyWidgetExpression(namedType), creation); - expect(Flutter.identifyWidgetExpression(argumentList), isNull); - expect( - Flutter.identifyWidgetExpression(argumentList.arguments[0]), - isNull, - ); + expect(creation.findWidgetExpression, creation); + expect(constructorName.findWidgetExpression, creation); + expect(namedType.findWidgetExpression, creation); + expect(argumentList.findWidgetExpression, isNull); + expect(argumentList.arguments[0].findWidgetExpression, isNull); } // MyWidget.named(5678); @@ -137,19 +65,16 @@ class MyWidget extends StatelessWidget { var constructorName = creation.constructorName; var namedType = constructorName.type; var argumentList = creation.argumentList; - expect(Flutter.identifyWidgetExpression(creation), creation); - expect(Flutter.identifyWidgetExpression(constructorName), creation); - expect(Flutter.identifyWidgetExpression(namedType), creation); - expect(Flutter.identifyWidgetExpression(constructorName.name), creation); - expect(Flutter.identifyWidgetExpression(argumentList), isNull); - expect( - Flutter.identifyWidgetExpression(argumentList.arguments[0]), - isNull, - ); + expect(creation.findWidgetExpression, creation); + expect(constructorName.findWidgetExpression, creation); + expect(namedType.findWidgetExpression, creation); + expect(constructorName.name.findWidgetExpression, creation); + expect(argumentList.findWidgetExpression, isNull); + expect(argumentList.arguments[0].findWidgetExpression, isNull); } } - Future test_identifyWidgetExpression_node_invocation() async { + Future test_enclosingWidgetExpression_node_invocation() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -163,24 +88,21 @@ Text createText(String txt) => Text(txt); '''); { var invocation = findNode.methodInvocation('createEmptyText();'); - expect(Flutter.identifyWidgetExpression(invocation), invocation); + expect(invocation.findWidgetExpression, invocation); var argumentList = invocation.argumentList; - expect(Flutter.identifyWidgetExpression(argumentList), isNull); + expect(argumentList.findWidgetExpression, isNull); } { var invocation = findNode.methodInvocation("createText('xyz');"); - expect(Flutter.identifyWidgetExpression(invocation), invocation); + expect(invocation.findWidgetExpression, invocation); var argumentList = invocation.argumentList; - expect(Flutter.identifyWidgetExpression(argumentList), isNull); - expect( - Flutter.identifyWidgetExpression(argumentList.arguments[0]), - isNull, - ); + expect(argumentList.findWidgetExpression, isNull); + expect(argumentList.arguments[0].findWidgetExpression, isNull); } } - Future test_identifyWidgetExpression_node_namedExpression() async { + Future test_enclosingWidgetExpression_node_namedExpression() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -191,11 +113,11 @@ void f() { Text createEmptyText() => Text(''); '''); var childExpression = findNode.namedExpression('child: '); - expect(Flutter.identifyWidgetExpression(childExpression), isNull); + expect(childExpression.findWidgetExpression, isNull); } Future - test_identifyWidgetExpression_node_prefixedIdentifier_identifier() async { + test_enclosingWidgetExpression_node_prefixedIdentifier_identifier() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -210,11 +132,11 @@ void f(Foo foo) { } '''); var bar = findNode.simple('bar; // ref'); - expect(Flutter.identifyWidgetExpression(bar), bar.parent); + expect(bar.findWidgetExpression, bar.parent); } Future - test_identifyWidgetExpression_node_prefixedIdentifier_prefix() async { + test_enclosingWidgetExpression_node_prefixedIdentifier_prefix() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -229,10 +151,10 @@ void f(Foo foo) { } '''); var foo = findNode.simple('foo.bar'); - expect(Flutter.identifyWidgetExpression(foo), foo.parent); + expect(foo.findWidgetExpression, foo.parent); } - Future test_identifyWidgetExpression_node_simpleIdentifier() async { + Future test_enclosingWidgetExpression_node_simpleIdentifier() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -241,10 +163,10 @@ void f(Widget widget) { } '''); var expression = findNode.simple('widget; // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } - Future test_identifyWidgetExpression_node_switchExpression() async { + Future test_enclosingWidgetExpression_node_switchExpression() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -253,10 +175,10 @@ Widget f() => switch (1) { }; '''); var expression = findNode.instanceCreation('Container'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } - Future test_identifyWidgetExpression_null() async { + Future test_enclosingWidgetExpression_null() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -267,19 +189,19 @@ void f() { Text createEmptyText() => Text(''); '''); - expect(Flutter.identifyWidgetExpression(null), isNull); + expect(null.findWidgetExpression, isNull); { var expression = findNode.integerLiteral('42;'); - expect(Flutter.identifyWidgetExpression(expression), isNull); + expect(expression.findWidgetExpression, isNull); } { var expression = findNode.simple('intVariable;'); - expect(Flutter.identifyWidgetExpression(expression), isNull); + expect(expression.findWidgetExpression, isNull); } } - Future test_identifyWidgetExpression_parent_argumentList() async { + Future test_enclosingWidgetExpression_parent_argumentList() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -291,11 +213,11 @@ void f() { void useWidget(Widget w) {} '''); var expression = findNode.simple('text); // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } Future - test_identifyWidgetExpression_parent_assignmentExpression() async { + test_enclosingWidgetExpression_parent_assignmentExpression() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -309,24 +231,24 @@ void useWidget(Widget w) {} // Assignment itself. { var expression = findNode.simple('text ='); - expect(Flutter.identifyWidgetExpression(expression), isNull); + expect(expression.findWidgetExpression, isNull); } // Left hand side. { var expression = findNode.assignment('text ='); - expect(Flutter.identifyWidgetExpression(expression), isNull); + expect(expression.findWidgetExpression, isNull); } // Right hand side. { var expression = findNode.instanceCreation('Text('); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } } Future - test_identifyWidgetExpression_parent_conditionalExpression() async { + test_enclosingWidgetExpression_parent_conditionalExpression() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -335,25 +257,25 @@ void f(bool condition, Widget w1, Widget w2) { } '''); var thenWidget = findNode.simple('w1 :'); - expect(Flutter.identifyWidgetExpression(thenWidget), thenWidget); + expect(thenWidget.findWidgetExpression, thenWidget); var elseWidget = findNode.simple('w2;'); - expect(Flutter.identifyWidgetExpression(elseWidget), elseWidget); + expect(elseWidget.findWidgetExpression, elseWidget); } Future - test_identifyWidgetExpression_parent_expressionFunctionBody() async { + test_enclosingWidgetExpression_parent_expressionFunctionBody() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; void f(Widget widget) => widget; // ref '''); var expression = findNode.simple('widget; // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } Future - test_identifyWidgetExpression_parent_expressionStatement() async { + test_enclosingWidgetExpression_parent_expressionStatement() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -362,10 +284,10 @@ void f(Widget widget) { } '''); var expression = findNode.simple('widget; // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } - Future test_identifyWidgetExpression_parent_forElement() async { + Future test_enclosingWidgetExpression_parent_forElement() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -378,10 +300,10 @@ void f(bool b) { void useWidget(Widget w) {} '''); var expression = findNode.instanceCreation('Container()'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } - Future test_identifyWidgetExpression_parent_ifElement() async { + Future test_enclosingWidgetExpression_parent_ifElement() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -397,13 +319,13 @@ void f(bool b) { void useWidget(Widget w) {} '''); var thenExpression = findNode.instanceCreation("Text('then')"); - expect(Flutter.identifyWidgetExpression(thenExpression), thenExpression); + expect(thenExpression.findWidgetExpression, thenExpression); var elseExpression = findNode.instanceCreation("Text('else')"); - expect(Flutter.identifyWidgetExpression(elseExpression), elseExpression); + expect(elseExpression.findWidgetExpression, elseExpression); } - Future test_identifyWidgetExpression_parent_listLiteral() async { + Future test_enclosingWidgetExpression_parent_listLiteral() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -412,10 +334,10 @@ List f(Widget widget) { } '''); var expression = findNode.simple('widget]; // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } - Future test_identifyWidgetExpression_parent_namedExpression() async { + Future test_enclosingWidgetExpression_parent_namedExpression() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -427,10 +349,10 @@ void f() { void useWidget({required Widget child}) {} '''); var expression = findNode.simple('text); // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); } - Future test_identifyWidgetExpression_parent_returnStatement() async { + Future test_enclosingWidgetExpression_parent_returnStatement() async { await resolveTestCode(''' import 'package:flutter/widgets.dart'; @@ -439,7 +361,76 @@ Widget f(Widget widget) { } '''); var expression = findNode.simple('widget; // ref'); - expect(Flutter.identifyWidgetExpression(expression), expression); + expect(expression.findWidgetExpression, expression); + } + + Future test_getWidgetPresentationText_icon() async { + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = const Icon(Icons.book); +'''); + var widget = _getTopVariableCreation('w'); + expect(widget.widgetPresentationText, 'Icon(Icons.book)'); + } + + Future test_getWidgetPresentationText_icon_withoutArguments() async { + verifyNoTestUnitErrors = false; + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = const Icon(); +'''); + var widget = _getTopVariableCreation('w'); + expect(widget.widgetPresentationText, 'Icon'); + } + + Future test_getWidgetPresentationText_notWidget() async { + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = Object(); +'''); + var widget = _getTopVariableCreation('w'); + expect(widget.widgetPresentationText, isNull); + } + + Future test_getWidgetPresentationText_text() async { + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = const Text('foo'); +'''); + var widget = _getTopVariableCreation('w'); + expect(widget.widgetPresentationText, "Text('foo')"); + } + + Future test_getWidgetPresentationText_text_longText() async { + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = const Text('${'abc' * 100}'); +'''); + var widget = _getTopVariableCreation('w'); + expect( + widget.widgetPresentationText, + "Text('abcabcabcabcab...cabcabcabcabc')", + ); + } + + Future test_getWidgetPresentationText_text_withoutArguments() async { + verifyNoTestUnitErrors = false; + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = const Text(); +'''); + var widget = _getTopVariableCreation('w'); + expect(widget.widgetPresentationText, 'Text'); + } + + Future test_getWidgetPresentationText_unresolved() async { + verifyNoTestUnitErrors = false; + await resolveTestCode(''' +import 'package:flutter/material.dart'; +var w = new Foo(); +'''); + var widget = _getTopVariableCreation('w'); + expect(widget.widgetPresentationText, isNull); } Future test_isWidget() async { @@ -453,19 +444,19 @@ class NotFlutter {} class NotWidget extends State {} '''); var myStatelessWidget = testUnitElement.getClass('MyStatelessWidget'); - expect(Flutter.isWidget(myStatelessWidget), isTrue); + expect(myStatelessWidget.isWidget, isTrue); var myStatefulWidget = testUnitElement.getClass('MyStatefulWidget'); - expect(Flutter.isWidget(myStatefulWidget), isTrue); + expect(myStatefulWidget.isWidget, isTrue); var myContainer = testUnitElement.getClass('MyContainer'); - expect(Flutter.isWidget(myContainer), isTrue); + expect(myContainer.isWidget, isTrue); var notFlutter = testUnitElement.getClass('NotFlutter'); - expect(Flutter.isWidget(notFlutter), isFalse); + expect(notFlutter.isWidget, isFalse); var notWidget = testUnitElement.getClass('NotWidget'); - expect(Flutter.isWidget(notWidget), isFalse); + expect(notWidget.isWidget, isFalse); } Future test_isWidgetCreation() async { @@ -475,13 +466,12 @@ import 'package:flutter/widgets.dart'; var a = Object(); var b = Text('bbb'); '''); - expect(Flutter.isWidgetCreation(null), isFalse); var a = _getTopVariableCreation('a'); - expect(Flutter.isWidgetCreation(a), isFalse); + expect(a.isWidgetCreation, isFalse); var b = _getTopVariableCreation('b'); - expect(Flutter.isWidgetCreation(b), isTrue); + expect(b.isWidgetCreation, isTrue); } Future test_isWidgetExpression() async { @@ -506,44 +496,44 @@ Text createEmptyText() => new Text(''); '''); { var expression = findNode.simple('named(); // use'); - expect(Flutter.isWidgetExpression(expression), isFalse); + expect(expression.isWidgetExpression, isFalse); var creation = expression.parent?.parent as InstanceCreationExpression; - expect(Flutter.isWidgetExpression(creation), isTrue); + expect(creation.isWidgetExpression, isTrue); } { var expression = findNode.instanceCreation("Text('abc')"); - expect(Flutter.isWidgetExpression(expression), isTrue); + expect(expression.isWidgetExpression, isTrue); } { var expression = findNode.simple('text;'); - expect(Flutter.isWidgetExpression(expression), isTrue); + expect(expression.isWidgetExpression, isTrue); } { var expression = findNode.methodInvocation('createEmptyText();'); - expect(Flutter.isWidgetExpression(expression), isTrue); + expect(expression.isWidgetExpression, isTrue); } { var expression = findNode.namedType('Container('); - expect(Flutter.isWidgetExpression(expression), isFalse); + expect(expression.isWidgetExpression, isFalse); } { var expression = findNode.namedExpression('child: '); - expect(Flutter.isWidgetExpression(expression), isFalse); + expect(expression.isWidgetExpression, isFalse); } { var expression = findNode.integerLiteral('42;'); - expect(Flutter.isWidgetExpression(expression), isFalse); + expect(expression.isWidgetExpression, isFalse); } { var expression = findNode.simple('intVariable;'); - expect(Flutter.isWidgetExpression(expression), isFalse); + expect(expression.isWidgetExpression, isFalse); } } diff --git a/pkg/analysis_server/tool/code_completion/flutter_metrics.dart b/pkg/analysis_server/tool/code_completion/flutter_metrics.dart index 448004c20af..313b8566b36 100644 --- a/pkg/analysis_server/tool/code_completion/flutter_metrics.dart +++ b/pkg/analysis_server/tool/code_completion/flutter_metrics.dart @@ -4,7 +4,7 @@ import 'dart:io' as io; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/context_root.dart'; import 'package:analyzer/dart/analysis/results.dart'; @@ -135,7 +135,7 @@ class FlutterDataCollector extends RecursiveAstVisitor { @override void visitInstanceCreationExpression(InstanceCreationExpression node) { var previousParentWidget = parentWidget; - if (Flutter.isWidgetCreation(node)) { + if (node.isWidgetCreation) { var element = node.constructorName.staticElement; if (element == null) { throw StateError( diff --git a/pkg/analysis_server/tool/code_completion/relevance_metrics.dart b/pkg/analysis_server/tool/code_completion/relevance_metrics.dart index f7caba7726f..f8e10400b3f 100644 --- a/pkg/analysis_server/tool/code_completion/relevance_metrics.dart +++ b/pkg/analysis_server/tool/code_completion/relevance_metrics.dart @@ -9,7 +9,7 @@ import 'package:_fe_analyzer_shared/src/base/syntactic_entity.dart'; import 'package:analysis_server/src/protocol_server.dart' show convertElementToElementKind, ElementKind; import 'package:analysis_server/src/services/completion/dart/feature_computer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/context_root.dart'; import 'package:analyzer/dart/analysis/results.dart'; @@ -303,8 +303,8 @@ class RelevanceDataCollector extends RecursiveAstVisitor { void visitArgumentList(ArgumentList node) { var context = _argumentListContext(node); var parent = node.parent; - var inWidgetConstructor = parent is InstanceCreationExpression && - Flutter.isWidgetType(parent.staticType); + var inWidgetConstructor = + parent is InstanceCreationExpression && parent.staticType.isWidgetType; for (var argument in node.arguments) { var realArgument = argument; var argumentKind = 'unnamed'; diff --git a/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart b/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart index b1fdeec76d0..3a7a79dc1e1 100644 --- a/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart +++ b/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart @@ -8,7 +8,7 @@ import 'dart:io' as io; import 'package:_fe_analyzer_shared/src/base/syntactic_entity.dart'; import 'package:analysis_server/src/protocol_server.dart' show ElementKind; import 'package:analysis_server/src/services/completion/dart/feature_computer.dart'; -import 'package:analysis_server/src/utilities/flutter.dart'; +import 'package:analysis_server/src/utilities/extensions/flutter.dart'; import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/context_root.dart'; import 'package:analyzer/dart/analysis/results.dart'; @@ -1276,7 +1276,7 @@ class RelevanceDataCollector extends RecursiveAstVisitor { } else if (parent is FunctionExpressionInvocation) { return 'function'; } else if (parent is InstanceCreationExpression) { - if (Flutter.isWidgetType(parent.staticType)) { + if (parent.staticType.isWidgetType) { return 'widgetConstructor'; } return 'constructor'; diff --git a/pkg/analyzer/lib/src/dart/analysis/session_helper.dart b/pkg/analyzer/lib/src/dart/analysis/session_helper.dart index 5c915709732..19236b03c6a 100644 --- a/pkg/analyzer/lib/src/dart/analysis/session_helper.dart +++ b/pkg/analyzer/lib/src/dart/analysis/session_helper.dart @@ -18,7 +18,7 @@ class AnalysisSessionHelper { AnalysisSessionHelper(this.session); - /// Return the [ClassElement] with the given [className] that is exported + /// Returns the [ClassElement] with the given [className] that is exported /// from the library with the given [libraryUri], or `null` if the library /// does not export a class with such name. Future getClass(String libraryUri, String className) async { @@ -55,6 +55,12 @@ class AnalysisSessionHelper { return null; } + /// Returns the [ClassElement] with the given [className] that is exported + /// from the Flutter widgets library, or `null` if the library does not export + /// a class with such name. + Future getFlutterClass(String className) => + getClass('package:flutter/widgets.dart', className); + /// Return the [MixinElement] with the given [name] that is exported /// from the library with the given [libraryUri], or `null` if the library /// does not export a class with such name.