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

[red-knot] Avoid undeclared path when raising conflicting declarations #14958

Merged
merged 3 commits into from
Dec 17, 2024

Conversation

dhruvmanila
Copy link
Member

@dhruvmanila dhruvmanila commented Dec 13, 2024

Summary

This PR updates the logic when raising conflicting declarations diagnostic to avoid the undeclared path if present.

The conflicting declaration diagnostics is added when there are two or more declarations in the control flow path of a definition whose type isn't equivalent to each other. This can be seen in the following example:

if flag:
	x: int
x = 1  # conflicting-declarations: Unknown, int

After this PR, we'd avoid considering "Unknown" as part of the conflicting declarations. This means we'd still flag it for the following case:

if flag:
	x: int
else:
	x: str
x = 1  # conflicting-declarations: int, str

A solution that's local to the exception control flow was also explored which required updating the logic for merging the flow snapshot to avoid considering declarations using a flag. This is preserved here: https://github.com/astral-sh/ruff/compare/dhruv/control-flow-no-declarations?expand=1.

The main motivation to avoid that is we don't really understand what the user experience is w.r.t. the Unknown type and the conflicting-declaration diagnostics. This makes us unsure on what the right semantics are as to whether that diagnostics should be raised or not and when to raise them. For now, we've decided to move forward with this PR and could decide to adopt another solution or remove the conflicting-declaration diagnostics in the future.

Closes: #13966

Test Plan

Update the existing mdtest case. Add an additional case specific to exception control flow to verify that the diagnostic is not being raised now.

@dhruvmanila dhruvmanila added the red-knot Multi-file analysis & type inference label Dec 13, 2024
Copy link
Contributor

github-actions bot commented Dec 13, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@dhruvmanila dhruvmanila marked this pull request as ready for review December 13, 2024 14:55
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

Tests look great! I think we should move the fix inside declarations_ty, and add a test for the "three paths, conflicting declarations in two of them" case.

Comment on lines 794 to 798
if let Some(undeclared_ty) = undeclared_ty {
if conflicting.iter().any(|ty| ty == &undeclared_ty) {
return ty;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this has an issue: there could be three or more conflicting types, e.g. int, str, and Unknown; this should still emit a diagnostic for the conflict between int and str.

It's also a little weird that we just search for Unknown, because there could be an actual annotation x: Foo in one path, where Foo resolves to Unknown (e.g. because it is imported from a module we can't find), and we would still treat this Unknown the same as the undeclared-path Unknown.

I think it would be easier to make a more correct fix if we do it inside declarations_ty instead, so that in the cases where we don't want to emit the diagnostic, we don't even return an Err result at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yes, thanks for pointing that out. I've pushed a commit that moves the logic to declarations_ty which doesn't consider the undeclared_ty when looking for conflicting types. It does add the type to the final declared type.

Comment on lines -34 to -50
"error[lint:conflicting-declarations] /src/tomllib/_parser.py:108:17 Conflicting declared types for `second_char`: Unknown, str | None",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:115:14 Name `char` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:126:12 Name `char` used when possibly not defined",
"error[lint:conflicting-declarations] /src/tomllib/_parser.py:267:9 Conflicting declared types for `char`: Unknown, str | None",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:348:20 Name `nest` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:353:5 Name `nest` used when possibly not defined",
"error[lint:conflicting-declarations] /src/tomllib/_parser.py:364:9 Conflicting declared types for `char`: Unknown, str | None",
"error[lint:conflicting-declarations] /src/tomllib/_parser.py:381:13 Conflicting declared types for `char`: Unknown, str | None",
"error[lint:conflicting-declarations] /src/tomllib/_parser.py:395:9 Conflicting declared types for `char`: Unknown, str | None",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:453:24 Name `nest` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:455:9 Name `nest` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:482:16 Name `char` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:566:12 Name `char` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:573:12 Name `char` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:579:12 Name `char` used when possibly not defined",
"warning[lint:possibly-unresolved-reference] /src/tomllib/_parser.py:580:63 Name `char` used when possibly not defined",
"error[lint:conflicting-declarations] /src/tomllib/_parser.py:590:9 Conflicting declared types for `char`: Unknown, str | None",
Copy link
Contributor

Choose a reason for hiding this comment

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

Love to see these go away!

@dhruvmanila dhruvmanila force-pushed the dhruv/avoid-undeclared-path branch from e31db29 to 914389f Compare December 16, 2024 06:38
@dhruvmanila dhruvmanila force-pushed the dhruv/avoid-undeclared-path branch from 914389f to 9967298 Compare December 16, 2024 06:40
@dhruvmanila dhruvmanila requested a review from carljm December 16, 2024 07:10
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

Looks great, thank you!

@dhruvmanila dhruvmanila merged commit dcdc6e7 into main Dec 17, 2024
21 checks passed
@dhruvmanila dhruvmanila deleted the dhruv/avoid-undeclared-path branch December 17, 2024 04:19
dcreager added a commit that referenced this pull request Dec 17, 2024
* main:
  [red-knot] Explicitly test diagnostics are emitted for unresolvable submodule imports (#15035)
  Fix stale File status in tests (#15030)
  [red-knot] Basic support for other legacy `typing` aliases (#14998)
  feat(AIR302): extend the following rules (#15015)
  [`perflint`] Simplify finding the loop target in `PERF401` (#15025)
  [red-knot] Avoid undeclared path when raising conflicting declarations (#14958)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[red-knot] declarations in try blocks
2 participants