diff --git a/DEPS b/DEPS index 3b1ea7f7eea9..d9f37976d7a3 100644 --- a/DEPS +++ b/DEPS @@ -98,7 +98,7 @@ vars = { # Prefer to use hashes of binaryen that have been reviewed & rolled into g3. "binaryen_rev" : "3f6831c0bd147ae1ae0ab1d9187d37bce7cca38b", - "boringssl_rev": "822902749a5956bba09c7e9e451104e8a74f02c5", + "boringssl_rev": "5a2194f43d8801335e4b20040156686eb4247b22", "browser-compat-data_tag": "ac8cae697014da1ff7124fba33b0b4245cc6cd1b", # v1.0.22 "cpu_features_rev": "936b9ab5515dead115606559502e3864958f7f6e", "devtools_rev": "8762b31f0d0ffeea6449fd02740e9ce7acb32503", diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_ignore.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_ignore.dart new file mode 100644 index 000000000000..63abdee66221 --- /dev/null +++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_ignore.dart @@ -0,0 +1,84 @@ +// Copyright (c) 2025, 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/services/correction/fix.dart'; +import 'package:analysis_server_plugin/edit/dart/correction_producer.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer/src/dart/ast/token.dart'; +import 'package:analyzer/src/ignore_comments/ignore_info.dart'; +import 'package:analyzer/src/utilities/extensions/ast.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; +import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; + +class RemoveIgnore extends ResolvedCorrectionProducer { + String _diagnosticName = ''; + RemoveIgnore({required super.context}); + + @override + CorrectionApplicability get applicability => + CorrectionApplicability.singleLocation; + + @override + List get fixArguments => [_diagnosticName]; + + @override + FixKind get fixKind => DartFixKind.REMOVE_IGNORED_DIAGNOSTIC; + + @override + Future compute(ChangeBuilder builder) async { + var diagnostic = this.diagnostic; + if (diagnostic == null) return; + + var diagnosticOffset = diagnostic.problemMessage.offset; + + var comment = node.commentTokenCovering(diagnosticOffset); + if (comment is! CommentToken) return; + + SourceRange? rangeToDelete; + + var ignoredElements = comment.ignoredElements.toList(); + + for (var (index, ignoredElement) in ignoredElements.indexed) { + if (ignoredElement is! IgnoredDiagnosticName) continue; + + var ignoredElementOffset = ignoredElement.offset; + if (ignoredElement.offset == diagnosticOffset) { + _diagnosticName = ignoredElement.name; + var scanBack = index != 0; + var commentText = comment.lexeme; + if (scanBack) { + // Scan back for a preceding comma: + for (var offset = ignoredElementOffset; offset > -1; --offset) { + if (commentText[offset] == ',') { + var backSteps = ignoredElementOffset - offset; + rangeToDelete = SourceRange( + diagnosticOffset - backSteps, + _diagnosticName.length + backSteps, + ); + break; + } + } + } else { + // Scan forward for a trailing comma: + var chars = commentText.substring(ignoredElementOffset).indexOf(','); + if (chars == -1) return; + + // Eat the comma + chars++; + + // Eat a trailing space if needed + if (commentText[ignoredElementOffset + chars] == ' ') chars++; + + rangeToDelete = SourceRange(ignoredElementOffset, chars); + } + } + } + + if (rangeToDelete != null) { + await builder.addDartFileEdit(file, (builder) { + builder.addDeletion(rangeToDelete!); + }); + } + } +} diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index 478783c3e33d..963bc3347c1c 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -2424,13 +2424,15 @@ LintCode.unnecessary_getters_setters: LintCode.unnecessary_ignore: status: needsFix notes: |- - Remove the ignore comment (or one code in the comment). + Remove the ignore comment. LintCode.unnecessary_ignore_file: status: needsFix + notes: |- + Remove the ignore comment. LintCode.unnecessary_ignore_name: - status: needsFix + status: hasFix LintCode.unnecessary_ignore_name_file: - status: needsFix + status: hasFix LintCode.unnecessary_lambdas: status: hasFix LintCode.unnecessary_late: diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart index 7d090430041b..190b7f24f81b 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix.dart @@ -1266,6 +1266,11 @@ abstract final class DartFixKind { DartFixKindPriority.inFile, "Remove unnecessary '??' operators everywhere in file", ); + static const REMOVE_IGNORED_DIAGNOSTIC = FixKind( + 'dart.fix.remove.ignored.diagnostic', + DartFixKindPriority.standard, + 'Remove {0}', + ); static const REMOVE_INVOCATION = FixKind( 'dart.fix.remove.invocation', DartFixKindPriority.standard, diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart index 598075187fb3..efafc75505ae 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -146,6 +146,7 @@ import 'package:analysis_server/src/services/correction/dart/remove_empty_else.d import 'package:analysis_server/src/services/correction/dart/remove_empty_statement.dart'; import 'package:analysis_server/src/services/correction/dart/remove_extends_clause.dart'; import 'package:analysis_server/src/services/correction/dart/remove_if_null_operator.dart'; +import 'package:analysis_server/src/services/correction/dart/remove_ignore.dart'; import 'package:analysis_server/src/services/correction/dart/remove_initializer.dart'; import 'package:analysis_server/src/services/correction/dart/remove_interpolation_braces.dart'; import 'package:analysis_server/src/services/correction/dart/remove_invocation.dart'; @@ -485,6 +486,11 @@ final _builtInLintProducers = >{ LinterLintCode.unnecessary_final_with_type: [ReplaceFinalWithVar.new], LinterLintCode.unnecessary_final_without_type: [ReplaceFinalWithVar.new], LinterLintCode.unnecessary_getters_setters: [MakeFieldPublic.new], + LinterLintCode.unnecessary_ignore_name: [RemoveIgnore.new], + LinterLintCode.unnecessary_ignore_name_file: [RemoveIgnore.new], + // TODO(pq): add => + // LinterLintCode.unnecessary_ignore: [RemoveComment.new], + // LinterLintCode.unnecessary_ignore_file: [RemoveComment.new], LinterLintCode.unnecessary_lambdas: [ReplaceWithTearOff.new], LinterLintCode.unnecessary_late: [RemoveUnnecessaryLate.new], LinterLintCode.unnecessary_library_directive: [ diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_ignore_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_ignore_test.dart new file mode 100644 index 000000000000..6e6330586f71 --- /dev/null +++ b/pkg/analysis_server/test/src/services/correction/fix/remove_ignore_test.dart @@ -0,0 +1,92 @@ +// Copyright (c) 2025, 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/services/correction/fix.dart'; +import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; +import 'package:linter/src/lint_names.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import 'fix_processor.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(RemoveUnnecessaryIgnoreTest); + }); +} + +// TODO(pq): add bulk fix tests +@reflectiveTest +class RemoveUnnecessaryIgnoreTest extends FixProcessorLintTest { + @override + FixKind get kind => DartFixKind.REMOVE_IGNORED_DIAGNOSTIC; + + @override + String get lintCode => LintNames.unnecessary_ignore; + + Future test_file_first() async { + await resolveTestCode(''' +// ignore_for_file: unused_local_variable, return_of_invalid_type +int f() => null; +'''); + await assertHasFix(''' +// ignore_for_file: return_of_invalid_type +int f() => null; +'''); + } + + Future test_file_last() async { + await resolveTestCode(''' +// ignore_for_file: return_of_invalid_type, unused_local_variable +int f() => null; +'''); + await assertHasFix(''' +// ignore_for_file: return_of_invalid_type +int f() => null; +'''); + } + + Future test_file_middle() async { + await resolveTestCode(''' +// ignore_for_file: return_of_invalid_type, unused_local_variable, non_bool_negation_expression +int f() => !null; +'''); + await assertHasFix(''' +// ignore_for_file: return_of_invalid_type, non_bool_negation_expression +int f() => !null; +'''); + } + + Future test_line_first() async { + await resolveTestCode(''' +// ignore: unused_local_variable, return_of_invalid_type +int f() => null; +'''); + await assertHasFix(''' +// ignore: return_of_invalid_type +int f() => null; +'''); + } + + Future test_line_last() async { + await resolveTestCode(''' +// ignore: return_of_invalid_type, unused_local_variable +int f() => null; +'''); + await assertHasFix(''' +// ignore: return_of_invalid_type +int f() => null; +'''); + } + + Future test_line_middle() async { + await resolveTestCode(''' +// ignore: return_of_invalid_type, unused_local_variable, non_bool_negation_expression +int f() => !null; +'''); + await assertHasFix(''' +// ignore: return_of_invalid_type, non_bool_negation_expression +int f() => !null; +'''); + } +} diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart index ac465354a9ba..a58cb1170c12 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart @@ -187,6 +187,7 @@ import 'remove_empty_else_test.dart' as remove_empty_else; import 'remove_empty_statement_test.dart' as remove_empty_statement; import 'remove_extends_clause_test.dart' as remove_extends_clause; import 'remove_if_null_operator_test.dart' as remove_if_null_operator; +import 'remove_ignore_test.dart' as remove_ignore; import 'remove_initializer_test.dart' as remove_initializer; import 'remove_interpolation_braces_test.dart' as remove_interpolation_braces; import 'remove_invocation_test.dart' as remove_invocation; @@ -465,6 +466,7 @@ void main() { remove_empty_statement.main(); remove_extends_clause.main(); remove_if_null_operator.main(); + remove_ignore.main(); remove_initializer.main(); remove_interpolation_braces.main(); remove_invocation.main(); diff --git a/pkg/analyzer/lib/src/dart/ast/extensions.dart b/pkg/analyzer/lib/src/dart/ast/extensions.dart index 21eeb33a86dc..11ea40e5ecce 100644 --- a/pkg/analyzer/lib/src/dart/ast/extensions.dart +++ b/pkg/analyzer/lib/src/dart/ast/extensions.dart @@ -220,6 +220,10 @@ extension IdentifierExtension on Identifier { return _readElement(this); } + Element2? get readElement2 { + return _readElement(this).asElement2; + } + SimpleIdentifier get simpleName { var self = this; if (self is SimpleIdentifier) { @@ -233,6 +237,10 @@ extension IdentifierExtension on Identifier { return _writeElement(this); } + Element2? get writeElement2 { + return _writeElement(this).asElement2; + } + Element? get writeOrReadElement { return _writeElement(this) ?? staticElement; } diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 16d0ae1d5fd2..ae003473a5d0 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart @@ -153,8 +153,12 @@ class BindPatternVariableElementImpl2 extends PatternVariableElementImpl2 ]; } - /// This flag is set to `true` if this variable clashes with another - /// pattern variable with the same name within the same pattern. + /// Whether this variable clashes with another pattern variable with the same + /// name within the same pattern. + bool get isDuplicate => _wrappedElement.isDuplicate; + + /// Set whether this variable clashes with another pattern variable with the + /// same name within the same pattern. set isDuplicate(bool value) => _wrappedElement.isDuplicate = value; DeclaredVariablePatternImpl get node => _wrappedElement.node; @@ -6793,6 +6797,23 @@ class JoinPatternVariableElementImpl2 extends PatternVariableElementImpl2 /// The identifiers that reference this element. List get references => _wrappedElement.references; + /// Returns this variable, and variables that join into it. + List get transitiveVariables { + var result = []; + + void append(PatternVariableElementImpl2 variable) { + result.add(variable); + if (variable is JoinPatternVariableElementImpl2) { + for (var variable in variable.variables2) { + append(variable as PatternVariableElementImpl2); + } + } + } + + append(this); + return result; + } + /// The variables that join into this variable. List get variables => _wrappedElement.variables; diff --git a/pkg/analyzer/lib/src/error/ignore_validator.dart b/pkg/analyzer/lib/src/error/ignore_validator.dart index 1a48304919fc..6644c308b40d 100644 --- a/pkg/analyzer/lib/src/error/ignore_validator.dart +++ b/pkg/analyzer/lib/src/error/ignore_validator.dart @@ -107,12 +107,21 @@ class IgnoreValidator { _reportUnignorableAndDuplicateIgnores( unignorable, duplicated, ignoredOnLine); } + + // If there's more than one ignore, the fix is to remove the name. + // Otherwise, the entire comment can be removed. + var ignoredForFileCount = ignoredForFile.length; + var ignoredOnLineCount = 0; + // // Remove all of the errors that are actually being ignored. // for (var error in _reportedErrors) { var lineNumber = _lineInfo.getLocation(error.offset).lineNumber; var ignoredOnLine = ignoredOnLineMap[lineNumber]; + if (ignoredOnLine != null) { + ignoredOnLineCount += ignoredOnLine.length; + } ignoredForFile.removeByName(error.ignoreName); ignoredForFile.removeByName(error.ignoreUniqueName); @@ -123,10 +132,15 @@ class IgnoreValidator { // // Report any remaining ignored names as being unnecessary. // - _reportUnnecessaryOrRemovedOrDeprecatedIgnores(ignoredForFile, - forFile: true); + _reportUnnecessaryOrRemovedOrDeprecatedIgnores( + ignoredForFile, + ignoredForFileCount: ignoredForFileCount, + ); for (var ignoredOnLine in ignoredOnLineMap.values) { - _reportUnnecessaryOrRemovedOrDeprecatedIgnores(ignoredOnLine); + _reportUnnecessaryOrRemovedOrDeprecatedIgnores( + ignoredOnLine, + ignoredOnLineCount: ignoredOnLineCount, + ); } } @@ -172,7 +186,8 @@ class IgnoreValidator { /// Report the [ignoredNames] as being unnecessary. void _reportUnnecessaryOrRemovedOrDeprecatedIgnores( List ignoredNames, - {bool forFile = false}) { + {int? ignoredForFileCount, + int? ignoredOnLineCount}) { if (!_validateUnnecessaryIgnores) return; for (var ignoredName in ignoredNames) { @@ -210,13 +225,14 @@ class IgnoreValidator { } late ErrorCode lintCode; - if (ignoredNames.length > 1) { - lintCode = forFile + + if (ignoredForFileCount != null) { + lintCode = ignoredForFileCount > 1 ? unnecessaryIgnoreNameFileLintCode - : unnecessaryIgnoreNameLocationLintCode; + : unnecessaryIgnoreFileLintCode; } else { - lintCode = forFile - ? unnecessaryIgnoreFileLintCode + lintCode = ignoredOnLineCount! > 1 + ? unnecessaryIgnoreNameLocationLintCode : unnecessaryIgnoreLocationLintCode; } diff --git a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart index e912be0b571b..e210d34c7a5f 100644 --- a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart +++ b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart @@ -2,21 +2,20 @@ // 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. -// ignore_for_file: analyzer_use_new_elements - import 'dart:collection'; import 'dart:math' as math; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/element2.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; -import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/element.dart' + show JoinPatternVariableElementImpl2, MetadataImpl; import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; import 'package:analyzer/src/dart/element/member.dart' show ExecutableMember; import 'package:analyzer/src/error/codes.dart'; @@ -27,9 +26,9 @@ import 'package:collection/collection.dart'; class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { final UsedLocalElements usedElements = UsedLocalElements(); - final LibraryElement _enclosingLibrary; - InterfaceElement? _enclosingClass; - ExecutableElement? _enclosingExec; + final LibraryElement2 _enclosingLibrary; + InterfaceElement2? _enclosingClass; + ExecutableElement2? _enclosingExec; /// Non-null when the visitor is inside an [IsExpression]'s type. IsExpression? _enclosingIsExpression; @@ -41,7 +40,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitAssignmentExpression(AssignmentExpression node) { - var element = node.staticElement; + var element = node.element; if (element != null) { usedElements.members.add(element); } @@ -50,7 +49,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitBinaryExpression(BinaryExpression node) { - var element = node.staticElement; + var element = node.element; usedElements.addMember(element); super.visitBinaryExpression(node); } @@ -60,14 +59,14 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { var exceptionParameter = node.exceptionParameter; var stackTraceParameter = node.stackTraceParameter; if (exceptionParameter != null) { - var element = exceptionParameter.declaredElement; + var element = exceptionParameter.declaredElement2; usedElements.addCatchException(element); if (stackTraceParameter != null || node.onKeyword == null) { usedElements.addElement(element); } } if (stackTraceParameter != null) { - var element = stackTraceParameter.declaredElement; + var element = stackTraceParameter.declaredElement2; usedElements.addCatchStackTrace(element); } super.visitCatchClause(node); @@ -75,16 +74,16 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitClassDeclaration(ClassDeclaration node) { - var element = node.declaredElement; + var element = node.declaredFragment?.element; if (element != null) { - if (element.hasJS) { + if (element.metadata2.hasJS) { usedElements.addElement(element); } } var enclosingClassOld = _enclosingClass; try { - _enclosingClass = node.declaredElement; + _enclosingClass = node.declaredFragment?.element; super.visitClassDeclaration(node); } finally { _enclosingClass = enclosingClassOld; @@ -93,15 +92,15 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitConstructorDeclaration(ConstructorDeclaration node) { - var element = node.declaredElement!; + var element = node.declaredFragment!.element; var redirectedConstructor = node.redirectedConstructor; if (redirectedConstructor != null) { - var redirectedElement = redirectedConstructor.staticElement; + var redirectedElement = redirectedConstructor.element; if (redirectedElement != null) { // TODO(scheglov): Only if not _isPubliclyAccessible _matchParameters( - element.parameters, - redirectedElement.parameters, + element.formalParameters, + redirectedElement.formalParameters, (first, second) { usedElements.addElement(second); }, @@ -114,9 +113,9 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitDefaultFormalParameter(DefaultFormalParameter node) { - var element = node.declaredElement; - if (element is SuperFormalParameterElement) { - usedElements.addElement(element.superConstructorParameter); + var element = node.declaredFragment?.element; + if (element is SuperFormalParameterElement2) { + usedElements.addElement(element.superConstructorParameter2); } super.visitDefaultFormalParameter(node); @@ -124,7 +123,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitEnumConstantDeclaration(EnumConstantDeclaration node) { - usedElements.addElement(node.constructorElement?.declaration); + usedElements.addElement(node.constructorElement2?.baseElement); var argumentList = node.arguments?.argumentList; if (argumentList != null) { @@ -138,7 +137,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { void visitFunctionDeclaration(FunctionDeclaration node) { var enclosingExecOld = _enclosingExec; try { - _enclosingExec = node.declaredElement; + _enclosingExec = node.declaredFragment?.element; super.visitFunctionDeclaration(node); } finally { _enclosingExec = enclosingExecOld; @@ -148,14 +147,14 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitFunctionExpression(FunctionExpression node) { if (node.parent is! FunctionDeclaration) { - usedElements.addElement(node.declaredElement); + usedElements.addElement(node.declaredFragment?.element); } super.visitFunctionExpression(node); } @override void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { - usedElements.addElement(node.staticElement); + usedElements.addElement(node.element); super.visitFunctionExpressionInvocation(node); } @@ -164,8 +163,8 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { if (!Identifier.isPrivateName(node.name.lexeme)) { var type = node.type.type; if (type is InterfaceType) { - for (var constructor in type.constructors) { - if (!Identifier.isPrivateName(constructor.name)) { + for (var constructor in type.constructors2) { + if (!Identifier.isPrivateName(constructor.name3!)) { usedElements.addElement(constructor); } } @@ -176,7 +175,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitIndexExpression(IndexExpression node) { - var element = node.writeOrReadElement; + var element = node.writeOrReadElement2; usedElements.addMember(element); super.visitIndexExpression(node); } @@ -203,7 +202,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { void visitMethodDeclaration(MethodDeclaration node) { var enclosingExecOld = _enclosingExec; try { - _enclosingExec = node.declaredElement; + _enclosingExec = node.declaredFragment?.element; super.visitMethodDeclaration(node); } finally { _enclosingExec = enclosingExecOld; @@ -212,8 +211,10 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitMethodInvocation(MethodInvocation node) { - var function = node.methodName.staticElement; - if (function is FunctionElement || function is MethodElement) { + var function = node.methodName.element; + if (function is LocalFunctionElement || + function is MethodElement2 || + function is TopLevelFunctionElement) { _addParametersForArguments(node.argumentList); } super.visitMethodInvocation(node); @@ -221,35 +222,35 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { @override void visitNamedType(NamedType node) { - _useIdentifierElement(node.element, parent: node); + _useIdentifierElement(node.element2, parent: node); super.visitNamedType(node); } @override void visitPatternField(PatternField node) { - usedElements.addMember(node.element); - usedElements.addReadMember(node.element); + usedElements.addMember(node.element2); + usedElements.addReadMember(node.element2); super.visitPatternField(node); } @override void visitPostfixExpression(PostfixExpression node) { - var element = node.staticElement; + var element = node.element; usedElements.addMember(element); super.visitPostfixExpression(node); } @override void visitPrefixExpression(PrefixExpression node) { - var element = node.staticElement; + var element = node.element; usedElements.addMember(element); super.visitPrefixExpression(node); } @override void visitRelationalPattern(RelationalPattern node) { - usedElements.addMember(node.element); - usedElements.addReadMember(node.element); + usedElements.addMember(node.element2); + usedElements.addReadMember(node.element2); super.visitRelationalPattern(node); } @@ -261,65 +262,66 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { if (_inCommentReference(node)) { return; } - var element = node.writeOrReadElement; + var element = node.writeOrReadElement2; // Store un-parameterized members. if (element is ExecutableMember) { - element = element.declaration; + element = element.baseElement; } - var variable = element.ifTypeOrNull()?.variable2; + var variable = element.ifTypeOrNull()?.variable3; bool isIdentifierRead = _isReadIdentifier(node); - if (element is PropertyAccessorElement && + if (element is PropertyAccessorElement2 && isIdentifierRead && - variable is TopLevelVariableElement) { + variable is TopLevelVariableElement2) { if (element.isSynthetic) { usedElements.addElement(variable); } else { usedElements.members.add(element); _addMemberAndCorrespondingGetter(element); } - } else if (element is LocalVariableElement) { + } else if (element is LocalVariableElement2) { if (isIdentifierRead) { usedElements.addElement(element); } } else { var parent = node.parent!; - _useIdentifierElement(node.readElement, parent: parent); - _useIdentifierElement(node.writeElement, parent: parent); - _useIdentifierElement(node.staticElement, parent: parent); + _useIdentifierElement(node.readElement2, parent: parent); + _useIdentifierElement(node.writeElement2, parent: parent); + _useIdentifierElement(node.element, parent: parent); var grandparent = parent.parent; // If [node] is a tear-off, assume all parameters are used. var functionReferenceIsCall = - (element is ExecutableElement && parent is MethodInvocation) || + (element is ExecutableElement2 && parent is MethodInvocation) || // named constructor - (element is ConstructorElement && + (element is ConstructorElement2 && parent is ConstructorName && grandparent is InstanceCreationExpression) || // unnamed constructor - (element is InterfaceElement && + (element is InterfaceElement2 && grandparent is ConstructorName && grandparent.parent is InstanceCreationExpression); - if (element is ExecutableElement && + if (element is ExecutableElement2 && isIdentifierRead && !functionReferenceIsCall) { - for (var parameter in element.parameters) { + for (var parameter in element.formalParameters) { usedElements.addElement(parameter); } } - var enclosingElement = element?.enclosingElement3; + var enclosingElement = element?.enclosingElement2; if (element == null) { if (isIdentifierRead) { usedElements.unresolvedReadMembers.add(node.name); } - } else if (enclosingElement is EnumElement && element.name == 'values') { + } else if (enclosingElement is EnumElement2 && + element.name3 == 'values') { // If the 'values' static accessor of the enum is accessed, then all of // the enum values have been read. - for (var field in enclosingElement.fields) { + for (var field in enclosingElement.fields2) { if (field.isEnumConstant) { - usedElements.readMembers.add(field.getter!); + usedElements.readMembers.add(field.getter2!); } } - } else if ((enclosingElement is InterfaceElement || - enclosingElement is ExtensionElement) && + } else if ((enclosingElement is InterfaceElement2 || + enclosingElement is ExtensionElement2) && !identical(element, _enclosingExec)) { usedElements.members.add(element); if (isIdentifierRead) { @@ -350,10 +352,10 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { /// Add [element] as a used member and, if [element] is a setter, add its /// corresponding getter as a used member. - void _addMemberAndCorrespondingGetter(Element element) { - if (element is PropertyAccessorElement && element.isSetter) { - usedElements.addMember(element.correspondingGetter); - usedElements.addReadMember(element.correspondingGetter); + void _addMemberAndCorrespondingGetter(Element2 element) { + if (element is SetterElement) { + usedElements.addMember(element.correspondingGetter2); + usedElements.addReadMember(element.correspondingGetter2); } else { usedElements.addReadMember(element); } @@ -361,21 +363,21 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { void _addParametersForArguments(ArgumentList argumentList) { for (var argument in argumentList.arguments) { - var parameter = argument.staticParameterElement; + var parameter = argument.correspondingParameter; usedElements.addElement(parameter); } } /// Marks the [element] as used in the library. void _useIdentifierElement( - Element? element, { + Element2? element, { required AstNode parent, }) { if (element == null) { return; } // Check if [element] is a local element. - if (!identical(element.library, _enclosingLibrary)) { + if (!identical(element.library2, _enclosingLibrary)) { return; } // Ignore references to an element from itself. @@ -388,7 +390,7 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { // Ignore places where the element is not actually used. // TODO(scheglov): Do we need 'parent' at all? if (parent is NamedType) { - if (element is InterfaceElement) { + if (element is InterfaceElement2) { var enclosingVariableDeclaration = _enclosingVariableDeclaration; if (enclosingVariableDeclaration != null) { // If it's a field's type, it still counts as used. @@ -445,24 +447,25 @@ class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { /// Ignores parameters that don't have a corresponding pair. // TODO(scheglov): There might be a better place for this function. static void _matchParameters( - List firstList, - List secondList, - void Function(ParameterElement first, ParameterElement second) f, + List firstList, + List secondList, + void Function(FormalParameterElement first, FormalParameterElement second) + f, ) { - Map? firstNamed; - Map? secondNamed; - var firstPositional = []; - var secondPositional = []; + Map? firstNamed; + Map? secondNamed; + var firstPositional = []; + var secondPositional = []; for (var element in firstList) { if (element.isNamed) { - (firstNamed ??= {})[element.name] = element; + (firstNamed ??= {})[element.name3!] = element; } else { firstPositional.add(element); } } for (var element in secondList) { if (element.isNamed) { - (secondNamed ??= {})[element.name] = element; + (secondNamed ??= {})[element.name3!] = element; } else { secondPositional.add(element); } @@ -509,24 +512,24 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { /// The current set of pattern variable elements, used to track whether _all_ /// within a [PatternVariableDeclaration] are used. - List? _patternVariableElements; + List? _patternVariableElements; /// Create a new instance of the [UnusedLocalElementsVerifier]. UnusedLocalElementsVerifier(this._errorListener, this._usedElements, - this._inheritanceManager, LibraryElement library) - : _libraryUri = library.source.uri, + this._inheritanceManager, LibraryElement2 library) + : _libraryUri = library.uri, _wildCardVariablesEnabled = library.featureSet.isEnabled(Feature.wildcard_variables); @override void visitCatchClauseParameter(CatchClauseParameter node) { - _visitLocalVariableElement(node.declaredElement!); + _visitLocalVariableElement(node.declaredElement2!); super.visitCatchClauseParameter(node); } @override void visitClassDeclaration(ClassDeclaration node) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredFragment!.element; _visitClassElement(declaredElement); super.visitClassDeclaration(node); @@ -535,7 +538,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitConstructorDeclaration(ConstructorDeclaration node) { if (node.name != null) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredFragment!.element; _visitConstructorElement(declaredElement); } @@ -544,7 +547,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitDeclaredIdentifier(DeclaredIdentifier node) { - _visitLocalVariableElement(node.declaredElement!); + _visitLocalVariableElement(node.declaredFragment!.element); super.visitDeclaredIdentifier(node); } @@ -552,7 +555,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { void visitDeclaredVariablePattern( covariant DeclaredVariablePatternImpl node, ) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredElement2!; if (!declaredElement.isDuplicate) { var patternVariableElements = _patternVariableElements; if (patternVariableElements != null) { @@ -567,7 +570,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitEnumConstantDeclaration(EnumConstantDeclaration node) { - var declaredElement = node.declaredElement as FieldElement; + var declaredElement = node.declaredFragment!.element; _visitFieldElement(declaredElement); super.visitEnumConstantDeclaration(node); @@ -575,7 +578,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitEnumDeclaration(EnumDeclaration node) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredFragment!.element; _visitClassElement(declaredElement); super.visitEnumDeclaration(node); @@ -583,7 +586,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredFragment!.element; _visitClassElement(declaredElement); super.visitExtensionTypeDeclaration(node); @@ -593,7 +596,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { void visitFieldDeclaration(FieldDeclaration node) { for (var field in node.fields.variables) { _visitFieldElement( - field.declaredElement as FieldElement, + field.declaredFragment!.element as FieldElement2, ); } @@ -602,8 +605,9 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitFormalParameterList(FormalParameterList node) { - for (var element in node.parameterElements) { - if (!_isUsedElement(element!)) { + for (var fragment in node.parameterFragments) { + var element = fragment!.element; + if (!_isUsedElement(element)) { _reportErrorForElement(WarningCode.UNUSED_ELEMENT_PARAMETER, element, [element.displayName]); } @@ -615,7 +619,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { void visitForPartsWithDeclarations(ForPartsWithDeclarations node) { for (var variable in node.variables.variables) { _visitLocalVariableElement( - variable.declaredElement as LocalVariableElement, + variable.declaredElement2 as LocalVariableElement2, ); } @@ -624,11 +628,13 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitFunctionDeclaration(FunctionDeclaration node) { - var declaredElement = node.declaredElement; - if (declaredElement is FunctionElement) { - _visitFunctionElement(declaredElement); - } else if (declaredElement is PropertyAccessorElement) { + var declaredElement = node.declaredFragment?.element; + if (declaredElement is LocalFunctionElement) { + _visitLocalFunctionElement(declaredElement); + } else if (declaredElement is PropertyAccessorElement2) { _visitPropertyAccessorElement(declaredElement); + } else if (declaredElement is TopLevelFunctionElement) { + _visitTopLevelFunctionElement(declaredElement); } super.visitFunctionDeclaration(node); @@ -636,7 +642,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitFunctionTypeAlias(FunctionTypeAlias node) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredFragment!.element; _visitTypeAliasElement(declaredElement); super.visitFunctionTypeAlias(node); @@ -644,7 +650,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitGenericTypeAlias(GenericTypeAlias node) { - var declaredElement = node.declaredElement as TypeAliasElement; + var declaredElement = node.declaredFragment?.element as TypeAliasElement2; _visitTypeAliasElement(declaredElement); super.visitGenericTypeAlias(node); @@ -652,10 +658,10 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitMethodDeclaration(MethodDeclaration node) { - var declaredElement = node.declaredElement; - if (declaredElement is MethodElement) { + var declaredElement = node.declaredFragment?.element; + if (declaredElement is MethodElement2) { _visitMethodElement(declaredElement); - } else if (declaredElement is PropertyAccessorElement) { + } else if (declaredElement is PropertyAccessorElement2) { _visitPropertyAccessorElement(declaredElement); } @@ -664,7 +670,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitMixinDeclaration(MixinDeclaration node) { - var declaredElement = node.declaredElement!; + var declaredElement = node.declaredFragment!.element; _visitClassElement(declaredElement); super.visitMixinDeclaration(node); @@ -676,7 +682,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { var patternVariableElements = _patternVariableElements = []; try { super.visitPatternVariableDeclaration(node); - var elementsToReport = []; + var elementsToReport = []; for (var element in patternVariableElements) { var isUsed = _usedElements.elements.contains(element); // Don't report any of the declared variables as unused, if any of them @@ -702,24 +708,24 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { @override void visitSimpleIdentifier(SimpleIdentifier node) { if (node.inDeclarationContext()) { - var element = node.staticElement; - if (element is ConstructorElement) { + var element = node.element; + if (element is ConstructorElement2) { _visitConstructorElement(element); - } else if (element is FieldElement) { + } else if (element is FieldElement2) { _visitFieldElement(element); - } else if (element is FunctionElement) { - _visitFunctionElement(element); - } else if (element is InterfaceElement) { + } else if (element is LocalFunctionElement) { + _visitLocalFunctionElement(element); + } else if (element is InterfaceElement2) { _visitClassElement(element); - } else if (element is LocalVariableElement) { + } else if (element is LocalVariableElement2) { _visitLocalVariableElement(element); - } else if (element is MethodElement) { + } else if (element is MethodElement2) { _visitMethodElement(element); - } else if (element is PropertyAccessorElement) { + } else if (element is PropertyAccessorElement2) { _visitPropertyAccessorElement(element); - } else if (element is TopLevelVariableElement) { + } else if (element is TopLevelVariableElement2) { _visitTopLevelVariableElement(element); - } else if (element is TypeAliasElement) { + } else if (element is TypeAliasElement2) { _visitTypeAliasElement(element); } } @@ -729,7 +735,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { for (var variable in node.variables.variables) { _visitTopLevelVariableElement( - variable.declaredElement as TopLevelVariableElement, + variable.declaredFragment?.element as TopLevelVariableElement2, ); } @@ -740,7 +746,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { void visitVariableDeclarationStatement(VariableDeclarationStatement node) { for (var variable in node.variables.variables) { _visitLocalVariableElement( - variable.declaredElement as LocalVariableElement, + variable.declaredElement2 as LocalVariableElement2, ); } @@ -749,34 +755,36 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { /// Returns the parameter element, if any, that corresponds to the given /// parameter in the overridden element. - ParameterElement? _getCorrespondingParameter(ParameterElement parameter, - ExecutableElement overridden, ExecutableElement enclosingElement) { - ParameterElement? correspondingParameter; + FormalParameterElement? _getCorrespondingParameter( + FormalParameterElement parameter, + ExecutableElement2 overridden, + ExecutableElement2 enclosingElement) { + FormalParameterElement? correspondingParameter; if (parameter.isNamed) { - correspondingParameter = overridden.parameters - .firstWhereOrNull((p) => p.name == parameter.name); + correspondingParameter = overridden.formalParameters + .firstWhereOrNull((p) => p.name3 == parameter.name3); } else { var parameterIndex = 0; - var parameterCount = enclosingElement.parameters.length; + var parameterCount = enclosingElement.formalParameters.length; while (parameterIndex < parameterCount) { - if (enclosingElement.parameters[parameterIndex] == parameter) { + if (enclosingElement.formalParameters[parameterIndex] == parameter) { break; } parameterIndex++; } - if (overridden.parameters.length <= parameterIndex) { + if (overridden.formalParameters.length <= parameterIndex) { // Something is wrong with the overridden element. Ignore it. return null; } - correspondingParameter = overridden.parameters[parameterIndex]; + correspondingParameter = overridden.formalParameters[parameterIndex]; } return correspondingParameter; } /// Returns whether the name of [element] should be treated as a wildcard. - bool _isNamedWildcard(LocalElement element) { + bool _isNamedWildcard(LocalElement2 element) { // TODO(pq): ask the element once implemented. - var name = element.name; + var name = element.name3; if (name == null) return false; var length = name.length; @@ -792,32 +800,32 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { return true; } - bool _isPrivateClassOrExtension(Element element) => - (element is InterfaceElement || element is ExtensionElement) && + bool _isPrivateClassOrExtension(Element2 element) => + (element is InterfaceElement2 || element is ExtensionElement2) && element.isPrivate; /// Returns whether [element] is accessible outside of the library in which /// it is declared. - bool _isPubliclyAccessible(ExecutableElement element) { + bool _isPubliclyAccessible(ExecutableElement2 element) { if (element.isPrivate) { return false; } - var enclosingElement = element.enclosingElement3; + var enclosingElement = element.enclosingElement2; - if (enclosingElement is EnumElement) { - if (element is ConstructorElement && element.isGenerative) { + if (enclosingElement is EnumElement2) { + if (element is ConstructorElement2 && element.isGenerative) { return false; } } - if (enclosingElement is InterfaceElement) { + if (enclosingElement is InterfaceElement2) { if (enclosingElement.isPrivate) { - if (element.isStatic || element is ConstructorElement) { + if (element.isStatic || element is ConstructorElement2) { return false; } } } - if (enclosingElement is ExtensionElement) { + if (enclosingElement is ExtensionElement2) { return enclosingElement.isPublic; } @@ -826,11 +834,11 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { /// Returns whether [element] is a private element which is read somewhere in /// the library. - bool _isReadMember(Element element) { + bool _isReadMember(Element2 element) { bool elementIsStaticVariable = - element is VariableElement && element.isStatic; + element is VariableElement2 && element.isStatic; if (element.isPublic) { - if (_isPrivateClassOrExtension(element.enclosingElement3!) && + if (_isPrivateClassOrExtension(element.enclosingElement2!) && elementIsStaticVariable) { // Public static fields of private classes, mixins, and extensions are // inaccessible from outside the library in which they are declared. @@ -841,15 +849,15 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { if (element.isSynthetic) { return true; } - if (element is FieldElement) { - var getter = element.getter; + if (element is FieldElement2) { + var getter = element.getter2; if (getter == null) { return false; } element = getter; } if (_usedElements.readMembers.contains(element) || - _usedElements.unresolvedReadMembers.contains(element.name)) { + _usedElements.unresolvedReadMembers.contains(element.name3)) { return true; } @@ -859,35 +867,36 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { return _overridesUsedElement(element); } - bool _isUsedElement(Element element) { + bool _isUsedElement(Element2 element) { if (element.isSynthetic) { return true; } - if (element is LocalVariableElement || - element is FunctionElement && !element.isStatic) { + if (element is LocalVariableElement2 || + element is LocalFunctionElement && !element.isStatic) { // local variable or function - } else if (element is ParameterElement) { - var enclosingElement = element.enclosingElement3; - // Only report unused parameters of constructors, methods, and functions. - if (enclosingElement is! ConstructorElement && - enclosingElement is! FunctionElement && - enclosingElement is! MethodElement) { + } else if (element is FormalParameterElement) { + var enclosingElement = element.enclosingElement2; + // Only report unused parameters of constructors, methods, and top-level + // functions. + if (enclosingElement is! ConstructorElement2 && + enclosingElement is! MethodElement2 && + enclosingElement is! TopLevelFunctionElement) { return true; } if (!element.isOptional) { return true; } - if (enclosingElement is ConstructorElement && - enclosingElement.enclosingElement3.typeParameters.isNotEmpty) { + if (enclosingElement is ConstructorElement2 && + enclosingElement.enclosingElement2.typeParameters2.isNotEmpty) { // There is an issue matching arguments of instance creation // expressions for generic classes with parameters, so for now, // consider every parameter of a constructor of a generic class // "used". See https://github.com/dart-lang/sdk/issues/47839. return true; } - if (enclosingElement is ConstructorElement) { - var superConstructor = enclosingElement.superConstructor; + if (enclosingElement is ConstructorElement2) { + var superConstructor = enclosingElement.superConstructor2; if (superConstructor != null) { var correspondingParameter = _getCorrespondingParameter( element, superConstructor, enclosingElement); @@ -899,8 +908,8 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { } } } - if (enclosingElement is ExecutableElement) { - if (enclosingElement.typeParameters.isNotEmpty) { + if (enclosingElement is ExecutableElement2) { + if (enclosingElement.typeParameters2.isNotEmpty) { // There is an issue matching arguments of generic function // invocations with parameters, so for now, consider every parameter // of a generic function "used". See @@ -925,7 +934,7 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { return _usedElements.elements.contains(element); } - bool _isUsedMember(ExecutableElement element) { + bool _isUsedMember(ExecutableElement2 element) { if (_isPubliclyAccessible(element)) { return true; } @@ -945,36 +954,38 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { return _overridesUsedElement(element); } - Iterable _overriddenElements(Element element) { - var fragment = element.enclosingElement3; - if (fragment is InterfaceElement) { - var enclosingElement = fragment.augmented.firstFragment; - Name name = Name(_libraryUri, element.name!); - var overridden = - _inheritanceManager.getOverridden2(enclosingElement, name); - if (overridden == null) { - return []; + Iterable _overriddenElements(Element2 element) { + var enclosingElement = element.enclosingElement2; + if (enclosingElement is InterfaceElement2) { + var elementName = element.name3; + if (elementName != null) { + Name name = Name(_libraryUri, elementName); + var overridden = + _inheritanceManager.getOverridden4(enclosingElement, name); + if (overridden == null) { + return []; + } + return overridden + .map((e) => (e is ExecutableMember) ? e.baseElement : e); } - return overridden.map((e) => (e is ExecutableMember) ? e.declaration : e); - } else { - return []; } + return []; } /// Check if [element] is a class member which overrides a super class's class /// member which is used. - bool _overridesUsedElement(Element element) { - return _overriddenElements(element).any((ExecutableElement e) => - _usedElements.members.contains(e) || _overridesUsedElement(e)); + bool _overridesUsedElement(Element2 element) { + return _overriddenElements(element).any( + (e) => _usedElements.members.contains(e) || _overridesUsedElement(e)); } /// Check if [element] is a parameter of a method which overrides a super /// class's method in which the corresponding parameter is used. bool _overridesUsedParameter( - ParameterElement element, ExecutableElement enclosingElement) { + FormalParameterElement element, ExecutableElement2 enclosingElement) { var overriddenElements = _overriddenElements(enclosingElement); for (var overridden in overriddenElements) { - ParameterElement? correspondingParameter = + FormalParameterElement? correspondingParameter = _getCorrespondingParameter(element, overridden, enclosingElement); // The parameter was added in the override. if (correspondingParameter == null) { @@ -993,13 +1004,16 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { } void _reportErrorForElement( - ErrorCode errorCode, Element? element, List arguments) { + ErrorCode errorCode, Element2? element, List arguments) { if (element != null) { + var fragment = element.firstFragment; _errorListener.onError( AnalysisError.tmp( - source: element.source!, - offset: element.nameOffset, - length: element.nameLength, + source: fragment.libraryFragment!.source, + offset: fragment.nameOffset2 ?? + fragment.enclosingFragment?.nameOffset2 ?? + 0, + length: fragment.name2?.length ?? 0, errorCode: errorCode, arguments: arguments, ), @@ -1007,44 +1021,41 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { } } - void _visitClassElement(InterfaceElement element) { + void _visitClassElement(InterfaceElement2 element) { if (!_isUsedElement(element)) { _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - void _visitConstructorElement(ConstructorElement element) { + void _visitConstructorElement(ConstructorElement2 element) { // Only complain about an unused constructor if it is not the only // constructor in the class. A single unused, private constructor may serve // the purpose of preventing the class from being extended. In serving this // purpose, the constructor is "used." - if (element.enclosingElement3.constructors.length > 1 && + if (element.enclosingElement2.constructors2.length > 1 && !_isUsedMember(element)) { _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - void _visitFieldElement(FieldElement element) { + void _visitFieldElement(FieldElement2 element) { if (!_isReadMember(element)) { _reportErrorForElement( WarningCode.UNUSED_FIELD, element, [element.displayName]); } } - void _visitFunctionElement(FunctionElement element) { + void _visitLocalFunctionElement(LocalFunctionElement element) { if (!_isUsedElement(element)) { - if (_wildCardVariablesEnabled) { - if (element.isLocal && _isNamedWildcard(element)) return; - } - + if (_wildCardVariablesEnabled && _isNamedWildcard(element)) return; _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - void _visitLocalVariableElement(LocalVariableElement element) { + void _visitLocalVariableElement(LocalVariableElement2 element) { if (!_isUsedElement(element) && !_isNamedWildcard(element)) { ErrorCode errorCode; if (_usedElements.isCatchException(element)) { @@ -1058,36 +1069,45 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { } } - void _visitMethodElement(MethodElement element) { + void _visitMethodElement(MethodElement2 element) { if (!_isUsedMember(element)) { _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - void _visitPropertyAccessorElement(PropertyAccessorElement element) { + void _visitPropertyAccessorElement(PropertyAccessorElement2 element) { if (!_isUsedMember(element)) { _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - void _visitTopLevelVariableElement(TopLevelVariableElement element) { + void _visitTopLevelFunctionElement(TopLevelFunctionElement element) { if (!_isUsedElement(element)) { _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - void _visitTypeAliasElement(TypeAliasElement element) { + void _visitTopLevelVariableElement(TopLevelVariableElement2 element) { if (!_isUsedElement(element)) { _reportErrorForElement( WarningCode.UNUSED_ELEMENT, element, [element.displayName]); } } - static bool _hasPragmaVmEntryPoint(Element element) { - return element is ElementImpl && element.hasPragmaVmEntryPoint; + void _visitTypeAliasElement(TypeAliasElement2 element) { + if (!_isUsedElement(element)) { + _reportErrorForElement( + WarningCode.UNUSED_ELEMENT, element, [element.displayName]); + } + } + + static bool _hasPragmaVmEntryPoint(Element2 element) { + return element is Annotatable && + ((element as Annotatable).metadata2 as MetadataImpl) + .hasPragmaVmEntryPoint; } } @@ -1096,21 +1116,21 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor { class UsedLocalElements { /// Resolved, locally defined elements that are used or potentially can be /// used. - final HashSet elements = HashSet(); + final HashSet elements = HashSet(); /// [LocalVariableElement]s that represent exceptions in [CatchClause]s. - final HashSet catchExceptionElements = - HashSet(); + final HashSet catchExceptionElements = + HashSet(); /// [LocalVariableElement]s that represent stack traces in [CatchClause]s. - final HashSet catchStackTraceElements = - HashSet(); + final HashSet catchStackTraceElements = + HashSet(); /// Resolved class members that are referenced in the library. - final HashSet members = HashSet(); + final HashSet members = HashSet(); /// Resolved class members that are read in the library. - final HashSet readMembers = HashSet(); + final HashSet readMembers = HashSet(); /// Unresolved class members that are read in the library. final HashSet unresolvedReadMembers = HashSet(); @@ -1132,32 +1152,32 @@ class UsedLocalElements { return result; } - void addCatchException(Element? element) { - if (element is LocalVariableElement) { + void addCatchException(Element2? element) { + if (element is LocalVariableElement2) { catchExceptionElements.add(element); } } - void addCatchStackTrace(Element? element) { - if (element is LocalVariableElement) { + void addCatchStackTrace(Element2? element) { + if (element is LocalVariableElement2) { catchStackTraceElements.add(element); } } - void addElement(Element? element) { - if (element is JoinPatternVariableElementImpl) { + void addElement(Element2? element) { + if (element is JoinPatternVariableElementImpl2) { elements.addAll(element.transitiveVariables); } else if (element is ExecutableMember) { - elements.add(element.declaration); + elements.add(element.baseElement); } else if (element != null) { elements.add(element); } } - void addMember(Element? element) { + void addMember(Element2? element) { // Store un-parameterized members. if (element is ExecutableMember) { - element = element.declaration; + element = element.baseElement; } if (element != null) { @@ -1165,10 +1185,10 @@ class UsedLocalElements { } } - void addReadMember(Element? element) { + void addReadMember(Element2? element) { // Store un-parameterized members. if (element is ExecutableMember) { - element = element.declaration; + element = element.baseElement; } if (element != null) { @@ -1176,17 +1196,11 @@ class UsedLocalElements { } } - bool isCatchException(LocalVariableElement element) { + bool isCatchException(LocalVariableElement2 element) { return catchExceptionElements.contains(element); } - bool isCatchStackTrace(LocalVariableElement element) { + bool isCatchStackTrace(LocalVariableElement2 element) { return catchStackTraceElements.contains(element); } } - -extension on FunctionElement { - bool get isLocal => - enclosingElement3 is FunctionElement || - enclosingElement3 is MethodElement; -} diff --git a/pkg/analyzer/test/src/dart/resolution/instance_creation_test.dart b/pkg/analyzer/test/src/dart/resolution/instance_creation_test.dart index 8d2fd2e2bc97..7de33efebc8e 100644 --- a/pkg/analyzer/test/src/dart/resolution/instance_creation_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/instance_creation_test.dart @@ -190,7 +190,7 @@ augment class A { A(T2 value); } '''); - await assertNoErrorsInCode(r''' + await assertErrorsInCode(r''' part 'a.dart'; class A { @@ -200,7 +200,9 @@ class A { void f() { A(0); } -'''); +''', [ + error(WarningCode.UNUSED_ELEMENT, 33, 1), + ]); var node = findNode.singleInstanceCreationExpression; assertResolvedNodeText(node, r''' @@ -533,7 +535,7 @@ augment class A { A(); } '''); - await assertNoErrorsInCode(r''' + await assertErrorsInCode(r''' part 'a.dart'; class A { @@ -543,7 +545,9 @@ class A { void f() { A(); } -'''); +''', [ + error(WarningCode.UNUSED_ELEMENT, 30, 1), + ]); var node = findNode.singleInstanceCreationExpression; assertResolvedNodeText(node, r''' @@ -2048,7 +2052,7 @@ augment class A { A(T2 value); } '''); - await assertNoErrorsInCode(r''' + await assertErrorsInCode(r''' part 'a.dart'; class A { @@ -2060,7 +2064,9 @@ typedef X = A; void f() { X(0); } -'''); +''', [ + error(WarningCode.UNUSED_ELEMENT, 33, 1), + ]); var node = findNode.singleInstanceCreationExpression; assertResolvedNodeText(node, r''' @@ -2415,7 +2421,7 @@ augment class A { A(); } '''); - await assertNoErrorsInCode(r''' + await assertErrorsInCode(r''' part 'a.dart'; class A { @@ -2427,7 +2433,9 @@ typedef X = A; void f() { X(); } -'''); +''', [ + error(WarningCode.UNUSED_ELEMENT, 30, 1), + ]); var node = findNode.singleInstanceCreationExpression; assertResolvedNodeText(node, r''' diff --git a/pkg/analyzer_plugin/CHANGELOG.md b/pkg/analyzer_plugin/CHANGELOG.md index d243c8afca54..2368dbea9844 100644 --- a/pkg/analyzer_plugin/CHANGELOG.md +++ b/pkg/analyzer_plugin/CHANGELOG.md @@ -2,6 +2,8 @@ - Remove `elementName()` from `RangeFactory`. Use `fragmentName()` instead. - Breaking changes to `DartFileEditBuilder` and `DartEditBuilder`. - Breaking changes to `AnalyzerConverter`. +- Support for a plugin to send an `AnalysisStatus` notification, featuring an + `isAnalyzing` `bool` field. ## 0.12.0 - Breaking changes to `DartFileEditBuilder`: `convertFunctionFromSyncToAsync` diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html index 6c4271dce65a..d75922d57fc3 100644 --- a/pkg/analyzer_plugin/doc/api.html +++ b/pkg/analyzer_plugin/doc/api.html @@ -138,6 +138,7 @@

plugin domain

+

Requests

plugin.versionCheck
request: {
   "id": String
   "method": "plugin.versionCheck"
@@ -262,6 +263,26 @@ 

parameters:

isFatal: bool
The stack trace associated with the generation of the error, used for debugging the plugin.

+
plugin.status
notification: {
+  "event": "plugin.status"
+  "params": {
+    "analysis": optional AnalysisStatus
+  }
+}
+

+ Reports the current status of the plugin. Parameters are omitted if there + has been no change in the status represented by that parameter. +

+

+ Only used for "new" analyzer plugins. Legacy plugins should not use + this type. +

+ +

parameters:

analysis: AnalysisStatus (optional)
+ +

+ The current status of analysis (whether analysis is being performed). +

analysis domain

@@ -844,6 +865,7 @@

Types

+
AddContentOverlay: object

A directive to begin overlaying the contents of a file. The supplied @@ -957,7 +979,19 @@

Types

related to a specific list of files.

-
FOLDING
HIGHLIGHTS
NAVIGATION
OCCURRENCES
OUTLINE
ChangeContentOverlay: object
+
FOLDING
HIGHLIGHTS
NAVIGATION
OCCURRENCES
OUTLINE
AnalysisStatus: object
+

+ An indication of the current state of analysis. +

+

+ Only used for "new" analyzer plugins. Legacy plugins should not use + this type. +

+ +
isAnalyzing: bool
+ +

True if analysis is currently being performed.

+
ChangeContentOverlay: object

A directive to modify an existing file content overlay. One or more ranges of text are deleted from the old file content overlay and replaced with @@ -2379,6 +2413,6 @@

Feedback:

offset: int

Index

-

Domains

plugin ()

Requests
Notifications

analysis ()

completion ()

edit ()

Types ()

Refactorings ()

+

Domains

plugin ()

Requests
Notifications

analysis ()

completion ()

edit ()

Types ()

Refactorings ()

diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_constants.dart b/pkg/analyzer_plugin/lib/protocol/protocol_constants.dart index 51857ecf02ee..f6ab4f800fb3 100644 --- a/pkg/analyzer_plugin/lib/protocol/protocol_constants.dart +++ b/pkg/analyzer_plugin/lib/protocol/protocol_constants.dart @@ -84,6 +84,8 @@ const String PLUGIN_NOTIFICATION_ERROR = 'plugin.error'; const String PLUGIN_NOTIFICATION_ERROR_IS_FATAL = 'isFatal'; const String PLUGIN_NOTIFICATION_ERROR_MESSAGE = 'message'; const String PLUGIN_NOTIFICATION_ERROR_STACK_TRACE = 'stackTrace'; +const String PLUGIN_NOTIFICATION_STATUS = 'plugin.status'; +const String PLUGIN_NOTIFICATION_STATUS_ANALYSIS = 'analysis'; const String PLUGIN_REQUEST_SHUTDOWN = 'plugin.shutdown'; const String PLUGIN_REQUEST_VERSION_CHECK = 'plugin.versionCheck'; const String PLUGIN_REQUEST_VERSION_CHECK_BYTE_STORE_PATH = 'byteStorePath'; diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_generated.dart b/pkg/analyzer_plugin/lib/protocol/protocol_generated.dart index aa3e59b3dfda..4534c27e8e40 100644 --- a/pkg/analyzer_plugin/lib/protocol/protocol_generated.dart +++ b/pkg/analyzer_plugin/lib/protocol/protocol_generated.dart @@ -1337,6 +1337,59 @@ class AnalysisSetSubscriptionsResult implements ResponseResult { int get hashCode => 218088493; } +/// AnalysisStatus +/// +/// { +/// "isAnalyzing": bool +/// } +/// +/// Clients may not extend, implement or mix-in this class. +class AnalysisStatus implements HasToJson { + /// True if analysis is currently being performed. + bool isAnalyzing; + + AnalysisStatus(this.isAnalyzing); + + factory AnalysisStatus.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object? json, + {ClientUriConverter? clientUriConverter}) { + json ??= {}; + if (json is Map) { + bool isAnalyzing; + if (json.containsKey('isAnalyzing')) { + isAnalyzing = jsonDecoder.decodeBool( + '$jsonPath.isAnalyzing', json['isAnalyzing']); + } else { + throw jsonDecoder.mismatch(jsonPath, 'isAnalyzing'); + } + return AnalysisStatus(isAnalyzing); + } else { + throw jsonDecoder.mismatch(jsonPath, 'AnalysisStatus', json); + } + } + + @override + Map toJson({ClientUriConverter? clientUriConverter}) { + var result = {}; + result['isAnalyzing'] = isAnalyzing; + return result; + } + + @override + String toString() => json.encode(toJson(clientUriConverter: null)); + + @override + bool operator ==(other) { + if (other is AnalysisStatus) { + return isAnalyzing == other.isAnalyzing; + } + return false; + } + + @override + int get hashCode => isAnalyzing.hashCode; +} + /// analysis.updateContent params /// /// { @@ -3623,6 +3676,74 @@ class PluginShutdownResult implements ResponseResult { int get hashCode => 9389109; } +/// plugin.status params +/// +/// { +/// "analysis": optional AnalysisStatus +/// } +/// +/// Clients may not extend, implement or mix-in this class. +class PluginStatusParams implements HasToJson { + /// The current status of analysis (whether analysis is being performed). + AnalysisStatus? analysis; + + PluginStatusParams({this.analysis}); + + factory PluginStatusParams.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object? json, + {ClientUriConverter? clientUriConverter}) { + json ??= {}; + if (json is Map) { + AnalysisStatus? analysis; + if (json.containsKey('analysis')) { + analysis = AnalysisStatus.fromJson( + jsonDecoder, '$jsonPath.analysis', json['analysis'], + clientUriConverter: clientUriConverter); + } + return PluginStatusParams(analysis: analysis); + } else { + throw jsonDecoder.mismatch(jsonPath, 'plugin.status params', json); + } + } + + factory PluginStatusParams.fromNotification(Notification notification, + {ClientUriConverter? clientUriConverter}) { + return PluginStatusParams.fromJson( + ResponseDecoder(null), 'params', notification.params, + clientUriConverter: clientUriConverter); + } + + @override + Map toJson({ClientUriConverter? clientUriConverter}) { + var result = {}; + var analysis = this.analysis; + if (analysis != null) { + result['analysis'] = + analysis.toJson(clientUriConverter: clientUriConverter); + } + return result; + } + + Notification toNotification({ClientUriConverter? clientUriConverter}) { + return Notification( + 'plugin.status', toJson(clientUriConverter: clientUriConverter)); + } + + @override + String toString() => json.encode(toJson(clientUriConverter: null)); + + @override + bool operator ==(other) { + if (other is PluginStatusParams) { + return analysis == other.analysis; + } + return false; + } + + @override + int get hashCode => analysis.hashCode; +} + /// plugin.versionCheck params /// /// { diff --git a/pkg/analyzer_plugin/test/integration/support/integration_test_methods.dart b/pkg/analyzer_plugin/test/integration/support/integration_test_methods.dart index a8348e836c22..5189e4ea432d 100644 --- a/pkg/analyzer_plugin/test/integration/support/integration_test_methods.dart +++ b/pkg/analyzer_plugin/test/integration/support/integration_test_methods.dart @@ -119,6 +119,22 @@ abstract class IntegrationTestMixin { /// Stream controller for [onPluginError]. late StreamController _onPluginError; + /// Reports the current status of the plugin. Parameters are omitted if there + /// has been no change in the status represented by that parameter. + /// + /// Only used for "new" analyzer plugins. Legacy plugins should not use this + /// type. + /// + /// Parameters + /// + /// analysis: AnalysisStatus (optional) + /// + /// The current status of analysis (whether analysis is being performed). + late Stream onPluginStatus; + + /// Stream controller for [onPluginStatus]. + late StreamController _onPluginStatus; + /// Return the navigation information associated with the given region of the /// given file. If the navigation information for the given file has not yet /// been computed, or the most recently computed navigation information for @@ -661,6 +677,8 @@ abstract class IntegrationTestMixin { void initializeInttestMixin() { _onPluginError = StreamController(sync: true); onPluginError = _onPluginError.stream.asBroadcastStream(); + _onPluginStatus = StreamController(sync: true); + onPluginStatus = _onPluginStatus.stream.asBroadcastStream(); _onAnalysisErrors = StreamController(sync: true); onAnalysisErrors = _onAnalysisErrors.stream.asBroadcastStream(); _onAnalysisFolding = StreamController(sync: true); @@ -688,6 +706,11 @@ abstract class IntegrationTestMixin { _onPluginError .add(PluginErrorParams.fromJson(decoder, 'params', params)); break; + case 'plugin.status': + outOfTestExpect(params, isPluginStatusParams); + _onPluginStatus + .add(PluginStatusParams.fromJson(decoder, 'params', params)); + break; case 'analysis.errors': outOfTestExpect(params, isAnalysisErrorsParams); _onAnalysisErrors diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart index b6775a84de53..efbc05645d21 100644 --- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart +++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart @@ -106,6 +106,14 @@ final Matcher isAnalysisErrorType = MatchesEnum('AnalysisErrorType', [ final Matcher isAnalysisService = MatchesEnum('AnalysisService', ['FOLDING', 'HIGHLIGHTS', 'NAVIGATION', 'OCCURRENCES', 'OUTLINE']); +/// AnalysisStatus +/// +/// { +/// "isAnalyzing": bool +/// } +final Matcher isAnalysisStatus = LazyMatcher( + () => MatchesJsonObject('AnalysisStatus', {'isAnalyzing': isBool})); + /// ChangeContentOverlay /// /// { @@ -1324,6 +1332,15 @@ final Matcher isPluginShutdownParams = isNull; /// plugin.shutdown result final Matcher isPluginShutdownResult = isNull; +/// plugin.status params +/// +/// { +/// "analysis": optional AnalysisStatus +/// } +final Matcher isPluginStatusParams = LazyMatcher(() => MatchesJsonObject( + 'plugin.status params', null, + optionalFields: {'analysis': isAnalysisStatus})); + /// plugin.versionCheck params /// /// { diff --git a/pkg/analyzer_plugin/tool/spec/plugin_spec.html b/pkg/analyzer_plugin/tool/spec/plugin_spec.html index 9984810a4c40..f5eb7f67a37c 100644 --- a/pkg/analyzer_plugin/tool/spec/plugin_spec.html +++ b/pkg/analyzer_plugin/tool/spec/plugin_spec.html @@ -144,6 +144,24 @@

Overview

+ +

+ Reports the current status of the plugin. Parameters are omitted if there + has been no change in the status represented by that parameter. +

+

+ Only used for "new" analyzer plugins. Legacy plugins should not use + this type. +

+ + + AnalysisStatus +

+ The current status of analysis (whether analysis is being performed). +

+
+
+

@@ -873,6 +891,28 @@

Types

OUTLINE + +

+ An indication of the current state of analysis. +

+

+ Only used for "new" analyzer plugins. Legacy plugins should not use + this type. +

+ + + bool +

True if analysis is currently being performed.

+
+ +
+

A description of an analysis context. diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart index 015f65003ee8..84bee0b49e81 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart @@ -103,8 +103,8 @@ class SsaOptimizerTask extends CompilerTask { measure(() { List phases = [ - // Run trivial instruction simplification first to optimize - // some patterns useful for type conversion. + // Run trivial instruction simplification first to optimize some + // patterns useful for type conversion. SsaInstructionSimplifier( globalInferenceResults, _options, @@ -113,6 +113,7 @@ class SsaOptimizerTask extends CompilerTask { registry, log, metrics, + beforeTypePropagation: true, ), SsaTypeConversionInserter(closedWorld), SsaRedundantPhiEliminator(), @@ -123,8 +124,7 @@ class SsaOptimizerTask extends CompilerTask { closedWorld, log, ), - // After type propagation, more instructions can be - // simplified. + // After type propagation, more instructions can be simplified. SsaInstructionSimplifier( globalInferenceResults, _options, @@ -309,6 +309,13 @@ class SsaInstructionSimplifier extends HBaseVisitor final CodegenRegistry _registry; final OptimizationTestLog? _log; final SsaMetrics _metrics; + + /// Most simplifications become enabled when the types are refined by type + /// propagation. Some simplifications remove code that helps type progagation + /// produce a better result. These simplifications are inhibited when + /// [beforeTypePropagation] is `true` to ensure they are seeing the propagated + /// types. + final bool beforeTypePropagation; late final HGraph _graph; SsaInstructionSimplifier( @@ -318,8 +325,9 @@ class SsaInstructionSimplifier extends HBaseVisitor this._typeRecipeDomain, this._registry, this._log, - this._metrics, - ); + this._metrics, { + this.beforeTypePropagation = false, + }); JCommonElements get commonElements => _closedWorld.commonElements; @@ -418,8 +426,6 @@ class SsaInstructionSimplifier extends HBaseVisitor // Simplify some CFG diamonds to equivalent expressions. void simplifyPhis(HBasicBlock block) { - if (block.predecessors.length != 2) return; - // Do 'statement' simplifications first, as they might reduce the number of // phis to one, enabling an 'expression' simplification. var phi = block.phis.firstPhi; @@ -429,6 +435,7 @@ class SsaInstructionSimplifier extends HBaseVisitor phi = next; } + if (block.predecessors.length != 2) return; phi = block.phis.firstPhi; if (phi != null && phi.next == null) { simplifyExpressionPhi(block, phi); @@ -438,6 +445,10 @@ class SsaInstructionSimplifier extends HBaseVisitor /// Simplify a single phi when there are possibly other phis (i.e. the result /// might not be an expression). void simplifyStatementPhi(HBasicBlock block, HPhi phi) { + if (simplifyStatementPhiToCommonInput(block, phi)) return; + + if (block.predecessors.length != 2) return; + HBasicBlock dominator = block.dominator!; // Extract the controlling condition. @@ -478,6 +489,52 @@ class SsaInstructionSimplifier extends HBaseVisitor } } + bool simplifyStatementPhiToCommonInput(HBasicBlock block, HPhi phi) { + // Replace phis that produce the same value on all arms. The test(s) for + // control flow often results in a refinement instruction (HTypeKnown), so + // we recognize that, allowing, e.g., + // + // condition ? HTypeKnown(x) : x --> x + // condition ? x : HTypeKnown(x) --> x + // + // We don't remove loop phis here. SsaRedundantPhiEliminator will eliminate + // redundant phis without HTypeKnown refinements, including loop phis. + + // There may be control flow that exits early, leaving refinements that + // cause the type of the phi to be stronger than the source. Don't attempt + // this simplification until the type of the phi is calculated. + if (beforeTypePropagation) return false; + + HBasicBlock dominator = block.dominator!; + + /// Find the input, skipping refinements that do not dominate the condition, + /// e.g., skipping refinements in the arm of the if-then-else. + HInstruction? dominatingRefinementInput(HInstruction input) { + while (true) { + if (input.block!.dominates(dominator)) return input; + if (input is! HTypeKnown) return null; + input = input.checkedInput; + } + } + + final commonInput = dominatingRefinementInput(phi.inputs.first); + if (commonInput == null) return false; + + for (int i = 1; i < phi.inputs.length; i++) { + final next = dominatingRefinementInput(phi.inputs[i]); + if (!identical(next, commonInput)) return false; + } + + HTypeKnown replacement = HTypeKnown.pinned( + phi.instructionType, + commonInput, + ); + block.addBefore(block.first, replacement); + block.rewrite(phi, replacement); + block.removePhi(phi); + return true; + } + /// Simplify some CFG diamonds to equivalent expressions. void simplifyExpressionPhi(HBasicBlock block, HPhi phi) { // Is [block] the join point for a simple diamond? diff --git a/pkg/compiler/test/codegen/data/partial_phi_redundancy.dart b/pkg/compiler/test/codegen/data/partial_phi_redundancy.dart new file mode 100644 index 000000000000..b743c866d01d --- /dev/null +++ b/pkg/compiler/test/codegen/data/partial_phi_redundancy.dart @@ -0,0 +1,33 @@ +// Copyright (c) 2025, 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. + +@pragma('dart2js:never-inline') +/*member: foo1:function(x) { + if (Date.now() > 0) { + if (typeof x != "string") + return "bad1"; + } else if (typeof x != "string") + return "bad2"; + return x; +}*/ +String foo1(Object x) { + final Object y; + if (DateTime.now().millisecondsSinceEpoch > 0) { + if (x is! String) return 'bad1'; + y = x; + } else { + if (x is! String) return 'bad2'; + y = x; + } + // The phi for y has refinements to String on both branches, so the return + // should not need stringification. + return '$y'; +} + +/*member: main:ignore*/ +main() { + print(foo1('a')); + print(foo1('b')); + print(foo1(123)); +} diff --git a/pkg/dtd_impl/dtd_protocol.md b/pkg/dtd_impl/dtd_protocol.md index 34e10eb3a692..771100f8e0a2 100644 --- a/pkg/dtd_impl/dtd_protocol.md +++ b/pkg/dtd_impl/dtd_protocol.md @@ -905,7 +905,7 @@ code | message | meaning 142 | Permission denied | Permission has been denied for the requested action. 143 | File scheme expected on uri | The uri parameter should be passed as a uri with a file scheme. - Success Responses +### Success Responses Methods that respond with `Success` do so with the following RPC. diff --git a/tools/VERSION b/tools/VERSION index b4401c3e81fd..d1674f3703f3 100644 --- a/tools/VERSION +++ b/tools/VERSION @@ -27,5 +27,5 @@ CHANNEL dev MAJOR 3 MINOR 8 PATCH 0 -PRERELEASE 1 +PRERELEASE 2 PRERELEASE_PATCH 0