Skip to content

Issue #13328: Kill mutation for UnusedLocalVariableCheck 8#13151

Merged
romani merged 1 commit intocheckstyle:masterfrom
Kevin222004:ul8
Oct 29, 2023
Merged

Issue #13328: Kill mutation for UnusedLocalVariableCheck 8#13151
romani merged 1 commit intocheckstyle:masterfrom
Kevin222004:ul8

Conversation

@Kevin222004
Copy link
Copy Markdown
Contributor

@Kevin222004 Kevin222004 commented Jun 3, 2023

Issue #13328: Kill mutation for UnusedLocalVariableCheck 8


Check :-

https://checkstyle.org/checks/coding/unusedlocalvariable.html#UnusedLocalVariable


Mutation

<mutation unstable="false">
<sourceFile>UnusedLocalVariableCheck.java</sourceFile>
<mutatedClass>com.puppycrawl.tools.checkstyle.checks.coding.UnusedLocalVariableCheck$TypeDeclDesc</mutatedClass>
<mutatedMethod>getUpdatedCopyOfVarStack</mutatedMethod>
<mutator>org.pitest.mutationtest.engine.gregor.mutators.experimental.NakedReceiverMutator</mutator>
<description>replaced call to com/puppycrawl/tools/checkstyle/api/DetailAST::getLastChild with receiver</description>
<lineContent>final DetailAST updatedScope = literalNewAst.getLastChild();</lineContent>
</mutation>


Explaination

the updated scope is changes as only literalNewAst Actually it is not affecting any thing if we make it as literalNewast or objBlock as its last child


Regression :-

Diff Regression config: https://gist.githubusercontent.com/Kevin222004/0dbdfb487f8e9a050ed0e6356f1a35b4/raw/31f7927f400e11e4285e86fd086b373095227848/unusedLocal.xml
Diff Regression projects: https://gist.githubusercontent.com/Kevin222004/21e3934e85f802e2fbd48af06d122364/raw/604256badd733d8568064f371d55657c04b00dfd/test-projects-2.properties
Report label: Final-Regression-1

@Kevin222004
Copy link
Copy Markdown
Contributor Author

Github, generate report

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jun 3, 2023

@Kevin222004 Kevin222004 changed the title Issue #13109: Kill mutation for UnusedLocalVariableCheck 8 Issue #13328: Kill mutation for UnusedLocalVariableCheck 8 Jul 1, 2023
@Kevin222004
Copy link
Copy Markdown
Contributor Author

Github, generate report

@Kevin222004 Kevin222004 marked this pull request as ready for review July 1, 2023 03:45
Copy link
Copy Markdown
Member

@romani romani left a comment

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

@romani romani left a comment

Choose a reason for hiding this comment

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

Ok to merge if reports are clean

@Kevin222004
Copy link
Copy Markdown
Contributor Author

Please ignore survivng mutation it will be covering at #13331

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jul 1, 2023

Copy link
Copy Markdown
Member

@rdiachenko rdiachenko left a comment

Choose a reason for hiding this comment

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

lgtm

