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

WiP: fix replacing of field access #1444

Closed

Conversation

pvojtechovsky
Copy link
Collaborator

No description provided.

@pvojtechovsky pvojtechovsky force-pushed the fixSubstOfVariableAccess branch from 61612ce to 2a6dd22 Compare June 28, 2017 20:05
@pvojtechovsky
Copy link
Collaborator Author

pvojtechovsky commented Jun 28, 2017

This PR changes behavior of the Templates. So it is not backward compatible.
See the first commit to reproduce the problems: Substitution of field access and local variable access behaves different:
A) substitution of template parameter of type String replaces whole field access with String literal
B) substitution of template parameter of type String replaces local variable/method/... name only.

I suggest to make it consistent and to always replace name of the named element.
If one needs to generate String literal by template s/he can simply use string literal in template:

@Parameter
String $string$ = "x"
...
{
   System.out.println("$string$"); //which substitutes to System.out.println("x");
   //this was legacy implementation which produced same result like line above
// System.out.println($string$); //but now it substitutes to System.out.println(x);
}

Another way how to substitute string literal which still works is:

@Parameter
CtLiteral<String> $string$;  //<!--- note CtLiteral as parameter type
...
{
   System.out.println($string$);
}

WDYT?

@pvojtechovsky pvojtechovsky changed the title WiP: fix replacing of field access review: fix replacing of field access Jun 28, 2017
@surli
Copy link
Collaborator

surli commented Jun 30, 2017

I'm a bit confused about the quote usage in your example:

@Parameter
String $string$ = "x";

@Parameter
CtLiteral<String>  $otherString$; // create proper ctLiteral with x value

...
{
   System.out.println("$string$"); // now produce: System.out.println(x)
   System.out.println($string$); // now produce? System.out.println(x)? error? 
   System.out.println($otherString$); // now produce: System.out.println("x")
   System.out.println("$otherString$"); // now produce? System.out.println(""x"") ? 
}

I agree the behaviour should be consistent, and then do your example works the same way with other primitive types?

@Parameter
int $int$ = 42;

@Parameter
CtLiteral<Integer>  $otherInt$; // create proper ctLiteral with 42 value

...
{
   System.out.println("$int$"); // now produce: System.out.println(42) ? 
   System.out.println($otherInt$); // now produce: System.out.println(42) too? 
}

@pvojtechovsky
Copy link
Collaborator Author

After this PR is merged it will behave like this

@Parameter
String $string$ = "x";

@Parameter
CtLiteral<String>  $otherString$; // create proper ctLiteral with x value

...
{
   System.out.println("$string$"); // now produce: System.out.println("x")
   System.out.println($string$); // now produce: System.out.println(x)
   System.out.println($otherString$); // now produce: System.out.println("x")
   System.out.println("$otherString$"); // now produce: System.out.println("x")
}

and with int I have not tested it ... but it might behave like this:

@Parameter
int $int$ = 42;

@Parameter
CtLiteral<Integer>  $otherInt$; // create proper ctLiteral with 42 value

...
{
   System.out.println("$int$"); // now produce: System.out.println("42") 
   System.out.println($int$); // now produce: System.out.println(42)
   System.out.println($otherInt$); // now produce: System.out.println(42)
}

@surli
Copy link
Collaborator

surli commented Jun 30, 2017

Ok thanks for clarifying.
I'm still wondering about this PR: I understand that it can be useful to be able to have this behaviour System.out.println($string$); // now produce: System.out.println(x) but I find it really counter intuitive. In my point of view it does not make sense that after the substitution the method will call a completely different field, because the name was contained inside a string. I would expect the function to call the content of the field, as the old behavior did.

WDYT @tdurieux @monperrus?

@pvojtechovsky
Copy link
Collaborator Author

I came with this PR mainly because of this use case - I need to generate a class with many fields and appropriate accessor methods. But actually it is not easily possible, because all field references are replaced by string literal :( ... OK, there is workaround - to use TemplateParameter ... but it is not so nice like the solution which is possible after this PR.

See this example template:

@Parameter("$name$")
String name = "x";

int $name$;
int m_$name$;
{
    System.out.println($name$+m_$name$)
}

This template is substituted like this by legacy spoon code

int x;
int m_x;
{
    System.out.println("x"+m_x)
}

and after this PR is applied it produces this:

int x;
int m_x;
{
    System.out.println(x+m_x)
}

which is correct ;-)

@monperrus
Copy link
Collaborator

Very interesting discussion, thanks Pavel for shaking the template engine. Let's discuss first #1457 and then com back to this one.

@monperrus
Copy link
Collaborator

