diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart index aa05db7164c..77e6194b6fc 100644 --- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart +++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart @@ -26,6 +26,7 @@ import 'package:analyzer/src/error/deprecated_functionality_verifier.dart'; import 'package:analyzer/src/error/deprecated_member_use_verifier.dart'; import 'package:analyzer/src/error/do_not_submit_member_use_verifier.dart'; import 'package:analyzer/src/error/doc_comment_verifier.dart'; +import 'package:analyzer/src/error/element_usage_detector.dart'; import 'package:analyzer/src/error/element_usage_frontier_detector.dart'; import 'package:analyzer/src/error/error_handler_verifier.dart'; import 'package:analyzer/src/error/experimental_member_use_verifier.dart'; @@ -66,7 +67,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { final DeprecatedFunctionalityVerifier _deprecatedFunctionalityVerifier; - final List _elementUsageFrontierDetectors; + final ElementUsageFrontierDetector _elementUsageFrontierDetector; final ErrorHandlerVerifier _errorHandlerVerifier; @@ -117,29 +118,29 @@ class BestPracticesVerifier extends RecursiveAstVisitor { _diagnosticReporter, _currentLibrary, ), - _elementUsageFrontierDetectors = [ - ElementUsageFrontierDetector( - workspacePackage: workspacePackage, - elementUsageSet: const DeprecatedElementUsageSet(), - elementUsageReporter: DeprecatedElementUsageReporter( - diagnosticReporter: _diagnosticReporter, + _elementUsageFrontierDetector = ElementUsageFrontierDetector( + workspacePackage: workspacePackage, + usagesAndReporters: [ + UsageSetAndReporter( + const DeprecatedElementUsageSet(), + DeprecatedElementUsageReporter( + diagnosticReporter: _diagnosticReporter, + ), ), - ), - ElementUsageFrontierDetector( - workspacePackage: workspacePackage, - elementUsageSet: const ExperimentalElementUsageSet(), - elementUsageReporter: ExperimentalElementUsageReporter( - diagnosticReporter: _diagnosticReporter, + UsageSetAndReporter( + const ExperimentalElementUsageSet(), + ExperimentalElementUsageReporter( + diagnosticReporter: _diagnosticReporter, + ), ), - ), - ElementUsageFrontierDetector( - workspacePackage: workspacePackage, - elementUsageSet: const DoNotSubmitElementUsageSet(), - elementUsageReporter: DoNotSubmitElementUsageReporter( - diagnosticReporter: _diagnosticReporter, + UsageSetAndReporter( + const DoNotSubmitElementUsageSet(), + DoNotSubmitElementUsageReporter( + diagnosticReporter: _diagnosticReporter, + ), ), - ), - ], + ], + ), _errorHandlerVerifier = ErrorHandlerVerifier( _diagnosticReporter, typeProvider, @@ -159,9 +160,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { ), _widgetPreviewVerifier = WidgetPreviewVerifier(_diagnosticReporter), _workspacePackage = workspacePackage { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(_currentLibrary); - } + _elementUsageFrontierDetector.pushElement(_currentLibrary); _inDoNotStoreMember = _currentLibrary.metadata.hasDoNotStore; } @@ -188,17 +187,13 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitAssignmentExpression(AssignmentExpression node) { - for (var v in _elementUsageFrontierDetectors) { - v.assignmentExpression(node); - } + _elementUsageFrontierDetector.assignmentExpression(node); super.visitAssignmentExpression(node); } @override void visitBinaryExpression(BinaryExpression node) { - for (var v in _elementUsageFrontierDetectors) { - v.binaryExpression(node); - } + _elementUsageFrontierDetector.binaryExpression(node); _checkForInvariantNanComparison(node); _checkForInvariantNullComparison(node); _invalidAccessVerifier.verifyBinary(node); @@ -233,9 +228,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { bool wasInDoNotStoreMember = _inDoNotStoreMember; _deprecatedFunctionalityVerifier.classDeclaration(node); - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(element); - } + _elementUsageFrontierDetector.pushElement(element); if (element.metadata.hasDoNotStore) { _inDoNotStoreMember = true; } @@ -250,9 +243,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { } finally { _enclosingClass = null; _invalidAccessVerifier._enclosingClass = null; - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); _inDoNotStoreMember = wasInDoNotStoreMember; } } @@ -262,16 +253,12 @@ class BestPracticesVerifier extends RecursiveAstVisitor { _immutableVerifier.checkDeclaration(node, nameToken: node.name); _checkForInvalidSealedSuperclass(node); _deprecatedFunctionalityVerifier.classTypeAlias(node); - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); try { super.visitClassTypeAlias(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @@ -314,29 +301,21 @@ class BestPracticesVerifier extends RecursiveAstVisitor { initializers: node.initializers, ); var element = node.declaredFragment!.element; - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(element); - } - for (var v in _elementUsageFrontierDetectors) { - v.constructorDeclaration(node); - } + _elementUsageFrontierDetector.pushElement(element); + _elementUsageFrontierDetector.constructorDeclaration(node); _deprecatedFunctionalityVerifier.constructorDeclaration(node); try { super.visitConstructorDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitConstructorName(ConstructorName node) { - for (var v in _elementUsageFrontierDetectors) { - v.constructorName(node); - } + _elementUsageFrontierDetector.constructorName(node); _deprecatedFunctionalityVerifier.constructorName(node); super.visitConstructorName(node); } @@ -359,20 +338,13 @@ class BestPracticesVerifier extends RecursiveAstVisitor { ); } } - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } - - for (var v in _elementUsageFrontierDetectors) { - v.formalParameter(node); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); + _elementUsageFrontierDetector.formalParameter(node); try { super.visitDefaultFormalParameter(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @@ -381,9 +353,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { DotShorthandConstructorInvocation node, ) { _deprecatedFunctionalityVerifier.dotShorthandConstructorInvocation(node); - for (var v in _elementUsageFrontierDetectors) { - v.dotShorthandConstructorInvocation(node); - } + _elementUsageFrontierDetector.dotShorthandConstructorInvocation(node); _checkForLiteralConstructorUseInDotShorthand(node); super.visitDotShorthandConstructorInvocation(node); } @@ -391,41 +361,31 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitDotShorthandInvocation(DotShorthandInvocation node) { _deprecatedFunctionalityVerifier.dotShorthandInvocation(node); - for (var v in _elementUsageFrontierDetectors) { - v.dotShorthandInvocation(node); - } + _elementUsageFrontierDetector.dotShorthandInvocation(node); super.visitDotShorthandInvocation(node); } @override void visitDotShorthandPropertyAccess(DotShorthandPropertyAccess node) { - for (var v in _elementUsageFrontierDetectors) { - v.dotShorthandPropertyAccess(node); - } + _elementUsageFrontierDetector.dotShorthandPropertyAccess(node); super.visitDotShorthandPropertyAccess(node); } @override void visitEnumDeclaration(EnumDeclaration node) { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); _deprecatedFunctionalityVerifier.enumDeclaration(node); try { super.visitEnumDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitExportDirective(covariant ExportDirectiveImpl node) { - for (var v in _elementUsageFrontierDetectors) { - v.exportDirective(node); - } + _elementUsageFrontierDetector.exportDirective(node); _checkForInternalExport(node); super.visitExportDirective(node); } @@ -440,47 +400,35 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitExtensionDeclaration(ExtensionDeclaration node) { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); try { super.visitExtensionDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitExtensionOverride(ExtensionOverride node) { - for (var v in _elementUsageFrontierDetectors) { - v.extensionOverride(node); - } + _elementUsageFrontierDetector.extensionOverride(node); super.visitExtensionOverride(node); } @override void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); try { super.visitExtensionTypeDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitFieldDeclaration(FieldDeclaration node) { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.firstVariableElement); - } + _elementUsageFrontierDetector.pushElement(node.firstVariableElement); try { super.visitFieldDeclaration(node); @@ -522,9 +470,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { } } } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @@ -538,9 +484,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { void visitFunctionDeclaration(FunctionDeclaration node) { bool wasInDoNotStoreMember = _inDoNotStoreMember; var element = node.declaredFragment!.element; - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(element); - } + _elementUsageFrontierDetector.pushElement(element); if (element.metadata.hasDoNotStore) { _inDoNotStoreMember = true; } @@ -559,9 +503,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { ); super.visitFunctionDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); _inDoNotStoreMember = wasInDoNotStoreMember; } } @@ -585,9 +527,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { - for (var v in _elementUsageFrontierDetectors) { - v.functionExpressionInvocation(node); - } + _elementUsageFrontierDetector.functionExpressionInvocation(node); super.visitFunctionExpressionInvocation(node); } @@ -595,16 +535,12 @@ class BestPracticesVerifier extends RecursiveAstVisitor { void visitFunctionTypeAlias(FunctionTypeAlias node) { _checkStrictInferenceReturnType(node.returnType, node, node.name.lexeme); _checkStrictInferenceInParameters(node.parameters); - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); try { super.visitFunctionTypeAlias(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @@ -634,24 +570,18 @@ class BestPracticesVerifier extends RecursiveAstVisitor { node.name.lexeme, ); } - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); try { super.visitGenericTypeAlias(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitImportDirective(ImportDirective node) { - for (var v in _elementUsageFrontierDetectors) { - v.importDirective(node); - } + _elementUsageFrontierDetector.importDirective(node); var import = node.libraryImport; if (import != null && import.prefix?.isDeferred == true) { _checkForLoadLibraryFunction(node, import); @@ -662,9 +592,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitIndexExpression(IndexExpression node) { - for (var v in _elementUsageFrontierDetectors) { - v.indexExpression(node); - } + _elementUsageFrontierDetector.indexExpression(node); super.visitIndexExpression(node); } @@ -672,9 +600,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { void visitInstanceCreationExpression( covariant InstanceCreationExpressionImpl node, ) { - for (var v in _elementUsageFrontierDetectors) { - v.instanceCreationExpression(node); - } + _elementUsageFrontierDetector.instanceCreationExpression(node); _deprecatedFunctionalityVerifier.instanceCreationExpression(node); _nullSafeApiVerifier.instanceCreation(node); _checkForLiteralConstructorUse(node); @@ -694,9 +620,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { var element = node.declaredFragment!.element; var enclosingElement = element.enclosingElement; - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(element); - } + _elementUsageFrontierDetector.pushElement(element); if (element.metadata.hasDoNotStore) { _inDoNotStoreMember = true; } @@ -776,18 +700,14 @@ class BestPracticesVerifier extends RecursiveAstVisitor { super.visitMethodDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); _inDoNotStoreMember = wasInDoNotStoreMember; } } @override void visitMethodInvocation(covariant MethodInvocationImpl node) { - for (var v in _elementUsageFrontierDetectors) { - v.methodInvocation(node); - } + _elementUsageFrontierDetector.methodInvocation(node); _deprecatedFunctionalityVerifier.methodInvocation(node); _errorHandlerVerifier.verifyMethodInvocation(node); _nullSafeApiVerifier.methodInvocation(node); @@ -802,9 +722,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { _invalidAccessVerifier._enclosingClass = _enclosingClass; _deprecatedFunctionalityVerifier.mixinDeclaration(node); - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(element); - } + _elementUsageFrontierDetector.pushElement(element); try { _immutableVerifier.checkDeclaration(node, nameToken: node.name); @@ -813,17 +731,13 @@ class BestPracticesVerifier extends RecursiveAstVisitor { } finally { _enclosingClass = null; _invalidAccessVerifier._enclosingClass = null; - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitNamedType(NamedType node) { - for (var v in _elementUsageFrontierDetectors) { - v.namedType(node); - } + _elementUsageFrontierDetector.namedType(node); _invalidAccessVerifier.verifyNamedType(node); var question = node.question; if (question != null) { @@ -845,18 +759,14 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitPatternField(PatternField node) { - for (var v in _elementUsageFrontierDetectors) { - v.patternField(node); - } + _elementUsageFrontierDetector.patternField(node); _invalidAccessVerifier.verifyPatternField(node as PatternFieldImpl); super.visitPatternField(node); } @override void visitPostfixExpression(PostfixExpression node) { - for (var v in _elementUsageFrontierDetectors) { - v.postfixExpression(node); - } + _elementUsageFrontierDetector.postfixExpression(node); if (node.operator.type == TokenType.BANG && node.operand.typeOrThrow.isDartCoreNull) { _diagnosticReporter.report(diag.nullCheckAlwaysFails.at(node)); @@ -866,23 +776,17 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitPrefixExpression(PrefixExpression node) { - for (var v in _elementUsageFrontierDetectors) { - v.prefixExpression(node); - } + _elementUsageFrontierDetector.prefixExpression(node); super.visitPrefixExpression(node); } @override void visitPrimaryConstructorBody(PrimaryConstructorBody node) { var element = node.declaration?.declaredFragment!.element; - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(element); - } + _elementUsageFrontierDetector.pushElement(element); // TODO(srawlins): Account for @doNotStore, as in `visitFunctionDeclaration`. super.visitPrimaryConstructorBody(node); - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } @override @@ -896,9 +800,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { void visitRedirectingConstructorInvocation( RedirectingConstructorInvocation node, ) { - for (var v in _elementUsageFrontierDetectors) { - v.redirectingConstructorInvocation(node); - } + _elementUsageFrontierDetector.redirectingConstructorInvocation(node); super.visitRedirectingConstructorInvocation(node); } @@ -918,55 +820,40 @@ class BestPracticesVerifier extends RecursiveAstVisitor { @override void visitSimpleFormalParameter(SimpleFormalParameter node) { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.declaredFragment!.element); - } - - for (var v in _elementUsageFrontierDetectors) { - v.formalParameter(node); - } + _elementUsageFrontierDetector.pushElement(node.declaredFragment!.element); + _elementUsageFrontierDetector.formalParameter(node); try { super.visitSimpleFormalParameter(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } @override void visitSimpleIdentifier(SimpleIdentifier node) { - for (var v in _elementUsageFrontierDetectors) { - v.simpleIdentifier(node); - } + _elementUsageFrontierDetector.simpleIdentifier(node); _invalidAccessVerifier.verify(node); super.visitSimpleIdentifier(node); } @override void visitSuperConstructorInvocation(SuperConstructorInvocation node) { - for (var v in _elementUsageFrontierDetectors) { - v.superConstructorInvocation(node); - } + _elementUsageFrontierDetector.superConstructorInvocation(node); _invalidAccessVerifier.verifySuperConstructorInvocation(node); super.visitSuperConstructorInvocation(node); } @override void visitSuperFormalParameter(SuperFormalParameter node) { - for (var v in _elementUsageFrontierDetectors) { - v.superFormalParameter(node); - } + _elementUsageFrontierDetector.superFormalParameter(node); _checkFinalParameter(node, node.keyword); super.visitSuperFormalParameter(node); } @override void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { - for (var v in _elementUsageFrontierDetectors) { - v.pushElement(node.firstVariableElement); - } + _elementUsageFrontierDetector.pushElement(node.firstVariableElement); if (!_invalidAccessVerifier._inTestDirectory) { for (var decl in node.variables.variables) { @@ -977,9 +864,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor { try { super.visitTopLevelVariableDeclaration(node); } finally { - for (var v in _elementUsageFrontierDetectors) { - v.popElement(); - } + _elementUsageFrontierDetector.popElement(); } } diff --git a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart index 090b1e948e5..866b2ee713b 100644 --- a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart +++ b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart @@ -63,12 +63,15 @@ class DeprecatedElementUsageReporter implements ElementUsageReporter { class DeprecatedElementUsageSet implements ElementUsageSet { const DeprecatedElementUsageSet(); - /// The message in the deprecated annotation on the given [element], or + @override + bool get reliesOnlyOnElementMetadata => true; + + /// The message in the deprecated annotation in [elementMetadata], or /// the empty string if the annotation does not have a message, or `null` if /// the element doesn't have a deprecated annotation. @override - String? getTagInfo(Element element) { - for (var annotation in element.metadata.annotations) { + String? getTagInfo(Element _, Metadata elementMetadata) { + for (var annotation in elementMetadata.annotations) { if (!annotation.isDeprecated) continue; var value = annotation.computeConstantValue(); if (value == null) continue; diff --git a/pkg/analyzer/lib/src/error/do_not_submit_member_use_verifier.dart b/pkg/analyzer/lib/src/error/do_not_submit_member_use_verifier.dart index 3ede1114b2a..58091360bd0 100644 --- a/pkg/analyzer/lib/src/error/do_not_submit_member_use_verifier.dart +++ b/pkg/analyzer/lib/src/error/do_not_submit_member_use_verifier.dart @@ -37,6 +37,9 @@ class DoNotSubmitElementUsageSet implements ElementUsageSet<()> { const DoNotSubmitElementUsageSet(); @override - ()? getTagInfo(Element element) => - element.metadata.hasDoNotSubmit ? () : null; + bool get reliesOnlyOnElementMetadata => true; + + @override + ()? getTagInfo(Element _, Metadata elementMetadata) => + elementMetadata.hasDoNotSubmit ? () : null; } diff --git a/pkg/analyzer/lib/src/error/element_usage_detector.dart b/pkg/analyzer/lib/src/error/element_usage_detector.dart index c8df6614ebd..3616ea9af3a 100644 --- a/pkg/analyzer/lib/src/error/element_usage_detector.dart +++ b/pkg/analyzer/lib/src/error/element_usage_detector.dart @@ -24,17 +24,21 @@ class ElementUsageDetector { /// parameter of [ElementUsageReporter.report]. final WorkspacePackage? _workspacePackage; - /// The set of elements to detect usages of. - final ElementUsageSet elementUsageSet; - - /// What to do when a usage of an element in [elementUsageSet] is detected. - final ElementUsageReporter elementUsageReporter; + final List usagesArbitrary = []; + final List usagesMetadataOnly = []; ElementUsageDetector({ required WorkspacePackage? workspacePackage, - required this.elementUsageSet, - required this.elementUsageReporter, - }) : _workspacePackage = workspacePackage; + required List usagesAndReporters, + }) : _workspacePackage = workspacePackage { + for (var usageSetAndReporter in usagesAndReporters) { + if (usageSetAndReporter.elementUsageSet.reliesOnlyOnElementMetadata) { + usagesMetadataOnly.add(usageSetAndReporter); + } else { + usagesArbitrary.add(usageSetAndReporter); + } + } + } void assignmentExpression(AssignmentExpression node) { checkUsage(node.readElement, node.leftHandSide); @@ -47,7 +51,7 @@ class ElementUsageDetector { } /// Reports the usage of [element] at [node] if [element] is in - /// [elementUsageSet]. + /// any of [usagesMetadataOnly] or [usagesArbitrary]. void checkUsage(Element? element, AstNode node) { if (element == null) { return; @@ -61,8 +65,33 @@ class ElementUsageDetector { return; } - var tagInfo = elementUsageSet.getTagInfo(element); - if (tagInfo == null) return; + List? givesNonNullResults; + var elementMetadata = element.metadata; + if (elementMetadata.annotations.isNotEmpty) { + for (int i = 0; i < usagesMetadataOnly.length; i++) { + if (!shouldCheckMetadataOnlyForIndex(i)) continue; + var usagesAndReporter = usagesMetadataOnly[i]; + var tagInfo = usagesAndReporter.elementUsageSet.getTagInfo( + element, + elementMetadata, + ); + if (tagInfo != null) { + (givesNonNullResults ??= []).add(usagesAndReporter); + } + } + } + for (int i = 0; i < usagesArbitrary.length; i++) { + if (!shouldCheckArbitraryForIndex(i)) continue; + var usagesAndReporter = usagesArbitrary[i]; + var tagInfo = usagesAndReporter.elementUsageSet.getTagInfo( + element, + elementMetadata, + ); + if (tagInfo != null) { + (givesNonNullResults ??= []).add(usagesAndReporter); + } + } + if (givesNonNullResults == null) return; if (_isLocalParameter(element, node)) { return; @@ -120,12 +149,15 @@ class ElementUsageDetector { // `ConstructorDeclaration.errorRange` here. This would stray from the API // of passing a SyntacticEntity here. - elementUsageReporter.report( - errorEntity, - displayName, - tagInfo, - isInSamePackage: _isLibraryInWorkspacePackage(element.library), - ); + for (var reportThis in givesNonNullResults) { + reportThis.elementUsageReporter.report( + errorEntity, + displayName, + // Getting it again might not be ideal... + reportThis.elementUsageSet.getTagInfo(element, elementMetadata)!, + isInSamePackage: _isLibraryInWorkspacePackage(element.library), + ); + } } void constructorDeclaration(ConstructorDeclaration node) { @@ -266,6 +298,18 @@ class ElementUsageDetector { _invocationArguments(node.element, node.argumentList); } + /// If false [checkUsage] may skip the call to `getTagInfo` on the [i]th + /// entry of [usagesArbitrary]. + bool shouldCheckArbitraryForIndex(int i) { + return true; + } + + /// If false [checkUsage] may skip the call to `getTagInfo` on the [i]th + /// entry of [usagesMetadataOnly]. + bool shouldCheckMetadataOnlyForIndex(int i) { + return true; + } + void simpleIdentifier(SimpleIdentifier node) { // Don't report declared identifiers. if (node.inDeclarationContext()) { @@ -426,12 +470,28 @@ abstract class ElementUsageReporter { /// string). If there is no auxiliary information, supply `()` for this type /// parameter. abstract class ElementUsageSet { + /// If [getTagInfo] only relies on the element metadata and can only return + /// non-null if the metadata is not empty. + /// + /// Used to skip calls to [getTagInfo] is there is no metadata. + bool get reliesOnlyOnElementMetadata; + /// If [element] is in the set of elements that [ElementUsageDetector] should /// detect usages of, returns auxiliary information associated with [element]. /// + /// [elementMetadata] is the result of [element].metadata, but should be used + /// to avoid the overhead of asking again. + /// /// Otherwise returns `null`. /// /// For example, [DeprecatedElementUsageSet]'s implementation of this method /// returns the deprecation message if [element] is deprecated. - TagInfo? getTagInfo(Element element); + TagInfo? getTagInfo(Element element, Metadata elementMetadata); +} + +class UsageSetAndReporter { + final ElementUsageSet elementUsageSet; + final ElementUsageReporter elementUsageReporter; + + UsageSetAndReporter(this.elementUsageSet, this.elementUsageReporter); } diff --git a/pkg/analyzer/lib/src/error/element_usage_frontier_detector.dart b/pkg/analyzer/lib/src/error/element_usage_frontier_detector.dart index fa12d9a20df..1b9848df428 100644 --- a/pkg/analyzer/lib/src/error/element_usage_frontier_detector.dart +++ b/pkg/analyzer/lib/src/error/element_usage_frontier_detector.dart @@ -13,35 +13,96 @@ import 'package:analyzer/src/error/element_usage_detector.dart'; /// that _are_ in the set. class ElementUsageFrontierDetector extends ElementUsageDetector { - /// Stack corresponding to elements being visited that might be in - /// [elementUsageSet]. + /// Stack corresponding to elements being visited that might be in ith + /// [usagesArbitrary], i.e. the first index corresponds to [usagesArbitrary] + /// and the second index to the stack depth. /// - /// Each element indicates whether the corresponding element is in - /// [elementUsageSet] or not. - final List _inElementStack = [false]; + /// Each element indicates whether the corresponding element is in ith + /// [usagesArbitrary] or not. + final List> _inElementStacksArbitrary = []; + + /// Stack corresponding to elements being visited that might be in ith + /// [usagesMetadataOnly], i.e. the first index corresponds to + /// [usagesMetadataOnly] and the second index to the stack depth. + /// + /// Each element indicates whether the corresponding element is in ith + /// [usagesMetadataOnly] or not. + final List> _inElementStacksMetadataOnly = []; ElementUsageFrontierDetector({ required super.workspacePackage, - required super.elementUsageSet, - required super.elementUsageReporter, - }); + required super.usagesAndReporters, + }) { + for (int i = 0; i < usagesArbitrary.length; i++) { + _inElementStacksArbitrary.add([false]); + } + for (int i = 0; i < usagesMetadataOnly.length; i++) { + _inElementStacksMetadataOnly.add([false]); + } + } @override void checkUsage(Element? element, AstNode node) { - if (_inElementStack.last) { - return; + bool allTrue = true; + for (var inElementStack in _inElementStacksArbitrary) { + if (!inElementStack.last) { + allTrue = false; + break; + } } + if (allTrue) { + for (var inElementStack in _inElementStacksMetadataOnly) { + if (!inElementStack.last) { + allTrue = false; + break; + } + } + } + if (allTrue) return; super.checkUsage(element, node); } void popElement() { - _inElementStack.removeLast(); + for (var inElementStack in _inElementStacksArbitrary) { + inElementStack.removeLast(); + } + for (var inElementStack in _inElementStacksMetadataOnly) { + inElementStack.removeLast(); + } } void pushElement(Element? element) { - var value = element != null && elementUsageSet.getTagInfo(element) != null; - var newValue = _inElementStack.last || value; - _inElementStack.add(newValue); + var elementMetadata = element?.metadata; + for (int i = 0; i < _inElementStacksArbitrary.length; i++) { + var inElementStack = _inElementStacksArbitrary[i]; + var newValue = inElementStack.last; + if (!newValue && element != null) { + var elementUsageSet = usagesArbitrary[i].elementUsageSet; + newValue = + elementUsageSet.getTagInfo(element, elementMetadata!) != null; + } + inElementStack.add(newValue); + } + for (int i = 0; i < _inElementStacksMetadataOnly.length; i++) { + var inElementStack = _inElementStacksMetadataOnly[i]; + var newValue = inElementStack.last; + if (!newValue && element != null) { + var elementUsageSet = usagesMetadataOnly[i].elementUsageSet; + newValue = + elementUsageSet.getTagInfo(element, elementMetadata!) != null; + } + inElementStack.add(newValue); + } + } + + @override + bool shouldCheckArbitraryForIndex(int i) { + return !_inElementStacksArbitrary[i].last; + } + + @override + bool shouldCheckMetadataOnlyForIndex(int i) { + return !_inElementStacksMetadataOnly[i].last; } } diff --git a/pkg/analyzer/lib/src/error/experimental_member_use_verifier.dart b/pkg/analyzer/lib/src/error/experimental_member_use_verifier.dart index 1eefbfe441a..f3a50e5853e 100644 --- a/pkg/analyzer/lib/src/error/experimental_member_use_verifier.dart +++ b/pkg/analyzer/lib/src/error/experimental_member_use_verifier.dart @@ -40,6 +40,9 @@ class ExperimentalElementUsageSet implements ElementUsageSet<()> { const ExperimentalElementUsageSet(); @override - ()? getTagInfo(Element element) => - element.metadata.annotations.any((e) => e.isExperimental) ? () : null; + bool get reliesOnlyOnElementMetadata => true; + + @override + ()? getTagInfo(Element _, Metadata elementMetadata) => + elementMetadata.annotations.any((e) => e.isExperimental) ? () : null; } diff --git a/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart b/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart index 659a398f212..8a569796d56 100644 --- a/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart +++ b/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart @@ -16,7 +16,7 @@ import 'package:analyzer/error/error.dart'; import 'package:analyzer/src/error/deprecated_member_use_verifier.dart' // ignore: implementation_imports show DeprecatedElementUsageSet, normalizeDeprecationMessage; import 'package:analyzer/src/error/element_usage_detector.dart' // ignore: implementation_imports - show ElementUsageReporter; + show ElementUsageReporter, UsageSetAndReporter; import 'package:analyzer/src/error/element_usage_frontier_detector.dart' // ignore: implementation_imports show ElementUsageFrontierDetector; import 'package:analyzer/src/utilities/extensions/ast.dart'; // ignore: implementation_imports @@ -95,8 +95,12 @@ class _RecursiveVisitor extends RecursiveAstVisitor { _RecursiveVisitor(MultiAnalysisRule rule, WorkspacePackage package) : _deprecatedVerifier = ElementUsageFrontierDetector( workspacePackage: package, - elementUsageSet: const DeprecatedElementUsageSet(), - elementUsageReporter: _DeprecatedElementUsageReporter(rule: rule), + usagesAndReporters: [ + UsageSetAndReporter( + const DeprecatedElementUsageSet(), + _DeprecatedElementUsageReporter(rule: rule), + ), + ], ); @override