Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange Xcode Analyze results

When I Analyze my project in Xcode, I get a few strange errors. All of this code is part of a single method which creates arrays that can be used to make MKAnnotations. Sorry if this is an inundation of code—I did my best to comment out the unrelated parts. I included the entire method in addition to the snippets just for some perspective. Thank you!

- (void)addLines {
    /*

    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

    // Create region and span variables
    MKCoordinateSpan span;
    MKCoordinateRegion region;

    NSArray* arrayOuter = [defaults objectForKey:@"mapSaveDataKey"];

    NSData* data = [arrayOuter objectAtIndex:[[defaults objectForKey:@"currentMap"] intValue]];

    NSArray* arrayOne = [NSKeyedUnarchiver unarchiveObjectWithData:data];

    if (arrayOne.count == 0)
        [self returns:nil];

    */

    NSArray* overlayLat = [arrayOne objectAtIndex:1];
    double lats[overlayLat.count];

    NSArray* overlayLong = [arrayOne objectAtIndex:2];
    double longs[overlayLong.count];

    NSArray* annotationLat = [arrayOne objectAtIndex:8];
    double annotationLats[annotationLat.count];

    NSArray* annotationLong = [arrayOne objectAtIndex:9];
    double annotationsLongs[annotationLong.count];

    /*

    CLLocationCoordinate2D startLocation = CLLocationCoordinate2DMake([[overlayLat objectAtIndex:0] doubleValue], [[overlayLong objectAtIndex:0] doubleValue]);

    CLLocationCoordinate2D finishLocation = CLLocationCoordinate2DMake([[overlayLat objectAtIndex:[overlayLat count] - 1] doubleValue], [[overlayLong objectAtIndex:[overlayLong count] - 1] doubleValue]);

    NSString* string1 = [[[NSString alloc] initWithFormat:@"Lat: %.3f, Long: %.3f", [[overlayLat objectAtIndex:0] doubleValue], [[overlayLong objectAtIndex:0] doubleValue]] autorelease];

    NSString* string2 = [[[NSString alloc] initWithFormat:@"Lat: %.3f, Long: %.3f", [[overlayLat objectAtIndex:([overlayLat count] - 1)] doubleValue], [[overlayLong objectAtIndex:([overlayLong count] - 1)] doubleValue]] autorelease];

    LocationAnnotation* startAnnotation = [[LocationAnnotation alloc] initWithCoordinate:startLocation title:@"Start" subtitle:string1];

    LocationAnnotation* finishAnnotation = [[LocationAnnotation alloc] initWithCoordinate:finishLocation title:@"Finish" subtitle:string2];

    */

    for (int iii = 0; iii < overlayLat.count; iii++) {
        NSNumber* a = (NSNumber*)[overlayLat objectAtIndex:iii];
        lats[iii] = [a doubleValue];
    }

    for (int iii = 0; iii < overlayLong.count; iii++) {
        NSNumber* a = (NSNumber*)[overlayLong objectAtIndex:iii];
        longs[iii] = [a doubleValue];
    }

    for (int iii = 0; iii < annotationLong.count; iii++) {
        NSNumber* a = (NSNumber*)[annotationLong objectAtIndex:iii];
        annotationsLongs[iii] = [a doubleValue];
    }

    for (int iii = 0; iii < annotationLat.count; iii++) {
        NSNumber* a = (NSNumber*)[annotationLat objectAtIndex:iii];
        annotationLats[iii] = [a doubleValue];
    }

    int sizeLats = 0;

    for (int iii = 0; iii < overlayLat.count; iii++)
        if (lats[iii] != 0)
            sizeLats++;

    CLLocationCoordinate2D coords[sizeLats];

    for (int iii = 0; iii < sizeLats; iii++) {
        if (lats[iii] != 0 && longs[iii] != 0) {
            coords[iii].latitude = lats[iii];
            coords[iii].longitude = longs[iii];
        } else {
            coords[iii].latitude = coords[iii - 1].latitude;
            coords[iii].longitude = coords[iii - 1].longitude;
        }
    }

    /*

    // Give variables value
    span = MKCoordinateSpanMake(.05, .05);       
    region = MKCoordinateRegionMake(coords[0], span);

    MKPolyline* line = [MKPolyline polylineWithCoordinates:coords count:sizeLats];

    [mapView addOverlay:line];

    [mapView addAnnotations:[NSArray arrayWithObjects:startAnnotation, finishAnnotation, nil]];
    [mapView selectAnnotation:startAnnotation animated:YES];

    */

    for (int iii = 0; iii < annotationLong.count; iii++) {
        CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(annotationLats[iii], annotationsLongs[iii]);
        /*

        NSString* subtitle = [[[NSString alloc] initWithFormat:@"Lat: %.3f, Long: %.3f", coord.latitude, coord.longitude] autorelease];
        NSString* title = [[[NSString alloc] initWithFormat:@"Stop %i", (iii + 1)] autorelease];
        LocationAnnotation* a = [[LocationAnnotation alloc] initWithCoordinate:coord title:title subtitle:subtitle];
        CLLocation* c = [[CLLocation alloc] initWithCoordinate:coord altitude:0 horizontalAccuracy:0 verticalAccuracy:0 course:0 speed:0 timestamp:0];
        CLLocation* d = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(finishAnnotation.coordinate.latitude, finishAnnotation.coordinate.longitude) altitude:0 horizontalAccuracy:0 verticalAccuracy:0 course:0 speed:0 timestamp:0];
        if ([c distanceFromLocation:d] > 20)
            [mapView addAnnotation:a];
        [c release];
        [d release];
        [a release];

        */
    }

    /*

    [mapView setRegion:region animated:YES];

    [startAnnotation release];
    [finishAnnotation release];

    */
}

