EmbCCdocs Repository

Diagnostics#

This page describes how EmbCC reports problems in a program: the format of an error or warning, how many errors one run reports, the JSON form an editor reads, fix-it hints and --fix, the --explain entries behind the E0001-style ids, every -W option, and the optimization remarks that say what the optimizer decided. It is for anyone who compiles code with EmbCC or integrates it into an editor or build system.

Section ids. Comments in the EmbCC sources cite "docs/manual/diagnostics.md T1" through "T6". Those ids refer to sections of this page: T1 is the diagnostic format, T2 is error recovery, T3 is fix-its, T4 is the warning options, T5 is the language server, and T6 is --explain together with embld --doctor. Dependency generation (-M, -MD, ...) and --syntax-only, which the old T4 also covered, are described in Invoking EmbCC.

The diagnostic format#

Anatomy of a diagnostic#

Every diagnostic is written to standard error. In the default text format it looks like this:

int area(int w, int h)
{
    int height = h;
    return w * heigth;
}
embcc: area.c:4:16: error: 'heigth' is not declared in 'area' — for a call, add a prototype or define it first [E0001]
      return w * heigth;
                 ^~~~~~
embcc: area.c:4:16: note: did you mean 'height'?
      return w * heigth;
                 ^~~~~~
                 height
embcc: compilation terminated: 1 error

The parts are, in order:

Part Meaning
embcc: Every diagnostic line starts with the program name.
area.c:4:16: File, line and column. Columns count bytes from 1. The column is omitted when it is not known (some diagnostics are tied to a whole line); the line is omitted when the diagnostic is about the file as a whole. A diagnostic about the command line has no location: embcc: error: ....
error: The severity: error, warning or note.
message What is wrong.
[--warn=name] Printed after a diagnostic that a warning option controls (a warning, or an error under --warnings-are-errors or --error=name): the option that turns it on, so --no-warn=name turns it off. JSON output names it as GCC does ("option": "-Wname"), for the tools that read GCC's format.
[E0001] Printed after a diagnostic that has an explanation: embcc --explain E0001 prints it.

When the source text of the file is available, the diagnostic is followed by the source line (indented by two spaces) and a caret line. The ^ marks the column; ~ underlines the rest of the identifier or number the caret lands on, or the range the front end recorded. Tabs in the source line are kept in the caret line so that the caret stays aligned. A diagnostic without a column shows the source line with no caret.

A diagnostic with a fix-it prints the replacement text on a further line, under the place it goes.

Severities#

Severity Meaning
error The program is not valid, or EmbCC cannot compile it. The compile fails and the exit status is 1.
warning The program is valid but probably wrong. The compile continues. Most warnings are controlled by a --warn= option; --no-warnings drops them and --warnings-are-errors turns them into errors.
note Extra information about the error or warning printed immediately before it: where an earlier declaration is, a suggested spelling, the header that declares a name, the macro an expansion came from, or the template instantiation being processed. A note does not affect the exit status.

Optimization remarks are not diagnostics. They are off by default and are described in Optimization remarks.

Notes EmbCC attaches#

Note Attached to
did you mean 'NAME'? An undeclared identifier whose spelling is close to a name in scope, or a member name close to a member the structure has. Carries a fix-it.
'NAME' is declared in <HEADER> An undeclared identifier that is a C library name EmbCC knows (see Which diagnostics carry fix-its). Carries a fix-it that inserts the #include at line 1.
insert ';' here A missing semicolon. Points at the end of the previous token and carries a fix-it.
use '.' here, use '->' here -> applied to a structure value, or . applied to a pointer to a structure. Carries a fix-it.
the one it hides is here A --warn=shadow warning.
'NAME' is declared here, with no initializer A --warn=uninitialized or --warn=maybe-uninitialized warning.
expanded from macro 'NAME' Any error or warning whose column falls on the name of a macro invoked on that line, or, when the column is unknown, whose line invokes a macro. Printed as a heading only, without a source line.
in the instantiation of ... A C++ error inside a template being instantiated. Up to 12 such notes are printed, innermost first.
this is a bug in EmbCC, not in the program being compiled An internal error:.

When diagnostics are printed#

EmbCC records diagnostics while it compiles and prints them all once the compile ends (or stops at an error it cannot recover from). They appear in the order they were found, each followed by its notes.

A small number of messages bypass this mechanism and are printed immediately as plain text:

  • driver messages about the command line itself, such as an unknown -W or --error= name;
  • escape sequence out of range ...; truncated, as gcc does, from the lexer;
  • the optimizer stopped after N rounds on 'F' without converging, from the optimizer.

These carry no [--warn=...] tag, are not affected by --no-warnings or --warnings-are-errors, and do not appear in JSON output.

Color#

--color=WHEN#

GCC: -fdiagnostics-color[=WHEN], -fno-diagnostics-color.

Color the text output with ANSI escape sequences. WHEN is auto, always or never; GCC's -fdiagnostics-color with no value means always, and -fno-diagnostics-color means never. Any other value is treated as auto. The default is auto: color is used when standard error is a terminal.

With color, the location and the message are bold, error: is bold red, warning: bold magenta, note: bold cyan, and the caret line and fix-it text bold green. EmbCC does not read GCC_COLORS or NO_COLOR.

Exit status#

Status Meaning
0 No errors. Warnings do not change the status.
1 At least one error, including a warning turned into an error by --warnings-are-errors or --error=NAME. No output file is left (see --warnings-are-errors).

With --fix, the status is 0 whenever at least one fix-it was applied (see --fix).

Internal errors#

An inconsistency inside the compiler is reported as

embcc: error: internal error: ...
embcc: note: this is a bug in EmbCC, not in the program being compiled

with no source location, because the problem is in EmbCC's state rather than at a place in the program. Please report it with the input that caused it.

How many errors one run reports#

Error recovery#

