Skip to content

Commit

Permalink
add NameLogicDisambiguationTest
Browse files Browse the repository at this point in the history
  • Loading branch information
ftomassetti committed Jul 25, 2018
1 parent 69c04cc commit 02cad0c
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 42 deletions.
Expand Up @@ -16,6 +16,7 @@

package com.github.javaparser.symbolsolver.core.resolution;

import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.declarations.*;
import com.github.javaparser.resolution.types.ResolvedType;
Expand Down Expand Up @@ -95,4 +96,18 @@ default Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType>
return Optional.empty();
}
}

default Optional<VariableDeclarator> localVariableDeclarationInScope(String name) {
// TODO SymbolDeclarator symbolDeclarator = JavaParserFactory.getSymbolDeclarator(wrappedNode, typeSolver);
// TODO getSymbolDeclarationsRecursively
throw new UnsupportedOperationException();
}

default Optional<VariableDeclarator> parameterDeclarationInScope(String name) {
throw new UnsupportedOperationException();
}

default Optional<VariableDeclarator> fieldDeclarationInScope(String name) {
throw new UnsupportedOperationException();
}
}
Expand Up @@ -18,13 +18,27 @@

import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;

import javax.swing.text.html.Option;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

/**
* @author Federico Tomassetti
*/
public interface SymbolDeclarator {

// Optional<SymbolDeclarator> parent();
List<ResolvedValueDeclaration> getSymbolDeclarations();

// default List<ResolvedValueDeclaration> getSymbolDeclarationsRecursively() {
// if (parent().isPresent()) {
// List<ResolvedValueDeclaration> all = new LinkedList<>();
// all.addAll(parent().get().getSymbolDeclarationsRecursively());
// all.addAll(getSymbolDeclarations());
// return all;
// } else {
// return getSymbolDeclarations();
// }
// }
}
@@ -1,5 +1,6 @@
package com.github.javaparser.symbolsolver.resolution.naming;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.PackageDeclaration;
Expand All @@ -10,6 +11,10 @@
import com.github.javaparser.ast.stmt.TryStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.TypeParameter;
import com.github.javaparser.symbolsolver.core.resolution.Context;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;