First:

int sizeLats = 0;

for(int iii = 0; iii < overlayLat.count; iii++)
    if(lats[iii] != 0) //"The left operand of '!=' is a garbage value"
        sizeLats++;

How dare Xcode accuse me of such shenanigans?

Second:

CLLocationCoordinate2D coords[sizeLats]; //"Declared variable-length array (VLA) has zero size"

Third:

for (int iii = 0; iii < sizeLats; iii++) {
    if (lats[iii] != 0 && longs[iii] != 0) { // "The left operand of '!=' is a garbage value"
        coords[iii].latitude = lats[iii];
        coords[iii].longitude = longs[iii];
    } else {
        coords[iii].latitude = coords[iii - 1].latitude; // "Assigned value is garbage or undefined"
        coords[iii].longitude = coords[iii - 1].longitude;
    }
}

Fourth:

CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(annotationLats[iii], annotationsLongs[iii]); // "Function call argument is an uninitialized value"

Except, I just initialized that array? I understand that it could be uninitialized if annotationLats and annotationLongs were initialized with zero length.


Are these warnings vital? If my program runs as expected without these warnings can I safely ignore them? I just feel like it's not a good idea to ignore Xcode—it's rather intelligent.

like image 495
eric.mitchell Avatar asked Nov 03 '11 01:11

eric.mitchell


People also ask

Can I run the Clang static analyzer in Xcode?

Since Xcode 3.2, users have been able to run the Clang Static Analyzer directly within Xcode. It integrates directly with the Xcode build system and presents analysis results directly within Xcode's editor. Can I use the open source analyzer builds with Xcode?

How to speed up Xcode builds?

Fixing the slow type-check by adding an explicit type. Speeding up Xcode builds by altering a few Xcode build settings. This used to be a common technique to easily gain seconds on an incremental build. Nowadays, Xcode has most of these settings set by default so there’s not much to cover.

How do I see the build time in Xcode?

After executing this action you’ll see that Xcode start building your project on the selected target device or simulator. Navigate to the Report Navigator after the build is finished and select the last build. Select “Recent” and scroll all the way down until you see the Build Timing Summary.

How to use the build timing summary action in Xcode?

The action can be triggered in the product menu using Product ➔ Perform Action ➔ Build with Timing Summary or using xcodebuild -showBuildTimingSummary. Analyse build performance using the Build Timing Summary Action. After executing this action you’ll see that Xcode start building your project on the selected target device or simulator.


1 Answers

the misunderstanding is that clang/checker cannot guarantee that the arrays' count will not change, and it cannot be sure that the program will flow as though your arrays are immutable.

