Saturday, September 16, 2017

Useful GCC warning options not enabled by -Wall -Wextra

GCC can warn about questionable constructs in the source code, but most such warnings are not enabled by default – developers need to use the options -Wall and -Wextra to get all generally useful warnings. There are many additional warning options that are not enabled by -Wall -Wextra as they may produce too many false positive warnings or be targeted to a specific obscure use case, but I think a few of them (listed below) may be useful for general use.

-Wduplicated-cond

Warn about duplicated condition in if-else-if chains, such as
int foo(int a)
{
  int b;
  if (a == 0)
    b = 42;
  else if (a == 0)
    b = 43;
  return b;
}
Note: -Wduplicated-cond was added in GCC 6.

-Wduplicated-branches

Warn when an if-else has identical branches, such as
int foo(int a)
{
  int b;
  if (a == 0)
    b = 42;
  else
    b = 42;
  return b;
}
It also warns for conditional operators having identical second and third expressions
int foo(int a)
{
  int b;
  b = (a == 0) ? 42 : 42;
  return b;
}
Note: -Wduplicated-branches was added in GCC 7.

-Wlogical-op

Warn about use of logical operations where a bitwise operation probably was intended, such as
int foo(int a)
{
  a = a || 0xf0;
  return a;
}
It also warns when the operands of logical operations are the same
int foo(int a)
{
  if (a < 0 && a < 0)
    return 0;
  return 1;
}
Note: -Wlogical-op was added in GCC 4.3.

-Wrestrict

Warn when the compiler detects that an argument passed to a restrict or __restrict qualified parameter alias with another parameter.
void bar(char * __restrict, char * __restrict);

void foo(char *p)
{
  bar(p, p);
}
Note: -Wrestrict was added in GCC 7.

-Wnull-dereference

Warn when the compiler detects paths that dereferences a null pointer.
void foo(int *p, int a)
{
  int *q = 0;
  if (0 <= a && a < 10)
    q = p + a;
  *q = 1;  // q may be NULL
}
Note: -Wnull-dereference was added in GCC 6.

-Wold-style-cast

Warn if a C-style cast to a non-void type is used within a C++ program.
int *foo(void *p)
{
  return (int *)p;
}
Note: -Wold-style-cast was added before GCC 3.
Note: -Wold-style-cast is only available for C++.

-Wuseless-cast

Warn when an expression is cast to its own type within a C++ program.
int *foo(int *p)
{
  return static_cast<int *>(p);
}
Note: -Wuseless-cast was added in GCC 4.8.
Note: -Wuseless-cast is only available for C++.

-Wjump-misses-init

Warn if a goto statement or a switch statement jumps forward across the initialization of a variable, or jumps backward to a label after the variable has been initialized.
int foo(int a)
{
  int b;
  switch (a)
  {
  case 0:
    b = 0;
    int c = 42;
    break;
  default:
    b = c;  // c not initialized here
  }
  return b;
}
Note: -Wjump-misses-init was added in GCC 4.5.
Note: -Wjump-misses-init is only available for C – jumping over variable initialization is an error in C++.

-Wdouble-promotion

Warn when a value of type float is implicitly promoted to double.

Floating point constants have the type double, which makes it easy to accidentally compute in a higher precision than intended. For example,
float area(float radius)
{
  return 3.14159 * radius * radius;
}
does all the computation in double precision instead of float. There is normally no difference in performance between float and double for scalar x86 code (although there may be a big difference for small, embedded, CPUs), but double may be much slower after vectorization as only half the number of elements fit in the vectors compared to float values.

Note: -Wdouble-promotion was added in GCC 4.6.

-Wshadow

Warn when a local variable or type declaration shadows another variable, parameter, type, or class member.
int result;

int foo(int *p, int len)
{
  int result = 0;  // Shadows the global variable
  for (int i = 0; i < len; i++)
    result += p[i];
  return result;
}
Note: -Wshadow was added before GCC 3.

