How to suppress a specific warning in Swift - swift

I have a Swift function doing something like this:
func f() -> Int {
switch (__WORDSIZE) {
case 32: return 1
case 64: return 2
default: return 0
}
}
Because __WORDSIZE is a constant, the compiler always gives at least one warning in the switch body. Which lines are actually marked depends on the target I am building for (e.g. iPhone 5 vs. 6; interestingly iPhone 5 gives a warning for the 64-bit case whereas iPhone 6 gives two warnings for 32-bit and default).
I found out that the Swift equivalent for #pragma is // MARK:, so I tried
// MARK: clang diagnostic push
// MARK: clang diagnostic ignored "-Wall"
func f() -> Int {
switch (__WORDSIZE) {
case 32: return 1
case 64: return 2
default: return 0
}
}
// MARK: clang diagnostic pop
but the warnings remain, the MARKs seem to have no effect.
As a workaround, I now have something like this:
#if arch(arm) || arch(i386)
return 1
#else
#if arch(arm64) || arch(x86_64)
return 2
#else
return 0
#endif
#endif
– but of course this is not the same. Any hints…?

At present (Xcode 7.1), there seems to be no way of suppressing a specific warning in Swift (see e.g. How to silence a warning in swift).
In your special case, you can fool the compiler by
computing the number of bytes in a word:
func f() -> Int {
switch (__WORDSIZE / CHAR_BIT) { // Or: switch (sizeof(Int.self))
case 4: return 1
case 8: return 2
default: return 0
}
}
This compiles without warnings on both 32-bit and 64-bit architectures.

Related

Xcode 10.1, 'Element' collides with 'XML.Accessor.Element'?

I need to add some minor changes to iOS app.
I inherited swift 3 code. which can not build on current Xcode 10.2.1
I almost managed to build it on Xcode 10.1 except for the following errors, in SwiftyXMLParser, Accessor.swift:
public func makeIterator() -> AnyIterator<Accessor> {
let generator: [Element]
switch self {
case .failure(_):
generator = [Element]() // Error 2
case .singleElement(let element):
generator = [element] // Error 1.1
case .sequence(let elements):
generator = elements // Error 1.2
}
var index = 0
return AnyIterator {
let nextAccessor: Accessor?
if index < generator.count {
nextAccessor = Accessor(generator[index]) // Error 1.3
index += 1
} else {
nextAccessor = nil
}
return nextAccessor
}
}
Error 1.1: Cannot assign value of type '[XML.Element]' to type '[XML.Accessor.Element]' (aka 'Array')
Error 1.2: Cannot assign value of type '[XML.Element]' to type '[XML.Accessor.Element]' (aka 'Array')
Error 1.3: Cannot invoke initializer for type 'XML.Accessor' with an argument list of type '(XML.Accessor.Element)'
I changed the first line to
let generator: [XML.Element]
as suggested in
https://github.com/yahoojapan/SwiftyXMLParser/issues/9
but I now get error 2:
Error 2: Cannot assign value of type '[XML.Accessor.Element]' (aka 'Array') to type '[XML.Element]'
How can I fix it, so I can build the project?
EDIT: when trying to archive the project, I get more errors as
Cannot convert value of type 'XML.Accessor.Element' (aka 'XML.Accessor') to expected argument type 'XML.Element'
in the same SwiftyXMLParser, Accessor.swift file.
I solved these errors using XML.Element instead of just Element where needed.
case .failure(_):
generator = [] // instead of // generator = [Element]()
(It was worth to learn git, I just need to remember to use it features, as "look in the current source code", as #MartinR suggested

Swift errors using #if, #endif

Using #if, #endif in Swift (using Xcode) produces errors if it cuts into the flow of an operation. This screenshot says it all:
Does anyone know a solution to make this example work, without repeating the entire code block twice? There can easily be situations where the entire block can be very large.
EDIT: My sample was a bit too simple. Here is a new sample where the "else if" depends on the same define (DEBUG). The "else if" must also be within the #if and #endif. And other samples can be much more complex than this.
Ideally, limit the usage of #if as much as possible. Using preprocessor directives is always a bit of a code smell. In this case, you can simply use a boolean variable:
#if DEBUG
let debug = true
#else
let debug = false
#endif
Then simply use the variable:
var a = 0
var b = 0
...
else if debug && a == b {
}
In release mode the code will become unreachable and the optimizer will remove it anyway.
With a bit of imagination, we can find other solutions, for example, we can move the check to a function:
func isDebugCheck(a: Int, b: Int) -> Bool {
#if DEBUG
return a == b
#else
return false
#endif
}
or we can move the whole code to a separate function and replace if-else by a return (or continue, depending on you needs), e.g.:
if a == 7 {
...
return
}
#if DEBUG
if a == b {
return
}
#endif
if ...
As #user28434 notes, there is no source-level pre-processor. This has gotten rid of a lot of very tricky pre-processor problems in C (such as bizarre needs for parentheses to make things work).
However, #if is integrated well into the language, and specifically supports switch for exactly these kinds of cases.
var a = 0
#if DEBUG
let b = 0
#endif
switch a {
case 7: a += 1
#if DEBUG
case b: a += 2
#endif
case 5: a += 3
default:
break
}
You can simply achieve this case with below code:
if a == b {
#if DEBUG
a += 2
#else
a += 1
#endif
} else if a == c {
a += 3
}

Swift switch statement considered all cases of Int, but compiler still display error

I understand switch statement in Swift must be exhaustive, otherwise we have to provide an default case. I saw the code below online, the switch statement already have covered all cases in Int, but the compiler still display error message that switch must be exhaustive, consider adding a default clause. Is there something I'm missing?
extension Int {
enum Kind {
case Negative, Zero, Positive
}
var kind: Kind {
switch self {
case 0:
return .Zero
case let x where x > 0:
return .Positive
case let x where x < 0:
return .Negative
}
}
}
Update for Swift 3: Swift 3 introduced ClosedRange which makes
it possible to define a range like 1...Int.max including the
largest possible integer (compare Ranges in Swift 3). So this compiles and works as expected,
but still requires a default case to satisfy the compiler:
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case 1...Int.max:
return .positive
case Int.min...(-1):
return .negative
default:
fatalError("Oops, this should not happen")
}
}
}
There are other bug reports where the Swift compiler does not
correctly determine the exhaustiveness of switch-statements, such
as https://bugs.swift.org/browse/SR-766, where Apple engineer Joe Groff
commented:
Unfortunately, integer operations like '...' and '<' are just plain functions to Swift, so it'd be difficult to do this kind of analysis. Even with special case understanding of integer intervals, I think there are still cases in the full generality of pattern matching for which exhaustiveness matching would be undecidable. We may eventually be able to handle some cases, but there will always be special cases involved in doing so.
Old answer: The compiler is not so smart to recognize that you have covered
all possible cases. One possible solution is to add a default
case with a fatalError():
var kind: Kind {
switch self {
case 0:
return .Zero
case let x where x > 0:
return .Positive
case let x where x < 0:
return .Negative
default:
fatalError("Oops, this should not happen")
}
}
Or make case 0: the default case:
var kind: Kind {
switch self {
case let x where x > 0:
return .Positive
case let x where x < 0:
return .Negative
default:
return .Zero
}
}
(Remark: I initially thought that the following would work correctly
without needed a default case:
var kind: Kind {
switch self {
case 0:
return .Zero
case 1 ... Int.max:
return .Positive
case Int.min ... -1:
return .Negative
}
}
However, this compiles, but aborts at runtime because you cannot
create the range 1 ... Int.max. More information around this
problem can be found in the article Ranges and Intervals in Swift.)

