-
Notifications
You must be signed in to change notification settings - Fork 0
Migrate ValidationCode
to PSI
#186
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
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #186 +/- ##
============================================
+ Coverage 32.16% 32.47% +0.31%
+ Complexity 329 312 -17
============================================
Files 141 131 -10
Lines 2786 2590 -196
Branches 227 213 -14
============================================
- Hits 896 841 -55
+ Misses 1824 1689 -135
+ Partials 66 60 -6 |
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.
Please see my comments.
* message which is going to be validated later. | ||
* | ||
* <p>Note: this annotation is not intended to be retained at runtime. |
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.
This block repeats the same idea: the retention is CLASS
because we don't need RUNTIME
. Please rephrase.
@@ -42,6 +42,11 @@ | |||
* is overridden with a version which returns only valid messages, that version should be marked | |||
* with this annotation. | |||
* | |||
* <p>Note: this annotation is not intended to be retained at runtime. |
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.
This block repeats the same idea: the retention is CLASS
because we don't need RUNTIME
. Please rephrase.
/** | ||
* Exposes [typeSystem] property, so that the code generation context could use it. | ||
*/ | ||
public override val typeSystem: TypeSystem |
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.
Let's keep the encapsulation here, as discussed vocally.
* | ||
* @param base The base plugin to extend. | ||
* @constructor Creates an instance that contains all the components from the given [base] plugin. | ||
* Note: [`(set_once)`][SetOnceRenderer] option is rendered with its own |
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.
It's not the option which is rendered. We render validation code serving the (set_once)
constraint.
I would start the explanation of the matter with the enumeration of the renderers included into the ValidationPlugin
, and then explain them both briefly.
|public java.util.Optional<io.spine.validate.ValidationError> validate() { | ||
| ${validateMethodBody(formattedConstraints)} | ||
|} | ||
""".trimMargin(), this |
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.
Why do we use the style which requires trimMargin()
here, and the trimIndent()
style below?
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.
@yevhenii-nadtochii LGTM in general. Please see my comments.
* Note: [`(set_once)`][SetOnceRenderer] validation code is rendered with its own | ||
* renderer because it significantly differs from the rest of constraints. This option | ||
* modifies the message builder behavior. The rest of the constraints are more about | ||
* message state assertions. |
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.
Did you mean "builder state assertions"?
* implement [io.spine.validate.ValidatingBuilder] interface. Logically, it is a part | ||
* of this renderer, but as for now, it is a standalone renderer before this class | ||
* migrates to PSI. | ||
* This rendered is applied to every [Message] being compiled, even if the message |
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.
"This rendered ..." has some grammar issues.
@@ -0,0 +1,237 @@ | |||
/* | |||
* Copyright 2024, TeamDev. All rights reserved. |
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.
It's 2025.
* All validation logic is injected into the [ValidatableMessage.validate] method | ||
* within the message class itself. This allows a message instance to be validated even | ||
* after it has been built. Note that this method does not throw exceptions directly; | ||
* instead, it returns any detected violations, if any. |
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.
"...it returns the detected violations, if any."
* instead, it returns any detected violations, if any. | ||
* | ||
* The message builder is modified to invoke the [ValidatableMessage.validate] just | ||
* before returning from its [build][com.google.protobuf.Message.Builder.build] method. |
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.
"before returning the result from ..."
} | ||
|
||
/** | ||
* Generates a Java [constraint][constraints], [supportingFields] and [supportingMethods] |
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.
"a" cannot be used with plurals. Please review.
* 1. [constraints] are joined to [String] using [joinToString] instead of `joinByLine()` | ||
* because the contained code blocks already have new lines when converted to string. | ||
* 2. For the same reason, we trim the result because a trailing empty line is not needed. | ||
* 3. When creating PSI method from the text, we have to use [trimMargin] and '|' symbols |
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.
Please review this p.3 using any grammar checker. I personally use Grazie (as a Chrome extension).
I suspect some prepositions are missing.
* Searches for a method in this [PsiClass] matching the given signature | ||
* Returns a nested class declared in this [PsiClass]. | ||
* | ||
* @param simpleName The class simple 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.
"The simple name of the class."
* The basic implementation of ProtoData validation plugin, which builds | ||
* language-agnostic representation of the declared constraints. | ||
* | ||
* The concrete implementations should provide [renderers], which will |
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 remove "will".
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.
LGTM
This change set migrates the main Java validation renderer to PSI.
All Java class modifications are now performed within a single method in
MessageValidationCode.render(psiFile)
, allowing for quick grasp of what is actually mutated and how.This class deals with JavaPoet only in a degree to remain compatible with rules-based
CodeGenerator
output. These generators produceCodeBlock
,MethodSpec
andFieldSpec
, which are easily converted toPsiStatements
(our custom wrapper forPsiCodeBlock
, see below),PsiMethod
andPsiField
.Note: this PR doesn't bring new features, only migration to PSI and refactoring. Another preparational step to introduce option-specific generators.
Particular changes
@Validated
and@NonValidated
that these annotations are not meant for runtime.ValidatableMessage
andValidatingBuilder
interfaces.CodeGenerator.supportingMemebers()
has been split intosupportingFields()
andsupportingMethods()
. We can't create general-purpose elements with PSI, only specific ones. Usage ofQueriyng
were replaced withTypeSystem
.ValidationPlugin
abstract, withrenderers
being expected on the constructor. This makes the declaration ofJavaValidationPlugin
more straightforward.MessageValidationCode
now contains both modifications of the message class and its builder. I prefer having them both in the same method because they are intertwined. Modifying ofBuilder.build()
doesn't make sense without presence ofMessage.validate()
.return java.util.Optional.empty();
invalidate()
when the message doesn't declare constraints. No need to put there anif
block, which always evaluates toOptional.empty()
.SetOnceValidationRenderer
toSetOnceRenderer
. Now, it looks like thisValidation
word is redundant.Removed classes
These classes were merged into a single
MessageValidationCode
:ImplementValidatingBuilder
.ValidateMethodCode
.ValidationCode
.ValidationConstraintsCode
.These classes are no longer needed (all from
java.point
package):BuilderInsertionPoint
BuilderMethodReturnTypeAnnotation
BuildPartialReturnTypeAnnotation
.CachingJavaClassSource
.ExtraValidation
.ParsedSources
.ValidateBeforeReturn
.BuilderMethodsSpec
was testing the insertion points, not the generated code.MockPrinter
was a fixture forBuilderMethodsSpec
.API suggestions for PSI
PsiClass.nested(simpleName: String): PsiClass
– allows easily get a nestedMessage.Builder
when you already have aPsiClass
of theMessage
at your disposal.PsiElement.findFirstByText(...)
, which acceptsString
. I have taken it from(set_once)
implementation because this is the most reliable way to find a place to inject the code somewhere in the method body. Even if we know, that we want to inject before the last statement – it doesn't help. The lastPsiElement
inPsiMethod.body
is alwaysright brace
. Ok, let's try its previous sibling (should it bereturn
?) – no, it can bereturn ...;
,whitespace
or evenline break
. Depends on the formatting. PSI stores formatting elements along with other elements.PsiStatements
wrapper to avoid dealing withPsiCodeBlock
when we need to represent a list of statements. Invalidation
codebase, we treat code blocks asList<Statement>
. But within PSI, a code block is more about Java grammar, where it is a list of statements and the curly braces{ <statements> }
. This sophisticates 1) creating a set of statements from text and 2) inserting them to other code blocks.PsiClass.findMethodBySignature()
andPsiClass.methodWithSignature()
, which were introduced for(set_once)
.