Skip to content

[CS] Diagnose misuse of CheckedCastExpr with ~= #76644

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

Merged
merged 1 commit into from
Sep 25, 2024

Conversation

jameesbrown
Copy link
Contributor

Issue

When applying unwrap operator to 'as' or using 'is' in a type-cast pattern (specifically one that binds a value), the diagnostic for the resulting error could provide more guidance and suggest a fix-it.

Example with current diagnostic:

let maybeInt: Any = 1
if case let intValue as? Int = maybeInt {}
            `- error: pattern variable binding cannot appear in an expression

Fix

  1. Added diagnostic invalid_cast_in_pattern and an associated fix-it to remove !/? or replace 'is' with 'as'.
  2. Modified test/Constraints/rdar106598067.swift to account for the new diagnostic.

Example with new diagnostic:

if case let intValue as? Int = maybeInt {}
            ~~~~~~~~ ^ ~~~~~ 
                   //`- error: cannot conditionally downcast in a type-casting pattern
                   // - fix-it: remove '?'
if case let intValue as! Int = maybeInt {}
            ~~~~~~~~ ^ ~~~~~ 
                   //`- error: cannot force downcast in a type-casting pattern
                   // - fix-it: remove '!'
if case let intValue is Int = maybeInt {}
            ~~~~~~~~ ^ ~~~~
                   //`- error: use 'as' keyword to bind a matched value
                   // - fix-it: replace 'is' with 'as'

Resolves #44631.

Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

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

Thank you for the contribution! I left a few comments inline but overall this looks good!

@@ -1419,6 +1419,11 @@ ERROR(optional_chain_isnt_chaining,none,
())
ERROR(pattern_in_expr,none,
"%0 cannot appear in an expression", (DescriptivePatternKind))
ERROR(invalid_cast_in_pattern,none,
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's split this into three distinct diagnostics instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, in retrospect I did overload this unnecessarily.

diagnose(2).fixItReplace(isExpr->getAsLoc(), "as");
return true;
}
llvm_unreachable("There is no other kind of checked cast!");
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: let's just return false; here so that we get a "failed to produce a diagnostic" instead of a crash.

if (!BE || !isPatternMatchingOperator(BE->getFn()))
return ignoreInvalidPattern();
CS.recordFix(IgnoreInvalidCastInPattern::create(
CS, CS.getConstraintLocator(castExpr)));
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like it belongs in InvalidPatternInExprFailure, I don't think we need a special fix kind for this, only a tailored diagnostic type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I like that better too.

@jameesbrown
Copy link
Contributor Author

@xedin Thanks for the review! Will address your comments shortly.

@xedin
Copy link
Contributor

xedin commented Sep 23, 2024

Please re-request a review once you are done to help me keep track of this.

@jameesbrown jameesbrown force-pushed the issue-44631 branch 3 times, most recently from cc5f26f to 74b00a8 Compare September 24, 2024 02:26
if (BE && isPatternMatchingOperator(BE->getFn())) {
anchor = castExpr;
isInvalidCast = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say all this logic here can go directly into diagnoseInvalidCheckedCast...

Copy link
Contributor Author

@jameesbrown jameesbrown Sep 24, 2024

Choose a reason for hiding this comment

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

Okay, the only issue I run into with it there is with tuples and emitting multiple of the same error. I was doing it here so I could put the anchor at the CheckedCastExpr and then since the locator is uniqued on that, we wouldn't get the multiple errors. However, maybe that was not a good way for me to go about it. Also, I may have misunderstood your instruction, with the diagnostic -- is it better to create just a specialized function like diagnoseInvalidCheckedCast? Or a separate subclass of FailureDiagnostic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will try moving the logic into the diagnostic function and then use hasFixFor potentially to see if we can skip diagnosing a tuple.

Copy link
Contributor

Choose a reason for hiding this comment

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

is it better to create just a specialized function like diagnoseInvalidCheckedCast? Or a separate subclass of FailureDiagnostic?

Either way is fine, up to you.

Copy link
Contributor

Choose a reason for hiding this comment

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

I will try moving the logic into the diagnostic function and then use hasFixFor potentially to see if we can skip diagnosing a tuple.

Hm, it's the same diagnostic but at a different location in the expression because the fix gets anchored on each invalid pattern, right? I think that's fine.

Copy link
Contributor Author

@jameesbrown jameesbrown Sep 24, 2024

Choose a reason for hiding this comment

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

Yeah you're right, fix is anchored to each invalid pattern. Good to know it's okay, will remove that check then.

@jameesbrown jameesbrown force-pushed the issue-44631 branch 2 times, most recently from 0863d43 to 39cc887 Compare September 24, 2024 05:24
Using an unwrap operator with 'as' or the wrong keyword (i.e. `is`)
when already checking a cast via ~= results in error:
'pattern variable binding cannot appear in an expression'.
Add a diagnostic that provides more guidance and a fix-it
for the removal of ?/! or replacement of 'is' with 'as'.
@xedin
Copy link
Contributor

xedin commented Sep 24, 2024

@swift-ci please test

@jameesbrown
Copy link
Contributor Author

@xedin Thanks for your help with this! It seems like the test failures should be fixed by #76679.

@xedin
Copy link
Contributor

xedin commented Sep 24, 2024

@swift-ci please test

@xedin
Copy link
Contributor

xedin commented Sep 24, 2024

@swift-ci please test Windows platform

@xedin
Copy link
Contributor

xedin commented Sep 24, 2024

swiftlang/swift-testing#731
@swift-ci please test Windows platform

@xedin
Copy link
Contributor

xedin commented Sep 25, 2024

@swift-ci please test macOS platform

@xedin xedin merged commit 762fd4a into swiftlang:main Sep 25, 2024
5 checks passed
@jameesbrown jameesbrown deleted the issue-44631 branch September 27, 2024 13:34
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.

[SR-2022] Better fixit for type checking in switch statements
2 participants