My real question is are these warnings vital, and if my program runs as expected, without errors, ( well, without errors related to these warnings :D ) can I safely ignore them? I just feel like it's not a good idea to ignore Xcode; it's rather intelligent.

Personally, I don't ignore them, and really strive to keep those issues down.

You can change your program slightly to align with the program flow the compiler/checker expects. The easy way to accomplish this and remove most of the warnings would be by introducing a variable for what you expect cannot change:

NSArray* overlayLat = [arrayOne objectAtIndex:1];
const NSUInteger overlayLatCount = [overlayLat count];
// now use the variable instead of calling the method where needed:
double lats[overlayLatCount];
...

Then use those variables instead of the methods. Naturally, you should be careful to ensure that the variables you create will always be equal to the value they represent.

There are a few other bits: the checker does not always know the type's semantics. For example: you might think an NSArray's count could be cached, but the checker would have to know a lot about many classes to understand these issues. Even more, the checker cannot guarantee that the program will flow as you expect for many other reasons. Let's say the array's count were cached as an optimization; this would lead to a ton of bugs in real world programs. So, you have to make some things a little more obvious when you have context that the checker may not rely on - you have to introduce that context in some cases.

The first set of issues, in more detail

NSArray * overlayLat = [arrayOne objectAtIndex:1];
const NSUInteger overlayLatCount = [overlayLat count];
double lats[overlayLatCount];

NSArray * overlayLong = [arrayOne objectAtIndex:2];
const NSUInteger overlayLongCount = [overlayLong count];
double longs[overlayLongCount];

NSArray * annotationLat = [arrayOne objectAtIndex:8];
const NSUInteger annotationLatCount = [annotationLat count];
double annotationLats[annotationLatCount];

NSArray * annotationLong = [arrayOne objectAtIndex:9];
const NSUInteger annotationLongCount = [annotationLong count];
double annotationsLongs[annotationLongCount];

for (int iii = 0; iii < overlayLatCount; iii++) {
    NSNumber * a = (NSNumber*)[overlayLat objectAtIndex:iii];
    lats[iii] = [a doubleValue];
}

for (int iii = 0; iii < overlayLongCount; iii++) {
    NSNumber * a = (NSNumber*)[overlayLong objectAtIndex:iii];
    longs[iii] = [a doubleValue];
}

for (int iii = 0; iii < annotationLongCount; iii++) {
    NSNumber * a = (NSNumber*)[annotationLong objectAtIndex:iii];
    annotationsLongs[iii] = [a doubleValue];
}

for (int iii = 0; iii < annotationLatCount; iii++) {
    NSNumber * a = (NSNumber*)[annotationLat objectAtIndex:iii];
    annotationLats[iii] = [a doubleValue];
}

Details for the remaining Issues

1) Declared VLA has zero size:

Meaning: The checker sees that sizeLats is initialized to zero, and incremented only conditionally. It's warning you that the array length could be zero.

Resolution: Test for zero before continuing:

if (0 == sizeLats) return;

CLLocationCoordinate2D coords[sizeLats];

That's one approach to guarantee to the compiler that you will not create or use the zero length VLA.

2) The Left operand of != is a garbage value:

if ((lats[iii] != 0) && (longs[iii] != 0)) {

Meaning: The checker cannot guarantee that the array was ever fully initialized and that sizeLats is less than [overlayLat count] and [overlayLong count]. More importantly, it should warn of of the potential of an out of range access. This message is not very clear.

Resolution: There's actually a bit of unchecked bounds and assumptions in the program (and perhaps some error checking was removed). That should be made clearer to checker and the compiler. You should make this clearer to the compiler and add some error detection just in case (will provide a more complete example).

Additional Notes - floating point comparisons are not very safe - you should not expect C arrays to be initialized

3) Function call argument is an uninitialized value.

Meaning: The origin of this problem is the same as #2 "The Left operand of != is a garbage value". The array at a specified index may not have been initialized. The worse problem again is that there is no guarantee that the array index will be in range. The fact that it is not flagged as such makes me wonder if the checker disabled this warning due to too many false positives.

Resolution: Same as #2

