Branch condition evaluates to a garbage value when dereferencing a pointer to a pointer - ios

I'm trying to clean up some issues from xcode's analyzer. One I haven't found a solution to is the "Branch condition evaluates to a garbage value". It's occurring in the following way:
int methodToCloseMyDatabase(sqlite3 **myDatabase, const char *callingFunctionName)
{
if (myDatabase)
{
if (*myDatabase) // The warning is thrown here
{
// Do something
}
}
}

This error sounds to me as if CLang has analyzed your code and found that *myDatabase is not set to anything.
It could even be that the analyzer has found a possible code branch that does not set the value.

How did you set myDatabase? You probably forgot to initialize it correctly, so it points to arbitrary place in memory.

Related

Compiling lambda as Objective-C++ causes block cast

I have a non-copyable C++ lambda which captures a unique_ptr, and certain situations when compiling with Apple Clang as Objective-C++ cause the lambda to get converted to a block pointer, at which point the compilation fails due to an attempted copy of the lambda. A simple example is as follows:
int main(int argc, const char * argv[])
{
std::unique_ptr<int> myHeapInt = std::make_unique<int>(4);
int myStackInt = 0;
auto myLambda = [&, myHeapInt = std::move(myHeapInt)]()
{
myStackInt = *myHeapInt;
};
if(bool(myLambda)) //Error ar this point
{
*myHeapInt = 5;
}
std::invoke(myLambda);
return 0;
}
The error is as follows:
Call to implicitly-deleted copy constructor of 'const lambda...
Implicit capture of lambda object due to conversion to block pointer here
Is there a way around this conversion?
What is that bool(myLambda)? I have no clue.
The only thing you can do with a lambda is evoke it: myLambda(). You cannot test for whether it exists or anything.
So, I'm not entirely seeing the relevance of Objective-C++ here, as this code doesn't compile as C++ either:
objc++-noncopy-lambda.cpp:15:9: error: cannot convert '(lambda at objc++-noncopy-lambda.cpp:9:21)' to 'bool' without a conversion operator
if (bool(myLambda))
^~~~~~~~~~~~~
1 error generated.
The error message is different; I assume there's some attempt at implicitly converting lambdas to blocks in Objective-C++, I've tried to stay away from weird edge cases like that, but it seems that in the absence of an operator bool it might try the conversion to a block first.
Either way the code you're attempting to write doesn't make any sense and the compiler is correctly rejecting it.
I see in the comments that you're actually trying to do something different. Could you perhaps post a reduced version of the code you're actually trying to write, which supposedly compiles as C++ but not as Objective-C++?
I was trying to compile a templated header-file class replacement for std::function (github.com/Naios/function2) as Objective-C++, which implements a vtable and optimises it if the callable implements operator bool() or can be converted to bool.
In the end I just decided to disable this optimisation if compiled as Objective-C++ as converting to a block pointer is by design in Clang for block-lambda interoperability (http://clang.llvm.org/docs/LanguageExtensions.html#interoperability-with-c-11-lambdas).

Create a compiler error when a variable is nil

I need to trigger a custom compiler error when the developer forgot to enter a key in LocalizedStrings, in order to alert him this would cause the app will show the key instead the text. Personally I think that's perfect, but I have been required to launch a compiler error because the app has to many cases and to protect the developer in case of absentmindedness.
The method inside which is suppose to receive the key, would be something like:
- (NSString *) localizedString:(NSString *)key {
HERE IS WHERE I WANT TO TRHROW THE COMPILER ERROR
}
If you want a compile time error, look into annotating the argument with _Nonnull:
-(NSString *)localizedString:(NSString * _Nonnull)key
...and turning on the -Wnullable-to-nonnull-conversion warning with -Werror to turn this into an error. The main problem to deal with here is going to be the masses of existing code that will now fail because of nullable-to-nonnull conversions elsewhere, requiring a proliferation of annotations throughout your source code. This probably isn't going to be a workable solution but you can investigate it at least.
If you want a runtime error in debug mode only, use NSAssert or NSParameterAssert:
NSParameterAssert(key != nil);
// OR:
NSAssert(key != nil, "key must be non-nil");
// or just
NSAssert(key, "key must be non-nil");
If you always want a runtime error, throw an exception either with #throw or the convenience method:
if (!key) {
[NSException raise:#"MyException" format:#"key must be non-nil"];
}
You can use in this way.
-(NSString *) localizedString:(NSString *)key
{
if (key == nil || key == (id)[NSNull null])
{
NSException* myException = [NSException
exceptionWithName:#"NullException"
reason:#"You have passed null value in key"
userInfo:nil];
#throw myException;
}
else
{
}
}
There are two things here.
1. Compile time error
2. Run time error
Your build will succeed as there is no problem with the syntax. Hence there can be no compile time error here.
What you actually need is a mechanism to fail the build when the app is about to be built.
This can be achieved by using build phases.
Go to build phases. Add your script which would look for keys in your code and check for the same key in your .string files. Essentially, you'll not initailize the building process when your script fails

why do i get error from clang but not with xcode's default compiler when i build the follow code?

func(char * buff)
{
if (buff == NULL || buff <= 0)
{
//do some things
}
}
from clang 4.0: "Ordered comparison between pointer and zero."
from xcode 8: "build successful"
i guess this might be the error that could be change to a warning.
What should i do if i want to close this error in clang?
I don't know why Xcode allows that code.
The code itself doesn't make any sense. A pointer is not a signed value, so checking to see if the pointer is 0 or negative is nonsensical.
You can fix it by getting rid of the second part of that if statement. It doesn't make sense.
BTW, using "func" in Objective-C code is very confusing. That is the label for functions in Swift. At first I could not make any sense out of your code because I saw the func and assumed it was Swift, but it did not make sense as Swift.

Clang nullability warnings and how to approach them

Recently I turned on CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION in Xcode and I am overwhelmed with nullability related warnings in my Objective-C code. The one warning type that is most prevalent is Implicit conversion from nullable pointer 'TypeA * _Nullable' to non-nullable pointer type 'TypeA * _Nonnull'.
I started my attempt to remove with these warnings by creating a local of the same type in a method as described here.
https://www.mail-archive.com/xcode-users%40lists.apple.com/msg02260.html
This article says by first using a local, that object is of attribute unspecified nullable, so it can be used as legit parameter to the methods expecting nonnull.
But I feel this is a cop out move and really not solving the issue in any beneficial way.
Has anyone gone through this exercise already? I would be grateful if you can share a strategy that you took.
Actually, I have messed around with that topic for a little bit. I wanted to improve nullability situation in a somewhat big project (make it more 'swiftier'). Here is what I found.
Firstly, you should turn on CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION (-Wnullable-to-nonnull-conversion)
Secondly, about
first using a local, that object is of attribute unspecified nullable,
so it can be used as legit parameter to the methods expecting nonnull.
This smells bad, and I created a macro called NONNUL_CAST(). Here is example how to implement it:
#define NONNUL_CAST(__var) ({ NSCAssert(__var, #"Variable is nil");\
(__typeof(*(__var))* _Nonnull)__var; })
Here you can see hacky __typeof(*(__var))* _Nonnull)__var, but it is not so bad. If __var is of type A* _Nullable we dereference __var, so it's type now just A, after we make reference again, but _Nonnull, and get nonnull __var as answer. Of course we assert to, in case something goes wrong.
Thirdly, you must specify nullabilty on every local variable, and you should put all your code in NS_ASSUME_NONNULL_BEGIN/END, like this:
NS_ASSUME_NONNULL_BEGIN
<your_code_goes_here>
NS_ASSUME_NONNULL_END`
You put there EVERY LINE (except imports) of your code, in .h and .m files. That will assume that all your arguments of methods, return types and properties are nonnull. If you want to make it nullable, put nullable there.
So, all done, now what?
Here is example of typical usage:
- (Atype*)makeAtypeWithBtype:(nullable BType*)btype {
Atype* _Nullable a = [btype makeAtype];
if (a) {
// All good here.
return NONNUL_CAST(a);
} else {
// a appeared as nil. Some fallback is needed.
[self reportError];
return [AtypeFactory makeDeafult];
}
}
Now you have more robust nullability situation. May be it is not looking nicely, but it is objective-c, so nothing to complain about.
Not every warning makes sense. Sometimes it's a shortcoming in the compiler. For instance, this code doesn't need a warning.
- (nullable id)transformedValue:(nullable id)value {
id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
return result;
}
We are checking to see if it's null! What more can we do? Why create an extra pointer?
So, we do this:
- (nullable id)transformedValue:(nullable id)value {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
#pragma clang diagnostic pop
return result;
}
Why is this the proper answer?
First, it's OK to be smarter than the compiler. You don't want to start trashing your code, just because of a bogus warning.
This solution specifies the exact warning message to suppress, and it suppresses it for just one line.

Can I enable warning for comparing a enum and a non enum values?

I recently debugged an issue which was caused because an enum was being compared with a non-enum value. Here is a simplified example:
typedef NS_ENUM(NSInteger, MyType) {
TypeVal1,
};
...
MyType type = TypeVal1;
int randomValue = 0;
BOOL compareTypeAndPrimiative = (randomValue == typeA); // No warning
Is it possible to turn on a warning for this?
I could suppress if if needed by explicitly casting:
BOOL iKnowWhatImDoing = (randomValue == (int) typeA);
There is no support for this kind of warning because C enums are not strongly typed, and I believe the standards require them to be treated as ints (or unsigned ints). Comparing them with regular integers has always been allowed as part of the C standard, and a warning of this type would end up flagging a lot of correct code. I'm sure somebody can link to the appropriate section of the C standards.
Particularly with iOS and Apple APIs, you will find that enum values are often used as bitmasks. In these situations it is common to write code like this:
if ((value & flag) == kFlag) { ... }
You could argue that using enums for this purpose is a bad idea, but you would probably end up having to disable this warning for all sorts of code.
I just ran through LLVM manual and it seems there is no option for this. Then I tried to turn on all the compiler warnings (-Weverything), however there was no warning your enum case.
So the answer is no.
The best solution to avoid such bugs is to name the variables/constants appropiately. Make obvious what the variable/constant represent. Then the error in comparison should be obvious, too.

Resources