143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)package org.chromium.devtools.jsdoc.checks; 243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 3197021e6b966cfb06891637935ef33fff06433d1Ben Murdochimport com.google.common.base.Preconditions; 4197021e6b966cfb06891637935ef33fff06433d1Ben Murdochimport com.google.javascript.rhino.Node; 5197021e6b966cfb06891637935ef33fff06433d1Ben Murdochimport com.google.javascript.rhino.Token; 643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)import java.util.ArrayList; 843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)import java.util.HashMap; 943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)import java.util.HashSet; 1043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)import java.util.List; 1143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)import java.util.Map; 1243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)import java.util.Set; 1343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 1443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)public final class FunctionReceiverChecker extends ContextTrackingChecker { 1543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 1643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private static final Set<String> FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT = 1743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) new HashSet<>(); 1843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private static final String SUPPRESSION_HINT = "This check can be suppressed using " 1943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) + "@suppressReceiverCheck annotation on function declaration."; 2043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) static { 2143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) // Array.prototype methods. 2243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("every"); 2343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("filter"); 2443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("forEach"); 2543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("map"); 2643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.add("some"); 2743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 2843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 2943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private final Map<String, FunctionRecord> nestedFunctionsByName = new HashMap<>(); 3043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private final Map<String, Set<CallSite>> callSitesByFunctionName = new HashMap<>(); 3143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private final Map<String, Set<SymbolicArgument>> symbolicArgumentsByName = new HashMap<>(); 3207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch private final Set<FunctionRecord> functionsRequiringThisAnnotation = new HashSet<>(); 3343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 3443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) @Override 35197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch void enterNode(Node node) { 3643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) switch (node.getType()) { 3743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) case Token.CALL: 38197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch handleCall(node); 3943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) break; 4007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch case Token.FUNCTION: { 41197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch handleFunction(node); 4207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch break; 4307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 4407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch case Token.THIS: { 4507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch handleThis(); 4643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) break; 4707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 4843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) default: 4943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) break; 5043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 5143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 5243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 53197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch private void handleCall(Node functionCall) { 54197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch Preconditions.checkState(functionCall.isCall()); 55197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch String[] callParts = getContext().getNodeText(functionCall.getFirstChild()).split("\\."); 5643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) String firstPart = callParts[0]; 57197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch List<Node> argumentNodes = AstUtil.getArguments(functionCall); 5843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) List<String> actualArguments = argumentsForCall(argumentNodes); 5943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) int partCount = callParts.length; 6043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) String functionName = callParts[partCount - 1]; 6143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 6243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) saveSymbolicArguments(functionName, argumentNodes, actualArguments); 6343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 6443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) boolean isBindCall = partCount > 1 && "bind".equals(functionName); 6543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (isBindCall && partCount == 3 && "this".equals(firstPart) && 6643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) !(actualArguments.size() > 0 && "this".equals(actualArguments.get(0)))) { 6743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) reportErrorAtNodeStart(functionCall, 6843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) "Member function can only be bound to 'this' as the receiver"); 6943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return; 7043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 7143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (partCount > 2 || "this".equals(firstPart)) { 7243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return; 7343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 7443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) boolean hasReceiver = isBindCall && isReceiverSpecified(actualArguments); 7543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) hasReceiver |= (partCount == 2) && 7643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) ("call".equals(functionName) || "apply".equals(functionName)) && 7743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) isReceiverSpecified(actualArguments); 7843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) getOrCreateSetByKey(callSitesByFunctionName, firstPart) 7943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) .add(new CallSite(hasReceiver, functionCall)); 8043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 8143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 8207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 83197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch private void handleFunction(Node node) { 84197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch Preconditions.checkState(node.isFunction()); 8507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch FunctionRecord function = getState().getCurrentFunctionRecord(); 8607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (function == null) { 8707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 8807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 8907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (function.isTopLevelFunction()) { 9007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch symbolicArgumentsByName.clear(); 9107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } else { 92197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch Node nameNode = AstUtil.getFunctionNameNode(node); 9307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (nameNode == null) { 9407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 9507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 9607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch nestedFunctionsByName.put(getContext().getNodeText(nameNode), function); 9707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 9807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 9907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 10007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch private void handleThis() { 10107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch FunctionRecord function = getState().getCurrentFunctionRecord(); 10207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (function == null) { 10307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 10407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 105197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (!function.isTopLevelFunction() && !function.isConstructor()) { 10607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch functionsRequiringThisAnnotation.add(function); 10707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 10807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 10907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 110197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch private List<String> argumentsForCall(List<Node> argumentNodes) { 11143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) int argumentCount = argumentNodes.size(); 11243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) List<String> arguments = new ArrayList<>(argumentCount); 113197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch for (Node argumentNode : argumentNodes) { 11443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) arguments.add(getContext().getNodeText(argumentNode)); 11543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 11643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return arguments; 11743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 11843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 11943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private void saveSymbolicArguments( 120197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch String functionName, List<Node> argumentNodes, List<String> arguments) { 12143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) int argumentCount = arguments.size(); 12243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) CheckedReceiverPresence receiverPresence = CheckedReceiverPresence.MISSING; 12343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (FUNCTIONS_WITH_CALLBACK_RECEIVER_AS_SECOND_ARGUMENT.contains(functionName)) { 12443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (argumentCount >= 2) { 12543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) receiverPresence = CheckedReceiverPresence.PRESENT; 12643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 12743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } else if ("addEventListener".equals(functionName) || 12843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) "removeEventListener".equals(functionName)) { 12943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) String receiverArgument = argumentCount < 3 ? "" : arguments.get(2); 13043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) switch (receiverArgument) { 13143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) case "": 13243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) case "true": 13343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) case "false": 13443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) receiverPresence = CheckedReceiverPresence.MISSING; 13543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) break; 13643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) case "this": 13743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) receiverPresence = CheckedReceiverPresence.PRESENT; 13843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) break; 13943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) default: 14043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) receiverPresence = CheckedReceiverPresence.IGNORE; 14143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 14243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 14343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 14443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) for (int i = 0; i < argumentCount; ++i) { 14543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) String argumentText = arguments.get(i); 14643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) getOrCreateSetByKey(symbolicArgumentsByName, argumentText).add( 14743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) new SymbolicArgument(receiverPresence, argumentNodes.get(i))); 14843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 14943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 15043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 15143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private static <K, T> Set<T> getOrCreateSetByKey(Map<K, Set<T>> map, K key) { 15243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) Set<T> set = map.get(key); 15343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (set == null) { 15443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) set = new HashSet<>(); 15543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) map.put(key, set); 15643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 15743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return set; 15843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 15943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 16043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private boolean isReceiverSpecified(List<String> arguments) { 16143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return arguments.size() > 0 && !"null".equals(arguments.get(0)); 16243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 16343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 16443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) @Override 165197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch void leaveNode(Node node) { 16643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (node.getType() != Token.FUNCTION) { 16743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return; 16843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 16943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 17007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch ContextTrackingState state = getState(); 17107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch FunctionRecord function = state.getCurrentFunctionRecord(); 17207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (function == null) { 17307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 17407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 17507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch checkThisAnnotation(function); 17607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 17707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch // The nested function checks are only run when leaving a top-level function. 17807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (!function.isTopLevelFunction()) { 17943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return; 18043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 18143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 18243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) for (FunctionRecord record : nestedFunctionsByName.values()) { 18343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) processFunctionUsesAsArgument(record, symbolicArgumentsByName.get(record.name)); 18443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) processFunctionCallSites(record, callSitesByFunctionName.get(record.name)); 18543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 18643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 18743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) nestedFunctionsByName.clear(); 18843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) callSitesByFunctionName.clear(); 18943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) symbolicArgumentsByName.clear(); 19043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 19143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 19207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch private void checkThisAnnotation(FunctionRecord function) { 193197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch Node functionNameNode = AstUtil.getFunctionNameNode(function.functionNode); 194197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (functionNameNode == null && function.info == null) { 195f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) // Do not check anonymous functions without a JSDoc. 19607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 19707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 198197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch int errorTargetOffset = functionNameNode == null 199197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch ? (function.info == null 200197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch ? function.functionNode.getSourceOffset() 201197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch : function.info.getOriginalCommentPosition()) 202197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch : functionNameNode.getSourceOffset(); 203197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch boolean hasThisAnnotation = function.hasThisAnnotation(); 20407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (hasThisAnnotation == functionReferencesThis(function)) { 20507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 20607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 20707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (hasThisAnnotation) { 20807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (!function.isTopLevelFunction()) { 209197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch reportErrorAtOffset( 210197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch errorTargetOffset, 21107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch "@this annotation found for function not referencing 'this'"); 21207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 21307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return; 21407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } else { 215197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch reportErrorAtOffset( 216197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch errorTargetOffset, 21707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch "@this annotation is required for functions referencing 'this'"); 21807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 21907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 22007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 22107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch private boolean functionReferencesThis(FunctionRecord function) { 22207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch return functionsRequiringThisAnnotation.contains(function); 22307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch } 22407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch 22543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private void processFunctionCallSites(FunctionRecord function, Set<CallSite> callSites) { 22643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (callSites == null) { 22743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return; 22843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 22907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch boolean functionReferencesThis = functionReferencesThis(function); 23043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) for (CallSite callSite : callSites) { 231197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (functionReferencesThis == callSite.hasReceiver || function.isConstructor()) { 23243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) continue; 23343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 23443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (callSite.hasReceiver) { 23543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) reportErrorAtNodeStart(callSite.callNode, 23607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch "Receiver specified for a function not referencing 'this'"); 23743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } else { 23843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) reportErrorAtNodeStart(callSite.callNode, 23907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch "Receiver not specified for a function referencing 'this'"); 24043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 24143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 24243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 24343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 24443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private void processFunctionUsesAsArgument( 24543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) FunctionRecord function, Set<SymbolicArgument> argumentUses) { 246197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (argumentUses == null || function.suppressesReceiverCheck()) { 24743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) return; 24843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 24943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 25007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch boolean referencesThis = functionReferencesThis(function); 25143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) for (SymbolicArgument argument : argumentUses) { 25243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (argument.receiverPresence == CheckedReceiverPresence.IGNORE) { 25343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) continue; 25443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 25543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) boolean receiverProvided = 25643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) argument.receiverPresence == CheckedReceiverPresence.PRESENT; 25707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (referencesThis == receiverProvided) { 25843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) continue; 25943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 26007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch if (referencesThis) { 26143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) reportErrorAtNodeStart(argument.node, 26207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch "Function referencing 'this' used as argument without " + 26343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) "a receiver. " + SUPPRESSION_HINT); 26443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } else { 26543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) reportErrorAtNodeStart(argument.node, 26607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch "Function not referencing 'this' used as argument with " + 26743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) "a receiver. " + SUPPRESSION_HINT); 26843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 26943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 27043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 27143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 27243e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private static enum CheckedReceiverPresence { 27343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) PRESENT, 27443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) MISSING, 27543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) IGNORE 27643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 27743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 27843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private static class SymbolicArgument { 27943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) CheckedReceiverPresence receiverPresence; 280197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch Node node; 28143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 282197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch public SymbolicArgument(CheckedReceiverPresence receiverPresence, Node node) { 28343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) this.receiverPresence = receiverPresence; 28443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) this.node = node; 28543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 28643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 28743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 28843e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) private static class CallSite { 28943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) boolean hasReceiver; 290197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch Node callNode; 29143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 292197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch public CallSite(boolean hasReceiver, Node callNode) { 29343e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) this.hasReceiver = hasReceiver; 29443e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) this.callNode = callNode; 29543e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 29643e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) } 29743e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles)} 298