<commit_before>package org.javacs;

import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.util.*;
import javax.lang.model.element.*;

class WarnUnused extends TreePathScanner<Void, Void> {
    private final Trees trees;
    // TODO ignore writes when calculating used
    private final Set<Element> reachable = new HashSet<>(), unused = new HashSet<>();

    WarnUnused(JavacTask task) {
        this.trees = Trees.instance(task);
    }

    Set<Element> notUsed() {
        unused.removeAll(reachable);
        return unused;
    }

    private void foundPrivateDeclaration() {
        unused.add(trees.getElement(getCurrentPath()));
    }

    private void foundReference() {
        var fromPath = getCurrentPath();
        var toEl = trees.getElement(fromPath);
        var toPath = trees.getPath(toEl);
        if (toPath == null) return;
        var fromUri = fromPath.getCompilationUnit().getSourceFile().toUri();
        var toUri = toPath.getCompilationUnit().getSourceFile().toUri();
        if (!fromUri.equals(toUri)) return;
        if (isLocalVariable(toPath)) {
            reachable.add(toEl);
        }
        if (!isReachable(toPath)) {
            reachable.add(toEl);
            scan(toPath, null);
        }
    }

    private boolean isReachable(TreePath path) {
        // Check if t is reachable because it's public
        var t = path.getLeaf();
        if (t instanceof VariableTree) {
            var v = (VariableTree) t;
            var isPrivate = v.getModifiers().getFlags().contains(Modifier.PRIVATE);
            if (!isPrivate || isLocalVariable(path)) {
                return true;
            }
        }
        if (t instanceof MethodTree) {
            var m = (MethodTree) t;
            var isPrivate = m.getModifiers().getFlags().contains(Modifier.PRIVATE);
            var isEmptyConstructor = m.getParameters().isEmpty() && m.getReturnType() == null;
            if (!isPrivate || isEmptyConstructor) {
                return true;
            }
        }
        if (t instanceof ClassTree) {
            var c = (ClassTree) t;
            var isPrivate = c.getModifiers().getFlags().contains(Modifier.PRIVATE);
            if (!isPrivate) {
                return true;
            }
        }
        // Check if t has been referenced by a reachable element
        var el = trees.getElement(path);
        if (reachable.contains(el)) {
            return true;
        }
        return false;
    }

    private boolean isLocalVariable(TreePath path) {
        if (path.getLeaf() instanceof VariableTree) {
            var parent = path.getParentPath().getLeaf();
            return !(parent instanceof ClassTree)
                    && !(parent instanceof MethodTree) // TODO hint for unused parameters
                    && !(parent instanceof LambdaExpressionTree);
        }
        return false;
    }

    @Override
    public Void visitVariable(VariableTree t, Void __) {
        if (isLocalVariable(getCurrentPath())) {
            foundPrivateDeclaration();
            super.visitVariable(t, null);
        } else if (isReachable(getCurrentPath())) {
            super.visitVariable(t, null);
        } else {
            foundPrivateDeclaration();
        }
        return null;
    }

    @Override
    public Void visitMethod(MethodTree t, Void __) {
        if (isReachable(getCurrentPath())) {
            super.visitMethod(t, null);
        } else {
            foundPrivateDeclaration();
        }
        return null;
    }

    @Override
    public Void visitClass(ClassTree t, Void __) {
        if (isReachable(getCurrentPath())) {
            super.visitClass(t, null);
        } else {
            foundPrivateDeclaration();
        }
        return null;
    }

    @Override
    public Void visitIdentifier(IdentifierTree t, Void __) {
        foundReference();
        return super.visitIdentifier(t, null);
    }

    @Override
    public Void visitMemberSelect(MemberSelectTree t, Void __) {
        foundReference();
        return super.visitMemberSelect(t, null);
    }

    @Override
    public Void visitMemberReference(MemberReferenceTree t, Void __) {
        foundReference();
        return super.visitMemberReference(t, null);
    }

    @Override
    public Void visitNewClass(NewClassTree t, Void __) {
        foundReference();
        return super.visitNewClass(t, null);
    }
}
<commit_msg>Fix null pointer when using scan(path, _) recursively<commit_after>package org.javacs;

import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.util.*;
import javax.lang.model.element.*;

class WarnUnused extends TreeScanner<Void, Void> {
    // Copied from TreePathScanner
    // We need to be able to call scan(path, _) recursively
    private TreePath path;

