Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Refaster matching algorithm #104

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Suggestions
  • Loading branch information
Stephan202 authored and rickie committed Aug 25, 2022
commit 9cc6c8448509bbcab8a77babe76adedc9aba9ea2

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,61 +1,99 @@
package tech.picnic.errorprone.bugpatterns;

import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
* A node in an immutable tree.
*
* <p>The tree's edges are string-labeled, while its leaves store values of type {@code T}.
*/
@AutoValue
abstract class Node<T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some documentation to this class. (And arguably we should add unit tests.)

static <T> Node<T> create(Map<String, Node<T>> children, ImmutableSet<T> candidateRules) {
return new AutoValue_Node<>(ImmutableMap.copyOf(children), candidateRules);
static <T> Node<T> create(Map<String, Node<T>> children, ImmutableList<T> values) {
return new AutoValue_Node<>(ImmutableSortedMap.copyOf(children), values);
}

public abstract ImmutableMap<String, Node<T>> children();

public abstract ImmutableSet<T> candidateRules();

static <C> Node<C> createRefasterTemplateTree(
List<C> refasterRules, Function<C, ImmutableSet<ImmutableSortedSet<String>>> edgeExtractor) {
// XXX: Improve this method...
List<ImmutableSet<ImmutableSortedSet<String>>> beforeTemplateIdentifiers =
refasterRules.stream().map(edgeExtractor).collect(toImmutableList());

BuildNode<C> tree = BuildNode.create();
for (int i = 0; i < refasterRules.size(); i++) {
tree.register(beforeTemplateIdentifiers.get(i), refasterRules.get(i));
}
static <T> Node<T> create(
List<T> values, Function<T, ImmutableSet<ImmutableSortedSet<String>>> pathExtractor) {
BuildNode<T> tree = BuildNode.create();
tree.register(values, pathExtractor);
return tree.immutable();
}

void collectCandidateTemplates(ImmutableList<String> sourceIdentifiers, Consumer<T> sink) {
candidateRules().forEach(sink);
abstract ImmutableMap<String, Node<T>> children();

if (sourceIdentifiers.isEmpty() || children().isEmpty()) {
abstract ImmutableList<T> values();

void collectCandidateTemplates(ImmutableList<String> candidateEdges, Consumer<T> sink) {
values().forEach(sink);

if (candidateEdges.isEmpty() || children().isEmpty()) {
return;
}

if (children().size() < sourceIdentifiers.size()) {
if (children().size() < candidateEdges.size()) {
for (Map.Entry<String, Node<T>> e : children().entrySet()) {
if (sourceIdentifiers.contains(e.getKey())) {
e.getValue().collectCandidateTemplates(sourceIdentifiers, sink);
if (candidateEdges.contains(e.getKey())) {
e.getValue().collectCandidateTemplates(candidateEdges, sink);
}
}
} else {
ImmutableList<String> remainingSourceCandidateEdges =
sourceIdentifiers.subList(1, sourceIdentifiers.size());
Node<T> child = children().get(sourceIdentifiers.get(0));
ImmutableList<String> remainingCandidateEdges =
candidateEdges.subList(1, candidateEdges.size());
Node<T> child = children().get(candidateEdges.get(0));
if (child != null) {
child.collectCandidateTemplates(remainingSourceCandidateEdges, sink);
child.collectCandidateTemplates(remainingCandidateEdges, sink);
}
collectCandidateTemplates(remainingCandidateEdges, sink);
}
}

@AutoValue
@SuppressWarnings("AutoValueImmutableFields" /* Type is used only during `Node` construction. */)
abstract static class BuildNode<T> {
static <T> BuildNode<T> create() {
return new AutoValue_Node_BuildNode<>(new HashMap<>(), new ArrayList<>());
}

abstract Map<String, BuildNode<T>> children();

abstract List<T> values();

private void register(
List<T> values, Function<T, ImmutableSet<ImmutableSortedSet<String>>> pathsExtractor) {
for (T value : values) {
for (ImmutableSet<String> path : pathsExtractor.apply(value)) {
registerPath(value, path.asList());
}
}
collectCandidateTemplates(remainingSourceCandidateEdges, sink);
}

private void registerPath(T value, ImmutableList<String> path) {
path.stream()
.findFirst()
.ifPresentOrElse(
edge ->
children()
.computeIfAbsent(edge, k -> BuildNode.create())
.registerPath(value, path.subList(1, path.size())),
() -> values().add(value));
}

private Node<T> immutable() {
return Node.create(
Maps.transformValues(children(), BuildNode::immutable), ImmutableList.copyOf(values()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,29 @@ public static String treeToString(Tree tree, VisitorState state) {
}

/**
* Returns a string representation of the given {@link Tree.Kind}.
* Returns a unique string representation of the given {@link Tree.Kind}.
*
* @return A string representation of the operator or else throws an exception.
* @return A string representation of the operator, if known
* @throws IllegalArgumentException If the given input is not supported.
*/
// XXX: List needs to be extended, as it currently only supports `BinaryTree`s and `UnaryTree`s.
// XXX: Extend list to cover remaining cases; at least for any `Kind` that may appear in a
// Refaster template.
static String treeKindToString(Tree.Kind kind) {
switch (kind) {
case ASSIGNMENT:
return "=";
case POSTFIX_INCREMENT:
return "x++";
case PREFIX_INCREMENT:
return "++";
return "++x";
case POSTFIX_DECREMENT:
return "x--";
case PREFIX_DECREMENT:
return "--";
return "--x";
case UNARY_PLUS:
case PLUS:
return "+";
return "+x";
case UNARY_MINUS:
case MINUS:
return "-";
return "-x";
case BITWISE_COMPLEMENT:
return "~";
case LOGICAL_COMPLEMENT:
Expand All @@ -55,6 +59,10 @@ static String treeKindToString(Tree.Kind kind) {
return "/";
case REMAINDER:
return "%";
case PLUS:
return "+";
case MINUS:
return "-";
case LEFT_SHIFT:
return "<<";
case RIGHT_SHIFT:
Expand Down Expand Up @@ -83,6 +91,28 @@ static String treeKindToString(Tree.Kind kind) {
return "&&";
case CONDITIONAL_OR:
return "||";
case MULTIPLY_ASSIGNMENT:
return "*=";
case DIVIDE_ASSIGNMENT:
return "/=";
case REMAINDER_ASSIGNMENT:
return "%=";
case PLUS_ASSIGNMENT:
return "+=";
case MINUS_ASSIGNMENT:
return "-=";
case LEFT_SHIFT_ASSIGNMENT:
return "<<=";
case RIGHT_SHIFT_ASSIGNMENT:
return ">>=";
case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
return ">>>=";
case AND_ASSIGNMENT:
return "&=";
case XOR_ASSIGNMENT:
return "^=";
case OR_ASSIGNMENT:
return "|=";
default:
throw new IllegalStateException("Cannot convert Tree.Kind to a String: " + kind);
}
Expand Down
Loading