How can I clear the swift warning "will never be excuted"?

Now I am learning the swift and when I use the if - else ,the Xcode shows me a warniing "will not be excuted".Though it isn't a big problem, I don't want to see this, how can I clear this warning in the project?
This is from a logic error that the compiler has picked up and is warning you about.
It gives the line number in your code that can never be reached.
a) Change the logic of your code
b) delete or comment out lines of code that can never be reached
The compiler will not give this message unnecessarily
example
if 1 == 2 {
a = 3
}
else {
a = 4
}
Obviously the condition is never met, the a=3 assignment can never happen.
let a = 3;
let b = 4;
if (a == 3) {
print("executed")
} else if (a == 5) {
print("never be executed")
} else {
print("not executed")
}
Apple's LLVM compiler is pretty verbose when it tells you about warnings - if you check the logs you would find something like:
warning: will never be executed [-Wunreachable-code]
So to suppres that warning, you need to set the flag -Wno-unreachable-code. This works for the ObjC compiler, but currently isn't supported by the Swift compiler

How do I best handle returning a specific object depending on certain conditions when there's no real "else" case to cover everything?

I want to create and return an object in a specific method. I get passed an integer in the method, and I know it will be 0, 1 or 2.
So fundamentally I would structure it like:
if num == 0 {
return 12
} else if num == 1 {
return 24
} else if num == 2 {
return 36
}
Which I know would cover every circumstance.
But the compiler would complain that this doesn't cover every specific circumstance and I'd get an error. I need an else statement of some sort.
But what do I put as the else statement? I can't think of anything valid that would make sense. I could just change the last condition to an else, but it's not as clear later that it actually refers to a 2. It doesn't seem maintainable.
What should I do?
Simply remove the if from the last statement and leave the else only:
if num == 0 {
return 12
} else if num == 1 {
return 24
} else {
return 36
}
But I'd rather use a switch statement:
switch(num) {
case 0: return 12
case 1: return 24
case 2: fallthrough
default: return 36
}
Here the 2 case is handled by the default case, using the fallthrough keyword
An alternative way to achieving the same, taking into account that the possible values are consecutive and starting from 0, is using an array:
let values = [12, 24, 36]
return values[num]
If the function is a class/struct method, the values array can be defined as a static (class) property outside the function, just to avoid instantiating it for each function call.
Note that this solution generates a runtime exception if the num value is not in the [0, 2] range
You can't eat your cake and have it to. If it obvious then an else is fine. If it is not obvious, then you could use an enumeration so that the three options are unambiguously constrained.
For the former, you could use a trailing comment and assertion - that is probably the simplest:
assert(num >= 0 && num <= 2)
if num == 0 {
return 12
} else if num == 1 {
return 24
} else { // case that num == 2
return 36
}
or use an enum type in a switch statement, and the compiler will know that there are only three cases and that you've covered them all. You can use the toRaw() method as needed and base your enum type on an Int to facilitate that.
I imagine your actual case is a little more complicated than the one you presented, but the approaches remain unchanged.
i would do case with exception
case num of
0 -> ...
1 -> ...
2 -> ...
otherwise -> throw exception('unsupported value')
end
compiler will be happy and your code will be clean. in some languages you can go even further and change num to Enumerator. then you will have same structure of case but withoutl the otherwise part. and compiler will warn you every time you decide to add new value for your enumerator but forget to add the new case in your function