So, a more thorough example with extensive error checking might flow like the following program. The program isn't pretty and could use some cleaning up/refactoring, but all the error checking exists, it passes with no checker issues, and makes the questionable code unreachable and detects a good amount of the errors. I still don't consider floating point comparisons or VLAs good, but that should be enough to solve your question.

/**
  @brief fills a double buffer with values from an NSArray
  @param destinationBuffer the buffer to fill
  @param countOfDestinationBuffer the number of elements in @a destinationBuffer
  @param the source values. an NSArray filled with NSNumber objects. @a countOfDestinationBuffer must be equal to @a [source count]
  @return false if an error occurred, else true
*/
static bool FillDoubleArrayFromNSArray(double* const destinationBuffer, const NSUInteger countOfDestinationBuffer, NSArray* source) {
    const NSUInteger sourceCount = [source count];
    if ((0 == destinationBuffer) || (0 == countOfDestinationBuffer) || (0 == [source count])) {
        assert(0 && "invalid argument");
        return false;
    }
    else if (sourceCount != countOfDestinationBuffer) {
        assert(0 && "buffer size mismatch");
        return false;
    }

    for (NSUInteger idx = 0; idx < sourceCount; ++idx) {
        NSNumber* a = (NSNumber*)[source objectAtIndex:idx];
        destinationBuffer[idx] = [a doubleValue];
    }

    return true;
}

- (void)addLines {
    NSArray* arrayOne = [NSArray array];

    NSArray* overlayLatArray = [arrayOne objectAtIndex:1];
    const NSUInteger overlayLatCount = [overlayLatArray count];
    if (0 == overlayLatCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double lats[overlayLatCount];
    if (!FillDoubleArrayFromNSArray(lats, overlayLatCount, overlayLatArray)) {
    /* do something */
        return;
    }

    NSArray* overlayLongArray = [arrayOne objectAtIndex:2];
    const NSUInteger overlayLongCount = [overlayLongArray count];
    if (0 == overlayLongCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double longs[overlayLongCount];
    if (!FillDoubleArrayFromNSArray(longs, overlayLongCount, overlayLongArray)) {
    /* do something */
        return;
    }

    NSUInteger sizeLat = 0;
    for (NSUInteger idx = 0; idx < overlayLatCount; ++idx) {
        if (lats[idx] != 0) {
            ++sizeLat;
        }
    }

    if (0 == sizeLat) {
        assert(0 && "what to do when no lines can be drawn?");
        return;
    }

    if ((overlayLatCount < sizeLat) || (overlayLongCount < sizeLat)) {
        assert(0 && "input range error (overlayLongCount is really what we are testing here)");
        return;
    }

    CLLocationCoordinate2D coords[sizeLat];
    for (NSUInteger idx = 0; idx < sizeLat; ++idx) {
        if ((lats[idx] != 0) && (longs[idx] != 0)) {
            coords[idx] = CLLocationCoordinate2DMake(lats[idx], longs[idx]);
        }
        else if (0 == idx) {
            assert(0 && "range error. access of invalid index would have occurred");
            return;
        }
        else {
            coords[idx] = coords[idx - 1];
        }
    }

    NSArray* annotationLatArray = [arrayOne objectAtIndex:8];
    const NSUInteger annotationLatCount = [annotationLatArray count];
    if (0 == annotationLatCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double annotationLat[annotationLatCount];
    if (!FillDoubleArrayFromNSArray(annotationLat, annotationLatCount, annotationLatArray)) {
    /* do something */
        return;
    }

    NSArray* annotationLongArray = [arrayOne objectAtIndex:9];
    const NSUInteger annotationLongCount = [annotationLongArray count];
    if (0 == annotationLongCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double annotationLong[annotationLongCount];
    if (!FillDoubleArrayFromNSArray(annotationLong, annotationLongCount, annotationLongArray)) {
    /* do something */
        return;
    }

    if (annotationLatCount < annotationLongCount) {
        assert(0 && "input range error in next loop. bailing");
        return;
    }

    for (NSUInteger idx = 0; idx < annotationLongCount; ++idx) {
        CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(annotationLat[idx], annotationLong[idx]);
/* ... */
    }
}
like image 161
justin Avatar answered Nov 04 '22 02:11

justin