swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.27k stars 10.33k forks source link

[SR-10952] xCode shows warnings about casting optional(?) value to Any while it unwrapped(!) #59619

Open swift-ci opened 5 years ago

swift-ci commented 5 years ago
Previous ID SR-10952
Radar None
Original Reporter idomo (JIRA User)
Type Bug
Environment macOS ‏10.14.4 ‏(18E226‏) xCode 11.0 beta (11M336w)
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler, Standard Library, SwiftSyntax | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: 924d60396351aa05301bb722f224bcf9

Issue Description:

When trying to pass value with type `Type!`, Xcode shows this warning:

Coercion of implicitly unwrappable value of type 'Type?' to 'Any' does not unwrap optional

As you can see, Xcode ignoring that the type is already unwrapped and treats it as an optional value.

For example:

@IBDesignable
class MyLabel: UILabel {
    @IBInspectable var paddingTopBottom: CGFloat = 0
    @IBInspectable var paddingRightLeft: CGFloat = 0

    override func drawText(in rect: CGRect) {
        let padding = UIEdgeInsets(top: paddingTopBottom, left: paddingRightLeft, bottom: paddingTopBottom, right: paddingRightLeft)

        guard let labelText = text else { return super.drawText(in: rect.inset(by: padding)) }

        var newRect = rect
        if labelText.matches("[א-ת]") {
            let attributedText = NSAttributedString(string: labelText, attributes: [.font: font])

            newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

            if numberOfLines != 0 {
                newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
            }
        }
        super.drawText(in: newRect.inset(by: padding))
    }
}

You may ignore the logic itself and focus on this line:

NSAttributedString(string: labelText, attributes: [.font: font])

I'm getting the warning on the `font` variable, although you know it's defined as unwrapped value in the `UILabel` class:

open class UILabel : UIView, NSCoding, UIContentSizeCategoryAdjusting {
    ...
    open var font: UIFont!
    ...
}
belkadan commented 5 years ago

The warning is correct: an implicitly-unwrapped optional type is unwrapped if it needs to be, and converting to Any is not a case where it needs to be. You can read more about this at https://swift.org/blog/iuo.

Any ideas on how to make the error message more clear?

swift-ci commented 5 years ago

Comment by Ido (JIRA)

I’ve just noticed it happens also when I’m trying to print the value of Type!, there is a reason for me to forced unwrapped this variable, so why the compiler want me to treat it as optional if I told it it shouldn’t ever be with nil value?

Sorry, but I still don’t know why the warning is correct when it’s just ignoring my force unwrapping.

belkadan commented 5 years ago

You aren't force-unwrapping it. ! on a type is different from ! in an expression; the first means "unwrap automatically if necessary" and the second means "unwrap now no matter what". They're related concepts but not the same.

swift-ci commented 5 years ago

Comment by Ido (JIRA)

So what is the case that it would unwrap it automatically if I always have to give it a default value/force unwrap it in the expression itself?

belkadan commented 5 years ago

You do not always have to force-unwrap it in the expression itself. As described in the blog post, it will be unwrapped automatically if the context is expecting a non-optional version of the value. But Any really can hold anything, including Optional values, and so it won't be unwrapped if that's the expected type.

swift-ci commented 5 years ago

Comment by Ido (JIRA)

I think I got it now, thanks for the explanation!

And about your question for more understandable message - You may change it to something like:

Value of type 'Optional(Type!)' could not be converted to 'Any', you may specify a default value or force-unwrap it

This would give more context to the developer about having an "auto unwrapped variable" that is an Optional in the time of the conversion and will give some examples for what should be done.