Why do you change example classes in src/test/java/spoon/test/template/testclasses?

if we change the existing behavior, this should only be seen in the expected values of assertions.

@pvojtechovsky
Copy link
Collaborator Author

if we change the existing behavior, this should only be seen in the expected values of assertions.

Interesting idea. I will extend the test instead of changing it. I have modified it to show how to migrate old templates to get the same behavior.

I just do not know, when I will have time for that.

@monperrus
Copy link
Collaborator

Looking again at the example in your last comment, I agree with the principle of your proposed behavioral change.

surli added a commit to pvojtechovsky/spoon that referenced this pull request Jul 6, 2017
@surli
Copy link
Collaborator

surli commented Jul 6, 2017

@pvojtechovsky so, following the discussion I added a new test corresponding to the example below and I changed SubsitutionVisitor to check between case A and case B to treat them differently. I don't know much about the helpers provided in template mechanism so maybe my solution is a bit complicated.

* String field = "x"
* System.printLn("field") //is substitutes as: System.printLn("x")
*/
// if parameter value is not the same name as field name
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I would preffer to have this check/decision sooner - somewhere in the Parameters class, where Map of substitution parameters is collected. Here we migh create value of type string literal and then code here would know what to do.

Why?

  • it will perform faster
  • it will not bad influence new substitution API feature: add fluent API for templates #1458 where client can decide whether s/he wants to modify field name or substitute by string literal early - during definition of map of parameters

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will try to move that code to Paramters#getNamesToValues today

@surli surli changed the title review: fix replacing of field access WiP: fix replacing of field access Jul 11, 2017
@pvojtechovsky
Copy link
Collaborator Author

pvojtechovsky commented Jul 15, 2017

I have copied FieldAccessTemplate into FieldAccessOfInnerClassTemplate with similar field name substitution, but this time in inner class. It shows that

My try to move Change SubstitutionVisitor to treat differently case A and case B code into Parameters#getNamesToValues failed on InvocationTemplate

public class InvocationTemplate extends ExtensionTemplate {
	IFace iface;
	
	void invoke() {
		iface.$method$();
	}
	@Parameter
	String $method$;
...
}

because I have converted "$method$" parameter value from String to StringLiteral, which is not correct in this case.

The solution for all these problems would be to modify the meaning of the proxy parameter like this: "Use the proxy parameter value if you want to replace simple name of the reference."

I will make new PR based on this PR which will show this solution soon.

@pvojtechovsky
Copy link
Collaborator Author

The tests actually fails here
TemplateTest.testFieldAccessNameSubstitutionInInnerClass:844 expected:<[value] = 7> but was:<["value"] = 7>
... as expected and explained in my previous comment. The possible solution is in #1476. I added it into another PR, because it rolls back some of Martin's and Simon's changes and modifies meaning of template Parameter#value.

@pvojtechovsky pvojtechovsky force-pushed the fixSubstOfVariableAccess branch from be800a9 to 5a9aa3a Compare July 20, 2017 17:31
@pvojtechovsky pvojtechovsky force-pushed the fixSubstOfVariableAccess branch from 5a9aa3a to 853e8a3 Compare July 23, 2017 05:10
@surli
Copy link
Collaborator

surli commented Jul 31, 2017

I close this one as you pick back the work in #1476

@surli surli closed this Jul 31, 2017
surli pushed a commit that referenced this pull request Sep 6, 2017
…ing parameter (#1476)

* test: reproduce the problem

* fix SubstitutionVisitor

* fix old tests - behavior changed!

* minor change to force Travis run again

* Revert "fix old tests - behavior changed!"

This reverts commit 7005aef.

* Add a new test corresponding to the example of #1444

* Only change the error comment when the contract is not respected with parameter value

* Add the new template test class

* Change SubstitutionVisitor to treat differently case A and case B

* Fix checkstyle

* test: field access in inner class

* rollback SubstitutionVisitor treat differently case A and case B

* convert String to Literal parameter value automatically

* disable one Substitution#checkTemplateContracts contract

* adapt test to pass SubstitutionVisitor changes

* adapt test InvocationTemplate

* adapt test FieldAccessOfInnerClassTemplate

* add GeneratedByMember required by new feature

* do not create CtLiteral automatically

* restore the Template Parameter constraint

* parameter of type String is simply substituted in method name

* If String literal is needed then template must already contain it

* report invalid field reference

* add path the the failing node into exception

* update Template documentation

* Fix PrinterTest

* Change documentation for Template

* Fix typo
@pvojtechovsky pvojtechovsky deleted the fixSubstOfVariableAccess branch September 1, 2018 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants