GNOME Bugzilla – Bug 769229
Generates C code with format errors
Last modified: 2016-11-12 08:12:58 UTC
vala-0.32.1-1.fc24.x86_64 This code in vala: throw new PluginError.NOT_A_PLUGIN (_("Couldn't create a new instance of plugin in '%s'."), module_path); is transformed into this C: _tmp5_ = _ ("Couldn't create a new instance of plugin in '%s'."); _tmp6_ = self->priv->module_path; _tmp7_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _tmp5_, _tmp6_); Which throws errors: gnome-games/src/core/plugin-registrar.c: In function ‘games_plugin_registrar_new_plugin’: gnome-games/src/core/plugin-registrar.c:366:3: error: format not a string literal, argument types not checked [-Werror=format-nonliteral] _tmp7_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _tmp5_, _tmp6_); ^~~~~~ Using variables when literals are supposed to be used is a potential security problem, which is why GCC throws those errors. Vala shouldn't be generating C code that cannot compile.
Do you get the same error when not using the gettext macro _() ?
(In reply to Al Thomas from comment #1) > Do you get the same error when not using the gettext macro _() ? No, because vala transforms that into: _tmp6_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, "Couldn't create a new instance of plugin in '%s'.", _tmp5_); Note that this C code also works: _tmp6_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _("Couldn't create a new instance of plugin in '%s'."), _tmp5_);
Thanks for supplying more details. Some further analysis: The Problem ----------- A useful explanation of using -Wformat -Werror=format-security: https://fedoraproject.org/wiki/Format-Security-FAQ Fedora and Ubuntu now build using -Wformat -Werror=format-security: https://fedorahosted.org/fesco/ticket/1185 https://wiki.ubuntu.com/ToolChain/CompilerFlags#A-Wformat_-Wformat-security The Vala User Code Solution --------------------------- Vala appears to do the right thing, so function calls that expect printf style parameters are generated as ( "%s", my_variable ). Vala also provides the [PrintfFormat] attribute to mark functions that expect printf style parameters. Compile the following with valac --ccode example.vala: const string GETTEXT_PACKAGE = "example"; void main() { Intl.setlocale (); string a = "abcdef"; print (a); string b = dgettext ( "example", "abcdef"); message (b); string c = _ ("abcdef"); print (c); string d = test ( "abcdef" ); print( d ); test( test( "abcdef" )); } [PrintfFormat] string test (string a, ...) { return a; } The nested call to test() produces: _tmp6_ = test ("abcdef"); _tmp7_ = _tmp6_; _tmp8_ = test ("%s", _tmp7_); when the [PrintfFormat] attribute is used. The Scope of this Bug --------------------- This narrows the scope of this bug report to throwing a new GError in Vala when the message is generated from a function, such as used by gettext. The C code to call g_error_new is generated in codegen/valaccodebasemodule.vala, but uses an existing argument list created earlier in the compilation process. There may be some way to add the PrintfFormat attribute somewhere like vala/valathrowstatement.vala or vala/valaerrottype.vala. This needs further investigation.
Scrap the conclusion of the last comment. It looks like this could be because the translation function is a non literal, as per the error message! The following: void main() { print( translate_to( "module is %s\n" ), "test" ); } string translate_to ( string a ) { return a; } compiled on Fedora 23 with: valac test.vala -X -Wformat -X -Werror=format-nonliteral produces the format not a string literal error. Using: valac ju.vala -X -Wformat -X -Werror=format-security does not produce the error. To overcome your problem you could: a) switch to -Werror=format-security, this seems to be what the major distros are using b) re-write your throw statement as: throw new PluginError.NOT_A_PLUGIN ( "%s '%s'", _("Couldn't create a new instance of plugin in"), module_path); I'm not sure why _tmp6_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _("Couldn't create a new instance of plugin in '%s'."), _tmp5_); compiles with -Werror=format-nonliteral The way I'm reading it now, is that gettext shouldn't be used with printf tokens if you want to compile with -Werror=format-nonliteral. _() is a runtime translation after all.
(In reply to Al Thomas from comment #4) > b) re-write your throw statement as: > throw new PluginError.NOT_A_PLUGIN ( "%s '%s'", _("Couldn't create a new > instance of plugin in"), module_path); > From a translation of view, rewriting things this way is not correct, the translator may need to move the module_path away from the end of the message eg maybe it will need to be formulated as "In %s it was not possible to create a new instance of the plugin" in some languages.
(In reply to Christophe Fergeau from comment #5) > From a translation of view, rewriting things this way is not correct, the > translator may need to move the module_path away from the end of the message > eg maybe it will need to be formulated as "In %s it was not possible to > create a new instance of the plugin" in some languages. Yes, you lose flexibility in the translation. There are tools like msgfmt -c to check ordering of printf tokens in gettext strings, see https://www.gnu.org/software/gettext/manual/html_node/c_002dformat-Flag.html , so re-ordering is a common thing. From what I understand, though, if you really want to use -Werror=format-nonliteral you will lose that flexibility in your translations. What I'm not finding, when searching on the Internet, is anything about gettext and -Werror=format-nonliteral. From GCC docs ( https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html ) the format-nonliteral option is "warn if the format string is not a string literal and so cannot be checked, unless the format function takes its format arguments as a va_list", whereas format-security is "This is currently a subset of what -Wformat-nonliteral warns about". The gettext functions do not take a va_list ( see https://www.gnu.org/software/libc/manual/html_node/Translation-with-gettext.html ). I'm not sure that it would make a difference if they did, but the functions take a const char* argument. If that argument is a function that returns a char* or a variable that contains a char* then my understanding is -Wformat-nonliteral will error because the function or variable are nonliterals. Now gettext _() is a macro for a function call, so if you use _() you will get the error when checking for nonliteral. The only way to use _() in those circumstances is to use function_call( "%s", _( "string to translate" )) This loses the flexibility of printf style tokens in the translation string. So you either have to work without them or not use -Werror=format-nonliteral. That's why I suggested the two solutions, maybe I have missed something.
Isn't this working: throw new PluginError.NOT_A_PLUGIN (_("Couldn't create a new instance of plugin in '%s'.").printf (module_path));
commit b905023ff33eb220f86a5a754a8828d89931517a Author: Jürg Billeter <j@bitron.ch> Date: Tue Nov 1 18:03:17 2016 +0100 codegen: Avoid temporary variables for calls to [FormatArg] methods This prevents bogus C compiler warnings with -Wformat-nonliteral.
That works in my tests, thanks.
*** Bug 725995 has been marked as a duplicate of this bug. ***