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