/**
* NameLogic contains a set of static methods to implement the abstraction of a "Name" as defined
Expand Down Expand Up @@ -94,10 +99,13 @@ public static NameRole classifyRole(Node name) {
if (whenParentIs(EnumConstantDeclaration.class, name, (p, c) -> p.getName() == c)) {
return NameRole.DECLARATION;
}
if (whenParentIs(FieldAccessExpr.class, name, (p, c) -> p.getName() == c || p.getScope() == c)) {
return NameRole.REFERENCE;
}
throw new UnsupportedOperationException("Unable to classify role of name contained in "+ name.getParentNode().get().getClass().getSimpleName());
}

public static NameCategory classifyReference(Node name) {
public static NameCategory classifyReference(Node name, TypeSolver typeSolver) {
if (!name.getParentNode().isPresent()) {
throw new IllegalArgumentException("We cannot understand the category of a name if it has no parent");
}
Expand All @@ -114,7 +122,7 @@ public static NameCategory classifyReference(Node name) {
// Second, a name that is initially classified by its context as an AmbiguousName or as a PackageOrTypeName is
// then reclassified to be a PackageName, TypeName, or ExpressionName.
if (first.isNeedingDisambiguation()) {
NameCategory second = reclassificationOfContextuallyAmbiguousNames(name, first);
NameCategory second = reclassificationOfContextuallyAmbiguousNames(name, first, typeSolver);
assert !second.isNeedingDisambiguation();
return second;
} else {
Expand All @@ -125,10 +133,56 @@ public static NameCategory classifyReference(Node name) {
/**
* JLS 6.5.2. Reclassification of Contextually Ambiguous Names
*/
private static NameCategory reclassificationOfContextuallyAmbiguousNames(Node name, NameCategory ambiguousCategory) {
private static NameCategory reclassificationOfContextuallyAmbiguousNames(Node name, NameCategory ambiguousCategory,
TypeSolver typeSolver) {
if (!ambiguousCategory.isNeedingDisambiguation()) {
throw new IllegalArgumentException("The Name Category is not ambiguous: " + ambiguousCategory);
}
if (ambiguousCategory == NameCategory.AMBIGUOUS_NAME && isSimpleName(name)) {
return reclassificationOfContextuallyAmbiguousSimpleAmbiguousName(name, typeSolver);
}
throw new UnsupportedOperationException();
}

private static NameCategory reclassificationOfContextuallyAmbiguousSimpleAmbiguousName(Node nameNode,
TypeSolver typeSolver) {
// If the AmbiguousName is a simple name, consisting of a single Identifier:
//
// * If the Identifier appears within the scope (§6.3) of a local variable declaration (§14.4) or parameter
// declaration (§8.4.1, §8.8.1, §14.20) or field declaration (§8.3) with that name, then the AmbiguousName is
// reclassified as an ExpressionName.

String name = nameAsString(nameNode);
Context context = JavaParserFactory.getContext(nameNode, typeSolver);
// if (context.solveSymbolAsValue(name, typeSolver).isPresent()) {
// return NameCategory.EXPRESSION_NAME;
// }
if (context.localVariableDeclarationInScope(nameAsString(nameNode)).isPresent()) {
return NameCategory.EXPRESSION_NAME;
}
if (context.parameterDeclarationInScope(nameAsString(nameNode)).isPresent()) {
return NameCategory.EXPRESSION_NAME;
}
if (context.fieldDeclarationInScope(nameAsString(nameNode)).isPresent()) {
return NameCategory.EXPRESSION_NAME;
}

// * Otherwise, if a field of that name is declared in the compilation unit (§7.3) containing the Identifier by
// a single-static-import declaration (§7.5.3), or by a static-import-on-demand declaration (§7.5.4) then the
// AmbiguousName is reclassified as an ExpressionName.
//
// * Otherwise, if the Identifier is a valid TypeIdentifier and appears within the scope (§6.3) of a top level
// class (§8 (Classes)) or interface type declaration (§9 (Interfaces)), a local class declaration (§14.3) or
// member type declaration (§8.5, §9.5) with that name, then the AmbiguousName is reclassified as a TypeName.
//
// * Otherwise, if the Identifier is a valid TypeIdentifier and a type of that name is declared in the
// compilation unit (§7.3) containing the Identifier, either by a single-type-import declaration (§7.5.1), or
// by a type-import-on-demand declaration (§7.5.2), or by a single-static-import declaration (§7.5.3), or by
// a static-import-on-demand declaration (§7.5.4), then the AmbiguousName is reclassified as a TypeName.
//
// Otherwise, the AmbiguousName is reclassified as a PackageName. A later step determines whether or not a
// package of that name actually exists.

throw new UnsupportedOperationException();
}

Expand Down
@@ -0,0 +1,64 @@
package com.github.javaparser.symbolsolver.resolution.naming;

import com.github.javaparser.*;
import com.github.javaparser.ast.Node;
import com.github.javaparser.symbolsolver.resolution.AbstractResolutionTest;

import java.util.List;
import java.util.stream.Collectors;

import static org.junit.Assert.assertTrue;

public abstract class AbstractNameLogicTest extends AbstractResolutionTest {

protected Node getNameInCodeTollerant(String code, String name, ParseStart parseStart) {
return getNameInCode(code, name, parseStart, true);
}

protected Node getNameInCode(String code, String name, ParseStart parseStart) {
return getNameInCode(code, name, parseStart, false);
}

private Node getNameInCode(String code, String name, ParseStart parseStart, boolean tollerant) {
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_10);
ParseResult<? extends Node> parseResult = new JavaParser(parserConfiguration).parse(parseStart, new StringProvider(code));
if (!parseResult.isSuccessful()) {
parseResult.getProblems().forEach(p -> System.out.println("ERR: " + p));
}
assertTrue(parseResult.isSuccessful());
Node root = parseResult.getResult().get();
List<Node> allNames = root.findAll(Node.class).stream()
.filter(NameLogic::isAName)
.collect(Collectors.toList());
List<Node> matchingNames = allNames.stream()
.filter(n -> NameLogic.nameAsString(n).equals(name))
.collect(Collectors.toList());
// In case of one name being contained in other as is, we remove it
for (int i=0;i<matchingNames.size();i++) {
Node container = matchingNames.get(i);
for (int j=i+1;j<matchingNames.size();j++) {
Node contained = matchingNames.get(j);
if (contained.getParentNode().isPresent() && contained.getParentNode().get() == container
&& NameLogic.nameAsString(contained).equals(NameLogic.nameAsString(container))) {
matchingNames.remove(j);
j--;
}
}
}

if (matchingNames.size() == 0) {
throw new IllegalArgumentException("Not found. Names found: " + String.join(", ",
allNames.stream().map(NameLogic::nameAsString).collect(Collectors.toList())));
} else if (matchingNames.size() > 1) {
if (tollerant) {
return matchingNames.get(matchingNames.size() - 1);
} else {
throw new IllegalArgumentException("Ambiguous: there are several matching.");
}
} else {
return matchingNames.get(0);
}
}

}
@@ -0,0 +1,36 @@
package com.github.javaparser.symbolsolver.resolution.naming;