EmbCC reports every independent error it can in one run, rather than stopping at the first.

  • Parsing. After a syntax error the parser skips to the next statement in the current block, or to the next external declaration, and carries on. A missing ; is a special case: the parser continues as if the semicolon were present, so a file missing several semicolons reports each of them.
  • Semantic analysis. An error inside a function body abandons the statement that contains it and continues with the next statement. A name already reported as undeclared in a function is not reported again in that function.
  • C++. The C++ front end recovers the same way, at declarations and at statements. Once parsing is finished and template instantiation has begun, an error ends the compile.

Stages do not run on a broken program. If parsing reported errors, semantic analysis does not start; if semantic analysis reported errors, no code is generated. The run then ends with

embcc: compilation terminated: N errors

N counts every error found, including repeated uses of an undeclared name that were not printed, so it can be larger than the number of error: lines above it.

Some errors end the compile at once, with no summary line:

  • semantic errors outside a function body, such as an invalid file-scope initializer;
  • command-line and file errors (cannot open file, an unknown option);
  • errors from code generation, the assembler and object writers (an unsupported construct for the target, for example).

The following file shows recovery: three syntax errors are reported in one run.

int f(void)
{
    int a = 1
    int b = 2
    return a + b;
}
int g(void) { return ) ; }
embcc: semi.c:4:5: error: expected ';' before 'int' [E0002]
      int b = 2
      ^~~
embcc: semi.c:3:14: note: insert ';' here
      int a = 1
               ^
               ;
embcc: semi.c:5:5: error: expected ';' before 'return' [E0002]
      return a + b;
      ^~~~~~
embcc: semi.c:4:14: note: insert ';' here
      int b = 2
               ^
               ;
embcc: semi.c:7:22: error: expected an expression, got ')'
  int g(void) { return ) ; }
                       ^
embcc: compilation terminated: 3 errors

--max-errors=N#

GCC: -fmax-errors=N.

Stop after N errors. When the limit is reached EmbCC prints the errors so far and

embcc: compilation terminated due to --max-errors=N

and exits with status 1. Warnings turned into errors by --warnings-are-errors or --error=NAME count toward the limit. 0 means no limit, which is the default. The value is read with atoi, so a value that is not a number also means no limit.

EmbCC does not accept Clang's spelling -ferror-limit=N or GCC's -Wfatal-errors (the first is an unknown argument, the second an unknown warning name).

Machine-readable output#

--diagnostics=FORMAT#

GCC: -fdiagnostics-format=FORMAT.

Select the output format. FORMAT is text (the default) or json. Any other value is refused:

embcc: unknown diagnostic format 'xml' (text, json)

json writes GCC's JSON diagnostic format to standard error: one JSON array holding every diagnostic of the run, with notes nested under the diagnostic they belong to. A tool that reads GCC's --diagnostics=json reads EmbCC's.

embcc --syntax-only --diagnostics=json area.c
[
  {"kind": "error", "message": "'heigth' is not declared in 'area' — for a call, add a prototype or define it first", "id": "E0001", "column-origin": 1, "locations": [{"caret": {"file": "area.c", "line": 4, "column": 16, "display-column": 16, "byte-column": 16}, "finish": {"file": "area.c", "line": 4, "column": 21, "display-column": 21, "byte-column": 21}}], "children": [
      {"kind": "note", "message": "did you mean 'height'?", "column-origin": 1, "locations": [{"caret": {"file": "area.c", "line": 4, "column": 16, "display-column": 16, "byte-column": 16}, "finish": {"file": "area.c", "line": 4, "column": 21, "display-column": 21, "byte-column": 21}}], "fixits": [{"start": {"file": "area.c", "line": 4, "column": 16, "display-column": 16, "byte-column": 16}, "next": {"file": "area.c", "line": 4, "column": 22, "display-column": 22, "byte-column": 22}, "string": "height"}]}
  ]}
]

Each element of the array is an object with these members:

Member Present Value
kind always "error", "warning" or "note". A warning turned into an error by --warnings-are-errors or --error=NAME has kind "error".
message always The message text, without the [-W...] and [E....] tags.
option warnings a -W option controls The option, for example "-Wunused-variable". Kept when --warnings-are-errors makes the diagnostic an error.
id diagnostics with an explanation The explain id, for example "E0001". EmbCC's own member; GCC has no equivalent.
column-origin always 1: columns count from 1.
locations always An array with one object. Its caret is the point the diagnostic is about. Its finish is the last column of the marked range (inclusive) and is present only when the column is known.
fixits when the diagnostic has fix-its An array of edits. Each replaces the text from start up to but not including next (both on the same line) with string. Insertion has start equal to next.
children when there are notes The notes, each an object of this same shape.

Each position is an object with file, line, column, display-column and byte-column. All three column values are the same byte column; EmbCC does not compute display widths for tabs or multibyte characters. A diagnostic about a whole line has column 0 and no finish; one about a whole file also has line 0.

