| Index: third_party/WebKit/Source/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/FunctionReceiverChecker.java
|
| diff --git a/third_party/WebKit/Source/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/FunctionReceiverChecker.java b/third_party/WebKit/Source/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/FunctionReceiverChecker.java
|
| deleted file mode 100644
|
| index d7b6c01ecb28048ca7901a1836cebc869a15c9e2..0000000000000000000000000000000000000000
|
| --- a/third_party/WebKit/Source/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/FunctionReceiverChecker.java
|
| +++ /dev/null
|
| @@ -1,290 +0,0 @@
|
| -package org.chromium.devtools.jsdoc.checks;
|
| -
|
| -import com.google.common.base.Preconditions;
|
| -import com.google.javascript.rhino.Node;
|
| -import com.google.javascript.rhino.Token;
|
| -
|
| -import java.util.ArrayList;
|
| -import java.util.HashMap;
|
| -import java.util.HashSet;
|
| -import java.util.List;
|
| -import java.util.Map;
|
| -import java.util.Set;
|
| -
|
| -public final class FunctionReceiverChecker extends ContextTrackingChecker {
|
| - private static final Set<String> FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT =
|
| - new HashSet<>();
|
| - private static final String SUPPRESSION_HINT = "This check can be suppressed using "
|
| - + "@suppressReceiverCheck annotation on function declaration.";
|
| - static {
|
| - // Array.prototype methods.
|
| - FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("every");
|
| - FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("filter");
|
| - FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("forEach");
|
| - FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("map");
|
| - FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("some");
|
| - }
|
| -
|
| - private final Map<String, FunctionRecord> nestedFunctionsByName = new HashMap<>();
|
| - private final Map<String, Set<CallSite>> callSitesByFunctionName = new HashMap<>();
|
| - private final Map<String, Set<SymbolicArgument>> symbolicArgumentsByName = new HashMap<>();
|
| - private final Set<FunctionRecord> functionsRequiringThisAnnotation = new HashSet<>();
|
| -
|
| - @Override
|
| - void enterNode(Node node) {
|
| - switch (node.getType()) {
|
| - case Token.CALL:
|
| - handleCall(node);
|
| - break;
|
| - case Token.FUNCTION: {
|
| - handleFunction(node);
|
| - break;
|
| - }
|
| - case Token.THIS: {
|
| - handleThis();
|
| - break;
|
| - }
|
| - default:
|
| - break;
|
| - }
|
| - }
|
| -
|
| - private void handleCall(Node functionCall) {
|
| - Preconditions.checkState(functionCall.isCall());
|
| - String[] callParts = getContext().getNodeText(functionCall.getFirstChild()).split("\\.");
|
| - String firstPart = callParts[0];
|
| - List<Node> argumentNodes = AstUtil.getArguments(functionCall);
|
| - List<String> actualArguments = argumentsForCall(argumentNodes);
|
| - int partCount = callParts.length;
|
| - String functionName = callParts[partCount - 1];
|
| -
|
| - saveSymbolicArguments(functionName, argumentNodes, actualArguments);
|
| -
|
| - boolean isBindCall = partCount > 1 && "bind".equals(functionName);
|
| - if (isBindCall && partCount == 3 && "this".equals(firstPart)
|
| - && !(actualArguments.size() > 0 && "this".equals(actualArguments.get(0)))) {
|
| - reportErrorAtNodeStart(
|
| - functionCall, "Member function can only be bound to 'this' as the receiver");
|
| - return;
|
| - }
|
| - if (partCount > 2 || "this".equals(firstPart)) {
|
| - return;
|
| - }
|
| - boolean hasReceiver = isBindCall && isReceiverSpecified(actualArguments);
|
| - hasReceiver |= (partCount == 2)
|
| - && ("call".equals(functionName) || "apply".equals(functionName))
|
| - && isReceiverSpecified(actualArguments);
|
| - getOrCreateSetByKey(callSitesByFunctionName, firstPart)
|
| - .add(new CallSite(hasReceiver, functionCall));
|
| - }
|
| -
|
| - private void handleFunction(Node node) {
|
| - Preconditions.checkState(node.isFunction());
|
| - FunctionRecord function = getState().getCurrentFunctionRecord();
|
| - if (function == null) {
|
| - return;
|
| - }
|
| - if (function.isTopLevelFunction()) {
|
| - symbolicArgumentsByName.clear();
|
| - } else {
|
| - Node nameNode = AstUtil.getFunctionNameNode(node);
|
| - if (nameNode == null) {
|
| - return;
|
| - }
|
| - nestedFunctionsByName.put(getContext().getNodeText(nameNode), function);
|
| - }
|
| - }
|
| -
|
| - private void handleThis() {
|
| - FunctionRecord function = getState().getCurrentFunctionRecord();
|
| - while (function != null && function.functionNode.isArrowFunction()) {
|
| - function = function.enclosingFunctionRecord;
|
| - }
|
| - if (function == null) {
|
| - return;
|
| - }
|
| - if (!function.isTopLevelFunction() && !function.isConstructor()) {
|
| - functionsRequiringThisAnnotation.add(function);
|
| - }
|
| - }
|
| -
|
| - private List<String> argumentsForCall(List<Node> argumentNodes) {
|
| - int argumentCount = argumentNodes.size();
|
| - List<String> arguments = new ArrayList<>(argumentCount);
|
| - for (Node argumentNode : argumentNodes) {
|
| - arguments.add(getContext().getNodeText(argumentNode));
|
| - }
|
| - return arguments;
|
| - }
|
| -
|
| - private void saveSymbolicArguments(
|
| - String functionName, List<Node> argumentNodes, List<String> arguments) {
|
| - int argumentCount = arguments.size();
|
| - CheckedReceiverPresence receiverPresence = CheckedReceiverPresence.MISSING;
|
| - if (FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.contains(functionName)) {
|
| - if (argumentCount >= 2) {
|
| - receiverPresence = CheckedReceiverPresence.PRESENT;
|
| - }
|
| - } else if ("addEventListener".equals(functionName)
|
| - || "removeEventListener".equals(functionName)) {
|
| - String receiverArgument = argumentCount < 3 ? "" : arguments.get(2);
|
| - switch (receiverArgument) {
|
| - case "":
|
| - case "true":
|
| - case "false":
|
| - receiverPresence = CheckedReceiverPresence.MISSING;
|
| - break;
|
| - case "this":
|
| - receiverPresence = CheckedReceiverPresence.PRESENT;
|
| - break;
|
| - default:
|
| - receiverPresence = CheckedReceiverPresence.IGNORE;
|
| - }
|
| - }
|
| -
|
| - for (int i = 0; i < argumentCount; ++i) {
|
| - String argumentText = arguments.get(i);
|
| - getOrCreateSetByKey(symbolicArgumentsByName, argumentText)
|
| - .add(new SymbolicArgument(receiverPresence, argumentNodes.get(i)));
|
| - }
|
| - }
|
| -
|
| - private static <K, T> Set<T> getOrCreateSetByKey(Map<K, Set<T>> map, K key) {
|
| - Set<T> set = map.get(key);
|
| - if (set == null) {
|
| - set = new HashSet<>();
|
| - map.put(key, set);
|
| - }
|
| - return set;
|
| - }
|
| -
|
| - private boolean isReceiverSpecified(List<String> arguments) {
|
| - return arguments.size() > 0 && !"null".equals(arguments.get(0));
|
| - }
|
| -
|
| - @Override
|
| - void leaveNode(Node node) {
|
| - if (node.getType() != Token.FUNCTION) {
|
| - return;
|
| - }
|
| -
|
| - ContextTrackingState state = getState();
|
| - FunctionRecord function = state.getCurrentFunctionRecord();
|
| - if (function == null) {
|
| - return;
|
| - }
|
| - checkThisAnnotation(function);
|
| -
|
| - // The nested function checks are only run when leaving a top-level function.
|
| - if (!function.isTopLevelFunction()) {
|
| - return;
|
| - }
|
| -
|
| - for (FunctionRecord record : nestedFunctionsByName.values()) {
|
| - processFunctionUsesAsArgument(record, symbolicArgumentsByName.get(record.name));
|
| - processFunctionCallSites(record, callSitesByFunctionName.get(record.name));
|
| - }
|
| -
|
| - nestedFunctionsByName.clear();
|
| - callSitesByFunctionName.clear();
|
| - symbolicArgumentsByName.clear();
|
| - }
|
| -
|
| - private void checkThisAnnotation(FunctionRecord function) {
|
| - Node functionNameNode = AstUtil.getFunctionNameNode(function.functionNode);
|
| - if (functionNameNode == null && function.info == null) {
|
| - // Do not check anonymous functions without a JSDoc.
|
| - return;
|
| - }
|
| - int errorTargetOffset = functionNameNode == null
|
| - ? (function.info == null ? function.functionNode.getSourceOffset()
|
| - : function.info.getOriginalCommentPosition())
|
| - : functionNameNode.getSourceOffset();
|
| - boolean hasThisAnnotation = function.hasThisAnnotation();
|
| - if (hasThisAnnotation == functionReferencesThis(function)) {
|
| - return;
|
| - }
|
| - if (hasThisAnnotation) {
|
| - if (!function.isTopLevelFunction()) {
|
| - reportErrorAtOffset(errorTargetOffset,
|
| - "@this annotation found for function not referencing 'this'");
|
| - }
|
| - return;
|
| - } else {
|
| - reportErrorAtOffset(errorTargetOffset,
|
| - "@this annotation is required for functions referencing 'this'");
|
| - }
|
| - }
|
| -
|
| - private boolean functionReferencesThis(FunctionRecord function) {
|
| - return functionsRequiringThisAnnotation.contains(function);
|
| - }
|
| -
|
| - private void processFunctionCallSites(FunctionRecord function, Set<CallSite> callSites) {
|
| - if (callSites == null) {
|
| - return;
|
| - }
|
| - boolean functionReferencesThis = functionReferencesThis(function);
|
| - for (CallSite callSite : callSites) {
|
| - if (functionReferencesThis == callSite.hasReceiver || function.isConstructor()) {
|
| - continue;
|
| - }
|
| - if (callSite.hasReceiver) {
|
| - reportErrorAtNodeStart(callSite.callNode,
|
| - "Receiver specified for a function not referencing 'this'");
|
| - } else {
|
| - reportErrorAtNodeStart(callSite.callNode,
|
| - "Receiver not specified for a function referencing 'this'");
|
| - }
|
| - }
|
| - }
|
| -
|
| - private void processFunctionUsesAsArgument(
|
| - FunctionRecord function, Set<SymbolicArgument> argumentUses) {
|
| - if (argumentUses == null || function.suppressesReceiverCheck()) {
|
| - return;
|
| - }
|
| -
|
| - boolean referencesThis = functionReferencesThis(function);
|
| - for (SymbolicArgument argument : argumentUses) {
|
| - if (argument.receiverPresence == CheckedReceiverPresence.IGNORE) {
|
| - continue;
|
| - }
|
| - boolean receiverProvided = argument.receiverPresence == CheckedReceiverPresence.PRESENT;
|
| - if (referencesThis == receiverProvided) {
|
| - continue;
|
| - }
|
| - if (referencesThis) {
|
| - reportErrorAtNodeStart(
|
| - argument.node, "Function referencing 'this' used as argument without "
|
| - + "a receiver. " + SUPPRESSION_HINT);
|
| - } else {
|
| - reportErrorAtNodeStart(
|
| - argument.node, "Function not referencing 'this' used as argument with "
|
| - + "a receiver. " + SUPPRESSION_HINT);
|
| - }
|
| - }
|
| - }
|
| -
|
| - private static enum CheckedReceiverPresence { PRESENT, MISSING, IGNORE }
|
| -
|
| - private static class SymbolicArgument {
|
| - CheckedReceiverPresence receiverPresence;
|
| - Node node;
|
| -
|
| - public SymbolicArgument(CheckedReceiverPresence receiverPresence, Node node) {
|
| - this.receiverPresence = receiverPresence;
|
| - this.node = node;
|
| - }
|
| - }
|
| -
|
| - private static class CallSite {
|
| - boolean hasReceiver;
|
| - Node callNode;
|
| -
|
| - public CallSite(boolean hasReceiver, Node callNode) {
|
| - this.hasReceiver = hasReceiver;
|
| - this.callNode = callNode;
|
| - }
|
| - }
|
| -}
|
|
|