Repository navigation
Conversation
|
First of all: Thank you for your contribution! 💙My thoughts about adapting
|
|
@hankem — Thanks for the feedback! You make a great point about explicit vs implicit behavior. I actually agree that having inconsistent little side effects scattered through the API isn't great for maintainability. I've updated the PR to take your suggested approach: haveOnlyFinalFields() now does exactly what it says — checks for final fields, no hidden filtering Users who hit synthetic field violations can now just switch to the new method if they need that behavior. Makes it clear in the code what's going on instead of burying it in the docs. The tests still cover both cases (that haveOnlyFinalFields reports synthetic violations, and that the new method ignores them), so the regression coverage is there. All 15k+ tests pass. Ready for another look whenever you get a chance! |
| * are ignored, since they do not appear in the source code and cannot be declared final by the developer. | ||
| */ | ||
| @PublicAPI(usage = ACCESS) | ||
| public static ArchCondition<JavaClass> haveOnlyFinalFieldsOrSynthetic() { |
There was a problem hiding this comment.
Did you pick the name haveOnlyFinalFieldsOrSynthetic to have it right next to haveOnlyFinalFields?
I'd think the natural language pattern would otherwise be "final or synthetic fields".
There was a problem hiding this comment.
Good point, I agree: haveOnlyFinalOrSyntheticFields() follows the natural language order and is also the name you suggested in your first comment. I renamed it in ArchConditions, ClassesShould and ClassesShouldInternal, so the rule text now reads "classes should have only final or synthetic fields" (asserted in the test). I also added a note with a link to the new method in the Javadoc of haveOnlyFinalFields(), as you proposed. The full :archunit:test, spotlessCheck, architectureTest and spotbugsMain run is green.
There was a problem hiding this comment.
Or another thought, since I realized that FINAL and SYNTHETIC are not handled symmetrically
(synthetic fields aren't considered; they don't end up in a violation event's correspondingObjects):
Should the method be called haveOnlyFinalNonSyntheticFields?
Sorry for the back and forth... 🙈
| JavaClasses classes = GeneratedClassWithNonFinalSyntheticField.importIntoArchUnit(); | ||
|
|
||
| assertThatRule(rule).checking(classes) | ||
| .hasOnlyViolations(String.format("Field <%s.%s> is not final in (%s.java:0)", |
There was a problem hiding this comment.
Wouldn't we expect
| .hasOnlyViolations(String.format("Field <%s.%s> is not final in (%s.java:0)", | |
| .hasOnlyViolations(String.format("Field <%s.%s> is not final and not synthetic in (%s.java:0)", |
in this case?
There was a problem hiding this comment.
| .hasOnlyViolations(String.format("Field <%s.%s> is not final in (%s.java:0)", | |
| .hasOnlyViolations(String.format("Non-synthetic field <%s.%s> is not final in (%s.java:0)", |
There was a problem hiding this comment.
Ah, I see:
It's tricky because we're still using the HaveOnlyModifiersCondition effectively using be(modifier(FINAL)).
Maybe it's not worth to mention synthetic fields in the violation message.
I'm just thinking out loud...
| assertThatRule(rule).hasDescriptionContaining("classes should have only final or synthetic fields"); | ||
| JavaClasses classes = GeneratedClassWithNonFinalSyntheticField.importIntoArchUnit(); | ||
|
|
||
| assertThatRule(rule).checking(classes) |
There was a problem hiding this comment.
A most minor suggeestion. just because I saw it (i.e. feel free to ignore 😅):
If you wanted to have this more concise, you could rearrange it to
| assertThatRule(rule).hasDescriptionContaining("classes should have only final or synthetic fields"); | |
| JavaClasses classes = GeneratedClassWithNonFinalSyntheticField.importIntoArchUnit(); | |
| assertThatRule(rule).checking(classes) | |
| assertThatRule(rule) | |
| .hasDescriptionContaining("classes should have only final or synthetic fields") | |
| .checking(GeneratedClassWithNonFinalSyntheticField.importIntoArchUnit()) |
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_FIELDS; | ||
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_DIRECT_DEPENDENCIES_FROM_SELF; | ||
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_DIRECT_DEPENDENCIES_TO_SELF; | ||
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_FIELDS; |
There was a problem hiding this comment.
We could keep the imports in alphabetical order. 😅
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_FIELDS; | |
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_DIRECT_DEPENDENCIES_FROM_SELF; | |
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_DIRECT_DEPENDENCIES_TO_SELF; | |
| import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_FIELDS; |
|
The DCO check complains about the second commit's missing sign-off, but I'd anyway ask you to squash them together. We don't have to preserve the back and forth (partly due to my undecisiveness; sorry!) in the history... |
Some compilers create non-final synthetic fields, e.g. the Eclipse compiler generates a $SWITCH_TABLE$... field for every switch over an enum. classes().should().haveOnlyFinalFields() reports such fields as violations, although they do not appear in the source code and cannot be declared final by the developer. Keep haveOnlyFinalFields() exactly as it is and add haveOnlyFinalNonSyntheticFields() to ArchConditions and ClassesShould as an explicit alternative that ignores fields with modifier SYNTHETIC. Link the alternative from the Javadoc of haveOnlyFinalFields(). As javac never emits a non-final synthetic field, the tests generate a class file with such a field via ASM and import it with the ClassFileImporter. Resolves: TNG#1313 Signed-off-by: Sharang Gupta <sharang@sharanggupta.dev>
|
Thanks @hankem, I incorporated all of it and squashed everything into a single signed-off commit, rebased on current
The full |
cdcf260 to
3706aa5
Compare
hankem
left a comment
There was a problem hiding this comment.
Thank you for your contribution to ArchUnit!
Some compilers create non-final synthetic fields, e.g. the Eclipse compiler generates a
$SWITCH_TABLE$...field for every switch over an enum.classes().should().haveOnlyFinalFields()reported such fields as violations although they do not appear in the source code and cannot be declaredfinalby the developer (see the discussion in the issue, where ignoring synthetic members in predefined predicates was suggested).Keep
haveOnlyFinalFields()exactly as it is and addhaveOnlyFinalNonSyntheticFields()toArchConditionsandClassesShouldas an explicit alternative that ignores fields with modifierSYNTHETIC.Link the alternative from the Javadoc of
haveOnlyFinalFields().Initial plan (discarded)
As javac never emits a non-final synthetic field (the
$SWITCH_TABLE$field is compiler specific), the regression test generates a class file with such a field via ASM and imports it with theClassFileImporter, asserting that only the regular non-final field is reported.Verified with
:archunit:test(15804 tests),spotlessCheck,architectureTestandspotbugsMain.Resolves #1313