Tutorial part 1: “Hello world”

Before we look at the details of the API, let’s look at building and running programs that use the library.

Here’s a toy program that uses libgdiagnostics to emit an error message to stderr.

/* Minimal usage example.  */
#include "libgdiagnostics.h"

static diagnostic_manager *diag_mgr;

static void
init_diagnostics (void)
{
  diag_mgr = diagnostic_manager_new ();
  diagnostic_manager_add_text_sink (diag_mgr, stderr,
				    DIAGNOSTIC_COLORIZE_IF_TTY);
}

static void
finish_diagnostics (void)
{
  diagnostic_manager_release (diag_mgr);
}

static void
do_stuff (void)
{
  const char *username = "Dave";
  diagnostic *d = diagnostic_begin (diag_mgr,
				    DIAGNOSTIC_LEVEL_ERROR);
  diagnostic_finish (d,
		     "I'm sorry %s, I'm afraid I can't do that",
		     username);
}

int
main ()
{
  init_diagnostics ();

  do_stuff ();

  finish_diagnostics ();
};

Copy the above to tut01-hello-world.c.

Assuming you have libgdiagnostics installed, build the test program using:

$ gcc \
    tut01-hello-world.c \
    -o tut01-hello-world \
    -lgdiagnostics

You should then be able to run the built program:

$ ./tut01-hello-world
progname: error: I'm sorry Dave, I'm afraid I can't do that

If stderr is connected to a terminal, you should get colorized output (using SGR control codes).

../_images/example-1.png

Otherwise, the output will be plain text.

Obviously a trivial example like the above could be done using fprintf on stderr, and it’s fairly easy to colorize text at the terminal.

In the next part of the tutorial we’ll add file/location information to our error messages, and libgdiagnostics will quote the pertinent parts of the file, underlining them, which is less trivial to reimplement. libgdiagnostics gives us many other such abilities, such as fix-it hints and execution paths, which we’ll cover in the following tutorials. Also, once a program’s diagnostics are using libgdiagnostics, it is trivial to add support for outputting them in machine-readable form as SARIF.

Structure

The above example shows the typical structure of a program using libgdiagnostics:

For non-trivial examples we’ll also want to create location information, which could happen during initialization, or during a parsing phase of the program using libgdiagnostics. See Tutorial part 2: physical locations for more information.

Formatted messages

The above example uses diagnostic_finish(), which takes a format string and arguments. libgdiagnostics has its own style of format string arguments used for diagnostic_finish() and some other entrypoints.

Note

The format syntax is not the same as printf; see supported formatting options.

You can use the q modifier on arguments to quote them, so, for example %qs is a quoted string, consuming a const char * argument:

diagnostic_finish (d, "can't find %qs", "foo");

This gives output like this:

progname: error: can't find ‘foo’

where the quoted string will appear in bold in a suitably-capable terminal, and the quotes will be internationalized, so that e.g. with LANG=fr_FR.UTF8 we might get:

progname: erreur: can't find « foo »

Note that:

  • the string error has been localized by libgdiagnostics to erreur,

  • locale-specific quoting has been used (« and » rather than and ),

  • foo hasn’t been localized - you would typically use quoted strings for referring to identifiers in the input language (such as function names in code, property names in JSON, etc),

  • the message itself hasn’t been localized: you are responsible for passing a translated format string to diagnostic_finish() if you want to internationalize the output.

There are many supported formatting options.

Naming the program

In the above output the message was preceded with progname. This appears for diagnostics that don’t have any location information associated with them. We’ll look at setting up location information in the next tutorial, but we can override this default name via diagnostic_manager_set_tool_name():

diagnostic_manager_set_tool_name (diag_mgr, "my-awesome-checker");

leading to output like this:

my-awesome-checker: error: can't find ‘foo’

There are various other functions for supplying metadata to libgdiagnostics.

Moving beyond trivial examples

Obviously it’s not very useful if we can’t refer to specific files and specific locations in those files in our diagnostics, so read part 2 of the tutorial for information on how to do this.