Points to be aware of when consuming the output:

  • When a compile produces no diagnostics, nothing at all is written, not even []. Treat empty standard error as an empty array.
  • In JSON mode the compilation terminated line is not printed; the array is the whole of the diagnostic output.
  • Driver messages about the command line (an unknown -W name, for example) are written as plain text before the array. Read from the first [.
  • -fdiagnostics-parseable-fixits has no effect in JSON mode; the fix-its are in the fixits members.

Using JSON diagnostics from an editor#

The usual way for an editor to get diagnostics is to run a syntax-only compile of the buffer with the project's flags and parse the array:

embcc --syntax-only --diagnostics=json -I include -DCONFIG=1 file.c

--syntax-only runs the preprocessor, the parser and semantic analysis and writes no output file. The editor maps each element to its own diagnostic: kind to a severity, caret and finish to a range (converting the 1-based columns to whatever the editor counts from), and children to related information. fixits can be offered as code actions: each is a self-contained text edit that needs no parsing of the message.

EmbCC's language server works this way; see the language server.

Fix-its#

A fix-it is an edit attached to a diagnostic or to one of its notes: a replacement of a span of one source line by new text. Fix-its are offered only where the edit is determined by the diagnostic. They appear in three forms: under the caret in the text output, in the fixits member of the JSON output, and in GCC's parseable line format.

Which diagnostics carry fix-its#

Diagnostic Edit
'NAME' is not declared in 'F' ... (E0001), when a name in scope is within a small edit distance Replace the identifier with the suggested name (on the did you mean note).
'NAME' is not declared ..., when NAME is a known C library function or macro Insert #include <HEADER> and a newline at line 1, column 1.
struct S has no member 'm' (E0004), when S has a member with a close spelling Replace the member name with the suggested one.
expected ';' before ... (E0002) Insert ; after the previous token.
'->' needs a pointer to a struct/union, got struct S (use '.' on a value) Replace -> with ..
'.' needs a struct/union, got struct S * (use '->' through a pointer) Replace . with ->.

The C library names that produce a header suggestion are:

Header Names
<stdio.h> printf, fprintf, sprintf, snprintf, puts, putchar, fputs, fopen, fclose, fread, fwrite, fgets, scanf, sscanf, perror, fflush, fseek, ftell
<stdlib.h> malloc, calloc, realloc, free, exit, abort, atoi, atol, strtol, strtoul, strtod, qsort, bsearch, getenv, rand, srand
<string.h> strlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp, strchr, strrchr, strstr, strdup, memcpy, memmove, memset, memcmp, memchr, strerror
<math.h> sqrt, pow, fabs, sin, cos, tan, log, log2, log10, exp, floor, ceil, round, fmod
<ctype.h> isalpha, isdigit, isalnum, isspace, isupper, islower, toupper, tolower
<assert.h> assert
<time.h> time, clock, strftime, localtime
<errno.h> errno
<unistd.h> write, read, close, lseek, unlink
<fcntl.h> open

A name suggestion (did you mean) is chosen by spelling distance. It is usually, but not always, the intended name; review the edit.

-fdiagnostics-parseable-fixits#

After each diagnostic in the text output, print its fix-its (and those of its notes) in GCC's machine-readable form, one per line:

fix-it:"FILE":{LINE:COL-LINE:NEXT}:"TEXT"

The edit replaces columns COL up to but not including NEXT on line LINE with TEXT. A " or \ in TEXT is preceded by \. For the file above:

fix-it:"area.c":{4:16-4:22}:"height"

The #include fix-it's text ends in a newline, which is written as a literal newline rather than escaped.

--fix#

Apply the fix-its to the source files instead of only printing them. --fix implies --syntax-only: no object file is written. The diagnostics are printed as usual, then each file named by a fix-it is rewritten in place:

embcc: semi_fix.c: applied 2 fixes
embcc: 2 fixes applied; compile again

The rules are:

  • Edits are applied from the end of the file backwards, so earlier positions stay valid.
  • At most one edit is applied per line. Two edits on one line could overlap; the second one is left for the next run.
  • At most 512 fix-its are collected per run.
  • A file with no fix-it is not touched. When nothing could be fixed the driver prints embcc: nothing to fix automatically.

The exit status is 0 if at least one fix was applied, even though the compile itself failed; otherwise it is the status of the compile. Run the compiler again after --fix to see what remains.

Explaining a diagnostic: --explain#

--explain ID, --explain=ID#

Print the explanation for the diagnostic id ID to standard output and exit. An explanation states the rule, shows the mistake and the fix side by side, and cites the paragraph of the C standard the rule comes from. ID is case-insensitive (e0004 works). An unknown id prints

embcc: no explanation for 'E9999'; `embcc --explain` lists what there is

and exits with status 1. --explain with no id lists every entry.

embcc --explain E0004
E0004: a struct or union has no such member

The member name is looked up in the type of the expression to the left of
the `.` or `->`, and that type has no member of that name.

    struct Point { int x; int y; };
    int f(struct Point p) { return p.z; }   /* E0004 */
...
C99 6.5.2.3.

Diagnostics that have an explanation print their id at the end of the message ([E0004]) and carry it in the JSON id member.

Diagnostic ids#

Id Explanation title Printed with
E0001 a name is used that was never declared 'NAME' is not declared in 'F' — for a call, add a prototype or define it first
E0002 a statement or declaration is missing its ';' expected ';' before TOKEN
E0003 a value of one type is used where another is required CONTEXT: cannot convert FROM to TO, where a structure or union is passed as an argument, returned, or used as an initializer for a different type. CONTEXT is argument, return or initialization.
E0004 a struct or union has no such member struct S has no member 'NAME'
E0005 an object is declared of a type whose size is not known 'NAME' has incomplete type TYPE, for an object defined in a block without an initializer
E0006 a function is called with the wrong number of arguments No diagnostic prints this id. The error for a wrong argument count is this call needs N arguments, got M.
E0007 a value is assigned to something that cannot be assigned to No diagnostic prints this id. The related errors are cannot assign to an array and assignment target must be a variable, *pointer, or member.
E0008 control reaches the end of a function that must return a value control may reach the end of 'F', which returns TYPE, a warning (--warn=return-type)

E0006 and E0007 have explanation entries but are not attached to any diagnostic. An assignment to a const-qualified object, the example in the E0007 entry, is the error assignment of read-only 'NAME' (its type is TYPE); see Const qualification.

E0008 is a warning, on by default; --error=return-type makes it an error. It applies to every function with a non-void return type except main. Reaching the closing brace of main returns 0, as C99 5.1.2.2.3 specifies, so int main(void) { } is a valid program.

Warning options#

--help-warnings lists every warning EmbCC has and the group each belongs to.

Controlling warnings#

Options are processed from left to right, and a later option overrides an earlier one about the same warning. A warning named on its own wins over a set, before it or after it, as in GCC: --no-warn=unused-variable --warn=all and --warn=all --no-warn=unused-variable both leave unused-variable off, so a Makefile that puts its exceptions before its warning set builds as it does with GCC.

The exception is --warn=conversion, which turns on --warn=sign-conversion and --warn=float-conversion unless either is named on the command line, before or after it, as in GCC: --no-warn=sign-conversion --warn=conversion and --warn=conversion --no-warn=sign-conversion both leave sign-conversion off.

--warn=all#

GCC: -Wall.

Enable --warn=address, --warn=format, --warn=maybe-uninitialized, --warn=parentheses, --warn=shift-count-overflow, --warn=uninitialized, --warn=unused-function and --warn=unused-variable.

--warn=extra#

GCC: -Wextra, -W.

Enable --warn=implicit-fallthrough, --warn=logical-op, --warn=old-style-declaration, --warn=sign-compare, --warn=type-limits and --warn=unused-parameter. --warn=extra does not imply --warn=all. -W is GCC's older spelling of -Wextra.

--warn=NAME#

GCC: -WNAME.

Enable the warning NAME. If EmbCC has no warning of that name, the option is accepted, has no effect, and the driver prints

embcc: warning: -Wcast-align is not a warning EmbCC has, so it turns nothing on (--help-warnings lists them)

This message is printed immediately, as plain text; --no-warnings does not suppress it and --warnings-are-errors does not make it an error.

#pragma GCC diagnostic#

A file can set warnings for a region of itself with #pragma GCC diagnostic (or #pragma clang diagnostic, or the _Pragma operator form); see #pragma GCC diagnostic. A pragma overrides the command line for the warnings it names, from its position on.

--no-warn=NAME#

GCC: -Wno-NAME.

Disable the warning NAME. A name EmbCC does not have is accepted silently and has no effect.

--warnings-are-errors#

GCC: -Werror.

Turn every warning into an error. The diagnostic is printed with error: instead of warning: and keeps its [--warn=name] tag, and the exit status becomes 1. Warnings that no option controls (a macro redefinition, #warning) are turned into errors too.

A warning turned into an error does not stop the compile: the remaining diagnostics are still reported. The compile has failed, though, and it leaves no output file: the object, assembly file or executable it was writing is removed, together with any file of that name an earlier compile left, so make builds it again on the next run. A dependency file from --deps or --deps-all and a .su file from --stack-usage are still written. A warning that is not an error still lets the object be written.

--no-warnings-are-errors#

GCC: -Wno-error.

Undo an earlier --warnings-are-errors.

--error=NAME, --no-error=NAME#

GCC: -Werror=NAME, -Wno-error=NAME.

--error=NAME turns the warning NAME on and reports it as an error, whether or not --warnings-are-errors is given. --no-error=NAME keeps NAME a warning when --warnings-are-errors is given; it does not turn the warning on. For the warning it names, either option overrides --warnings-are-errors and --no-warnings-are-errors wherever they appear on the command line:

embcc --warn=all --warnings-are-errors --no-error=unused-variable -c f.c   # --warn=unused-variable stays a warning
embcc --error=format -c f.c                            # --warn=format on, and an error

--no-warn=NAME after --error=NAME turns the warning off again, and --no-warnings suppresses it. The warnings no option controls follow --warnings-are-errors alone. A NAME that is not a warning EmbCC has is reported and ignored:

embcc: warning: -Werror=cast-align names no warning EmbCC has (--help-warnings lists them)

Like the unknown-warning message, it is printed immediately as plain text.

--no-warnings#

GCC: -w.

Suppress all warnings that go through the diagnostic engine, including those --warnings-are-errors would have turned into errors. --no-warnings takes effect wherever it appears on the command line. See Limitations for the notes --no-warnings does not suppress.

--warn-in-system-headers#

GCC: -Wsystem-headers.

Report warnings in system headers. By default a warning whose location is in a system header is dropped, because the header is not the project's to fix. Errors are never dropped. A header is a system header when it was found through an --system-include directory or one of EmbCC's own include directories, or was included by another system header. --no-warn=system-headers is accepted and has no effect.

--pedantic, --pedantic-errors#

GCC: -pedantic, -pedantic-errors; GCC's -Wpedantic is not one (below).

EmbCC compiles one C dialect, C11 with the GNU extensions, and has no diagnostics for extensions to ISO C; see C language support. --pedantic and --pedantic-errors are accepted with a warning that they turn nothing on:

embcc: warning: -pedantic: EmbCC has no diagnostics for extensions to ISO C, so this turns nothing on

The message is printed immediately as plain text; --no-warnings does not suppress it and --warnings-are-errors does not make it an error. --pedantic-errors makes no diagnostic an error. -Wpedantic is an unknown warning name and turns nothing on.

Summary of warnings#

The warning reference lists every warning, what it diagnoses and the set that turns it on, printed by the compiler from its own table (embcc --reference=warnings). A warning that is on by default is on unless --no-warn=NAME or --no-warnings is given; one in neither set is on only when --warn=NAME names it.

Warning reference#

Each warning below is turned on by --warn=NAME, which GCC spells -WNAME, and off by --no-warn=NAME (-Wno-NAME).

--warn=address#

Warn when the name of a function is used where its address is always non-null: as the condition of an if, or compared with a null pointer constant using == or !=. This is almost always a missing call. A function declared __attribute__((weak)) is exempt, because its address can be null. A function pointer variable is never reported. Enabled by --warn=all.

void tick(void);
int f(void) { return tick == 0; }
embcc: addr.c:2:22: warning: the address of 'tick' is never null, so this is always false -- a call may be missing [--warn=address]
  int f(void) { return tick == 0; }
                       ^~~~

In an if, the message is ... so this branch is always taken -- a call may be missing.

--warn=attributes#

Warn about an __attribute__ that has no effect. Two cases are reported:

  • an attribute EmbCC does not know: attribute 'NAME' is not one EmbCC knows, and is ignored;
  • an attribute EmbCC accepts but does not implement, currently error and warning: __attribute__((error)) is accepted but does nothing here: ..., followed by the reason.

On by default. Attributes that EmbCC refuses outright are errors, not warnings; see Extensions.

int odd(void) __attribute__((frobnicate));
embcc: attr.c:1: warning: attribute 'frobnicate' is not one EmbCC knows, and is ignored [--warn=attributes]
  int odd(void) __attribute__((frobnicate));

--warn=conversion#

Warn about an implicit conversion that may change a value. The checked conversions are assignment, initialization, argument passing, return, the conversion of the operands of an arithmetic or bitwise operator to their common type, and the conversion of the second and third operands of ?:. The rules are GCC's, so a program that compiles without these warnings under GCC compiles without them under EmbCC:

  • An integer converted to a narrower integer type, a bit-field included, or to a floating type that cannot represent every value of its type (int to float, long long to double).
  • An arithmetic result is reported only when one of its operands would be reported converted on its own. unsigned char x = b + 1; with b an unsigned char is not reported; x = i + 1; with i an int is. --warn=arith-conversion reports both.
  • A conversion that only widens is looked through: (int)b has the values of b.
  • x & C, u % 2^k and an expression built from constants and ?: are not reported when every value they can have fits the target type.
  • A comparison, !, && and || give 0 or 1 and are not reported.
  • A conversion to or from an enumerated type, or to _Bool, is not reported.
  • A constant is reported only when its value changes, and the message gives both values.

--warn=conversion also enables --warn=sign-conversion and --warn=float-conversion unless either is named on the command line.

unsigned char f(int i, unsigned char b) { b += 1; return i & 0xff; }
embcc: conv.c:1:58: warning: conversion from 'int' to 'unsigned char' may change value [--warn=conversion]
  unsigned char f(int i, unsigned char b) { b += 1; return i & 0xff; }
                                                           ^

0xff does not fit a signed char, so the mask does not decide the result's sign; i & 0x7f is not reported.

EmbCC checks only C. With -x c++ these options report nothing.

--warn=sign-conversion#

Warn about an implicit conversion between a signed and an unsigned integer type that may change the sign of the value: a signed value that may be negative converted to an unsigned type, or an unsigned value converted to a signed type of the same width. This includes the operands that an arithmetic operator converts: in i + u, i is converted to unsigned int. A negative constant is reported with its converted value (unsigned conversion from 'int' to 'unsigned int' changes value from '-1' to '4294967295'). Comparisons are -Wsign-compare's. Enabled by --warn=conversion.

--warn=float-conversion#

Warn about an implicit conversion from a floating type to an integer type, or to a narrower floating type, that may change the value: int k = d;, float f = d;, and float f = 0.1;. A floating constant that converts exactly (int k = 3.0;, float f = 1.5;) is not reported, and neither is an integer constant a floating type holds exactly (float f = 16777216;; 16777217 is reported). Enabled by --warn=conversion.

--warn=overflow#

Warn when an integer constant is converted to an integer type that cannot hold it either as a signed or as an unsigned value of that width: unsigned char c = 300; or a 3-bit field assigned 9. unsigned char c = -1; is not reported here, since -1 fits as a signed value; it is a -Wsign-conversion. On by default, as in GCC.

embcc: o.c:1:19: warning: unsigned conversion from 'int' to 'unsigned char' changes value from '300' to '44' [--warn=overflow]

--warn=deprecated-declarations#

Warn when a function or variable declared with __attribute__((deprecated)) or [[deprecated]] is called, read, or has its address taken. The message is 'NAME' is deprecated; the text argument of deprecated("...") is not printed. On by default.

int old(void) __attribute__((deprecated));
int f(void) { return old(); }
embcc: dep.c:2:22: warning: 'old' is deprecated [--warn=deprecated-declarations]
  int f(void) { return old(); }
                       ^~~

--warn=discarded-qualifiers#

Warn when a pointer to a const-qualified type is converted, without a cast, to a pointer whose pointed-to type is not const: in an initialization, an assignment, an argument or a return. A store through the result would modify a read-only object. On by default, as in GCC.

void take(char *p);
void f(const char *s) { take(s); }
embcc: q.c:2:30: warning: argument discards the 'const' qualifier of const char * [--warn=discarded-qualifiers]
  void f(const char *s) { take(s); }
                               ^

--warn=div-by-zero#

Warn about an integer division or remainder whose right operand is a constant expression equal to zero: division by zero. On by default.

int f(int x) { return x / 0; }
embcc: div.c:1:23: warning: division by zero [--warn=div-by-zero]
  int f(int x) { return x / 0; }
                        ^

--warn=format#

Check direct calls to functions declared with __attribute__((format(printf, N, M))) or __attribute__((format(scanf, N, M))) whose format argument is a string literal. The declarations in EmbCC's <stdio.h> carry the attribute, and a program's own functions can use it. Enabled by --warn=all.

The check compares each conversion with the argument it consumes, after the default argument promotions, so printf("%f", 1.0f) and printf("%d", (short)x) are correct and are not reported. For scanf, nothing is promoted through the pointer: %f writes a float and %lf a double. It reports:

  • an integer conversion whose argument has a different size (%d reads 4 bytes, but this argument is long, which is 8), by the target's sizes: %ld reads 8 bytes on x86-64 and AArch64, 4 on the 32-bit targets and AVR, where %d reads 2;
  • an integer conversion given a pointer or a non-integer, a floating conversion given a non-floating argument, %Lf-style mismatches between double and long double;
  • %s given something that is not a pointer to characters, and a pointer conversion given a non-pointer;
  • a scanf conversion whose pointer points at the wrong class or size of object;
  • a * width or precision whose argument is not an integer;
  • an unknown conversion character ('%y' is not a conversion this format understands); the argument count is not checked after one;
  • too few or too many arguments for the format.

A difference in signedness alone (printf("%d", 3u), printf("%x", -1)) is not reported. When M is 0 (a function taking a va_list), only the format string itself is checked.

#include <stdio.h>
void f(long n) { printf("%d\n", n); }
embcc: fmt.c:2:33: warning: %d reads 4 bytes, but this argument is long, which is 8 [--warn=format]
  void f(long n) { printf("%d\n", n); }
                                  ^

--warn=logical-op#

Warn when both operands of && or || are the same expression: the same variable, the same chain of member accesses, or the same number, with no side effects. Enabled by --warn=extra.

int f(int x) { return x || x; }
embcc: logic.c:1:23: warning: both operands of '||' are the same expression [--warn=logical-op]
  int f(int x) { return x || x; }
                        ^

--warn=maybe-uninitialized#

Warn when a local variable is read at a point that some, but not all, paths reach with a value written: a write inside an if with no else, a loop that may run zero times, a switch with no default. Each variable is reported once, with a note at its declaration. Enabled by --warn=all. See --warn=uninitialized for what the analysis covers.

int g(int c)
{
    int y;
    if (c)
        y = 1;
    return y;
}
embcc: maybe.c:6:12: warning: 'y' may be used uninitialized [--warn=maybe-uninitialized]
      return y;
             ^
embcc: maybe.c:3:9: note: 'y' is declared here, with no initializer
      int y;
          ^

A value carried around a loop (written late in the body and read earlier in it on the next iteration) is not reported when the read is under a test inside that loop, because such a test is usually the guard that makes the read safe.

--warn=parentheses#

Warn when a comparison (==, !=, <, <=, >, >=) is an operand of a bitwise &, | or ^ without parentheses around it. Because comparisons bind more tightly, reg & MASK == 0 means reg & (MASK == 0). Enabled by -Wall. Other cases GCC's --warn=parentheses covers, such as an assignment used as a condition, are not reported.

int f(unsigned reg) { return reg & 0x4 == 0; }
embcc: paren.c:1:30: warning: comparison binds tighter than '&' here: this is 'a & (b == c)', not '(a & b) == c' [--warn=parentheses]
  int f(unsigned reg) { return reg & 0x4 == 0; }
                               ^~~

The message always shows ==, whichever comparison operator was used.

--warn=shadow#

Warn when a declaration in a block hides a local variable or parameter that is still in scope, or when a local variable or a parameter hides a file-scope variable declared before the function, with a note at the hidden declaration. A local that has the name of a function is not reported, as with GCC and Clang. Not enabled by -Wall or -Wextra. The message for a file-scope variable is declaration of 'NAME' shadows a global declaration.

int f(int n)
{
    int total = 0;
    for (int i = 0; i < n; i++) {
        int total = i;
        (void)total;
    }
    return total;
}
embcc: shadow.c:5:13: warning: declaration of 'total' shadows an earlier one [--warn=shadow]
          int total = i;
              ^~~~~
embcc: shadow.c:3:9: note: the one it hides is here
      int total = 0;
          ^~~~~

When the hidden name is a parameter the message is ... shadows a parameter.

--warn=shift-count-overflow#

Warn when the right operand of << or >> is a constant that is negative or not less than the width in bits of the promoted left operand. Such a shift is undefined, and the hardware typically masks the count rather than producing zero. Enabled by --warn=all.

int f(int x) { return x << 32; }
embcc: shift.c:1:23: warning: shift count 32 is not less than the width of int (32 bits) [--warn=shift-count-overflow]
  int f(int x) { return x << 32; }
                        ^

A negative count gives shift count -1 is negative.

--warn=sign-compare#

Warn when a comparison between a signed and an unsigned integer is performed in an unsigned type, so that a negative value compares as a large one. A signed operand that is a non-negative constant is not reported. Enabled by --warn=extra.

int f(int i, unsigned n) { return i < n; }
embcc: sign.c:1:35: warning: comparison between int and unsigned int [--warn=sign-compare]
  int f(int i, unsigned n) { return i < n; }
                                    ^

--warn=type-limits#

Warn when a value of unsigned type is compared with the constant 0 in a way whose result the type already decides: u < 0 (always false) and u >= 0 (always true), in either operand order. Enabled by --warn=extra. Other range comparisons, such as a char compared with 300, are not reported.

int f(unsigned u) { return u < 0; }
embcc: limits.c:1:28: warning: comparison of unsigned int with 0 is always false: it has no negative values [--warn=type-limits]
  int f(unsigned u) { return u < 0; }
                             ^

--warn=uninitialized#

Warn when a local variable is read at a point that no path reaches with a value written. Each variable is reported once, with a note at its declaration. Enabled by --warn=all.

The analysis follows the control flow of the function body: if, loops, break and continue, switch and case labels, &&, || and ?:, return, and calls to functions declared noreturn. va_start(ap, ...) counts as a write of ap. It checks scalar local variables only; arrays, structures, static locals and globals are not checked. It also does not check:

  • a variable whose address is taken (&x), because writes through the pointer are not followed;
  • a variable used as an operand of an asm statement;
  • any variable in a function that contains a goto, a label, or a label address (&&label): such a function is not analysed at all.
int f(int c)
{
    int x;
    return x + c;
}
embcc: uninit.c:4:12: warning: 'x' is used uninitialized [--warn=uninitialized]
      return x + c;
             ^
embcc: uninit.c:3:9: note: 'x' is declared here, with no initializer
      int x;
          ^

(void)x counts as a read: it is reported if x was never written.

--warn=unused-function#

Warn about a static function that is defined and never called or referred to. A non-static function is never reported, because another translation unit may call it, and neither is main. A function declared __attribute__((unused)) or __attribute__((used)) is exempt, and so is a static inline function defined in a header, as with Clang. Enabled by --warn=all. The location has a line but no column.

static int helper(int x) { return x; }
embcc: func.c:1: warning: unused function 'helper' [--warn=unused-function]
  static int helper(int x) { return x; }

--warn=unused-parameter#

Warn about a named parameter that the function body never uses. Enabled by --warn=extra. The location has a line but no column. Writing (void)name; in the body marks the parameter as used, and so does __attribute__((unused)) or [[maybe_unused]] on it, written before its type, between the type and the name, or after the name (int f(int x __attribute__((unused)))).

int f(int a, int b) { return a; }
embcc: param.c:1: warning: unused parameter 'b' [--warn=unused-parameter]
  int f(int a, int b) { return a; }

--warn=unused-result#

Warn when a call to a function declared __attribute__((warn_unused_result)) or [[nodiscard]] is an expression statement on its own, so its value is discarded. Casting the call to void marks the discard as intended. On by default.

int must(void) __attribute__((warn_unused_result));
void f(void) { must(); }
embcc: result.c:2:16: warning: result of 'must' is discarded, and it is declared warn_unused_result [--warn=unused-result]
  void f(void) { must(); }
                 ^~~~

--warn=unused-variable#

Warn about a local variable that is declared and never referred to. A variable that is assigned but never read counts as used. A variable declared __attribute__((unused)) is exempt, as are names the compiler introduces for its own purposes. Enabled by --warn=all.

int f(int a)
{
    int unused;
    return a;
}
embcc: var.c:3:9: warning: unused variable 'unused' [--warn=unused-variable]
      int unused;
          ^~~~~~

--warn=windows-abi#

Issued once for every object file written for a Windows target (--target=x86_64-windows-gnu or x86_64-w64-mingw32): the code EmbCC generates for Windows is not yet fully the Microsoft x64 ABI. long is 8 bytes and wchar_t 4 (Windows has 4 and 2), and rsi, rdi, xmm6 and xmm7 are not preserved across calls, so objects EmbCC compiles agree with each other but not with code built by other compilers. On by default. The warning carries a file name but no line, and is not issued for --syntax-only, -E or -S. See Targets for the state of the Windows target.

embcc: win.c: warning: x86_64-windows-gnu is not yet the Microsoft x64 ABI: `long` is 8 bytes and wchar_t 4 (Windows has 4 and 2), and rsi, rdi, xmm6 and xmm7 are not preserved across a call: objects EmbCC compiles agree with each other and with nothing else [--warn=windows-abi]

Warnings no option controls#

These warnings have no --warn= option and no [--warn=...] tag. --no-warnings suppresses them and --warnings-are-errors turns them into errors; --warn=no-... cannot turn them off individually.

Message Cause
macro 'NAME' redefined A #define that changes an existing macro's definition. The new definition replaces the old one.
#warning: TEXT A #warning directive.
section("NAME") on function 'F' is ignored C++ only: a section attribute on a function.

The lexer's escape sequence out of range warnings and the optimizer's non-convergence warning are printed outside the diagnostic engine (see When diagnostics are printed); no option, including --no-warnings, affects them.

Warnings in C++#

The analyses behind the warnings run in the C front end, over the C that EmbCC lowers C++ to. For a C++ compile:

  • --warn=unused-variable, --warn=unused-parameter, --warn=unused-function, --warn=shadow and --warn=sign-compare are switched off, whatever the command line says, because the lowered code contains names and conversions the programmer did not write.
  • The other warnings report what survives lowering in a recognizable form. --warn=format, --warn=uninitialized, --warn=maybe-uninitialized, --warn=div-by-zero, --warn=shift-count-overflow and --warn=type-limits report on C++ source. --warn=conversion, --warn=sign-conversion, --warn=float-conversion, --warn=arith-conversion and --warn=overflow do not report for C++. The C++ front end handles attributes itself, so --warn=attributes, --warn=deprecated-declarations and --warn=unused-result do not report for C++ declarations; --warn=parentheses, --warn=address and --warn=logical-op do not report either.
  • The line and column of a warning can be wrong, because they come from the lowered code.

See C++ support.

Limitations#

  • A note that belongs to a warning that is not printed (because its option is off, because of --no-warnings, or because it is in a system header) is still printed. With --warn=uninitialized on and --warn=maybe-uninitialized off, for example, the 'y' is declared here, with no initializer note of the suppressed warning appears on its own. In JSON it is attached to whichever diagnostic came before it.
  • __attribute__((unused)) is honoured on local variables and functions, not on parameters.
  • The C++ limitations described in Warnings in C++.

Options EmbCC does not implement#

The driver keeps no list of GCC or Clang warning options to accept and ignore. Instead, every option that starts with -W is handled by the rules above, with these consequences:

Option What EmbCC does
--warn=NAME for a name not in the warning reference (--warn=cast-align, --warn=null-dereference, --warn=pedantic, --warn=unused, --warn=everything, --warn=fatal-errors, ...) Accepted; prints embcc: warning: -WNAME is not a warning EmbCC has, so it turns nothing on (--help-warnings lists them); no effect.
--no-warn=NAME for a name not in the summary (--no-warn=unused, --no-warn=system-headers, ...) Accepted silently; no effect. --no-warn=unused does not turn off the -Wunused-* warnings.
--error=NAME, --no-error=NAME for a name not in the summary Accepted; prints embcc: warning: -Werror=NAME names no warning EmbCC has (--help-warnings lists them); no effect.
-Wp,ARGS Not passed to the preprocessor. Treated as an unknown warning name: the is not a warning EmbCC has message is printed and the arguments are ignored.
-Wl,ARGS, --assembler-arg=ARGS Options for the link and the assembler, not warnings; see Invoking EmbCC.
--pedantic, --pedantic-errors Accepted; prints embcc: warning: -pedantic: EmbCC has no diagnostics for extensions to ISO C, so this turns nothing on; no effect.

Optimization remarks#

A remark records a decision an optimization pass made and the reason for it, at the point where the pass made it. Remarks are not collected unless asked for, because building them costs time on every compile.

EmbCC does not implement Clang's -Rpass=, -Rpass-missed= and -Rpass-analysis= options or -fsave-optimization-record; they are unknown arguments. Use --remarks and embcc why.

--remarks#

GCC: -fremarks.

Collect remarks during the compile and print them to standard error when it ends, one per line, in the order the passes made them:

FILE:LINE: remark: DECISION 'SUBJECT': DETAIL [PASS/REASON]

For example, at --opt=speed:

rem.c:11: remark: inlined 'add': 4 instructions into sum, budget 24 [inline/small-enough]
rem.c:12: remark: not-inlined 'variadic' [inline/callee-is-varargs]
rem.c:6: remark: kept-in-memory 'vol' [mem2reg/declared-volatile]
rem.c:7: remark: kept-in-memory 'addressed' [mem2reg/address-is-taken]
rem.c:13: remark: branch-never-jumps 'sum': the condition folded to 1, so one arm is unreachable [sccp/condition-is-a-constant]
rem.c:11: remark: unrolled 'sum': 8 copies of a 8-instruction body, the original kept for the remainder [unroll/counted-loop]

REASON is a stable code meant to be matched by tools and searched for. DETAIL is free text giving the facts that settled the decision, and may change between releases. Most remarks come from passes that run only when optimizing; at --opt=none there are few or none.

--remarks=json#

GCC: -fremarks=json.

The same remarks as a JSON array on standard error. Each element has pass, decision, subject and reason; detail when there is one; and location (an object with file and line) when the remark has a location.

[
  {"pass": "inline", "decision": "inlined", "subject": "add", "reason": "small-enough", "detail": "4 instructions into sum, budget 24", "location": {"file": "rem.c", "line": 11}},
  {"pass": "inline", "decision": "not-inlined", "subject": "variadic", "reason": "callee-is-varargs", "location": {"file": "rem.c", "line": 12}},
  ...
]

The remark array is separate from the diagnostic array of --diagnostics=json; when both are requested, standard error holds both.

Remark producers#

Pass Decisions Reasons
inline inlined, not-inlined small-enough, always_inline; callee-too-large, not-always_inline-at-O0, not-a-sole-callee-at-O1, callee-not-defined-here, would-be-recursive, callee-is-noinline, callee-is-weak, callee-is-varargs, callee-computes-in-__int128, callee-has-exception-regions, returns-a-struct, parameter-is-not-a-simple-scalar, returns-a-value-wider-than-a-vreg, callee-uses-va_start, callee-has-a-vla, callee-uses-a-computed-goto, callee-calls-a-struct-returning-function
mem2reg promoted-to-register, kept-in-memory (one per local variable, at its declaration) scalar-and-never-addressed; address-is-taken, declared-volatile, read-is-volatile, write-is-volatile, read-is-partial-or-extending, write-is-partial, not-a-scalar-integer-pointer-or-float, not-4-or-8-bytes, type-unknown
sroa split-into-scalars, kept-whole (one per aggregate local) address-never-escapes; address-escapes, address-held-in-a-reassigned-temp, declared-volatile, access-is-volatile, access-is-not-1-2-4-or-8-bytes, access-runs-outside-the-object, two-accesses-overlap-at-different-widths, too-many-pieces, too-large-to-split, is-a-wide-scalar, is-a-variable-length-array
sccp branch-always-jumps, branch-never-jumps condition-is-a-constant
unroll unrolled counted-loop
switch-thread threaded state-known
regalloc spilled-to-stack, split no-register-free, loop-live-range
opt optimized, rewrote, inferred, hoisted, promoted, rotated, vectorized, strength-reduced, recognized-memcpy, recognized-memzero, dropped fixpoint-reached, pass-counts, attr/const, attr/pure, licm/loop-invariant, licm/load, licm/promote, licm/bottom-tested-loop, vec/constant-trip-count, vec/runtime-trip-count, ivsr/address, idiom/loop, unreachable

The SCCP decisions describe the branch in the intermediate code: a branch that "always jumps" corresponds to a source condition that is false when the condition is the test of an if, because an if is lowered to a jump over its body. The remark does not translate this back to the source condition.

optimized reports a function's instruction count before and after optimization; rewrote reports, per function, how many changes the passes that make no remark of their own (value numbering, CSE, load reuse, copy propagation, dead code and dead stores, selects, partial redundancy elimination) made.

embcc why DECISION [SUBJECT] FILE [OPTIONS]#

Compile FILE with OPTIONS (any ordinary compile options, such as --opt=speed, -I or --target=), collect the remarks, and print to standard output the ones whose decision is DECISION and, when SUBJECT is given, whose subject is SUBJECT. No output file is written. why must be the first argument. The word after DECISION is taken as SUBJECT when it neither starts with - nor contains a . and at least one more argument follows it.

embcc why kept-in-memory vol rem.c --opt=speed
vol (rem.c:6): kept-in-memory
  because declared-volatile
  decided by the mem2reg pass

When nothing matches, why says so instead of printing an empty answer:

nothing recorded: no pass made a 'not-inlined' decision
(remarks come from the passes that RAN -- an optimization
decision needs -O2)

The exit status is 0 whether or not anything matched, and 1 for a usage error. The dropped decision is recorded only under --remarks, so embcc why dropped does not find it.

embcc inspect#

embcc inspect STAGE FILE [OPTIONS] prints what one stage of the compiler built (tokens, pp, ast, symbols, types, ir, cfg, callgraph) instead of compiling. It shows the result of the decisions that remarks explain; see Invoking EmbCC.

The language server#

embls is EmbCC's language server: it speaks the Language Server Protocol over standard input and output. For diagnostics it runs embcc --syntax-only --diagnostics=json over the editor's buffer and publishes each error and warning, with its notes appended to the message, so the editor shows what the build will report. It does not offer the fix-its as code actions. It also answers completion, hover, go-to-definition, references, rename, signature help and document symbols from EmbCC's own front end. It reads compile flags from compile_flags.txt in the file's directory or above it, and from the EMBLS_FLAGS environment variable. See embls.

embld --doctor#

embld --doctor FILES... reads the objects and archives a link would use and explains, for every undefined symbol at once, why it is undefined: a definition that exists but is static, a C library function that needs a library the link does not add, the C++ runtime, a missing key function for a vtable, a member function declared and never defined, or no definition anywhere. See embld.

embld --doctor main.o util.o
embld: undefined: helper
  wanted by main.o
  util.o does define it — but as `static`, which keeps it inside that
  unit. Drop the `static`, or move the caller into that file.
embld: doctor: 1 symbol undefined