Skip to content

[core] Cleanup Node and AbstractNode#2447

Merged
adangel merged 32 commits into
pmd:pmd/7.0.xfrom
oowekyala:cleanup-abstract-node
May 2, 2020
Merged

[core] Cleanup Node and AbstractNode#2447
adangel merged 32 commits into
pmd:pmd/7.0.xfrom
oowekyala:cleanup-abstract-node

Conversation

@oowekyala

@oowekyala oowekyala commented Apr 26, 2020

Copy link
Copy Markdown
Member

Describe the PR

This removes deprecated methods of Node and AbstractNode, and tears down the functionality that is in AbstractNode but not used by all subclasses. It also cleans up some tests that were either using deprecated functionality, or were just building invalid nodes.

Changes in pmd-core:

  • Remove most deprecated methods from AbstractNode and Node
  • Move AbstractNode into the impl subpackage
  • Remove some fields from AbstractNode
    • the image field, which is pushed down to abstract base classes that need it
    • the begin/end/line/column fields, pushed down to a new AbstractNodeWithTextCoordinates base class (which eg Jjtree nodes don't extend). This base class name is ugly but I think we can remove it later if we add an object to encapsulate this position information
    • the id field is pushed down to AbstractJjtreeNode
    • the fields about DataflowNode are removed, instead the data flow framework uses a key in the data map
  • Make the remaining fields private
  • Make the methods for tree construction protected
    • This includes eg jjtClose/jjtOpen, which are pulled down from Node to AbstractJjtreeNode
  • Pull up some methods that are currently on the java-grammar branch and only available to AbstractJavaNode to AbstractNode (eg insertChild, this is now used by the apex module too)
  • Ensure Parser returns a RootNode
  • NodeStream methods in Node now use upper bounded wildcards, eg Node::ancestors returns NodeStream<? extends Node>. This is to make it overridable by eg NodeStream<JavaNode>. Duplication of the overrides is removed by an interface "GenericNode", because I found it was hard to add a type parameter to Node directly (very contagious), and it also wouldn't be useful in language-independent code, only in language modules. Some type parameters are added to AbstractNode, but this keeps complexity to a minimum while improving the type safety and ease of use of the API. For example, it's impossible to add an arbitrary Node as a child to a JavaNode now, the child must be an AbstractJavaNode. This in turn enables us to say for sure, that the ancestor stream contains only JavaNodes. Of course this is Java, so unchecked conversions can still break the type safety, but since mutation methods are now protected, we can be reasonably sure of it.

Changes in language modules:

  • Make AbstractPLSQLNode package-private (this moves up some of its methods to the interface, removes the method dump, because now we have getText anyway)
  • AbstractApexNodeBase is merged into AbstractApexNode and removed
  • Remove all childrenAccept methods (in several modules)
  • In pmd-java, ClassScope doesn't build its custom method declaration itself anymore. It just asks InternalApiBridge, which lets us make all node constructors package-private
  • In pmd-java, Comment now extends AbstractJjtreeNode, it's technically a separate tree: the parent of a comment cannot be a JavaNode, the child of a JavaNode cannot be a Comment. Thankfully this is now enforced at compile time. This needs some more thought, I don't know if this should stay or go, or what relation this would have to a "javadoc language module".

The changes in test sources are mostly fixing the following problems:

  • using testingOnlySetBeginLine/Column without setting other coordinates (which makes these methods inconsistent)
  • setting invalid coordinates (eg not correctly ordered, argument is 0 but line and columns start at 1)

Related issues

Ready?

  • Added unit tests for fixed bug/feature
  • Passing all unit tests
  • Complete build ./mvnw clean verify passes (checked automatically by travis)
  • Added (in-code) documentation (if needed)

@ghost

ghost commented Apr 26, 2020

Copy link
Copy Markdown
1 Message
📖 No java rules are changed!

Generated by 🚫 Danger

@adangel adangel added this to the 7.0.0 milestone May 2, 2020

@adangel adangel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the huge cleanup!

Consider my comments as food for thought 😄

*
* @param image The image to check
*/
default boolean hasImageEqualTo(String image) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Once we removed the generic "getImage" method in favor of more specific methods likes "getName" etc., this method also doesn't make sense and should be removed

* @param child The child to add
* @param index The index to which the child will be added
*/
protected void addChild(final B child, final int index) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need three different ways to add children? (addChild, insertChild, setChildren)
Can we reduce this maybe?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

setChildren in used only in tests AFAIK so could be pulled down to DummyNode.

I think addChild is a bit unsafe/hard to use:

  • nothing prevents you from writing addChild(child, 42), and leave 42 null slots in the array (which breaks the contract of the class)...
  • adding children with this method needs to be done right to left, otherwise adding n children performs n array resizes

OTOH I can't see another way to improve it right now apart from documentation

*
* @param index the child index
*/
protected void setChildIndex(final int index) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this needed as a protected method? It looks error prone, if this is ever forgotten to be called. And it probably shouldn't needed to be called -> addChild etc. should automatically make sure, the tree is consistent.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Indeed, I'll look into whether we can remove it


/**
* Interface that binds the return type of some node methods to a type
* parameter. This enforces that eg all children of such a node are from

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess, we should demand that all language impls use this interface. Maybe that's just in the documentation then?

* it must be set to some value that depends on the node type, not some
* arbitrary "1" or "2", and not necessarily a unique value.
*/
protected AbstractJjtreeNode(int id) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe we can name the "id" something like "productionId" or "jjtNodeNameIndex", to avoid the misinterpretation of "unique id"...


import net.sourceforge.pmd.lang.ast.AbstractNode;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.impl.AbstractNode;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In theory, AbstractNode should be irrelevant in this context - AbstractNode should only help to implement the Node API, but should not add to the API... (so no additional methods).

Maybe we don't need the NoAttrScope at all? If a intermediate (abstract) class adds a public method, it is public API... so why not expose it as XPath attribute?

public void setImage(String image) {
super.setImage(image);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm... why do we have a "DummyJavaNode" in src/main? Looks like, this should have gone into src/test....

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Indeed, I recall it's still in main because some rules about imports create dummy nodes for ImportWrapper... Hopefully those rules may be rewritten with the newer symbol table and ImportWrapper can be nuked

* Normally this is uppercase, unless the names is quoted ("name").
* </p>
*/
default String getCanonicalImage() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hm...yes, these methods are not ideal, too generic like "getImage". but this time on plsql...

@adangel adangel merged commit 57177ab into pmd:pmd/7.0.x May 2, 2020
@oowekyala oowekyala deleted the cleanup-abstract-node branch May 2, 2020 17:27
oowekyala added a commit to pmd/pmd-designer that referenced this pull request May 2, 2020
@adangel adangel added the in:ast About the AST structure or API, the parsing step label Jan 12, 2023
@adangel adangel mentioned this pull request Jan 23, 2023
55 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in:ast About the AST structure or API, the parsing step

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants