ios - Objective-C: "format string is not a string literal (potentially insecure)" warning with macro -
i'm using macro simplify returning localised strings, so:
#define getlocalstr(key, ...) \ [nsstring stringwithformat:[[nsbundle mainbundle] localizedstringforkey:key value:@"" table:nil], ##__va_args__]
basically, if have entry in localisation strings file "name" = "my name %@";
, calling
getlocalstr( @"name", @"foo" );
will return nsstring @"my name foo"
when run however, like:
nsstring * str = getlocalstr( @"name", @"foo" );
i "format string not string literal" warning. following advice of other answers on warning , replacing with:
nsstring * str = [nsstring stringwithformat:@"%@", getlocalstr( @"name", @"foo" )];
i still warning, , besides, kind of defeats point of macro making life easier.
how can rid of warning short of wrapping getlocalstr
calls in #pragma suppressors?
edit 27/08
after running through crd's answer , doing more tests, seems made bad assumption on error. clarify:
localisation strings file:
"testnoargs" = "hello world"; "testargs" = "hello world %@";
code:
nsstring * str1 = getlocalstr( @"testnoargs" ); // gives warning nsstring * str2 = getlocalstr( @"testargs", @"foo" ); // doesn't give warning
the majority of translations take no arguments, , ones giving warning, didn't make connection until read through crd's answer.
i changed single macro two, so:
#define getlocalstrnoargs(key) \ [[nsbundle mainbundle] localizedstringforkey:key value:@"" table:nil] #define getlocalstrargs(key, ...) \ [nsstring stringwithformat:[[nsbundle mainbundle] localizedstringforkey:key value:@"" table:nil], ##__va_args__]
and if call each 1 separately, there's no warnings.
i'd getlocalstr
expand either getlocalstrnoargs
or getlocalstrargs
depending on if arguments passed or not, far i've been having no luck (macros not strong suit :d).
i'm using sizeof(#__va_args__)
determine if there's arguments passed - stringifys arguments, , if size 1, it's empty (i.e. `\0'). perhaps it's not ideal method, seems work.
if rewrite getlocalstr
macro to:
#define getlocalstr(key,...) (sizeof(#__va_args__) == 1) ? getlocalstrnoargs(key) : getlocalstrargs(key,##__va_args__)
i can use it, still warnings everywhere it's used , there's no arguments passed, while
#define getlocalstr( key,...) \ #if ( sizeof(#__va_args__) == 1 ) \ getlocalstrnoargs(key) \ #else \ getlocalstrargs(key,##__va_args__)
won't compile. how can getlocalstr
macro expand properly?
the clang & gcc compilers check format strings , supplied arguments conform, cannot if format string not literal - hence error message see obtaining format string bundle.
to address issue there attribute, format_arg(n)
(docs), mark functions take format string; alter in way without changing actual format specifiers, e.g translate it; , return it. cocoa provides convenient macro ns_format_arg(n)
attribute.
to fix problem need 2 things:
wrap call
nsbundle
in function attribute specified; andchange "key" include format specifiers.
second first, strings file should contain:
"name %@" = "my name %@"
so key has same format specifiers result (if need reorder specifiers particular language use positional format specifiers).
now define simple function lookup, attributing format translation function. note mark static inline
, using macro ns_inline
hint compiler both inline macro expansion; static
allows include in multiple files without symbol clashes:
ns_inline nsstring *localize(nsstring *string) ns_format_argument(1); nsstring *localize(nsstring *string) { return [[nsbundle mainbundle] localizedstringforkey:string value:@"" table:nil]; }
and macro becomes:
#define getlocalstr(key, ...) [nsstring stringwithformat:localize(key), ##__va_args__]
now when you:
getlocalstr(@"name %@", @"foo")
you both localised format string , format checking.
update
after greg's comment went , checked - had reproduced error , assumed down missing attribute. greg points out localizedstringforkey:value:table:
has attribute, why error? had absentmindedly done in reproducing error was:
nslog( getlocalstr( @"name %@", @"foo" ) );
and compiler pointed @ macro definition , not line - should have spotted compiler misleading me.
so leave you? maybe you've done similar? key format string must either literal or result of function/method attributed format translating function. , don't forget, must had format specifier key above.
update 2
after additional comments need use function, rather macro, along format
attribute, cocoa provides convenient ns_format_function(f,a)
macro. attribute informs compiler function formatting one, value of f
number of format string , a
number of first argument format. gives function declaration:
nsstring *getlocalstr(nsstring *key, ...) ns_format_function(1,2);
and definition (assuming arc):
nsstring *getlocalstr(nsstring *key, ...) { va_list args; va_start(args, key); nsstring *format = [[nsbundle mainbundle] localizedstringforkey:key value:@"" table:nil]; nsstring *result = [[nsstring alloc] initwithformat:format arguments:args]; va_end (args); return result; }
(which same @a-live's).
uses of checked appropriately, example:
int x; ... nsstring *s1 = getlocalstr(@"name = %d", x); // ok nsstring *s2 = getlocalstr(@"name = %d"); // compile warning - more '%" conversions data arguments nsstring *s3 = getlocalstr(@"name", x); // compile warning - data argument not used format string nsstring *s4 = getlocalstr(@"name"); // ok
Comments
Post a Comment