Skip to content
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

SONARPY-2204 Migrate S5795 IdentityComparisonWithCachedTypesCheck to the new type model #2097

Merged
merged 3 commits into from
Oct 29, 2024

Conversation

Seppli11
Copy link
Contributor

@Seppli11 Seppli11 commented Oct 23, 2024

SONARPY-2204

Part of

@Seppli11 Seppli11 marked this pull request as ready for review October 24, 2024 10:01
Copy link
Contributor

Choose a reason for hiding this comment

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

The changes on the rule look good.
I'm not fully certain on the changes for the propagation of unary expression as I believe it should probably be done in FlowSensitiveTypeInference. I also think it would have deserved an explicitly separate ticket.
Small nitpick on the commit history that is not as clean as it could be.

@@ -49,12 +52,40 @@ public class IdentityComparisonWithCachedTypesCheck extends PythonSubscriptionCh
public static final String IS_QUICK_FIX_MESSAGE = "Replace with \"==\"";
public static final String IS_NOT_QUICK_FIX_MESSAGE = "Replace with \"!=\"";

/**
* Fully qualified names of constructors and functions that would are guaranteed to create fresh objects with

Choose a reason for hiding this comment

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

I know you didn't add this, but there's a typo here (that would are guaranteed -> that are guaranteed). Can you take the opportunity to fix it?

* <code>is</code>-comparison with such a reference will always return <code>False</code>.
* <p>
* Note that these are fully qualified names of (value-level) expressions, not types.
*/
private static final Set<String> FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF = new HashSet<>(
asList("frozenset", "bytes", "int", "float", "str", "tuple", "hash"));
private static final List<String> FQNS_CONSTRUCTORS_RETURNING_UNIQUE_REF =

Choose a reason for hiding this comment

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

Note: I'm confused to see this being further changed in this propagate unary expressions commit. I feel it would have been cleaner to squash this with the first commit.

@@ -18,6 +18,9 @@ def literal_comparison(param):
(4, 5) is param # Noncompliant {{Replace this "is" operator with "=="; identity operator is not reliable here.}}
# ^^

param is -1 #Noncompliant

Choose a reason for hiding this comment

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

Nitpick: We usually add a space # Noncompliant.
Also, just out of curiosity did you add these cases because of regressions on ruling?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it was from a ruling

@@ -152,6 +156,23 @@ public void visitNumericLiteral(NumericLiteral numericLiteral) {
numericLiteralImpl.typeV2(new ObjectType(pythonType, new ArrayList<>(), new ArrayList<>()));
}

@Override
public void visitUnaryExpression(UnaryExpression unaryExpr) {

Choose a reason for hiding this comment

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

I think by having this in the trivial type inference visitor rather than the flow-sensitive one, you are actually not covering cases such as:

x = 42
y = -x

The trivial type inference visitor is really meant to evaluate the type of trivial expressions that do not depend on control flow, such as builtins or the names of class and function definitions. It is only used for propagation in some cases where "normal" propagation would not lead to the desired result (see SONARPY-2244 and SONARPY-2245 for an example).

Here, the expression -42 has a trivial type only because the expression 42 has a trivial type (INT), but you don't have a guarantee that the expression for a unary operation will be trivial.

PythonType type = expr.typeV2();
if (isUnsuitableType(type)) {
if (expr instanceof Name name) {
SymbolV2 symb = name.symbolV2();

Choose a reason for hiding this comment

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

Does the comment and condition below still make sense? I don't think Name#symbolV2 can return null.

@Seppli11 Seppli11 force-pushed the SONARPY-2204 branch 2 times, most recently from 5f92243 to 596343c Compare October 25, 2024 13:44
@Seppli11
Copy link
Contributor Author

Seppli11 commented Oct 25, 2024

As discussed, the way this PR implements the propagation of unary expressions means that only the type of unary literal expressions are properly propagated (e.g. -1, but not -x or -(1 + 1)). However, as a baby step, this moves us in the right direction. Therefore, a disabled test was added, testing the more complex behaviour. The test can be enabled as part of SONARPY-2257, which should implement the proper propagation of unary expressions.

Copy link
Contributor

Choose a reason for hiding this comment

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

The overall changes look good.
However, I think it would make sense to make intentions a bit clearer.
I'd like you to split this in 3 commits:

  1. Infer types of unary expressions for literals
  2. Implement the changes of the rule (keeping the null check for symbols & the test that covers it, but not the ticket references to SONARPY-2259 and SONARPY-2260)
  3. Document missing symbols for builtins and unassigned variables. (add unit tests in SymbolTableBuilderV2Test highlighting SONARPY-2259 and SONARPY-2260).

(Normally, I'd split the 3rd commit if implementing the migration from scratch, but I think the cost of adding it at this point of the PR is rather low)

Comment on lines 157 to 158
type(arr) is tuple # see SONARPY-2259
some_thing is other_thing # see SONARPY-2260

Choose a reason for hiding this comment

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

I'm not sure it makes sense to reference these other tickets here.
I think there is a distinction to be made between:

  • A test case added to highlight an unwanted behavior of the rule (e.g FP/FN).
  • A test case added to ensure proper test coverage, because of an unrelated limitation.

In the first case, it is normal to reference the ticket that would fix the unwanted behavior. In the second, fixing the ticket will not change the test result anyway, and the reference to the ticket is likely to stay in the code because of that (c.f Michael's comments on our weekly about stale ticket references in our code). Simply having the case for coverage is enough.

That being said, I think it would make sense to have actual frontend tests for our symbol tables (most likely in SymbolTableBuilderV2Test) that highlight these behaviors and link to a ticket.

@@ -18,6 +18,9 @@ def literal_comparison(param):
(4, 5) is param # Noncompliant {{Replace this "is" operator with "=="; identity operator is not reliable here.}}
# ^^

param is -1 # Noncompliant
param is 1 + 1 #Noncompliant

Choose a reason for hiding this comment

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

Nitpick: Missing space.

PythonType type = expr.typeV2();
if (isUnsuitableType(type)) {
if (expr instanceof Name name) {
SymbolV2 symbol = name.symbolV2();

Choose a reason for hiding this comment

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

Name#symbolV2 should probably be annotated @CheckForNull since it can return null

@Seppli11 Seppli11 force-pushed the SONARPY-2204 branch 9 times, most recently from 96fde07 to 2795cfd Compare October 29, 2024 07:55
Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM. When SONARPY-2275 is merged, I believe this can be rebased, which will make ruling green then this can be merged.

@Seppli11 Seppli11 merged commit fe7e351 into master Oct 29, 2024
10 checks passed
@Seppli11 Seppli11 deleted the SONARPY-2204 branch October 29, 2024 15:07
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.

2 participants