    public Void scan(TreePath path, Void p) {
        TreePath prev = this.path;
        this.path = path;
        try {
            return path.getLeaf().accept(this, p);
        } finally {
            this.path = prev; // So we can call scan(path, _) recursively
        }
    }

    @Override
    public Void scan(Tree tree, Void p) {
        if (tree == null) return null;

        TreePath prev = path;
        path = new TreePath(path, tree);
        try {
            return tree.accept(this, p);
        } finally {
            path = prev;
        }
    }

    private final Trees trees;
    // TODO ignore writes when calculating used
    private final Set<Element> reachable = new HashSet<>(), unused = new HashSet<>();

    WarnUnused(JavacTask task) {
        this.trees = Trees.instance(task);
    }

    Set<Element> notUsed() {
        unused.removeAll(reachable);
        return unused;
    }

    private void foundPrivateDeclaration() {
        unused.add(trees.getElement(path));
    }

    private void foundReference() {
        var fromPath = path;
        var toEl = trees.getElement(fromPath);
        var toPath = trees.getPath(toEl);
        if (toPath == null) return;
        var fromUri = fromPath.getCompilationUnit().getSourceFile().toUri();
        var toUri = toPath.getCompilationUnit().getSourceFile().toUri();
        if (!fromUri.equals(toUri)) return;
        if (isLocalVariable(toPath)) {
            reachable.add(toEl);
        }
        if (!isReachable(toPath)) {
            reachable.add(toEl);
            scan(toPath, null);
        }
    }

    private boolean isReachable(TreePath path) {
        // Check if t is reachable because it's public
        var t = path.getLeaf();
        if (t instanceof VariableTree) {
            var v = (VariableTree) t;
            var isPrivate = v.getModifiers().getFlags().contains(Modifier.PRIVATE);
            if (!isPrivate || isLocalVariable(path)) {
                return true;
            }
        }
        if (t instanceof MethodTree) {
            var m = (MethodTree) t;
            var isPrivate = m.getModifiers().getFlags().contains(Modifier.PRIVATE);
            var isEmptyConstructor = m.getParameters().isEmpty() && m.getReturnType() == null;
            if (!isPrivate || isEmptyConstructor) {
                return true;
            }
        }
        if (t instanceof ClassTree) {
            var c = (ClassTree) t;
            var isPrivate = c.getModifiers().getFlags().contains(Modifier.PRIVATE);
            if (!isPrivate) {
                return true;
            }
        }
        // Check if t has been referenced by a reachable element
        var el = trees.getElement(path);
        if (reachable.contains(el)) {
            return true;
        }
        return false;
    }

    private boolean isLocalVariable(TreePath path) {
        if (path.getLeaf() instanceof VariableTree) {
            var parent = path.getParentPath().getLeaf();
            return !(parent instanceof ClassTree)
                    && !(parent instanceof MethodTree) // TODO hint for unused parameters
                    && !(parent instanceof LambdaExpressionTree);
        }
        return false;
    }

    @Override
    public Void visitVariable(VariableTree t, Void __) {
        if (isLocalVariable(path)) {
            foundPrivateDeclaration();
            super.visitVariable(t, null);
        } else if (isReachable(path)) {
            super.visitVariable(t, null);
        } else {
            foundPrivateDeclaration();
        }
        return null;
    }

    @Override
    public Void visitMethod(MethodTree t, Void __) {
        if (isReachable(path)) {
            super.visitMethod(t, null);
        } else {
            foundPrivateDeclaration();
        }
        return null;
    }

    @Override
    public Void visitClass(ClassTree t, Void __) {
        if (isReachable(path)) {
            super.visitClass(t, null);
        } else {
            foundPrivateDeclaration();
        }
        return null;
    }

    @Override
    public Void visitIdentifier(IdentifierTree t, Void __) {
        foundReference();
        return super.visitIdentifier(t, null);
    }

    @Override
    public Void visitMemberSelect(MemberSelectTree t, Void __) {
        foundReference();
        return super.visitMemberSelect(t, null);
    }

    @Override
    public Void visitMemberReference(MemberReferenceTree t, Void __) {
        foundReference();
        return super.visitMemberReference(t, null);
    }

    @Override
    public Void visitNewClass(NewClassTree t, Void __) {
        foundReference();
        return super.visitNewClass(t, null);
    }
}

{'fraction_non_alphanumeric': 0.10555790363482671, 'fraction_numerical': 0.0, 'mean_word_length': 2.003808314820692, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3507083', 'n_tokens_mistral': 2611, 'n_tokens_neox': 2545, 'n_words': 635}