import com.github.javaparser.*;
import com.github.javaparser.ast.Node;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.AbstractResolutionTest;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import org.junit.Test;

import java.util.List;
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class NameLogicDisambiguationTest extends AbstractNameLogicTest {

private void assertNameInCodeIsDisambiguited(String code, String name,
NameCategory syntacticClassification,
NameCategory nameCategory,
ParseStart parseStart, TypeSolver typeSolver) {
Node nameNode = getNameInCodeTollerant(code, name, parseStart);
assertEquals(syntacticClassification, NameLogic.syntacticClassificationAccordingToContext(nameNode));
assertEquals(nameCategory, NameLogic.classifyReference(nameNode, typeSolver));
}

@Test
public void ambiguousNameToLocalVar() {
assertNameInCodeIsDisambiguited("class A { void foo() {\n" +
"SomeClass a; a.aField;" + "\n" +
"} }", "a", NameCategory.AMBIGUOUS_NAME, NameCategory.EXPRESSION_NAME, ParseStart.COMPILATION_UNIT,
new ReflectionTypeSolver());
}


}
Expand Up @@ -20,45 +20,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class NameLogicTest extends AbstractResolutionTest {

private Node getNameInCode(String code, String name, ParseStart parseStart) {
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_10);
ParseResult<? extends Node> parseResult = new JavaParser(parserConfiguration).parse(parseStart, new StringProvider(code));
if (!parseResult.isSuccessful()) {
parseResult.getProblems().forEach(p -> System.out.println("ERR: " + p));
}
assertTrue(parseResult.isSuccessful());
Node root = parseResult.getResult().get();
List<Node> allNames = root.findAll(Node.class).stream()
.filter(NameLogic::isAName)
.collect(Collectors.toList());
List<Node> matchingNames = allNames.stream()
.filter(n -> NameLogic.nameAsString(n).equals(name))
.collect(Collectors.toList());
// In case of one name being contained in other as is, we remove it
for (int i=0;i<matchingNames.size();i++) {
Node container = matchingNames.get(i);
for (int j=i+1;j<matchingNames.size();j++) {
Node contained = matchingNames.get(j);
if (contained.getParentNode().isPresent() && contained.getParentNode().get() == container
&& NameLogic.nameAsString(contained).equals(NameLogic.nameAsString(container))) {
matchingNames.remove(j);
j--;
}
}
}

if (matchingNames.size() == 0) {
throw new IllegalArgumentException("Not found. Names found: " + String.join(", ",
allNames.stream().map(NameLogic::nameAsString).collect(Collectors.toList())));
} else if (matchingNames.size() > 1) {
throw new IllegalArgumentException("Ambiguous: there are several matching.");
} else {
return matchingNames.get(0);
}
}
public class NameLogicTest extends AbstractNameLogicTest {



private void assertNameInCodeIsSyntactically(String code, String name, NameCategory nameCategory, ParseStart parseStart) {
Node nameNode = getNameInCode(code, name, parseStart);
Expand Down

0 comments on commit 02cad0c

Please sign in to comment.