-
-
Notifications
You must be signed in to change notification settings - Fork 354
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
WiP: fix replacing of field access #1444
Conversation
61612ce
to
2a6dd22
Compare
This PR changes behavior of the Templates. So it is not backward compatible. I suggest to make it consistent and to always replace name of the named element. @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? |
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?
} |
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)
} |
Ok thanks for clarifying. WDYT @tdurieux @monperrus? |
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 ;-) |
Very interesting discussion, thanks Pavel for shaking the template engine. Let's discuss first #1457 and then com back to this one. |
Why do you change example classes in 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. |
Looking again at the example in your last comment, I agree with the principle of your proposed behavioral change. |
@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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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 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. |
857edef
to
15e8b1a
Compare
The tests actually fails here |
15e8b1a
to
be800a9
Compare
This reverts commit 7005aef.
be800a9
to
5a9aa3a
Compare
5a9aa3a
to
853e8a3
Compare
I close this one as you pick back the work in #1476 |
…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
No description provided.