@rdiachenko rdiachenko assigned Vyom-Yadav and unassigned rdiachenko Jul 1, 2023
Comment on lines +1019 to +1022
final DetailAST updatedScope = literalNewAst.getLastChild();
final Deque<VariableDesc> instAndClassVarDeque = new ArrayDeque<>();
instanceAndClassVarStack.forEach(instVar -> {
final VariableDesc variableDesc = new VariableDesc(instVar.getName(),
instVar.getTypeAst(), updatedScope);
instVar.getTypeAst(), literalNewAst);
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 am not ok with pitest complaining here. The logic is correct but it breaks the integrity of the check. We use last-child because we have a set definition of scope.

/**
* An array of scope tokens.
*/
private static final int[] SCOPES = {
TokenTypes.SLIST,
TokenTypes.LITERAL_FOR,
TokenTypes.OBJBLOCK,
};

The check by design uses { or for for determining scope. Extending this to literalNew is error-prone as it serves no value. The check is designed to have only the above-mentioned token as scope and this change can make extending this check difficult because one more case has to be handled.

I vote for suppressing this pitest mutation.

@romani @rdiachenko Please share your thoughts.

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.

If this value does not matter in current context, let's put null there or use some ctor without this value.

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.

The value of scope matters, it is used to pop out the variables at the right time (from the proper scope). I am saying this should stay:

final DetailAST updatedScope = literalNewAst.getLastChild();

And pitest should be suppressed in this case. This is an intentional check design otherwise every other parent-level token can be considered as scope but we have limited it due to intentional design and ease of maintenance.

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 do not see this.

there are only 2 usages of this class with scope defined in constructor: modified case and case

final VariableDesc desc = new VariableDesc(ident.getText(),
varDefAst.findFirstToken(TokenTypes.TYPE), findScopeOfVariable(varDefAst));

but this method also use parent, not a last child:

private static DetailAST findScopeOfVariable(DetailAST variableDef) {
final DetailAST result;
final DetailAST parentAst = variableDef.getParent();
if (TokenUtil.isOfType(parentAst, TokenTypes.SLIST, TokenTypes.OBJBLOCK)) {
result = parentAst;
}
else {
result = parentAst.getParent();
}
return result;

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.

but you might mean that we do validation based on scope token type

public void leaveToken(DetailAST ast) {
if (TokenUtil.isOfType(ast, SCOPES)) {
logViolations(ast, variables);

so any not SCOPES will be skiped here
private void logViolations(DetailAST scopeAst, Deque<VariableDesc> variablesStack) {
while (!variablesStack.isEmpty() && variablesStack.peek().getScope() == scopeAst) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Once again after evaluating and following pitest of pr #13152 pitest) it looks to me that the design needs to be changed or the logic looks very clean to me so let it be suppressed

Copy link
Copy Markdown
Member

@romani romani Jul 31, 2023

Choose a reason for hiding this comment

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

I lost track of this PR, sorry for no response for long, I will do deep dive one more time to make decision.

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.

@Vyom-Yadav

Why complicate the check more by adding a new scope when the current definition of scope makes everything work.

We are not extending SCOPES with a new scope.

So it does not matter if we do checking at OBJBLOCK or LITERAL_NEW, the result would be the same.

I didn't get it, why the result would be the same? If it's the same and we are not extending SCOPES and there is no input test, why shouldn't we accept Kevin's change?

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.

We are not extending SCOPES with a new scope.

We are not adding anything to SCOPES static field but now a variable has LITERAL_NEW as scope which is not what the check promises. Sort of violates the contract.

I didn't get it, why the result would be the same? If it's the same and we are not extending SCOPES and there is no input test, why shouldn't we accept Kevin's change?

The result would be the same because no instead of both OBJBLOCK and LITERAL_NEW don't contribute anything to the violations (as they are non-local). On top of that, it won't affect the stack order because they are popped off at the right time. (With this change, at LITERAL_NEW instead of OBJBLOCK)

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 tried to play with code preserving OBJBLOCK and resolving the mutation - https://github.com/checkstyle/checkstyle/pull/13546/files.

@Vyom-Yadav what do you think about such a change, assuming it passes all needed ci checks?

@romani
Copy link
Copy Markdown
Member

romani commented Jul 4, 2023

@Kevin222004 , please rebase to resolve conflict.

@romani
Copy link
Copy Markdown
Member

romani commented Aug 21, 2023

@Kevin222004 , conflict again

@Kevin222004
Copy link
Copy Markdown
Contributor Author

#13151 (comment) Resolved

Copy link
Copy Markdown
Member

@rdiachenko rdiachenko left a comment

Choose a reason for hiding this comment

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

lgtm

I tried a different approach here #13546 (comment) but it is less safe and may break existing logic. I'd rather go with the current approach with is simpler and safer.

@rdiachenko
Copy link
Copy Markdown
Member

@Kevin222004 please resolve conflicts

Copy link
Copy Markdown
Member

@romani romani left a comment

Choose a reason for hiding this comment

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

I am ok to merge.

Absence of any mutation will not next contributor to extend list of situations.

Copy link
Copy Markdown
Member

@Vyom-Yadav Vyom-Yadav left a comment

Choose a reason for hiding this comment

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

Though this is not what I wanted, but I can see why this is the better solution.

While designing this check, I didn't keep local classes in mind ('cause colliding variable names in local classes is really rare, and then their nesting, it can get reallyyy complex).

Pitest is indirectly indicating that only, this breaks the contract of the check but the logic is working fine. I will come back to this check sometime with hopefully a better design.


LGTM!

@Vyom-Yadav Vyom-Yadav assigned romani and unassigned Vyom-Yadav Oct 29, 2023
@romani romani merged commit eacee58 into checkstyle:master Oct 29, 2023
@romani
Copy link
Copy Markdown
Member

romani commented Oct 29, 2023

By merging this we close a door to all other leaks for survival, one day we will see how to make design better, we already spent too much time on this, we need a break.

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.

4 participants