Hi,
recently, i was looking into Clang’s fixits generated for the format string used in
“printf” / “scanf” for some common ObjC types.
There is some relevant public documentation here: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html .
It turns out that in the case of “scanf” there are some extra complications and
I would like to ask for your advice.
So let’s take a look at the following two examples:
(Example 1)
void printNSInteger() {
NSInteger x = 123;
printf("%d", x);
}
A. If the triple is 32-bit this code compiles cleanly with -Wformat -fixit
B. If the triple is 64-bit Clang will generate a warning and the corresponding fixit
replaces printf("%d", x) with printf("%ld", (long)x).
The new code has an important property that it’s correct for 32-bit targets as well.
(Example 2)
void readNSInteger() {
NSInteger x;
scanf("%d", &x);
}
A. If the triple is 32-bit this code compiles cleanly with -Wformat -fixit
B. If the triple is 64-bit Clang will generate a warning and the corresponding fixit
which replaces scanf("%d", &x); with scanf("%ld", &x);
Unfortunately, the new code is broken for 32-bits targets:
Simple test:
#include <stdio.h>
typedef struct {
int x;
int y;
} Point;
int main() {
Point p = { 1, 1 };
scanf("%ld", &p.x);
printf("%d %d\n", p.x, p.y);
return 0;
}
./main <<< ‘2’
will print 2 0 (so p.y was overwritten by scanf).
This becomes an issue for some shared code which is compiled for 32-bit and 64 bit targets.
A natural question comes up what the proper “fixit” for readNSInteger() should look like
and how it would coexist with the current warnings/fixits. I will list a few options (mostly to start a discussion) which come on my mind first, but i am interested in your opinion, plus
i assume i might be missing something.
Option 1:
Make “fixit” produce the following code modification
(if the code is compiled for 64 bits target):
replace (that’s just a sketch, because there are some complications with function-style macros and macro parameters)
scanf("%d", &x);
with
#if defined (LP64)
scanf("%ld", &x);
#else
scanf("%d", &x);
#endif
Option 2:
“Hacky” (inspired by https://twitter.com/gparker/status/377910611453046784?lang=en )
Make “fixit” produce the following code modification
(if the code is compiled for 64 bits target):
replace
scanf("%d", &x);
with
scanf("%zd", &x); // aka size_t