blob: a2bd7bb2a3c641c2f6cf1d883627de2bd25bf195 [file] [log] [blame]
// Copyright (c) 2021, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
import 'package:collection/collection.dart';
class UseResultVerifier {
final ErrorReporter _errorReporter;
UseResultVerifier(this._errorReporter);
void checkMethodInvocation(MethodInvocation node) {
var element = node.methodName.staticElement;
if (element == null) {
return;
}
_check(node, element);
}
void checkPropertyAccess(PropertyAccess node) {
var element = node.propertyName.staticElement;
if (element == null) {
return null;
}
_check(node, element);
}
void checkSimpleIdentifier(SimpleIdentifier node) {
if (node.inDeclarationContext()) {
return;
}
var parent = node.parent;
// Covered by checkPropertyAccess and checkMethodInvocation respectively.
if (parent is PropertyAccess || parent is MethodInvocation) {
return;
}
var element = node.staticElement;
if (element == null) {
return null;
}
_check(node, element);
}
void _check(AstNode node, Element element) {
if (node.parent is CommentReference) {
// Don't flag references in comments.
return;
}
var annotation = _getUseResultMetadata(element);
if (annotation == null) {
return;
}
if (_passesUsingParam(node, annotation)) {
return;
}
if (_isUsed(node)) {
return;
}
var displayName = element.displayName;
var message = _getUseResultMessage(annotation);
if (message == null || message.isEmpty) {
_errorReporter.reportErrorForNode(
HintCode.UNUSED_RESULT, _getNodeToAnnotate(node), [displayName]);
} else {
_errorReporter.reportErrorForNode(HintCode.UNUSED_RESULT_WITH_MESSAGE,
_getNodeToAnnotate(node), [displayName, message]);
}
}
bool _passesUsingParam(AstNode node, ElementAnnotation annotation) {
if (node is! MethodInvocation) {
return false;
}
var unlessParam = _getUseResultUnlessParam(annotation);
if (unlessParam == null) {
return false;
}
var argumentList = node.argumentList as ArgumentListImpl;
var parameters = argumentList.correspondingStaticParameters;
if (parameters == null) {
return false;
}
for (var param in parameters) {
var name = param?.name;
if (unlessParam == name) {
return true;
}
}
return false;
}
static AstNode _getNodeToAnnotate(AstNode node) {
if (node is MethodInvocation) {
return node.methodName;
}
if (node is PropertyAccess) {
return node.propertyName;
}
return node;
}
static String? _getUseResultMessage(ElementAnnotation annotation) {
if (annotation.element is PropertyAccessorElement) {
return null;
}
var constantValue = annotation.computeConstantValue();
return constantValue?.getField('message')?.toStringValue();
}
static ElementAnnotation? _getUseResultMetadata(Element element) {
// Implicit getters/setters.
if (element.isSynthetic && element is PropertyAccessorElement) {
element = element.variable;
}
return element.metadata.firstWhereOrNull((e) => e.isUseResult);
}
static String? _getUseResultUnlessParam(ElementAnnotation annotation) {
var constantValue = annotation.computeConstantValue();
return constantValue?.getField('parameterDefined')?.toStringValue();
}
static bool _isUsed(AstNode node) {
var parent = node.parent;
if (parent == null) {
return false;
}
if (parent is CascadeExpression) {
return parent.target == node;
}
if (parent is PrefixedIdentifier) {
if (parent.prefix == node) {
return true;
} else {
return _isUsed(parent);
}
}
if (parent is ParenthesizedExpression ||
parent is ConditionalExpression ||
parent is AwaitExpression) {
return _isUsed(parent);
}
return parent is ArgumentList ||
// Node should always be RHS so no need to check for a property assignment.
parent is AssignmentExpression ||
parent is VariableDeclaration ||
parent is MethodInvocation ||
parent is PropertyAccess ||
parent is ExpressionFunctionBody ||
parent is ReturnStatement ||
parent is FunctionExpressionInvocation ||
parent is ListLiteral ||
parent is SetOrMapLiteral ||
parent is MapLiteralEntry;
}
}