-Wformat=2

The -Wformat option warns when calls to printf, scanf, and similar functions have an incorrect format string or when the arguments do not have the correct type for the format string. The option is enabled by -Wall, but it can be made more aggressive by adding -Wformat=2 which adds security-related warnings. For example, it warns for
#include <stdio.h>

void foo(char *p)
{
  printf(p);
}
that may be a security hole if the format string came from untrusted input and contains ‘%n’.

Note: -Wformat=2 was added in GCC 3.0.

12 comments:

  1. It would be useful to know which gcc version is required for each of this flag.

    ReplyDelete
    Replies
    1. Indded. Also some of them can only be applied to gcc and others only to g++.

      Delete
    2. I was a bit lazy when I wrote the blog post... Sorry!

      But I have now added information about when the options were introduced, and which are restricted to C/C++.

      Delete
  2. Great post! I wonder why -Wnull-dereference is not enabled. It is such a common mistake!

    ReplyDelete
    Replies
    1. There are some cases where it can warn incorrectly. It does not happen too often, but those cases often need big changes in the code's structure for the warning to disappear (i.e. it is not enough to just initialize a variable as for the incorrect warnings you may get from \(\verb!-Wuninitialize!\)).

      Delete
  3. There is the very useful -Wmisleading-indentation

    ReplyDelete
    Replies
    1. Yes, it is useful – but it is already included in \(\verb!-Wall!\).

      Delete
  4. Very interesting! Some of these warnings are more what one would expect from static analysis, rather than from the compiler.

    One caution, though -- at least in my testing -Wshadow is pretty much unusable on gcc because of the many false positives. clang seems to do a much better job. I wrote about this at http://btorpey.github.io/blog/2015/03/17/shadow/

    ReplyDelete
    Replies
    1. I have not had any such problems in the code I work with, but I can see how this may be a problem for large code bases...

      GCC 7 introduced a modifier to the warning option, so \(\verb!-Wshadow=local!\) does only warn when a local variable shadows another local variable or parameter. This solves the problem Linus complained about, but it fails to warn about your example...

      Delete
  5. Thanks! These are some nice examples. I've been using a slew extra GCC warnings for 5 years or so by now, so its nice surprise to find more.

    Do you ever/selectively promote warnings to warnings to errors?
    I usually promote most of "definitely undefined behavior"-warnings to errors, and I'm considering promoting -Wduplicated-cond and
    -Wduplicated-branches to errors.
    For reference, these are my usual warning flags:

    -Wno-variadic-macros -W -Wpedantic -Wextra -Wall -Wcast-align -Wcast-qual -Wstrict-aliasing=2 -Wframe-larger-than=32768 -Wno-strict-overflow -Wsync-nand -Wtrampolines -Wsign-compare -Werror=float-equal -Werror=missing-braces -Werror=init-self -Werror=logical-op -Werror=write-strings -Werror=address -Werror=array-bounds -Werror=char-subscripts -Werror=enum-compare -Werror=implicit-int -Werror=empty-body -Werror=main -Werror=aggressive-loop-optimizations -Werror=nonnull -Werror=parentheses -Werror=pointer-sign -Werror=return-type -Werror=sequence-point -Werror=uninitialized -Werror=volatile-register-var -Werror=ignored-qualifiers -Werror=missing-parameter-type -Werror=old-style-declaration -Wno-error=maybe-uninitialized -Wno-unused-function -Wodr -Wformat-signedness -Wsuggest-final-types -Wsuggest-final-methods -Wno-ignored-attributes -Wno-missing-field-initializers -Wshift-overflow=2 -Wduplicated-cond \
    -Wduplicated-branches -Werror=restrict -Wdouble-promotion -Wformat=2

    ReplyDelete
  6. I don't see -Wlogical-op in GCC 4.7 ... was it actually introduced in GCC 6, not 4.3 ?

    ReplyDelete