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

review: feat: Add support to Java 10 'var' keyword #2054

Merged
merged 37 commits into from
Jun 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7d8b51c
Upgrade JDT library version
surli Jan 3, 2018
eba7039
Update pom.xml
surli Jun 6, 2018
45858c7
Fix MavenLauncherTest
surli Jun 11, 2018
c610586
Fix another assertion
surli Jun 11, 2018
ec4ec77
Add a resource to provide an example of usage of var keyword in Java 10
surli Jun 11, 2018
d9aeb34
Add a new concept for an inferred variable
surli Jun 11, 2018
39259f3
Instead of creating a new node type, add a property in CtLocalVariable
surli Jun 11, 2018
2b3bab5
Start building a test
surli Jun 11, 2018
35d8a61
Merge branch 'upgrade-jdt-version' into add-var-keyword
surli Jun 11, 2018
632daab
Fix compliance issue with Java 10 and more.
surli Jun 11, 2018
5c33cba
First test is passing
surli Jun 11, 2018
df2e14d
Minor change in DJPP to print var when needed
surli Jun 11, 2018
c2edf14
Add contracts
surli Jun 11, 2018
beb4404
Add a resource to provide an example of usage of var keyword in Java 10
surli Jun 11, 2018
cbbe3bb
Add a new concept for an inferred variable
surli Jun 11, 2018
c48c4de
Instead of creating a new node type, add a property in CtLocalVariable
surli Jun 11, 2018
7136fdc
Start building a test
surli Jun 11, 2018
bcba3c7
Fix compliance issue with Java 10 and more.
surli Jun 11, 2018
8293439
First test is passing
surli Jun 11, 2018
b38a358
Minor change in DJPP to print var when needed
surli Jun 11, 2018
8df170a
Add contracts
surli Jun 11, 2018
994cfec
Merge branch 'add-var-keyword' of github.com:surli/spoon into add-var…
surli Jun 14, 2018
1f101f9
Fix CloneBuilder
surli Jun 14, 2018
3613829
Add right metamodel property on getter and setter
surli Jun 14, 2018
7855393
Fix AstCheckerTest to only fail if the parameters are not primitive t…
surli Jun 14, 2018
862626b
Fix last errors
surli Jun 14, 2018
cb69592
Fix checkstyle
surli Jun 14, 2018
a0ceeac
Add the modified ModelRoleHandlers
surli Jun 15, 2018
98ff3ab
Fix documentation
surli Jun 15, 2018
afeac73
Merge remote-tracking branch 'inria/master' into add-var-keyword
surli Jun 15, 2018
cbfaa6a
Fix the test
surli Jun 15, 2018
f160f0d
Fix licenses headers
surli Jun 15, 2018
96d3fec
Merge remote-tracking branch 'inria/master' into add-var-keyword
surli Jun 18, 2018
4d08254
Refactor: rename Role
surli Jun 18, 2018
60dd731
Fix missing license header
surli Jun 18, 2018
4335c6c
Fix test and contract
surli Jun 20, 2018
b32a427
Fix checkstyle and test
surli Jun 20, 2018
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
4 changes: 4 additions & 0 deletions doc/code_elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ switch(x) {
// defines a local variable x
int x = 0;


// local variable in Java 10
var x = 0;

```
### CtNewArray
[(javadoc)](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtNewArray.html)
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/spoon/compiler/builder/ComplianceOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ public ComplianceOptions() {
}

public T compliance(int version) {
args.add("-1." + version);
if (version < 10) {
args.add("-1." + version);
} else {
args.add("-" + version);
}

return myself;
}
}
24 changes: 24 additions & 0 deletions src/main/java/spoon/reflect/code/CtLocalVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
*/
package spoon.reflect.code;

import spoon.reflect.annotations.PropertyGetter;
import spoon.reflect.annotations.PropertySetter;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.reference.CtLocalVariableReference;
import spoon.support.DerivedProperty;
import spoon.support.UnsettableProperty;

import static spoon.reflect.path.CtRole.IS_INFERRED;

/**
* This code element defines a local variable definition (within an executable
* body).
Expand All @@ -31,6 +35,13 @@
* int x = 0;
* </pre>
*
* With Java 10, the local variable inference is now authorized, then the following code is valid too in a block scope:
*
* <pre>
* // local variable in Java 10
* var x = 0;
* </pre>
*
* @param <T>
* type of the variable
* @see spoon.reflect.declaration.CtExecutable
Expand Down Expand Up @@ -58,4 +69,17 @@ public interface CtLocalVariable<T> extends CtStatement, CtVariable<T>, CtRHSRec
@UnsettableProperty
<U extends CtRHSReceiver<T>> U setAssignment(CtExpression<T> assignment);

/**
* Return true if this variable's type is not explicitely defined in the source code, but was using the `var` keyword of Java 10.
*/
@PropertyGetter(role = IS_INFERRED)
boolean isInferred();

/**
* Set true if the variable must be inferred.
* Warning: this method should only be used if compliance level is set to 10 or more.
*/
@PropertySetter(role = IS_INFERRED)
<U extends CtLocalVariable<T>> U setInferred(boolean inferred);

}
71 changes: 44 additions & 27 deletions src/main/java/spoon/reflect/meta/impl/ModelRoleHandlers.java

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/main/java/spoon/reflect/path/CtRole.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public enum CtRole {
OPENED_PACKAGE(MODULE_DIRECTIVE),
SERVICE_TYPE(MODULE_DIRECTIVE),
IMPLEMENTATION_TYPE,
PROVIDED_SERVICE(MODULE_DIRECTIVE);
PROVIDED_SERVICE(MODULE_DIRECTIVE),
IS_INFERRED;

private final CtRole superRole;
private final List<CtRole> subRoles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,11 @@ public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) {
}
if (!context.noTypeDecl()) {
elementPrinterHelper.writeModifiers(localVariable);
scan(localVariable.getType());
if (localVariable.isInferred() && this.env.getComplianceLevel() >= 10) {
getPrinterTokenWriter().writeKeyword("var");
} else {
scan(localVariable.getType());
}
printer.writeSpace();
}
printer.writeIdentifier(localVariable.getSimpleName());
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,12 @@ public boolean visit(LabeledStatement labeledStatement, BlockScope scope) {
@Override
public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
CtLocalVariable<Object> v = factory.Core().createLocalVariable();

boolean isVar = localDeclaration.type.isTypeNameVar(scope);

if (isVar) {
v.setInferred(true);
}
v.setSimpleName(CharOperation.charToString(localDeclaration.name));
if (localDeclaration.binding != null) {
v.setExtendedModifiers(getModifiers(localDeclaration.binding.modifiers, true, false));
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/spoon/support/reflect/code/CtLocalVariableImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.Set;

import static spoon.reflect.path.CtRole.DEFAULT_EXPRESSION;
import static spoon.reflect.path.CtRole.IS_INFERRED;
import static spoon.reflect.path.CtRole.NAME;
import static spoon.reflect.path.CtRole.TYPE;

Expand All @@ -55,6 +56,9 @@ public class CtLocalVariableImpl<T> extends CtStatementImpl implements CtLocalVa
@MetamodelPropertyField(role = CtRole.MODIFIER)
private CtModifierHandler modifierHandler = new CtModifierHandler(this);

@MetamodelPropertyField(role = CtRole.IS_INFERRED)
private boolean inferred;

@Override
public void accept(CtVisitor visitor) {
visitor.visitCtLocalVariable(this);
Expand Down Expand Up @@ -179,6 +183,18 @@ public <C extends CtRHSReceiver<T>> C setAssignment(CtExpression<T> assignment)
return (C) this;
}

@Override
public boolean isInferred() {
return this.inferred;
}

@Override
public <U extends CtLocalVariable<T>> U setInferred(boolean inferred) {
getFactory().getEnvironment().getModelChangeListener().onObjectUpdate(this, IS_INFERRED, inferred, this.inferred);
this.inferred = inferred;
return (U) this;
}

@Override
public CtLocalVariable<T> clone() {
return (CtLocalVariable<T>) super.clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ public <T> void visitCtLiteral(spoon.reflect.code.CtLiteral<T> e) {
public <T> void visitCtLocalVariable(spoon.reflect.code.CtLocalVariable<T> e) {
((spoon.reflect.code.CtLocalVariable<T>) (other)).setSimpleName(e.getSimpleName());
((spoon.reflect.code.CtLocalVariable<T>) (other)).setModifiers(e.getModifiers());
((spoon.reflect.code.CtLocalVariable<T>) (other)).setInferred(e.isInferred());
super.visitCtLocalVariable(e);
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/java/spoon/reflect/ast/AstCheckerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import spoon.reflect.code.CtThrow;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtTypeReference;
Expand Down Expand Up @@ -86,6 +87,7 @@ public boolean matches(CtInvocation<?> element) {

@Test
public void testPushToStackChanges() throws Exception {
// contract: setters should check the given parameters against NPE and the ModelChangeListener must be called!
final Launcher launcher = new Launcher();
launcher.getEnvironment().setNoClasspath(true);
// Implementations.
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/spoon/test/api/Metamodel.java
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ private static void initTypes(List<Type> types) {
.field(CtRole.TYPE, false, false)
.field(CtRole.DEFAULT_EXPRESSION, false, false)
.field(CtRole.COMMENT, false, false)

.field(CtRole.IS_INFERRED, false, false)
));

types.add(new Type("CtIf", spoon.reflect.code.CtIf.class, spoon.support.reflect.code.CtIfImpl.class, fm -> fm
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/spoon/test/api/MetamodelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public void testRuntimeMetamodel() {
if (expectedTypesByName.isEmpty() == false) {
problems.add("These Metamodel.Type instances are missing:" + expectedTypesByName.keySet());
}
assertTrue(String.join("\n", problems), problems.isEmpty());
assertTrue("You might need to update api/Metamodel.java: " + String.join("\n", problems), problems.isEmpty());
}
@Test
public void testGetterSetterFroRole() {
Expand Down
1 change: 1 addition & 0 deletions src/test/java/spoon/test/comment/CommentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ public void testDocumentationContract() throws Exception {
final Launcher launcher = new Launcher();
launcher.getEnvironment().setNoClasspath(true);
launcher.getEnvironment().setCommentEnabled(true);
launcher.getEnvironment().setComplianceLevel(10);
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this required here?

Copy link
Collaborator Author

@surli surli Jun 18, 2018

Choose a reason for hiding this comment

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

I added a new documentation snippet in CtLocalVariable with the following code:

var x = 10;

This won't compile if I do not use a compliance level to 10.

// interfaces.
launcher.addInputResource("./src/main/java/spoon/reflect/");
launcher.addInputResource("./src/main/java/spoon/support/reflect/");
Expand Down
83 changes: 83 additions & 0 deletions src/test/java/spoon/test/variable/InferredVariableTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package spoon.test.variable;

import com.google.common.io.Files;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import spoon.Launcher;
import spoon.reflect.CtModel;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.factory.TypeFactory;
import spoon.reflect.visitor.filter.TypeFilter;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

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

public class InferredVariableTest {

@Test
public void testInferredVariableAreMarked() {
// contract: if a variable is declared with 'var' keyword, it must be marked as inferred in the model
Launcher launcher = new Launcher();
launcher.getEnvironment().setComplianceLevel(10);
launcher.addInputResource("./src/test/resources/spoon/test/var/Main.java");

CtModel model = launcher.buildModel();
List<CtLocalVariable> localVariables = model.getElements(new TypeFilter<>(CtLocalVariable.class));
assertEquals(8, localVariables.size());

TypeFactory typeFactory = launcher.getFactory().Type();

assertTrue(localVariables.get(0).isInferred());
assertEquals(typeFactory.STRING, localVariables.get(0).getType());

assertFalse(localVariables.get(1).isInferred());
assertEquals(typeFactory.STRING, localVariables.get(1).getType());

assertTrue(localVariables.get(2).isInferred());
assertEquals("java.io.FileReader", localVariables.get(2).getType().getQualifiedName());

assertFalse(localVariables.get(3).isInferred());
assertEquals("java.io.FileReader", localVariables.get(3).getType().getQualifiedName());

assertTrue(localVariables.get(4).isInferred());
assertEquals(typeFactory.BOOLEAN_PRIMITIVE, localVariables.get(4).getType());

assertFalse(localVariables.get(5).isInferred());
assertEquals(typeFactory.BOOLEAN_PRIMITIVE, localVariables.get(5).getType());

assertTrue(localVariables.get(6).isInferred());
assertEquals(typeFactory.INTEGER_PRIMITIVE, localVariables.get(6).getType());

assertFalse(localVariables.get(7).isInferred());
assertEquals(typeFactory.INTEGER_PRIMITIVE, localVariables.get(7).getType());
}

@Test
public void testInferredVariableArePrintedWithVar() throws IOException {
// contract: if a variable is marked as inferred in the model, it must be pretty-printed with a 'var' keyword
Launcher launcher = new Launcher();
launcher.getEnvironment().setComplianceLevel(10);
launcher.addInputResource("./src/test/resources/spoon/test/var/Main.java");

File outputDir = Files.createTempDir();
launcher.setSourceOutputDirectory(outputDir);

launcher.run();

File outputFile = new File(outputDir, "fr/inria/sandbox/Main.java");
assertTrue(outputFile.exists());

String fileContent = StringUtils.join(Files.readLines(outputFile, Charset.defaultCharset()));

assertTrue(fileContent.contains("var mySubstring = \"bla\";"));
assertTrue(fileContent.contains("var myFile = new java.io.FileReader(new java.io.File(\"/tmp/myfile\")"));
assertTrue(fileContent.contains("var myboolean = true;"));
assertTrue(fileContent.contains("for (var i = 0;"));
}
}
49 changes: 49 additions & 0 deletions src/test/resources/spoon/test/var/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package fr.inria.sandbox;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Main {

public String toString() {
// 0
var mySubstring = "bla";

// 1 -> not inferred
String anotherSub = "bidule";

// 2
try (var myFile = new FileReader(new File("/tmp/myfile"));

// 3 -> not inferred
FileReader anotherOne = new FileReader(new File("/other/path"))) {
mySubstring += myFile.toString();
} catch (IOException e) {
mySubstring += "error";
}

switch (mySubstring) {
case "bla":

// 4
var myboolean = true;

// 5
boolean another = false;

return "";
}

// 6
for (var i = 0; i < mySubstring.length(); i++) {

// 7 -> not inferred
for (int j = i; j < 10; j++) {
return mySubstring;
}
}

return mySubstring;
}
}