mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-23 02:44:18 +08:00
Add libdiagnostics (v4)
This patch adds a new libdiagnostics shared library available as part of the GCC build via --enable-libdiagnostics when configuring GCC. It combines the following patches from: https://gcc.gnu.org/pipermail/gcc-patches/2024-November/668632.html [PATCH 1/8] libdiagnostics v4: header [PATCH 2/8] libdiagnostics v4: implementation [PATCH 3/8] libdiagnostics: add API docs [PATCH 4/8] libdiagnostics v4: add C++ wrapper API [PATCH 6/8] libdiagnostics v4: test suite ChangeLog: * configure.ac (--enable-libdiagnostics): New. * configure: Regenerate. gcc/ChangeLog: * configure.ac (check_languages): Add check-libdiagnostics. (--enable-libdiagnostics): New. * configure: Regenerate. * Makefile.in (enable_libdiagnostics): New. (lang_checks): If libdiagnostics is enabled, add check-libdiagnostics. (ALL_HOST_OBJS): If libdiagnostics is enabled, add $(libdiagnostics_OBJS). (start.encap): Add LIBDIAGNOSTICS. (libdiagnostics_OBJS): New. (LIBDIAGNOSTICS_VERSION_NUM): New, adapted from code in jit/Make-lang.in. (LIBDIAGNOSTICS_MINOR_NUM): Likewise. (LIBDIAGNOSTICS_RELEASE_NUM): Likewise. (LIBDIAGNOSTICS_FILENAME): Likewise. (LIBDIAGNOSTICS_IMPORT_LIB): Likewise. (libdiagnostics): Likewise. (LIBDIAGNOSTICS_AGE): Likewise. (LIBDIAGNOSTICS_BASENAME): Likewise. (LIBDIAGNOSTICS_SONAME): Likewise. (LIBDIAGNOSTICS_LINKER_NAME): Likewise. (LIBDIAGNOSTICS_COMMA): Likewise. (LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION): Likewise. (LIBDIAGNOSTICS_SONAME_OPTION): Likewise. (LIBDIAGNOSTICS_SONAME_SYMLINK): Likewise. (LIBDIAGNOSTICS_LINKER_NAME_SYMLINK): Likewise. (LIBDIAGNOSTICS_FILENAME): Likewise. (libdiagnostics.serial): Likewise. (LIBDIAGNOSTICS_EXTRA_OPTS): Likewise. (install): If libdiagnostics is enabled, add install-libdiagnostics. (libdiagnostics.install-headers): New. (libdiagnostics.install-common): New, adapted from code in jit/Make-lang.in. (install-libdiagnostics): New. * diagnostic-format-text.h (diagnostic_text_output_format::get_location_text): Make public. * doc/install.texi (--enable-libdiagnostics): New. * doc/libdiagnostics/Makefile: New file. * doc/libdiagnostics/conf.py: New file. * doc/libdiagnostics/index.rst: New file. * doc/libdiagnostics/make.bat: New file. * doc/libdiagnostics/topics/diagnostic-manager.rst: New file. * doc/libdiagnostics/topics/diagnostics.rst: New file. * doc/libdiagnostics/topics/execution-paths.rst: New file. * doc/libdiagnostics/topics/fix-it-hints.rst: New file. * doc/libdiagnostics/topics/index.rst: New file. * doc/libdiagnostics/topics/logical-locations.rst: New file. * doc/libdiagnostics/topics/message-formatting.rst: New file. * doc/libdiagnostics/topics/metadata.rst: New file. * doc/libdiagnostics/topics/physical-locations.rst: New file. * doc/libdiagnostics/topics/retrofitting.rst: New file. * doc/libdiagnostics/topics/sarif.rst: New file. * doc/libdiagnostics/topics/text-output.rst: New file. * doc/libdiagnostics/topics/ux.rst: New file. * doc/libdiagnostics/tutorial/01-hello-world.rst: New file. * doc/libdiagnostics/tutorial/02-physical-locations.rst: New file. * doc/libdiagnostics/tutorial/03-logical-locations.rst: New file. * doc/libdiagnostics/tutorial/04-notes.rst: New file. * doc/libdiagnostics/tutorial/05-warnings.rst: New file. * doc/libdiagnostics/tutorial/06-fix-it-hints.rst: New file. * doc/libdiagnostics/tutorial/07-execution-paths.rst: New file. * doc/libdiagnostics/tutorial/index.rst: New file. * libdiagnostics++.h: New file. * libdiagnostics.cc: New file. * libdiagnostics.h: New file. * libdiagnostics.map: New file. gcc/testsuite/ChangeLog: * libdiagnostics.dg/libdiagnostics.exp: New, adapted from jit.exp. * libdiagnostics.dg/sarif.py: New. * libdiagnostics.dg/test-dump.c: New test. * libdiagnostics.dg/test-error-c.py: New test. * libdiagnostics.dg/test-error-with-note-c.py: New test. * libdiagnostics.dg/test-error-with-note.c: New test. * libdiagnostics.dg/test-error-with-note.cc: New test. * libdiagnostics.dg/test-error.c: New test. * libdiagnostics.dg/test-error.cc: New test. * libdiagnostics.dg/test-example-1.c: New test. * libdiagnostics.dg/test-fix-it-hint-c.py: New test. * libdiagnostics.dg/test-fix-it-hint.c: New test. * libdiagnostics.dg/test-fix-it-hint.cc: New test. * libdiagnostics.dg/test-helpers++.h: New test. * libdiagnostics.dg/test-helpers.h: New test. * libdiagnostics.dg/test-labelled-ranges.c: New test. * libdiagnostics.dg/test-labelled-ranges.cc: New test. * libdiagnostics.dg/test-labelled-ranges.py: New test. * libdiagnostics.dg/test-logical-location-c.py: New test. * libdiagnostics.dg/test-logical-location.c: New test. * libdiagnostics.dg/test-metadata-c.py: New test. * libdiagnostics.dg/test-metadata.c: New test. * libdiagnostics.dg/test-multiple-lines-c.py: New test. * libdiagnostics.dg/test-multiple-lines.c: New test. * libdiagnostics.dg/test-no-column-c.py: New test. * libdiagnostics.dg/test-no-column.c: New test. * libdiagnostics.dg/test-no-diagnostics-c.py: New test. * libdiagnostics.dg/test-no-diagnostics.c: New test. * libdiagnostics.dg/test-note-with-fix-it-hint-c.py: New test. * libdiagnostics.dg/test-note-with-fix-it-hint.c: New test. * libdiagnostics.dg/test-text-sink-options.c: New test. * libdiagnostics.dg/test-warning-c.py: New test. * libdiagnostics.dg/test-warning-with-path-c.py: New test. * libdiagnostics.dg/test-warning-with-path.c: New test. * libdiagnostics.dg/test-warning.c: New test. * libdiagnostics.dg/test-write-sarif-to-file-c.py: New test. * libdiagnostics.dg/test-write-sarif-to-file.c: New test. * libdiagnostics.dg/test-write-text-to-file.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
c9d21e19df
commit
99a909aba3
42
configure
vendored
42
configure
vendored
@ -691,6 +691,7 @@ extra_host_libiberty_configure_flags
|
||||
stage1_languages
|
||||
host_libs_picflag
|
||||
CRAB1_LIBS
|
||||
enable_libdiagnostics
|
||||
PICFLAG
|
||||
host_shared
|
||||
gcc_host_pie
|
||||
@ -844,6 +845,7 @@ enable_linker_plugin_configure_flags
|
||||
enable_linker_plugin_flags
|
||||
enable_host_pie
|
||||
enable_host_shared
|
||||
enable_libdiagnostics
|
||||
enable_stage1_languages
|
||||
enable_objc_gc
|
||||
with_target_bdw_gc
|
||||
@ -1578,6 +1580,7 @@ Optional Features:
|
||||
plugins [none]
|
||||
--enable-host-pie build position independent host executables
|
||||
--enable-host-shared build host code as shared libraries
|
||||
--enable-libdiagnostics build libdiagnostics shared library
|
||||
--enable-stage1-languages[=all]
|
||||
choose additional languages to build during stage1.
|
||||
Mostly useful for compiler development
|
||||
@ -9603,6 +9606,45 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
# Check for libdiagnostics support.
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable libdiagnostics" >&5
|
||||
$as_echo_n "checking whether to enable libdiagnostics... " >&6; }
|
||||
# Check whether --enable-libdiagnostics was given.
|
||||
if test "${enable_libdiagnostics+set}" = set; then :
|
||||
enableval=$enable_libdiagnostics; enable_libdiagnostics=$enableval
|
||||
else
|
||||
enable_libdiagnostics=no
|
||||
fi
|
||||
|
||||
|
||||
if test x$enable_libdiagnostics = xyes; then
|
||||
# Disable libdiagnostics if -enable-host-shared not specified
|
||||
# but not if building for Mingw. All code in Windows
|
||||
# is position independent code (PIC).
|
||||
case $target in
|
||||
*mingw*) ;;
|
||||
*)
|
||||
if test x$host_shared != xyes; then
|
||||
as_fn_error $? "
|
||||
Enabling libdiagnostics requires --enable-host-shared.
|
||||
|
||||
--enable-host-shared typically slows the rest of the compiler down by
|
||||
a few %, so you must explicitly enable it.
|
||||
|
||||
If you want to build both libdiagnostics and the regular compiler, it is often
|
||||
best to do this via two separate configure/builds, in separate
|
||||
directories, to avoid imposing the performance cost of
|
||||
--enable-host-shared on the regular compiler." "$LINENO" 5
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_libdiagnostics" >&5
|
||||
$as_echo "$enable_libdiagnostics" >&6; }
|
||||
|
||||
|
||||
|
||||
# Rust requires -ldl and -lpthread if you are using an old glibc that does not include them by
|
||||
# default, so we check for them here
|
||||
# We are doing the test here and not in the gcc/configure to be able to nicely disable the
|
||||
|
35
configure.ac
35
configure.ac
@ -2044,6 +2044,41 @@ fi
|
||||
|
||||
AC_SUBST(PICFLAG)
|
||||
|
||||
|
||||
# Check for libdiagnostics support.
|
||||
AC_MSG_CHECKING([whether to enable libdiagnostics])
|
||||
AC_ARG_ENABLE(libdiagnostics,
|
||||
[AS_HELP_STRING([--enable-libdiagnostics],
|
||||
[build libdiagnostics shared library])],
|
||||
enable_libdiagnostics=$enableval,
|
||||
enable_libdiagnostics=no)
|
||||
|
||||
if test x$enable_libdiagnostics = xyes; then
|
||||
# Disable libdiagnostics if -enable-host-shared not specified
|
||||
# but not if building for Mingw. All code in Windows
|
||||
# is position independent code (PIC).
|
||||
case $target in
|
||||
*mingw*) ;;
|
||||
*)
|
||||
if test x$host_shared != xyes; then
|
||||
AC_MSG_ERROR([
|
||||
Enabling libdiagnostics requires --enable-host-shared.
|
||||
|
||||
--enable-host-shared typically slows the rest of the compiler down by
|
||||
a few %, so you must explicitly enable it.
|
||||
|
||||
If you want to build both libdiagnostics and the regular compiler, it is often
|
||||
best to do this via two separate configure/builds, in separate
|
||||
directories, to avoid imposing the performance cost of
|
||||
--enable-host-shared on the regular compiler.])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
AC_MSG_RESULT($enable_libdiagnostics)
|
||||
AC_SUBST(enable_libdiagnostics)
|
||||
|
||||
|
||||
# Rust requires -ldl and -lpthread if you are using an old glibc that does not include them by
|
||||
# default, so we check for them here
|
||||
# We are doing the test here and not in the gcc/configure to be able to nicely disable the
|
||||
|
179
gcc/Makefile.in
179
gcc/Makefile.in
@ -436,6 +436,8 @@ endif
|
||||
|
||||
enable_host_shared = @enable_host_shared@
|
||||
|
||||
enable_libdiagnostics = @enable_libdiagnostics@
|
||||
|
||||
enable_as_accelerator = @enable_as_accelerator@
|
||||
|
||||
CPPLIB = ../libcpp/libcpp.a
|
||||
@ -618,6 +620,9 @@ xm_include_list=@xm_include_list@
|
||||
xm_defines=@xm_defines@
|
||||
lang_checks=
|
||||
lang_checks_parallelized=
|
||||
ifeq (@enable_libdiagnostics@,yes)
|
||||
lang_checks += check-libdiagnostics
|
||||
endif
|
||||
lang_opt_files=@lang_opt_files@ $(srcdir)/c-family/c.opt $(srcdir)/common.opt $(srcdir)/params.opt $(srcdir)/analyzer/analyzer.opt
|
||||
lang_specs_files=@lang_specs_files@
|
||||
lang_tree_files=@lang_tree_files@
|
||||
@ -1880,6 +1885,10 @@ endif
|
||||
# compilation or not.
|
||||
ALL_HOST_OBJS = $(ALL_HOST_FRONTEND_OBJS) $(ALL_HOST_BACKEND_OBJS)
|
||||
|
||||
ifeq (@enable_libdiagnostics@,yes)
|
||||
ALL_HOST_OBJS += $(libdiagnostics_OBJS)
|
||||
endif
|
||||
|
||||
BACKEND = libbackend.a main.o libcommon-target.a libcommon.a \
|
||||
$(CPPLIB) $(LIBDECNUMBER)
|
||||
|
||||
@ -2186,7 +2195,7 @@ all.cross: native gcc-cross$(exeext) cpp$(exeext) specs \
|
||||
libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra
|
||||
# This is what must be made before installing GCC and converting libraries.
|
||||
start.encap: native xgcc$(exeext) cpp$(exeext) specs \
|
||||
libgcc-support lang.start.encap @GENINSRC@ srcextra
|
||||
libgcc-support lang.start.encap @LIBDIAGNOSTICS@ @GENINSRC@ srcextra
|
||||
# These can't be made until after GCC can run.
|
||||
rest.encap: lang.rest.encap
|
||||
# This is what is made with the host's compiler
|
||||
@ -2275,6 +2284,129 @@ cpp$(exeext): $(GCC_OBJS) c-family/cppspec.o libcommon-target.a $(LIBDEPS) \
|
||||
c-family/cppspec.o $(EXTRA_GCC_OBJS) libcommon-target.a \
|
||||
$(EXTRA_GCC_LIBS) $(LIBS)
|
||||
|
||||
|
||||
libdiagnostics_OBJS = libdiagnostics.o \
|
||||
libcommon.a
|
||||
|
||||
# libdiagnostics
|
||||
|
||||
LIBDIAGNOSTICS_VERSION_NUM = 0
|
||||
LIBDIAGNOSTICS_MINOR_NUM = 0
|
||||
LIBDIAGNOSTICS_RELEASE_NUM = 1
|
||||
|
||||
ifneq (,$(findstring mingw,$(target)))
|
||||
LIBDIAGNOSTICS_FILENAME = libdiagnostics-$(LIBDIAGNOSTICS_VERSION_NUM).dll
|
||||
LIBDIAGNOSTICS_IMPORT_LIB = libdiagnostics.dll.a
|
||||
|
||||
libdiagnostics: $(LIBDIAGNOSTICS_FILENAME)
|
||||
|
||||
else
|
||||
|
||||
ifneq (,$(findstring darwin,$(host)))
|
||||
|
||||
LIBDIAGNOSTICS_AGE = 1
|
||||
LIBDIAGNOSTICS_BASENAME = libdiagnostics
|
||||
|
||||
LIBDIAGNOSTICS_SONAME = \
|
||||
${libdir}/$(LIBDIAGNOSTICS_BASENAME).$(LIBDIAGNOSTICS_VERSION_NUM).dylib
|
||||
LIBDIAGNOSTICS_FILENAME = $(LIBDIAGNOSTICS_BASENAME).$(LIBDIAGNOSTICS_VERSION_NUM).dylib
|
||||
LIBDIAGNOSTICS_LINKER_NAME = $(LIBDIAGNOSTICS_BASENAME).dylib
|
||||
|
||||
# Conditionalize the use of the LD_VERSION_SCRIPT_OPTION and
|
||||
# LD_SONAME_OPTION depending if configure found them, using $(if)
|
||||
# We have to define a LIBDIAGNOSTICS_COMMA here, otherwise the commas in the "true"
|
||||
# result are treated as separators by the $(if).
|
||||
LIBDIAGNOSTICS_COMMA := ,
|
||||
LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION = \
|
||||
$(if $(LD_VERSION_SCRIPT_OPTION),\
|
||||
-Wl$(LIBDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBDIAGNOSTICS_COMMA)$(srcdir)/libdiagnostics.map)
|
||||
|
||||
LIBDIAGNOSTICS_SONAME_OPTION = \
|
||||
$(if $(LD_SONAME_OPTION), \
|
||||
-Wl$(LIBDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBDIAGNOSTICS_COMMA)$(LIBDIAGNOSTICS_SONAME))
|
||||
|
||||
LIBDIAGNOSTICS_SONAME_SYMLINK = $(LIBDIAGNOSTICS_FILENAME)
|
||||
LIBDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBDIAGNOSTICS_LINKER_NAME)
|
||||
|
||||
libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \
|
||||
$(LIBDIAGNOSTICS_SYMLINK) \
|
||||
$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK)
|
||||
|
||||
else
|
||||
|
||||
LIBDIAGNOSTICS_LINKER_NAME = libdiagnostics.so
|
||||
LIBDIAGNOSTICS_SONAME = $(LIBDIAGNOSTICS_LINKER_NAME).$(LIBDIAGNOSTICS_VERSION_NUM)
|
||||
LIBDIAGNOSTICS_FILENAME = \
|
||||
$(LIBDIAGNOSTICS_SONAME).$(LIBDIAGNOSTICS_MINOR_NUM).$(LIBDIAGNOSTICS_RELEASE_NUM)
|
||||
|
||||
LIBDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBDIAGNOSTICS_LINKER_NAME)
|
||||
LIBDIAGNOSTICS_SONAME_SYMLINK = $(LIBDIAGNOSTICS_SONAME)
|
||||
|
||||
# Conditionalize the use of the LD_VERSION_SCRIPT_OPTION and
|
||||
# LD_SONAME_OPTION depending if configure found them, using $(if)
|
||||
# We have to define a LIBDIAGNOSTICS_COMMA here, otherwise the commas in the "true"
|
||||
# result are treated as separators by the $(if).
|
||||
LIBDIAGNOSTICS_COMMA := ,
|
||||
LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION = \
|
||||
$(if $(LD_VERSION_SCRIPT_OPTION),\
|
||||
-Wl$(LIBDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBDIAGNOSTICS_COMMA)$(srcdir)/libdiagnostics.map)
|
||||
|
||||
LIBDIAGNOSTICS_SONAME_OPTION = \
|
||||
$(if $(LD_SONAME_OPTION), \
|
||||
-Wl$(LIBDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBDIAGNOSTICS_COMMA)$(LIBDIAGNOSTICS_SONAME))
|
||||
|
||||
libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \
|
||||
$(LIBDIAGNOSTICS_SYMLINK) \
|
||||
$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK)
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
libdiagnostics.serial = $(LIBDIAGNOSTICS_FILENAME)
|
||||
|
||||
# Tell GNU make to ignore these if they exist.
|
||||
.PHONY: libdiagnostics
|
||||
|
||||
ifneq (,$(findstring mingw,$(target)))
|
||||
# Create import library
|
||||
LIBDIAGNOSTICS_EXTRA_OPTS = -Wl,--out-implib,$(LIBDIAGNOSTICS_IMPORT_LIB)
|
||||
else
|
||||
|
||||
ifneq (,$(findstring darwin,$(host)))
|
||||
# TODO : Construct a Darwin-style symbol export file.
|
||||
LIBDIAGNOSTICS_EXTRA_OPTS = -Wl,-compatibility_version,$(LIBDIAGNOSTICS_VERSION_NUM) \
|
||||
-Wl,-current_version,$(LIBDIAGNOSTICS_VERSION_NUM).$(LIBDIAGNOSTICS_MINOR_NUM).$(LIBDIAGNOSTICS_AGE) \
|
||||
$(LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION) \
|
||||
$(LIBDIAGNOSTICS_SONAME_OPTION)
|
||||
else
|
||||
|
||||
LIBDIAGNOSTICS_EXTRA_OPTS = $(LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION) \
|
||||
$(LIBDIAGNOSTICS_SONAME_OPTION)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(LIBDIAGNOSTICS_FILENAME): $(libdiagnostics_OBJS) $(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \
|
||||
$(LIBDEPS) $(srcdir)/libdiagnostics.map
|
||||
@$(call LINK_PROGRESS,$(INDEX.libdiagnostics),start)
|
||||
+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ -shared \
|
||||
$(libdiagnostics_OBJS) \
|
||||
$(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \
|
||||
$(LIBDIAGNOSTICS_EXTRA_OPTS)
|
||||
@$(call LINK_PROGRESS,$(INDEX.libdiagnostics),end)
|
||||
|
||||
# Create symlinks when not building for Windows
|
||||
ifeq (,$(findstring mingw,$(target)))
|
||||
|
||||
ifeq (,$(findstring darwin,$(host)))
|
||||
# but only one level for Darwin, version info is embedded.
|
||||
$(LIBDIAGNOSTICS_SONAME_SYMLINK): $(LIBDIAGNOSTICS_FILENAME)
|
||||
ln -sf $(LIBDIAGNOSTICS_FILENAME) $(LIBDIAGNOSTICS_SONAME_SYMLINK)
|
||||
endif
|
||||
|
||||
$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK): $(LIBDIAGNOSTICS_SONAME_SYMLINK)
|
||||
ln -sf $(LIBDIAGNOSTICS_SONAME_SYMLINK) $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK)
|
||||
endif
|
||||
|
||||
# Dump a specs file to make -B./ read these specs over installed ones.
|
||||
$(SPECS): xgcc$(exeext)
|
||||
$(GCC_FOR_TARGET) -dumpspecs > tmp-specs
|
||||
@ -3816,6 +3948,10 @@ ifeq ($(enable_plugin),yes)
|
||||
install: install-plugin
|
||||
endif
|
||||
|
||||
ifeq ($(enable_libdiagnostics),yes)
|
||||
install: install-libdiagnostics
|
||||
endif
|
||||
|
||||
install-strip: override INSTALL_PROGRAM = $(INSTALL_STRIP_PROGRAM)
|
||||
ifneq ($(STRIP),)
|
||||
install-strip: STRIPPROG = $(STRIP)
|
||||
@ -3992,6 +4128,47 @@ install-driver: installdirs xgcc$(exeext)
|
||||
fi; \
|
||||
fi
|
||||
|
||||
libdiagnostics.install-headers: installdirs
|
||||
$(INSTALL_DATA) $(srcdir)/libdiagnostics.h \
|
||||
$(DESTDIR)$(includedir)/libdiagnostics.h
|
||||
$(INSTALL_DATA) $(srcdir)/libdiagnostics++.h \
|
||||
$(DESTDIR)$(includedir)/libdiagnostics++.h
|
||||
|
||||
ifneq (,$(findstring mingw,$(target)))
|
||||
libdiagnostics.install-common: installdirs libdiagnostics.install-headers
|
||||
# Install import library
|
||||
$(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_IMPORT_LIB) \
|
||||
$(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_IMPORT_LIB)
|
||||
# Install DLL file
|
||||
$(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \
|
||||
$(DESTDIR)$(bindir)/$(LIBDIAGNOSTICS_FILENAME)
|
||||
|
||||
else
|
||||
ifneq (,$(findstring darwin,$(host)))
|
||||
# but only one level for Darwin
|
||||
|
||||
libdiagnostics.install-common: installdirs libdiagnostics.install-headers
|
||||
$(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \
|
||||
$(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_FILENAME)
|
||||
ln -sf \
|
||||
$(LIBDIAGNOSTICS_SONAME_SYMLINK)\
|
||||
$(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK)
|
||||
|
||||
else
|
||||
libdiagnostics.install-common: installdirs libdiagnostics.install-headers
|
||||
$(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \
|
||||
$(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_FILENAME)
|
||||
ln -sf \
|
||||
$(LIBDIAGNOSTICS_FILENAME) \
|
||||
$(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_SONAME_SYMLINK)
|
||||
ln -sf \
|
||||
$(LIBDIAGNOSTICS_SONAME_SYMLINK)\
|
||||
$(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK)
|
||||
endif
|
||||
endif
|
||||
|
||||
install-libdiagnostics: libdiagnostics.install-common
|
||||
|
||||
# Install the info files.
|
||||
# $(INSTALL_DATA) might be a relative pathname, so we can't cd into srcdir
|
||||
# to do the install.
|
||||
|
26
gcc/configure
vendored
26
gcc/configure
vendored
@ -637,6 +637,8 @@ LD_PICFLAG
|
||||
PICFLAG
|
||||
enable_default_pie
|
||||
enable_host_bind_now
|
||||
LIBDIAGNOSTICS
|
||||
enable_libdiagnostics
|
||||
enable_host_pie
|
||||
enable_host_shared
|
||||
enable_plugin
|
||||
@ -1051,6 +1053,7 @@ enable_version_specific_runtime_libs
|
||||
enable_plugin
|
||||
enable_host_shared
|
||||
enable_host_pie
|
||||
enable_libdiagnostics
|
||||
enable_host_bind_now
|
||||
enable_libquadmath_support
|
||||
with_linker_hash_style
|
||||
@ -1826,6 +1829,7 @@ Optional Features:
|
||||
--enable-plugin enable plugin support
|
||||
--enable-host-shared build host code as shared libraries
|
||||
--enable-host-pie build host code as PIE
|
||||
--enable-libdiagnostics build libdiagnostics shared library
|
||||
--enable-host-bind-now link host code as BIND_NOW
|
||||
--disable-libquadmath-support
|
||||
disable libquadmath support for Fortran
|
||||
@ -21456,7 +21460,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 21459 "configure"
|
||||
#line 21463 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@ -21562,7 +21566,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 21565 "configure"
|
||||
#line 21569 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@ -33838,6 +33842,9 @@ for language in $all_selected_languages
|
||||
do
|
||||
check_languages="$check_languages check-$language"
|
||||
done
|
||||
if test x$enable_libdiagnostics = xyes; then
|
||||
check_languages="$check_languages check-libdiagnostics"
|
||||
fi
|
||||
|
||||
selftest_languages=
|
||||
for language in $all_selected_languages
|
||||
@ -34276,6 +34283,21 @@ fi
|
||||
|
||||
|
||||
|
||||
# Check whether --enable-libdiagnostics was given.
|
||||
if test "${enable_libdiagnostics+set}" = set; then :
|
||||
enableval=$enable_libdiagnostics;
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test "$enable_libdiagnostics" = "yes"; then
|
||||
LIBDIAGNOSTICS='libdiagnostics'
|
||||
else
|
||||
LIBDIAGNOSTICS=''
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Enable --enable-host-bind-now
|
||||
# Check whether --enable-host-bind-now was given.
|
||||
if test "${enable_host_bind_now+set}" = set; then :
|
||||
|
@ -7380,6 +7380,9 @@ for language in $all_selected_languages
|
||||
do
|
||||
check_languages="$check_languages check-$language"
|
||||
done
|
||||
if test x$enable_libdiagnostics = xyes; then
|
||||
check_languages="$check_languages check-libdiagnostics"
|
||||
fi
|
||||
|
||||
selftest_languages=
|
||||
for language in $all_selected_languages
|
||||
@ -7612,6 +7615,19 @@ AC_ARG_ENABLE(host-pie,
|
||||
[build host code as PIE])])
|
||||
AC_SUBST(enable_host_pie)
|
||||
|
||||
AC_ARG_ENABLE(libdiagnostics,
|
||||
[AS_HELP_STRING([--enable-libdiagnostics],
|
||||
[build libdiagnostics shared library])])
|
||||
AC_SUBST(enable_libdiagnostics)
|
||||
|
||||
if test "$enable_libdiagnostics" = "yes"; then
|
||||
LIBDIAGNOSTICS='libdiagnostics'
|
||||
else
|
||||
LIBDIAGNOSTICS=''
|
||||
fi
|
||||
AC_SUBST(LIBDIAGNOSTICS)
|
||||
|
||||
|
||||
# Enable --enable-host-bind-now
|
||||
AC_ARG_ENABLE(host-bind-now,
|
||||
[AS_HELP_STRING([--enable-host-bind-now],
|
||||
|
@ -103,13 +103,14 @@ public:
|
||||
m_show_nesting_levels = show_nesting_levels;
|
||||
}
|
||||
|
||||
label_text get_location_text (const expanded_location &s) const;
|
||||
|
||||
protected:
|
||||
void print_any_cwe (const diagnostic_info &diagnostic);
|
||||
void print_any_rules (const diagnostic_info &diagnostic);
|
||||
void print_option_information (const diagnostic_info &diagnostic,
|
||||
diagnostic_t orig_diag_kind);
|
||||
|
||||
label_text get_location_text (const expanded_location &s) const;
|
||||
bool includes_seen_p (const line_map_ordinary *map);
|
||||
|
||||
/* For handling diagnostic_buffer. */
|
||||
|
@ -1231,6 +1231,13 @@ virtual calls in verifiable mode at all. However the libvtv library will
|
||||
still be built (see @option{--disable-libvtv} to turn off building libvtv).
|
||||
@option{--disable-vtable-verify} is the default.
|
||||
|
||||
@item --enable-libdiagnostics
|
||||
Specify whether to build @code{libdiagnostics}, a shared library exposing
|
||||
GCC's diagnostics capabilities via a C API, and a C++ wrapper API adding
|
||||
``syntactic sugar''.
|
||||
|
||||
This option requires @option{--enable-host-shared} on non-Windows hosts.
|
||||
|
||||
@item --disable-gcov
|
||||
Specify that the run-time library used for coverage analysis
|
||||
and associated host tools should not be built.
|
||||
|
20
gcc/doc/libdiagnostics/Makefile
Normal file
20
gcc/doc/libdiagnostics/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
27
gcc/doc/libdiagnostics/conf.py
Normal file
27
gcc/doc/libdiagnostics/conf.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'libdiagnostics'
|
||||
copyright = '2024, David Malcolm'
|
||||
author = 'David Malcolm'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = []
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'alabaster'
|
||||
html_static_path = ['_static']
|
113
gcc/doc/libdiagnostics/index.rst
Normal file
113
gcc/doc/libdiagnostics/index.rst
Normal file
@ -0,0 +1,113 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
libdiagnostics
|
||||
==============
|
||||
|
||||
This document describes `libdiagnostics <https://gcc.gnu.org/wiki/libdiagnostics>`_,
|
||||
an API for programs to use to emit diagnostics (such as for "lint"-style checker
|
||||
tools), supporting:
|
||||
|
||||
* text output similar to GCC's errors and warnings::
|
||||
|
||||
test-typo.c:19:13: error: unknown field 'colour'
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
|
||||
quoting pertinent source code (with a cache), and underlining
|
||||
:doc:`points and ranges in the files being tested <tutorial/02-physical-locations>`,
|
||||
possibly with labels::
|
||||
|
||||
test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char *'
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
||||
|
||||
* emitting :doc:`fix-it hints <tutorial/06-fix-it-hints>`::
|
||||
|
||||
test-fix-it-hint.c:19:13: error: unknown field 'colour'; did you mean 'color'
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
|
||||
and generating patches from them::
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
struct rgb
|
||||
get_color (struct object *p)
|
||||
{
|
||||
- return p->colour;
|
||||
+ return p->color;
|
||||
}
|
||||
|
||||
* capturing :doc:`execution paths<tutorial/07-execution-paths>` through code::
|
||||
|
||||
In function 'make_a_list_of_random_ints_badly':
|
||||
test-warning-with-path.c:30:5: warning: passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter"
|
||||
30 | PyList_Append(list, item);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
make_a_list_of_random_ints_badly': events 1-3
|
||||
26 | list = PyList_New(0);
|
||||
| ^~~~~~~~~~~~~
|
||||
| |
|
||||
| (1) when 'PyList_New' fails, returning NULL
|
||||
27 |
|
||||
28 | for (i = 0; i < count; i++) {
|
||||
| ~~~~~~~~~
|
||||
| |
|
||||
| (2) when 'i < count'
|
||||
29 | item = PyLong_FromLong(random());
|
||||
30 | PyList_Append(list, item);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
| |
|
||||
| (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
|
||||
|
||||
* support for emitting machine-readable representations of the above
|
||||
using the :doc:`SARIF file format <topics/sarif>`
|
||||
|
||||
There are actually two APIs for the library:
|
||||
|
||||
* a pure C API: ``libdiagnostics.h``
|
||||
|
||||
* a C++ wrapper API: ``libdiagnostics+.h``. This is a header-only
|
||||
collection of wrapper classes around the C API to give a less
|
||||
verbose API.
|
||||
|
||||
This documentation covers the C API.
|
||||
|
||||
Contents
|
||||
********
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
tutorial/index.rst
|
||||
topics/index.rst
|
||||
|
||||
libdiagnostics is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
Indices and tables
|
||||
******************
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
35
gcc/doc/libdiagnostics/make.bat
Normal file
35
gcc/doc/libdiagnostics/make.bat
Normal file
@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
58
gcc/doc/libdiagnostics/topics/diagnostic-manager.rst
Normal file
58
gcc/doc/libdiagnostics/topics/diagnostic-manager.rst
Normal file
@ -0,0 +1,58 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Diagnostic Managers
|
||||
===================
|
||||
|
||||
.. type:: diagnostic_manager;
|
||||
|
||||
A :type:`diagnostic_manager` is an opaque bundle of state for a client of
|
||||
libdiagnostics.
|
||||
|
||||
It has zero of more "output sinks" to which diagnostics are emitted.
|
||||
|
||||
Responsibilities include:
|
||||
|
||||
* location-management
|
||||
|
||||
* caching of source file content
|
||||
|
||||
* patch generation
|
||||
|
||||
.. function:: diagnostic_manager *diagnostic_manager_new (void)
|
||||
|
||||
Create a new diagnostic_manager.
|
||||
The caller will need to call :func:`diagnostic_release_manager`
|
||||
on it at some point.
|
||||
|
||||
.. note:: No output sinks are created by default; so you will want
|
||||
to create one with something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
diagnostic_manager_add_text_sink (diag_mgr, stderr,
|
||||
DIAGNOSTIC_COLORIZE_IF_TTY);
|
||||
|
||||
.. function:: void diagnostic_manager_release (diagnostic_manager *diag_mgr)
|
||||
|
||||
Release a diagnostic_manager.
|
||||
|
||||
This will flush output to all of the output sinks, and clean up.
|
||||
|
||||
The parameter must be non-NULL.
|
127
gcc/doc/libdiagnostics/topics/diagnostics.rst
Normal file
127
gcc/doc/libdiagnostics/topics/diagnostics.rst
Normal file
@ -0,0 +1,127 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Diagnostics
|
||||
===========
|
||||
|
||||
.. type:: diagnostic
|
||||
|
||||
A :type:`diagnostic` is an opaque bundle of state for a particular
|
||||
diagnostic that is being constructed in memory.
|
||||
|
||||
|
||||
Lifecycle of a diagnostic
|
||||
*************************
|
||||
|
||||
Diagnostics are
|
||||
|
||||
* *created* from a :type:`diagnostic_manager` by using
|
||||
:func:`diagnostic_begin`, then
|
||||
|
||||
* *populated* with data, such as physical locations, logical locations,
|
||||
metadata, execution paths, or fix-it hints, then
|
||||
|
||||
* *finished*, in which a formatting string and arguments are given,
|
||||
via a call to :func:`diagnostic_finish` or :func:`diagnostic_finish_va`.
|
||||
The :type:`diagnostic_manager` will emit the diagnostic to all of the
|
||||
manager's output sinks (either immediately, or at some later time,
|
||||
depending on the sink).
|
||||
|
||||
Once a :type:`diagnostic` has had one of these "finish" functions called
|
||||
on it, it is freed, and is no longer valid for use.
|
||||
|
||||
The formatting strings use their own syntax; see :doc:`message-formatting`.
|
||||
|
||||
.. function:: diagnostic *diagnostic_begin (diagnostic_manager *diag_mgr, \
|
||||
enum diagnostic_level level)
|
||||
|
||||
Create a new :type:`diagnostic` associated with the given
|
||||
:type:`diagnostic_manager`.
|
||||
|
||||
The parameter ``diag_mgr`` must be non-NULL.
|
||||
|
||||
The parameter ``level`` describes the severity of the diagnostic.
|
||||
|
||||
.. enum:: diagnostic_level
|
||||
|
||||
This enum describes the severity of a particular diagnostic.
|
||||
|
||||
.. macro:: DIAGNOSTIC_LEVEL_ERROR
|
||||
|
||||
A problem sufficiently severe that the program cannot successfully
|
||||
complete, or where the input being analyzed is definitely wrong
|
||||
(e.g. malformed).
|
||||
|
||||
.. macro:: DIAGNOSTIC_LEVEL_WARNING
|
||||
|
||||
A problem where the input is technically correct, but is likely
|
||||
not what the user intended, such as common mistakes, or other
|
||||
unusual conditions that *may* indicate trouble, such as use of
|
||||
obsolete features.
|
||||
|
||||
.. macro:: DIAGNOSTIC_LEVEL_NOTE
|
||||
|
||||
A supplementary message added to another :type:`diagnostic`, giving
|
||||
extra information that may help the user understand it.
|
||||
|
||||
.. macro:: DIAGNOSTIC_LEVEL_SORRY
|
||||
|
||||
A problem where the input is valid, but the tool isn't
|
||||
able to handle it.
|
||||
|
||||
.. function:: void diagnostic_finish (diagnostic *diag, const char *fmt, ...)
|
||||
|
||||
Emit ``diag`` to all sinks of its manager, and release ``diag``. It is not
|
||||
valid to use ``diag`` after this call.
|
||||
|
||||
Use parameter ``fmt`` for the message.
|
||||
Note that this uses gcc's pretty-print format, which is *not* printf.
|
||||
See :doc:`message-formatting`.
|
||||
|
||||
Both ``diag`` and ``fmt`` must be non-NULL.
|
||||
|
||||
TODO: who is responsible for putting FMT through gettext?
|
||||
|
||||
.. function:: void diagnostic_finish_va (diagnostic *diag, const char *fmt, va_list *args)
|
||||
|
||||
This is equivalent to :func:`diagnostic_finish`, but using a
|
||||
:type:`va_list` rather than directly taking variadic arguments.
|
||||
|
||||
All three parameters must be non-NULL.
|
||||
|
||||
|
||||
Diagnostic groups
|
||||
*****************
|
||||
|
||||
See :doc:`the "adding notes" section of the tutorial <../tutorial/04-notes>`
|
||||
for an example of a diagnostic group.
|
||||
|
||||
.. function:: void diagnostic_manager_begin_group (diagnostic_manager *diag_mgr)
|
||||
|
||||
Begin a diagnostic group. All diagnostics emitted within
|
||||
``diag_mgr`` after the first one will be treated as additional information
|
||||
relating to the initial diagnostic.
|
||||
|
||||
The parameter ``diag_mgr`` must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_manager_end_group (diagnostic_manager *diag_mgr)
|
||||
|
||||
Finish a diagnostic group.
|
||||
|
||||
The parameter ``diag_mgr`` must be non-NULL.
|
93
gcc/doc/libdiagnostics/topics/execution-paths.rst
Normal file
93
gcc/doc/libdiagnostics/topics/execution-paths.rst
Normal file
@ -0,0 +1,93 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Execution paths
|
||||
===============
|
||||
|
||||
.. type:: diagnostic_execution_path
|
||||
|
||||
A :type:`diagnostic` can optionally contain a :type:`diagnostic_execution_path`
|
||||
describing a path of execution through code.
|
||||
|
||||
.. function:: diagnostic_execution_path * diagnostic_add_execution_path (diagnostic *diag)
|
||||
|
||||
Create and borrow a pointer to an execution path for ``diag``.
|
||||
|
||||
The path is automatically cleaned up when ``diag`` is finished.
|
||||
|
||||
``diag`` must be non-NULL.
|
||||
|
||||
.. function:: diagnostic_execution_path * diagnostic_manager_new_execution_path (diagnostic_manager *diag_mgr)
|
||||
|
||||
Create a new execution path. This is owned by the caller and must have either
|
||||
:func:`diagnostic_take_execution_path` or
|
||||
:func:`diagnostic_execution_path_release` called on it.
|
||||
|
||||
``diag_mgr`` must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_take_execution_path (diagnostic *diag, diagnostic_execution_path *path)
|
||||
|
||||
Set ``diag`` to use ``path`` as its execution path, taking ownership of ``path``.
|
||||
|
||||
Both parameters must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_execution_path_release (diagnostic_execution_path *path)
|
||||
|
||||
Release ownership of ``path``, which must not have been taken by a diagnostic.
|
||||
|
||||
.. type:: diagnostic_event_id
|
||||
|
||||
A :type:`diagnostic_event_id` identifies a particular event within a
|
||||
:type:`diagnostic_execution_path` and can be used for expressing
|
||||
cross-references between events. In particular FIXME
|
||||
|
||||
.. function:: diagnostic_event_id diagnostic_execution_path_add_event (diagnostic_execution_path *path, \
|
||||
const diagnostic_physical_location *physical_loc, \
|
||||
const diagnostic_logical_location *logical_loc, \
|
||||
unsigned stack_depth, \
|
||||
const char *fmt, ...)
|
||||
|
||||
Append an event to the end of ``path``, which must be non-NULL.
|
||||
|
||||
``physical_loc`` can be NULL, or non-NULL to associate the event
|
||||
with a :type:`diagnostic_physical_location`.
|
||||
|
||||
``logical_loc`` can be NULL, or non-NULL to associate the event
|
||||
with a :type:`diagnostic_logical_location`.
|
||||
|
||||
``stack_depth`` is for use in interprocedural paths and identifies the
|
||||
depth of the stack at the event. Purely intraprocedural paths should
|
||||
use a stack depth of 1 for their events
|
||||
|
||||
``fmt`` must be non-NULL. See :doc:`message-formatting` for details of
|
||||
how to use it.
|
||||
|
||||
.. function:: diagnostic_event_id diagnostic_execution_path_add_event_va (diagnostic_execution_path *path, \
|
||||
const diagnostic_physical_location *physical_loc, \
|
||||
const diagnostic_logical_location *logical_loc, \
|
||||
unsigned stack_depth, \
|
||||
const char *fmt, \
|
||||
va_list *args)
|
||||
|
||||
Equivalent to :func:`diagnostic_execution_path_add_event`, but using a
|
||||
:type:`va_list` rather than directly taking variadic arguments.
|
||||
|
||||
Paths are printed to text sinks, and for SARIF sinks each path is added as
|
||||
a ``codeFlow`` object (see SARIF 2.1.0
|
||||
`3.36 codeFlow object <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790990>`_).
|
135
gcc/doc/libdiagnostics/topics/fix-it-hints.rst
Normal file
135
gcc/doc/libdiagnostics/topics/fix-it-hints.rst
Normal file
@ -0,0 +1,135 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Fix-it hints
|
||||
============
|
||||
|
||||
Adding fix-it hints to a diagnostic
|
||||
***********************************
|
||||
|
||||
A :type:`diagnostic` can contain "fix-it hints", giving suggestions
|
||||
for the user on how to edit their code to fix a problem. These
|
||||
can be expressed as insertions, replacements, and removals of text.
|
||||
|
||||
There is only limited support for newline characters in fix-it hints:
|
||||
only hints with newlines which insert an entire new line are permitted,
|
||||
inserting at the start of a line, and finishing with a newline
|
||||
(with no interior newline characters). Other attempts to add
|
||||
fix-it hints containing newline characters will fail.
|
||||
Similarly, attempts to delete or replace a range *affecting* multiple
|
||||
lines will fail.
|
||||
|
||||
The API handles these failures gracefully, so that diagnostics can attempt
|
||||
to add fix-it hints without each needing extensive checking.
|
||||
|
||||
Fix-it hints are printed to text sinks, and are emitted by SARIF sinks
|
||||
as ``fix`` objects (see SARIF 2.1.0
|
||||
`3.55 fix object <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141791131>`_).
|
||||
|
||||
Fix-it hints within a :type:`diagnostic` are "atomic": if any hints can't
|
||||
be applied, none of them will be, and no fix-its hints will be displayed
|
||||
for that diagnostic. This implies that diagnostic messages need to be worded
|
||||
in such a way that they make sense whether or not the fix-it hints
|
||||
are displayed.
|
||||
|
||||
All fix-it hints within one :type:`diagnostic` must affect the same
|
||||
:type:`diagnostic_file`.
|
||||
|
||||
.. function:: void diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, \
|
||||
const diagnostic_physical_location *loc, \
|
||||
const char *addition)
|
||||
|
||||
Add a fix-it hint to ``diag`` suggesting the insertion of the string
|
||||
``addition`` before ``LOC``.
|
||||
|
||||
For example::
|
||||
|
||||
ptr = arr[0];
|
||||
^~~~~~
|
||||
&
|
||||
|
||||
This :type:`diagnostic` has a single location covering ``arr[0]``,
|
||||
with the caret at the start. It has a single insertion fix-it hint,
|
||||
inserting ``&`` before the start of ``loc``.
|
||||
|
||||
.. function:: void diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, \
|
||||
const diagnostic_physical_location *loc, \
|
||||
const char *addition)
|
||||
|
||||
Add a fix-it hint to ``diag`` suggesting the insertion of the string
|
||||
``addition`` after the end of ``LOC``.
|
||||
|
||||
For example, in::
|
||||
|
||||
#define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
|
||||
^~~~ ^~~~ ^~~~
|
||||
( ) ( ) ( )
|
||||
|
||||
|
||||
the :type:`diagnostic` has three physical locations, covering ``ARG0``,
|
||||
``ARG1``, and ``ARG2``, and 6 insertion fix-it hints: each arg
|
||||
has a pair of insertion fix-it hints, suggesting wrapping
|
||||
them with parentheses: one a '(' inserted before,
|
||||
the other a ')' inserted after.
|
||||
|
||||
.. function:: void diagnostic_add_fix_it_hint_replace (diagnostic *diag, \
|
||||
const diagnostic_physical_location *loc, \
|
||||
const char *replacement)
|
||||
|
||||
Add a fix-it hint to ``diag`` suggesting the replacement of the text
|
||||
at ``LOC`` with the string ``replacement``.
|
||||
|
||||
For example, in::
|
||||
|
||||
c = s.colour;
|
||||
^~~~~~
|
||||
color
|
||||
|
||||
This :type:`diagnostic` has a single physical location covering ``colour``,
|
||||
and a single "replace" fix-it hint, covering the same range, suggesting
|
||||
replacing it with ``color``.
|
||||
|
||||
.. function:: void diagnostic_add_fix_it_hint_delete (diagnostic *diag, \
|
||||
const diagnostic_physical_location *loc)
|
||||
|
||||
Add a fix-it hint to ``diag`` suggesting the deletion of the text
|
||||
at ``LOC``.
|
||||
|
||||
|
||||
For example, in::
|
||||
|
||||
struct s {int i};;
|
||||
^
|
||||
-
|
||||
|
||||
This :type:`diagnostic` has a single physical location at the stray
|
||||
trailing semicolon, along with a single removal fix-it hint, covering
|
||||
the same location.
|
||||
|
||||
|
||||
Generating patches
|
||||
******************
|
||||
|
||||
.. function:: void diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, \
|
||||
FILE *dst_stream)
|
||||
|
||||
Write a patch to ``dst_stream`` consisting of the effect of all fix-it hints
|
||||
on all diagnostics that have been finished on ``diag_mgr``.
|
||||
|
||||
Both parameters must be non-NULL.
|
38
gcc/doc/libdiagnostics/topics/index.rst
Normal file
38
gcc/doc/libdiagnostics/topics/index.rst
Normal file
@ -0,0 +1,38 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
|
||||
Topic reference
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
retrofitting.rst
|
||||
diagnostic-manager.rst
|
||||
diagnostics.rst
|
||||
message-formatting.rst
|
||||
physical-locations.rst
|
||||
logical-locations.rst
|
||||
metadata.rst
|
||||
fix-it-hints.rst
|
||||
execution-paths.rst
|
||||
text-output.rst
|
||||
sarif.rst
|
||||
ux.rst
|
109
gcc/doc/libdiagnostics/topics/logical-locations.rst
Normal file
109
gcc/doc/libdiagnostics/topics/logical-locations.rst
Normal file
@ -0,0 +1,109 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Logical locations
|
||||
=================
|
||||
|
||||
A "logical" location is a location expressed in terms of
|
||||
construct in a programming language, such as ``within function 'foo'``
|
||||
(as opposed to a :doc:`"physical" location <physical-locations>`, which
|
||||
refers to a specific file, and line(s) and/or column(s))
|
||||
|
||||
Creating location information
|
||||
*****************************
|
||||
|
||||
.. type:: diagnostic_logical_location
|
||||
|
||||
A :type:`diagnostic_logical_location` is an opaque type describing a "logical"
|
||||
source location
|
||||
|
||||
.. function:: const diagnostic_logical_location * diagnostic_manager_new_logical_location (diagnostic_manager *diag_mgr, \
|
||||
enum diagnostic_logical_location_kind_t kind, \
|
||||
const diagnostic_logical_location *parent, \
|
||||
const char *short_name, \
|
||||
const char *fully_qualified_name, \
|
||||
const char *decorated_name)
|
||||
|
||||
Create a :type:`diagnostic_logical_location`.
|
||||
|
||||
``diag_mgr`` must be non-NULL.
|
||||
|
||||
``kind`` describes the kind of logical location:
|
||||
|
||||
.. enum:: diagnostic_logical_location_kind_t
|
||||
|
||||
This roughly corresponds to the ``kind`` property in SARIF v2.1.0
|
||||
(`§3.33.7 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790976>`_).
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER
|
||||
|
||||
.. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE
|
||||
|
||||
``parent`` can be NULL; if non-NULL it can be used to express tree-like
|
||||
nesting of logical locations, such as in::
|
||||
|
||||
namespace foo { namespace bar { class baz { baz (); }; } }
|
||||
|
||||
where a diagnostic within ``baz``'s constructor could be reported
|
||||
as being within ``foo::bar::baz::baz`` where the logical locations
|
||||
are two namespaces, a type, and a member, respectively.
|
||||
|
||||
``short_name`` can be NULL, or else a string suitable for use by
|
||||
the SARIF logicalLocation ``name`` property
|
||||
(SARIF v2.1.0 `§3.33.4 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790973>`_).
|
||||
|
||||
``fully_qualified_name`` can be NULL or else a string suitable for use by
|
||||
the SARIF logicalLocation ``fullyQualifiedName`` property
|
||||
(SARIF v2.1.0 `§3.33.5 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790974>`_).
|
||||
|
||||
``decorated_name`` can be NULL or else a string suitable for use by
|
||||
the SARIF logicalLocation ``decoratedName`` property
|
||||
(SARIF v2.1.0 `§3.33.6 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790975>`_).
|
||||
|
||||
.. function:: void diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, \
|
||||
const diagnostic_logical_location *loc, \
|
||||
FILE *out)
|
||||
|
||||
Write a representation of ``file`` to ``out``, for debugging.
|
||||
Both ``diag_mgr`` and ``out`` must be non-NULL.
|
||||
``file`` may be NULL.
|
||||
|
||||
TODO: example of output
|
||||
|
||||
Associating diagnostics with locations
|
||||
**************************************
|
||||
|
||||
.. function:: void diagnostic_set_logical_location (diagnostic *diag, \
|
||||
const diagnostic_logical_location *logical_loc)
|
||||
|
||||
Set the logical location of ``diag``.
|
||||
|
||||
``diag`` must be non-NULL; ``logical_loc`` can be NULL.
|
224
gcc/doc/libdiagnostics/topics/message-formatting.rst
Normal file
224
gcc/doc/libdiagnostics/topics/message-formatting.rst
Normal file
@ -0,0 +1,224 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Message formatting
|
||||
==================
|
||||
|
||||
Various libdiagnostics entrypoints take a format string and
|
||||
variadic arguments.
|
||||
|
||||
The format strings take codes prefixed by ``%``, or ``%q`` to put
|
||||
the result in quotes. For example::
|
||||
|
||||
"hello %s", "world"
|
||||
|
||||
would print::
|
||||
|
||||
hello world
|
||||
|
||||
whereas::
|
||||
|
||||
"hello %qs", "world"
|
||||
|
||||
would print::
|
||||
|
||||
hello `world'
|
||||
|
||||
where ```world'`` would be displayed in bold if colorization were enabled
|
||||
in the terminal.
|
||||
|
||||
The following format specifiers are accepted:
|
||||
|
||||
|
||||
Numbers
|
||||
*******
|
||||
|
||||
``d`` and ``i`` (``signed int``), ``u`` (``unsigned int``)
|
||||
``%d``, ``%i``, and ``%u`` print integers in base ten. For example::
|
||||
|
||||
"the answer is %i", 42
|
||||
|
||||
would print::
|
||||
|
||||
the answer is 42
|
||||
|
||||
``o`` (``unsigned int``)
|
||||
Print the integer in base eight
|
||||
|
||||
``x`` (``unsigned int``)
|
||||
Print the integer in base sixteen
|
||||
|
||||
The above can be prefixed with ``l`` and ``ll`` prefixes to take
|
||||
``long`` and ``long long`` values of the appropriate signedness.
|
||||
|
||||
For example::
|
||||
|
||||
"address: %lx", (unsigned long)0x108b516
|
||||
|
||||
would print::
|
||||
|
||||
address: 108b516
|
||||
|
||||
Similarly, the prefix ``z`` can be used for ``size_t``::
|
||||
|
||||
"size: %zd", sizeof(struct foo)
|
||||
size: 32
|
||||
|
||||
and ``t`` for ptrdiff_t.
|
||||
|
||||
``f`` (``double``)
|
||||
``%f`` prints a floating-point value. For example::
|
||||
|
||||
"value: %f", 1.0
|
||||
|
||||
might print::
|
||||
|
||||
value: 1.000000
|
||||
|
||||
|
||||
Strings
|
||||
*******
|
||||
|
||||
``c`` (``char``)
|
||||
``%c`` prints a single character.
|
||||
|
||||
``s`` (``const char *``)
|
||||
``%s`` prints a string.
|
||||
|
||||
Note that if the string refers to something that might
|
||||
appear in the input file (such as the name of a function), it's better
|
||||
to quote the value; for example::
|
||||
|
||||
"unrecognized identifier: %qs", "foo"
|
||||
|
||||
might print::
|
||||
|
||||
unrecognized identifier: `foo'
|
||||
|
||||
``m`` (no argument)
|
||||
Prints ``strerror(errno)``, for example::
|
||||
|
||||
"can't open %qs: %m"
|
||||
|
||||
might print::
|
||||
|
||||
can't open `foo.txt': No such file or directory
|
||||
|
||||
``%`` (no argument)
|
||||
``%%`` prints a `%` character, for example::
|
||||
|
||||
"8%% of 75 is 75%% of 8, and is thus 6"
|
||||
|
||||
prints::
|
||||
|
||||
8% of 75 is 75% of 8, and is thus 6
|
||||
|
||||
``'`` (no argument)
|
||||
``%'`` prints an apostrophe. This should only be used in untranslated messages;
|
||||
translations should use appropriate punctuation directly.
|
||||
|
||||
|
||||
Other format specifiers
|
||||
***********************
|
||||
|
||||
``p`` (pointer)
|
||||
``%p`` prints a pointer, although the precise format is
|
||||
implementation-defined.
|
||||
|
||||
``r`` (``const char *``)
|
||||
``%r`` starts colorization on suitable text sinks, where the argument
|
||||
specifies the name of the kind of entity to be colored, such as ``error``.
|
||||
|
||||
``R`` (no argument)
|
||||
``%R`` stops colorization
|
||||
|
||||
``<`` and ``>`` (no arguments)
|
||||
``%<`` adds an opening quote and ``%>`` a closing quote, such as::
|
||||
|
||||
"missing element %<%s:%s%>", ns, name
|
||||
|
||||
which might be printed as::
|
||||
|
||||
missing element `xhtml:head'
|
||||
|
||||
If the thing to be quoted can be handled with another format specifier,
|
||||
then it's simpler to use ``q`` with it. For example, it's much
|
||||
simpler to print a ``const char *`` in quotes via::
|
||||
|
||||
"%qs", str
|
||||
|
||||
rather than the error-prone::
|
||||
|
||||
"%<%s%>", str
|
||||
|
||||
``{`` (``const char *``)
|
||||
``%{`` starts a link; the argument is the URL. This will be displayed
|
||||
in a suitably-capable terminal if a text sink is directly connected to
|
||||
a tty, and will be captured in SARIF output.
|
||||
|
||||
``}`` (no argument)
|
||||
``%}`` stops a link started with ``%{``.
|
||||
|
||||
For example::
|
||||
|
||||
"for more information see %{the documentation%}", "https://example.com"
|
||||
|
||||
would be printed as::
|
||||
|
||||
for more information see the documentation
|
||||
|
||||
with the URL emitted in suitable output sinks.
|
||||
|
||||
``@`` (``diagnostic_event_id *``)
|
||||
``%@`` prints a reference to an event in a
|
||||
:type:`diagnostic_execution_path`, where the :type:`diagnostic_event_id`
|
||||
is passed by pointer.
|
||||
|
||||
For example, if ``event_id`` refers to the first event in a path, then::
|
||||
|
||||
"double-%qs of %qs; first %qs was at %@",
|
||||
function, ptr, function, &event_id
|
||||
|
||||
might print::
|
||||
|
||||
double-`free' of `p'; first `free` was at (1)
|
||||
|
||||
.. :
|
||||
|
||||
TODO:
|
||||
|
||||
%.*s: a substring the length of which is specified by an argument
|
||||
integer.
|
||||
%Ns: likewise, but length specified as constant in the format string.
|
||||
%Z: Requires two arguments - array of int, and len. Prints elements
|
||||
of the array.
|
||||
|
||||
%e: Consumes a pp_element * argument.
|
||||
|
||||
Arguments can be used sequentially, or through %N$ resp. *N$
|
||||
notation Nth argument after the format string. If %N$ / *N$
|
||||
notation is used, it must be used for all arguments, except %m, %%,
|
||||
%<, %>, %} and %', which may not have a number, as they do not consume
|
||||
an argument. When %M$.*N$s is used, M must be N + 1. (This may
|
||||
also be written %M$.*s, provided N is not otherwise used.) The
|
||||
format string must have conversion specifiers with argument numbers
|
||||
1 up to highest argument; each argument may only be used once.
|
||||
A format string can have at most 30 arguments. */
|
||||
|
||||
|
149
gcc/doc/libdiagnostics/topics/metadata.rst
Normal file
149
gcc/doc/libdiagnostics/topics/metadata.rst
Normal file
@ -0,0 +1,149 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Adding metadata
|
||||
===============
|
||||
|
||||
Tool metadata
|
||||
*************
|
||||
|
||||
It's possible to set up various metadata on the :type:`diagnostic_manager`
|
||||
as a whole, describing the program creating the diagnostics.
|
||||
|
||||
.. note::
|
||||
|
||||
It's not required to set up any of this up on a
|
||||
:type:`diagnostic_manager`. However, if you are doing
|
||||
:doc:`SARIF output <sarif>`, then you need to at least call
|
||||
:func:`diagnostic_manager_set_tool_name` or the generated ``.sarif``
|
||||
file will not validate against the schema.
|
||||
|
||||
.. function:: void diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr, \
|
||||
const char *value)
|
||||
|
||||
Set a string for the name of the tool emitting the diagnostics.
|
||||
|
||||
Both parameters must be non-NULL.
|
||||
|
||||
If set, this string will be used
|
||||
|
||||
* by :doc:`text output sinks <text-output>` as a prefix for output
|
||||
when no physical location is available, replacing ``progname``
|
||||
in the following:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./tut01-hello-world
|
||||
progname: error: I'm sorry Dave, I'm afraid I can't do that
|
||||
|
||||
* by :doc:`SARIF output sinks <sarif>` as the value for the
|
||||
``name`` property of the ``driver``
|
||||
(`SARIF v2.1.0 §3.19.8 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790791>`_).
|
||||
|
||||
.. function:: void diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr, \
|
||||
const char *value)
|
||||
|
||||
Set a string giving the name of the tool along with the its version and
|
||||
other useful information::
|
||||
|
||||
diagnostic_manager_set_full_name (diag_mgr, "FooChecker 0.1 (en_US)");
|
||||
|
||||
If set, this string will be used by :doc:`SARIF output sinks <sarif>` as
|
||||
the value for the ``fullName`` property of the ``driver``
|
||||
(`SARIF v2.1.0 §3.19.9 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790792>`_).
|
||||
|
||||
Both parameters must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr, \
|
||||
const char *value)
|
||||
|
||||
Set a string suitable for use as the value of the SARIF ``version`` property
|
||||
of the ``driver``.
|
||||
(`SARIF v2.1.0 §3.19.13 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790796>`_)::
|
||||
|
||||
diagnostic_manager_set_version_string (diag_mgr, "0.1");
|
||||
|
||||
Both parameters must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr, \
|
||||
const char *value)
|
||||
|
||||
Set a string suitable for use as the value of the SARIF ``informationUri``
|
||||
property of the ``driver``.
|
||||
(`SARIF v2.1.0 §3.19.17 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790800>`_)::
|
||||
|
||||
diagnostic_manager_set_version_url (diag_mgr,
|
||||
"https://www.example.com/foo-checker/releases/0.1/");
|
||||
|
||||
Both parameters must be non-NULL.
|
||||
|
||||
Adding metadata to a diagnostic
|
||||
*******************************
|
||||
|
||||
.. function:: void diagnostic_set_cwe (diagnostic *diag, \
|
||||
unsigned cwe_id)
|
||||
|
||||
Associate ``diag`` with the given ID within
|
||||
the `Common Weakness Enumeration <https://cwe.mitre.org/>`_::
|
||||
|
||||
/* CWE-242: Use of Inherently Dangerous Function. */
|
||||
diagnostic_set_cwe (d, 242);
|
||||
|
||||
``diag`` must be non-NULL.
|
||||
|
||||
The CWE value will be printed by text sinks after the message::
|
||||
|
||||
test-metadata.c:21:3: warning: never use 'gets' [CWE-242]
|
||||
|
||||
and in a sufficiently-capable terminal will be a link to
|
||||
documentation about the CWE.
|
||||
|
||||
.. function:: void diagnostic_add_rule (diagnostic *diag, \
|
||||
const char *title, \
|
||||
const char *url)
|
||||
|
||||
Associate this :type:`diagnostic` with a particular rule that has been
|
||||
violated (such as in a coding standard, or within a specification).
|
||||
|
||||
A diagnostic can be associated with zero or more rules.
|
||||
|
||||
``diag`` must be non-NULL. The rule must have at least one of a
|
||||
title and a URL, but these can be NULL.
|
||||
|
||||
For example, given::
|
||||
|
||||
diagnostic_add_rule (d,
|
||||
"MSC24-C",
|
||||
"https://wiki.sei.cmu.edu/confluence/display/c/MSC24-C.+Do+not+use+deprecated+or+obsolescent+functions");
|
||||
|
||||
the rule name will be printed by text sinks after the message::
|
||||
|
||||
test-metadata.c:21:3: warning: never use 'gets' [MSC24-C]
|
||||
21 | gets (buf);
|
||||
| ^~~~~~~~~~
|
||||
|
||||
and if so, the URL will be available in a sufficiently capable
|
||||
terminal.
|
||||
|
||||
This can be used in conjunction with :func:`diagnostic_set_cwe`,
|
||||
giving output like this::
|
||||
|
||||
test-metadata.c:21:3: warning: never use 'gets' [CWE-242] [MSC24-C]
|
||||
21 | gets (buf);
|
||||
| ^~~~~~~~~~
|
281
gcc/doc/libdiagnostics/topics/physical-locations.rst
Normal file
281
gcc/doc/libdiagnostics/topics/physical-locations.rst
Normal file
@ -0,0 +1,281 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Physical locations
|
||||
==================
|
||||
|
||||
A "physical" source location is a location expressed in terms of
|
||||
a specific file, and line(s) and column(s) (as opposed to a
|
||||
:doc:`"logical" location <logical-locations>`,
|
||||
which refers to semantic constructs in a programming language).
|
||||
|
||||
Creating location information
|
||||
*****************************
|
||||
|
||||
The :type:`diagnostic_manager` manages various objects relating to
|
||||
locations.
|
||||
|
||||
.. type:: diagnostic_file
|
||||
|
||||
A :type:`diagnostic_file` is an opaque type describing a particular input file.
|
||||
|
||||
.. function:: const diagnostic_file * diagnostic_manager_new_file (diagnostic_manager *diag_mgr, \
|
||||
const char *name, \
|
||||
const char *sarif_source_language)
|
||||
|
||||
Create a new :type:`diagnostic_file` for file ``name``. Repeated calls
|
||||
with strings that match ``name`` will return the same object.
|
||||
|
||||
Both ``diag_mgr`` and ``name`` must be non-NULL.
|
||||
|
||||
If ``sarif_source_language`` is non-NULL, it specifies a
|
||||
``sourceLanguage`` value for the file for use when writing
|
||||
:doc:`SARIF <sarif>`
|
||||
(`SARIF v2.1.0 §3.24.10 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790871>`_).
|
||||
See
|
||||
`SARIF v2.1.0 Appendix J <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141791197>`_
|
||||
for suggested values for various programmming languages.
|
||||
|
||||
For example, this creates a :type:`diagnostic_file` for ``foo.c``
|
||||
and identifies it as C source code::
|
||||
|
||||
foo_c = diagnostic_manager_new_file (diag_mgr,
|
||||
"foo.c",
|
||||
"c" /* source_language */);
|
||||
|
||||
.. function:: void diagnostic_manager_debug_dump_file (diagnostic_manager *diag_mgr, \
|
||||
const diagnostic_file *file, \
|
||||
FILE *out)
|
||||
|
||||
Write a representation of ``file`` to ``out``, for debugging.
|
||||
Both ``diag_mgr`` and ``out`` must be non-NULL.
|
||||
`file`` may be NULL.
|
||||
|
||||
For example::
|
||||
|
||||
diagnostic_manager_debug_dump_file (diag_mgr, foo_c, stderr);
|
||||
|
||||
might lead to this output::
|
||||
|
||||
file(name="foo.c", sarif_source_language="c")
|
||||
|
||||
.. type:: diagnostic_line_num_t
|
||||
|
||||
A :type:`diagnostic_line_num_t` is used for representing line numbers
|
||||
within text files. libdiagnostics treats the first line of a text file
|
||||
as line 1.
|
||||
|
||||
.. type:: diagnostic_column_num_t
|
||||
|
||||
A :type:`diagnostic_column_num_t` is used for representing column numbers
|
||||
within text files. libdiagnostics treats the first column of a text line
|
||||
as column 1, **not** column 0.
|
||||
|
||||
.. note::
|
||||
|
||||
Both libdiagnostics and Emacs number source *lines* starting at 1, but
|
||||
they have differing conventions for *columns*.
|
||||
|
||||
libdiagnostics uses a 1-based convention for source columns,
|
||||
whereas Emacs's ``M-x column-number-mode`` uses a 0-based convention.
|
||||
|
||||
For example, an error in the initial, left-hand
|
||||
column of source line 3 is reported by libdiagnostics as::
|
||||
|
||||
some-file.c:3:1: error: ...etc...
|
||||
|
||||
On navigating to the location of that error in Emacs
|
||||
(e.g. via ``next-error``),
|
||||
the locus is reported in the Mode Line
|
||||
(assuming ``M-x column-number-mode``) as::
|
||||
|
||||
some-file.c 10% (3, 0)
|
||||
|
||||
i.e. ``3:1:`` in libdiagnostics corresponds to ``(3, 0)`` in Emacs.
|
||||
|
||||
.. type:: diagnostic_physical_location
|
||||
|
||||
A :type:`diagnostic_physical_location` is an opaque type representing a
|
||||
key into a database of source locations within a :type:`diagnostic_manager`.
|
||||
|
||||
:type:`diagnostic_physical_location` instances are created by various API
|
||||
calls into the :type:`diagnostic_manager` expressing source code points
|
||||
and ranges.
|
||||
|
||||
They persist until the :type:`diagnostic_manager` is released, which
|
||||
cleans them up.
|
||||
|
||||
A ``NULL`` value means "unknown", and can be returned by the
|
||||
:type:`diagnostic_manager` as a fallback when a problem occurs
|
||||
(e.g. too many locations).
|
||||
|
||||
A :type:`diagnostic_physical_location` can be a single point within the
|
||||
source code, such as here (at the the '"' at the start of the string literal)::
|
||||
|
||||
int i = "foo";
|
||||
^
|
||||
|
||||
or be a range with a start and finish, and a "caret" location::
|
||||
|
||||
a = (foo && bar)
|
||||
~~~~~^~~~~~~
|
||||
|
||||
where the caret here is at the first "&", and the start and finish
|
||||
are at the parentheses.
|
||||
|
||||
.. function:: const diagnostic_physical_location *diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, \
|
||||
const diagnostic_file *file, \
|
||||
diagnostic_line_num_t line_num)
|
||||
|
||||
Attempt to create a :type:`diagnostic_physical_location` representing
|
||||
``FILENAME:LINE_NUM``, with no column information (thus representing
|
||||
the whole of the given line.
|
||||
|
||||
Both ``diag_mgr`` and ``file`` must be non-NULL.
|
||||
|
||||
.. function:: const diagnostic_physical_location * diagnostic_manager_new_location_from_file_line_column (diagnostic_manager *diag_mgr, \
|
||||
const diagnostic_file *file, \
|
||||
diagnostic_line_num_t line_num, \
|
||||
diagnostic_column_num_t column_num)
|
||||
|
||||
Attempt to create a :type:`diagnostic_physical_location` for
|
||||
``FILENAME:LINE_NUM:COLUMN_NUM`` representing a particular point
|
||||
in the source file.
|
||||
|
||||
Both ``diag_mgr`` and ``file`` must be non-NULL.
|
||||
|
||||
.. function:: const diagnostic_physical_location *diagnostic_manager_new_location_from_range (diagnostic_manager *diag_mgr,\
|
||||
const diagnostic_physical_location *loc_caret,\
|
||||
const diagnostic_physical_location *loc_start,\
|
||||
const diagnostic_physical_location *loc_end)
|
||||
|
||||
Attempt to create a diagnostic_physical_location representing a
|
||||
range within a source file, with a highlighted "caret" location.
|
||||
|
||||
All must be within the same file, but they can be on different lines.
|
||||
|
||||
For example, consider the location of the binary expression below::
|
||||
|
||||
...|__________1111111112222222
|
||||
...|12345678901234567890123456
|
||||
...|
|
||||
521|int sum (int foo, int bar)
|
||||
522|{
|
||||
523| return foo + bar;
|
||||
...| ~~~~^~~~~
|
||||
524|}
|
||||
|
||||
The location's caret is at the "+", line 523 column 15, but starts
|
||||
earlier, at the "f" of "foo" at column 11. The finish is at the "r"
|
||||
of "bar" at column 19.
|
||||
|
||||
``diag_mgr`` must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr,\
|
||||
const diagnostic_physical_location *loc, \
|
||||
FILE *out)
|
||||
|
||||
Write a representation of ``loc`` to ``out``, for debugging.
|
||||
|
||||
Both ``diag_mgr`` and ``out`` must be non-NULL.
|
||||
`loc`` may be NULL.
|
||||
|
||||
TODO: example of output
|
||||
|
||||
Associating diagnostics with locations
|
||||
**************************************
|
||||
|
||||
A :type:`diagnostic` has an optional primary physical location
|
||||
and zero or more secondary physical locations. For example::
|
||||
|
||||
a = (foo && bar)
|
||||
~~~~~^~~~~~~
|
||||
|
||||
This diagnostic has a single :type:`diagnostic_physical_location`,
|
||||
with the caret at the first "&", and the start/finish at the parentheses.
|
||||
|
||||
Contrast with::
|
||||
|
||||
a = (foo && bar)
|
||||
~~~ ^~ ~~~
|
||||
|
||||
This diagnostic has three locations
|
||||
|
||||
* The primary location (at "&&") has its caret and start location at
|
||||
the first "&" and end at the second "&.
|
||||
|
||||
* The secondary location for "foo" has its start and finish at the "f"
|
||||
and "o" of "foo"; the caret is not displayed, but is perhaps at
|
||||
the "f" of "foo".
|
||||
|
||||
* Similarly, the other secondary location (for "bar") has its start and
|
||||
finish at the "b" and "r" of "bar"; the caret is not displayed, but
|
||||
is perhaps at the"b" of "bar".
|
||||
|
||||
.. function:: void diagnostic_set_location (diagnostic *diag, \
|
||||
const diagnostic_physical_location * loc)
|
||||
|
||||
Set the primary location of ``diag``.
|
||||
|
||||
``diag`` must be non-NULL; ``loc`` can be NULL.
|
||||
|
||||
.. function:: void diagnostic_set_location_with_label (diagnostic *diag, \
|
||||
const diagnostic_physical_location *loc, \
|
||||
const char *fmt, ...)
|
||||
|
||||
Set the primary location of ``diag``, with a label. The label is
|
||||
formatted as per the rules FIXME
|
||||
|
||||
``diag`` and ``fmt`` must be non-NULL; ``loc`` can be NULL.
|
||||
|
||||
See :doc:`message-formatting` for details of how to use ``fmt``.
|
||||
|
||||
TODO: example of use
|
||||
|
||||
.. function:: void diagnostic_add_location (diagnostic *diag, \
|
||||
const diagnostic_physical_location * loc)
|
||||
|
||||
Add a secondary location to ``diag``.
|
||||
|
||||
``diag`` must be non-NULL; ``loc`` can be NULL.
|
||||
|
||||
|
||||
.. function:: void diagnostic_add_location_with_label (diagnostic *diag, \
|
||||
const diagnostic_physical_location *loc, \
|
||||
const char *text)
|
||||
|
||||
Add a secondary location to ``diag``, with a label. The label is
|
||||
formatted as per the rules FIXME
|
||||
|
||||
``diag`` and ``fmt`` must be non-NULL; ``loc`` can be NULL.
|
||||
|
||||
For example,
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-labelled-ranges.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
might give this text output::
|
||||
|
||||
test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char *'
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
23
gcc/doc/libdiagnostics/topics/retrofitting.rst
Normal file
23
gcc/doc/libdiagnostics/topics/retrofitting.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Adding libdiagnostics to an existing project
|
||||
============================================
|
||||
|
||||
TODO
|
51
gcc/doc/libdiagnostics/topics/sarif.rst
Normal file
51
gcc/doc/libdiagnostics/topics/sarif.rst
Normal file
@ -0,0 +1,51 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
SARIF support
|
||||
=============
|
||||
|
||||
`SARIF <https://www.sarif.info/>`_ is a machine-readable format, originally
|
||||
designed for the output of static analysis tools, but which can be used
|
||||
for diagnostics in general.
|
||||
|
||||
.. function:: void diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, \
|
||||
FILE *dst_stream, \
|
||||
const diagnostic_file *main_input_file, \
|
||||
enum diagnostic_sarif_version version)
|
||||
|
||||
Add a new output sink to ``diag_mgr``, which writes SARIF of the given
|
||||
version to ``dst_stream``.
|
||||
|
||||
The output is not written until ``diag_mgr`` is released.
|
||||
|
||||
``dst_stream`` is borrowed, and must outlive ``diag_mgr``.
|
||||
|
||||
For the result to be a valid SARIF file according to the schema,
|
||||
``diag_mgr`` must have had :func:`diagnostic_manager_set_tool_name`
|
||||
called on it.
|
||||
|
||||
``diag_mgr``, ``dst_stream``, and ``main_input_file`` must all be non-NULL.
|
||||
|
||||
.. enum:: diagnostic_sarif_version
|
||||
|
||||
An enum for choosing the SARIF version for a SARIF output sink.
|
||||
|
||||
.. macro:: DIAGNOSTIC_SARIF_VERSION_2_1_0
|
||||
|
||||
.. macro:: DIAGNOSTIC_SARIF_VERSION_2_2_PRERELEASE
|
87
gcc/doc/libdiagnostics/topics/text-output.rst
Normal file
87
gcc/doc/libdiagnostics/topics/text-output.rst
Normal file
@ -0,0 +1,87 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Text output
|
||||
===========
|
||||
|
||||
.. type:: diagnostic_text_sink
|
||||
|
||||
.. function:: diagnostic_text_sink * diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr,\
|
||||
FILE *dst_stream, \
|
||||
enum diagnostic_colorize colorize)
|
||||
|
||||
Add a new output sink to ``diag_mgr``, which writes GCC-style diagnostics
|
||||
to ``dst_stream``.
|
||||
Return a borrowed pointer to the sink, which is cleaned up when ``diag_mgr``
|
||||
is released.
|
||||
|
||||
``diag_mgr`` must be non-NULL.
|
||||
|
||||
``dst_stream`` must be non-NULL. It is borrowed and must outlive ``DIAG_MGR``.
|
||||
|
||||
The output for each diagnostic is written and flushed as each
|
||||
:type:`diagnostic` is finished.
|
||||
|
||||
.. enum:: diagnostic_colorize
|
||||
|
||||
An enum for determining if we should colorize a text output sink.
|
||||
|
||||
.. macro:: DIAGNOSTIC_COLORIZE_IF_TTY
|
||||
|
||||
Diagnostics should be colorized if the destination stream is
|
||||
directly connected to a tty.
|
||||
|
||||
.. macro:: DIAGNOSTIC_COLORIZE_NO
|
||||
|
||||
Diagnostics should not be colorized.
|
||||
|
||||
.. macro:: DIAGNOSTIC_COLORIZE_YES
|
||||
|
||||
Diagnostics should be colorized.
|
||||
|
||||
.. function:: void diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink, \
|
||||
int value)
|
||||
|
||||
Enable or disable printing of source text in the text sink.
|
||||
|
||||
``text_sink`` must be non-NULL.
|
||||
|
||||
Default: enabled.
|
||||
|
||||
.. function:: void diagnostic_text_sink_set_colorize (diagnostic_text_sink *text_sink, \
|
||||
enum diagnostic_colorize colorize)
|
||||
|
||||
Update colorization of text sink.
|
||||
|
||||
``text_sink`` must be non-NULL.
|
||||
|
||||
.. function:: void diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink, \
|
||||
int value)
|
||||
|
||||
``text_sink`` must be non-NULL.
|
||||
|
||||
Enable or disable colorization of the characters of source text
|
||||
that are underlined.
|
||||
|
||||
This should be true for clients that generate range information
|
||||
(so that the ranges of code are colorized), and false for clients that
|
||||
merely specify points within the source code (to avoid e.g. colorizing
|
||||
just the first character in a token, which would look strange).
|
||||
|
||||
Default: enabled.
|
26
gcc/doc/libdiagnostics/topics/ux.rst
Normal file
26
gcc/doc/libdiagnostics/topics/ux.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
User Experience
|
||||
===============
|
||||
|
||||
Refer to
|
||||
`GCC's user experience guidelines <https://gcc.gnu.org/onlinedocs/gccint/User-Experience-Guidelines.html>`_
|
||||
for notes on
|
||||
`what makes a good diagnostic <https://gcc.gnu.org/onlinedocs/gccint/Guidelines-for-Diagnostics.html>`_.
|
173
gcc/doc/libdiagnostics/tutorial/01-hello-world.rst
Normal file
173
gcc/doc/libdiagnostics/tutorial/01-hello-world.rst
Normal file
@ -0,0 +1,173 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
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 libdiagnostics to emit an error message
|
||||
to stderr.
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-example-1.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
Copy the above to `tut01-hello-world.c`.
|
||||
|
||||
Assuming you have libdiagnostics installed, build the test program
|
||||
using:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ gcc \
|
||||
tut01-hello-world.c \
|
||||
-o tut01-hello-world \
|
||||
-ldiagnostics
|
||||
|
||||
You should then be able to run the built program:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./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 <https://en.wikipedia.org/wiki/ANSI_escape_code>`_).
|
||||
|
||||
.. image:: 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 :doc:`the next part of the tutorial <02-physical-locations>` we'll add
|
||||
file/location information to our error messages, and libdiagnostics will
|
||||
quote the pertinent parts of the file, underlining them, which is less trivial
|
||||
to reimplement. libdiagnostics 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 libdiagnostics,
|
||||
it is trivial to add support for outputting them in
|
||||
machine-readable form as :doc:`SARIF <../topics/sarif>`.
|
||||
|
||||
|
||||
Structure
|
||||
*********
|
||||
|
||||
The above example shows the typical structure of a program using
|
||||
libdiagnostics:
|
||||
|
||||
* **initialization**: create a :type:`diagnostic_manager` instance,
|
||||
and create an output sink for it, and other one-time initialization
|
||||
|
||||
* **emission**: create various :type:`diagnostic` instances, populating
|
||||
them with data, and calling "finish" once they're ready to be emitted.
|
||||
:doc:`Text sinks <../topics/text-output>` emit their diagnostics as soon
|
||||
as "finish" is called on them.
|
||||
|
||||
* **cleanup**: call :func:`diagnostic_manager_release` on the
|
||||
:type:`diagnostic_manager` to finish and free up resources.
|
||||
:doc:`SARIF sinks <../topics/sarif>` write their output when
|
||||
:func:`diagnostic_manager_release` is called on the manager.
|
||||
|
||||
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 libdiagnostics. See :doc:`02-physical-locations` for
|
||||
more information.
|
||||
|
||||
|
||||
Formatted messages
|
||||
******************
|
||||
|
||||
The above example uses :func:`diagnostic_finish`, which takes a format
|
||||
string and arguments. libdiagnostics has its own style of format
|
||||
string arguments used for :func:`diagnostic_finish` and some other
|
||||
entrypoints.
|
||||
|
||||
.. note:: The format syntax is *not* the same as ``printf``; see
|
||||
:doc:`supported formatting options <../topics/message-formatting>`.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
progname: erreur: can't find « free »
|
||||
|
||||
Note that:
|
||||
|
||||
* the string ``error`` has been localized by libdiagnostics 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 :func:`diagnostic_finish` if you
|
||||
want to internationalize the output.
|
||||
|
||||
There are many :doc:`supported formatting options <../topics/message-formatting>`.
|
||||
|
||||
|
||||
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
|
||||
:doc:`next tutorial <02-physical-locations>`, but we can override this
|
||||
default name via :func:`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
|
||||
:doc:`supplying metadata to libdiagnostics <../../topics/metadata>`.
|
||||
|
||||
|
||||
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
|
||||
:doc:`part 2 of the tutorial <02-physical-locations>` for information on
|
||||
how to do this.
|
260
gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst
Normal file
260
gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst
Normal file
@ -0,0 +1,260 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Tutorial part 2: physical locations
|
||||
===================================
|
||||
|
||||
libdiagnostics has two kinds of location:
|
||||
|
||||
* *physical locations* expressed in terms of a specific file, and line(s)
|
||||
and perhaps column(s), such as ``some-file.c:3:1``, or a range of
|
||||
columns, such as in::
|
||||
|
||||
test-typo.c:19:13: error: unknown field 'colour'
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
|
||||
or even a range spanning multiple lines of a file.
|
||||
|
||||
All of these are instances of :type:`diagnostic_physical_location`.
|
||||
|
||||
* *logical locations* which refers to semantic constructs
|
||||
in the input, such as ``within function 'foo'``, or within
|
||||
namespace ``foo``'s class ``bar``'s member function ``get_color``.
|
||||
|
||||
These are instances of :type:`diagnostic_logical_location`,
|
||||
|
||||
A :type:`diagnostic` can have zero or more physical locations,
|
||||
and optionally have a logical location.
|
||||
|
||||
Let's extend the previous example to add a physical location to the
|
||||
:type:`diagnostic`; we'll cover logical locations in the
|
||||
:doc:`next section <03-logical-locations>`.
|
||||
|
||||
|
||||
Source files
|
||||
************
|
||||
|
||||
Given these declarations::
|
||||
|
||||
static diagnostic_manager *diag_mgr;
|
||||
static const diagnostic_file *main_file;
|
||||
|
||||
we can create a :type:`diagnostic_file` describing an input file ``foo.c``
|
||||
via :func:`diagnostic_manager_new_file`::
|
||||
|
||||
foo_c = diagnostic_manager_new_file (diag_mgr,
|
||||
"foo.c",
|
||||
"c" /* source_language */);
|
||||
|
||||
You can use :func:`diagnostic_manager_debug_dump_file` to print a
|
||||
representation of a :type:`diagnostic_file` for debugging.
|
||||
For example::
|
||||
|
||||
diagnostic_manager_debug_dump_file (diag_mgr, foo_c, stderr);
|
||||
|
||||
might lead to this output on ``stderr``::
|
||||
|
||||
file(name="foo.c", sarif_source_language="c")
|
||||
|
||||
Once we have a :type:`diagnostic_file` we can use it to create instances
|
||||
of :type:`diagnostic_physical_location` within the :type:`diagnostic_manager`.
|
||||
These are owned by the :type:`diagnostic_manager` and cleaned up
|
||||
automatically when :func:`diagnostic_manager_release` is called.
|
||||
|
||||
Instances of :type:`diagnostic_physical_location` can refer to
|
||||
|
||||
* a source line as a whole, created via
|
||||
:func:`diagnostic_manager_new_location_from_file_and_line`.
|
||||
|
||||
* a particular point within a source file (line/column), created via
|
||||
:func:`diagnostic_manager_new_location_from_file_line_column`.
|
||||
|
||||
* a range of text within of source file, created via
|
||||
:func:`diagnostic_manager_new_location_from_range`.
|
||||
|
||||
|
||||
Diagnostics affecting a whole source line
|
||||
*****************************************
|
||||
|
||||
If we want a diagnostic to refer to an entire source line,
|
||||
we can use :func:`diagnostic_manager_new_location_from_file_and_line`.
|
||||
|
||||
For example, given this example input where the tool can't find the header::
|
||||
|
||||
#include <foo.h>
|
||||
|
||||
we could complain about it via libdiagnostics via:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-no-column.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
leading to output like this::
|
||||
|
||||
foo.c:17: error: can't find 'foo.h'"
|
||||
17 | #include <foo.h>
|
||||
|
||||
where libdiagnostics will attempt to load the source file and
|
||||
quote the pertinent line.
|
||||
|
||||
If libdiagnostics cannot open the file, it will merely print::
|
||||
|
||||
foo.c:17: error: can't find 'foo.h'
|
||||
|
||||
You can use :func:`diagnostic_manager_debug_dump_location` to dump a
|
||||
:type:`diagnostic_physical_location`. For the above example::
|
||||
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc, stderr);
|
||||
|
||||
might print::
|
||||
|
||||
foo.c:17
|
||||
|
||||
to stderr.
|
||||
|
||||
|
||||
Columns and ranges
|
||||
******************
|
||||
|
||||
If we want to generate output like this::
|
||||
|
||||
foo.c:17:11: error: can't find 'foo'"
|
||||
17 | #include <foo.h>
|
||||
| ^~~~~
|
||||
|
||||
where the diagnostic is marked as relating to the above range of
|
||||
characters in line 17, we need to express the range of characters
|
||||
within the line of interest.
|
||||
|
||||
We can do this by creating a :type:`diagnostic_physical_location` for the
|
||||
start of the range, another one for the end of the range, and then using
|
||||
these two to create a :type:`diagnostic_physical_location` for the
|
||||
range as a whole:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-error.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
On compiling and running the program, we should get this output::
|
||||
|
||||
foo.c:17:11: error: can't find 'foo.h'
|
||||
17 | #include <foo.h>
|
||||
| ^~~~~
|
||||
|
||||
where libdiagnostics will attempt to load the source file and
|
||||
underling the pertinent part of the given line.
|
||||
|
||||
If libdiagnostics cannot open the file, it will merely print::
|
||||
|
||||
foo.c:17:8: error: can't find 'foo'
|
||||
|
||||
A range can span multiple lines within the same file.
|
||||
|
||||
As before, you can use :func:`diagnostic_manager_debug_dump_location` to
|
||||
dump the locations. For the above example::
|
||||
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc_start, stderr);
|
||||
|
||||
and::
|
||||
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc_range, stderr);
|
||||
|
||||
might print::
|
||||
|
||||
foo.c:17:11
|
||||
|
||||
to stderr, whereas::
|
||||
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc_end, stderr);
|
||||
|
||||
might print::
|
||||
|
||||
foo.c:17:15
|
||||
|
||||
|
||||
Multiple locations
|
||||
******************
|
||||
|
||||
As well as the primary physical location seen above, a :type:`diagnostic`
|
||||
can have additional physical locations. You can add these secondary
|
||||
locations via :func:`diagnostic_add_location`.
|
||||
|
||||
For example, for this valid but suspicious-looking C code::
|
||||
|
||||
const char *strs[3] = {"foo",
|
||||
"bar"
|
||||
"baz"};
|
||||
|
||||
the following :type:`diagnostic` has its primary location where the missing
|
||||
comma should be, and secondary locations for each of the string literals
|
||||
``"foo"``, ``"bar"``, and ``"baz"``, added via :func:`diagnostic_add_location`:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-multiple-lines.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
where the text output might be::
|
||||
|
||||
test-multiple-lines.c:23:29: warning: missing comma
|
||||
22 | const char *strs[3] = {"foo",
|
||||
| ~~~~~
|
||||
23 | "bar"
|
||||
| ~~~~~^
|
||||
24 | "baz"};
|
||||
| ~~~~~
|
||||
|
||||
|
||||
Labelling locations
|
||||
*******************
|
||||
|
||||
You can give the locations labels using
|
||||
:func:`diagnostic_set_location_with_label` and
|
||||
:func:`diagnostic_add_location_with_label`.
|
||||
|
||||
Consider emitting a "type mismatch" diagnostic for::
|
||||
|
||||
42 + "foo"
|
||||
|
||||
where the primary location is on the ``+``, with secondary locations on the``42``
|
||||
and the ``"foo"``:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-labelled-ranges.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
giving this text output::
|
||||
|
||||
test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char *'
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
||||
|
||||
|
||||
More on locations
|
||||
*****************
|
||||
|
||||
For more details on the above, see :doc:`../topics/physical-locations`.
|
||||
Otherwise the :doc:`next part of the tutorial <03-logical-locations>`
|
||||
covers logical locations.
|
60
gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst
Normal file
60
gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst
Normal file
@ -0,0 +1,60 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Tutorial part 3: logical locations
|
||||
==================================
|
||||
|
||||
Let's extend the previous example to add a
|
||||
:doc:`logical location <../topics/logical-locations>` to the
|
||||
:type:`diagnostic`.
|
||||
|
||||
First we create a :type:`diagnostic_logical_location` representing a
|
||||
particular function::
|
||||
|
||||
const diagnostic_logical_location *logical_loc
|
||||
= diagnostic_manager_new_logical_location (diag_mgr,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
|
||||
NULL, /* parent */
|
||||
"foo",
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
In this simple example we specify that it is a function, and just give
|
||||
it a name (``foo``). For more complicated cases we can set up tree-like
|
||||
hierarchies of logical locations, set qualified names, "mangled" names,
|
||||
and so on; see :func:`diagnostic_manager_new_logical_location` for details.
|
||||
|
||||
Once we have :type:`diagnostic_logical_location` we can associate it with
|
||||
a :type:`diagnostic` with :func:`diagnostic_set_logical_location`::
|
||||
|
||||
diagnostic_set_logical_location (d, logical_loc);
|
||||
|
||||
The logical location will be printed by text output sinks like this::
|
||||
|
||||
In function 'foo':
|
||||
|
||||
and will be captured in :doc:`SARIF <../topics/sarif>` output.
|
||||
|
||||
|
||||
Find out more
|
||||
*************
|
||||
|
||||
For more details on the above, see :doc:`../topics/logical-locations`.
|
||||
Otherwise the :doc:`next part of the tutorial <04-notes>` covers adding
|
||||
supplementary "notes" to a :type:`diagnostic`.
|
66
gcc/doc/libdiagnostics/tutorial/04-notes.rst
Normal file
66
gcc/doc/libdiagnostics/tutorial/04-notes.rst
Normal file
@ -0,0 +1,66 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Tutorial part 4: adding notes
|
||||
=============================
|
||||
|
||||
Let's further extend the previous example to add a "note" to it.
|
||||
|
||||
We want to generate output like this::
|
||||
|
||||
test-with-note.c:17:11: error: can't find 'foo'
|
||||
17 | #include <foo.h>
|
||||
| ^~~~~
|
||||
test-with-note.c:17:11: note: have you looked behind the couch?
|
||||
|
||||
The "error" and "note" are both instances of :type:`diagnostic`.
|
||||
We want to let libdiagnostics know that they are grouped together.
|
||||
The way to do this is to use :func:`diagnostic_manager_begin_group`
|
||||
and :func:`diagnostic_manager_end_group` around the "finish" calls
|
||||
to the diagnostics.
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-error-with-note.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
On compiling and running the program, we should get the desired output::
|
||||
|
||||
test-with-note.c:17:11: error: can't find 'foo'
|
||||
17 | #include <foo.h>
|
||||
| ^~~~~
|
||||
test-with-note.c:17:11: note: have you looked behind the couch?
|
||||
|
||||
The grouping doesn't affect text output sinks, but a
|
||||
:doc:`SARIF sink <../topics/sarif>` will group the note within the error
|
||||
(via the ``relatedLocations`` property of ``result`` objects; see SARIF v2.1.0
|
||||
`§3.27.22 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790910>`_).
|
||||
|
||||
In the above, the note had the same physical location as the error
|
||||
(``loc_range``). This can be useful for splitting up a message into two
|
||||
parts to make localization easier, but they could have different locations, such
|
||||
as in::
|
||||
|
||||
test.xml:10:2: error: 'foo' is not valid here
|
||||
test.xml:5:1: note: within element 'bar'
|
||||
|
||||
where each :type:`diagnostic` had its own :type:`diagnostic_physical_location`.
|
||||
|
||||
In :doc:`the next tutorial <05-warnings>` we'll look at issuing warnings,
|
||||
rather than errors.
|
44
gcc/doc/libdiagnostics/tutorial/05-warnings.rst
Normal file
44
gcc/doc/libdiagnostics/tutorial/05-warnings.rst
Normal file
@ -0,0 +1,44 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Tutorial part 5: warnings
|
||||
=========================
|
||||
|
||||
So far we've only emitted errors, but other kinds of diagnostic are possible,
|
||||
such as warnings.
|
||||
|
||||
We can select different kinds of diagnostic via :enum:`diagnostic_level`
|
||||
when calling :func:`diagnostic_begin`:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-warning.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
On compiling and running the program, we should get output similar to::
|
||||
|
||||
test-warning.c:17:11: warning: this is a warning
|
||||
17 | #include <foo.h>
|
||||
| ^~~~~
|
||||
|
||||
Various severities are possible, see :enum:`diagnostic_level` for more
|
||||
information.
|
||||
|
||||
In :doc:`the next section of the tutorial <06-fix-it-hints>` we'll look
|
||||
at adding fix-it hints to diagnostics.
|
61
gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst
Normal file
61
gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst
Normal file
@ -0,0 +1,61 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Tutorial part 6: fix-it hints
|
||||
=============================
|
||||
|
||||
libdiagnostics supports adding "fix-it hints" to a :type:`diagnostic`:
|
||||
suggestions for the user on how to edit their code to fix a problem. These
|
||||
can be expressed as insertions, replacements, and removals of text.
|
||||
|
||||
For example, here we add a replacement fix-it hint to a diagnostic:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-fix-it-hint.c
|
||||
:language: c
|
||||
:start-after: /* begin quoted source */
|
||||
:end-before: /* end quoted source */
|
||||
|
||||
On compiling and running the program, we should get output similar to::
|
||||
|
||||
test-fix-it-hint.c:19:13: error: unknown field 'colour'; did you mean 'color'
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
|
||||
We can also add a call to :func:`diagnostic_manager_write_patch` to the
|
||||
program cleanup code::
|
||||
|
||||
diagnostic_manager_write_patch (diag_mgr, stderr);
|
||||
|
||||
This will write a patch to the stream (here ``stderr``) giving the effect
|
||||
of all fix-it hints on all diagnostics emitted by the
|
||||
:type:`diagnostic_manager`, giving something like::
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
struct rgb
|
||||
get_color (struct object *p)
|
||||
{
|
||||
- return p->colour;
|
||||
+ return p->color;
|
||||
}
|
||||
|
||||
|
||||
See the :doc:`guide to fix-it hints <../topics/fix-it-hints>`
|
||||
for more information, or go on to
|
||||
:doc:`the next section of the tutorial <07-execution-paths>`.
|
141
gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst
Normal file
141
gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst
Normal file
@ -0,0 +1,141 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
.. default-domain:: c
|
||||
|
||||
Tutorial part 7: execution paths
|
||||
================================
|
||||
|
||||
A :type:`diagnostic` can optionally have a :type:`diagnostic_execution_path`
|
||||
describing a path of execution through code.
|
||||
|
||||
For example, let's pretend we're writing a static analyis tool for finding
|
||||
bugs in `CPython extension code <https://docs.python.org/3/c-api/index.html>`_.
|
||||
|
||||
Let's say we're analyzing this code:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
|
||||
:language: c
|
||||
:start-after: begin fake source
|
||||
:end-before: end fake source
|
||||
|
||||
This code attempts to take an Python integer parameter and then build a
|
||||
list of that length, containing random integers. However, there are
|
||||
**numerous bugs** in this code: a type mismatch, mistakes in
|
||||
reference-counting, and an almost total lack of error-handling.
|
||||
|
||||
For example, ``PyList_Append`` requires a non-NULL first parameter (``list``),
|
||||
but ``PyList_New`` can fail, returning NULL, and this isn't checked for,
|
||||
which would lead to a segfault if ``PyList_New`` fails.
|
||||
|
||||
We can add a :type:`diagnostic_execution_path` to the :type:`diagnostic`
|
||||
via :func:`diagnostic_add_execution_path`, and then add events to it
|
||||
using :func:`diagnostic_execution_path_add_event`.
|
||||
|
||||
For example, with::
|
||||
|
||||
diagnostic_event_id alloc_event_id
|
||||
= diagnostic_execution_path_add_event (path,
|
||||
loc_call_to_PyList_New,
|
||||
logical_loc, 0,
|
||||
"when %qs fails, returning NULL",
|
||||
"PyList_New");
|
||||
|
||||
we create an event that will be worded as::
|
||||
|
||||
(1) when `PyList_New' fails, returning NULL
|
||||
|
||||
Note that :func:`diagnostic_execution_path_add_event` returns a
|
||||
:type:`diagnostic_event_id`. We can use this to refer to this event
|
||||
in another event using the ``%@`` format code in its message, which
|
||||
takes the address of a :type:`diagnostic_event_id`::
|
||||
|
||||
diagnostic_execution_path_add_event (path,
|
||||
loc_call_to_PyList_Append,
|
||||
logical_loc, 0,
|
||||
"when calling %qs, passing NULL from %@ as argument %i",
|
||||
"PyList_Append", &alloc_event_id, 1);
|
||||
|
||||
where the latter event will be worded as::
|
||||
|
||||
(2) when calling `PyList_Append', passing NULL from (1) as argument 1
|
||||
|
||||
where the ``%@`` reference to the other event has been printed as ``(1)``.
|
||||
In SARIF output the text "(1)" will have a embedded link referring within the sarif
|
||||
log to the ``threadFlowLocation`` object for the other event, via JSON
|
||||
pointer (see `§3.10.3 "URIs that use the sarif scheme" <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790707>`_).
|
||||
|
||||
Let's add an event between these describing control flow, creating three
|
||||
events in all:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
|
||||
:language: c
|
||||
:start-after: begin path creation
|
||||
:end-before: end path creation
|
||||
|
||||
Assuming we also gave it :type:`diagnostic_logical_location` with:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
|
||||
:language: c
|
||||
:start-after: begin create logical locs
|
||||
:end-before: end create logical locs
|
||||
|
||||
and finish the :type:`diagnostic` with :func:`diagnostic_finish` like this::
|
||||
|
||||
diagnostic_finish (d,
|
||||
"passing NULL as argument %i to %qs"
|
||||
" which requires a non-NULL parameter",
|
||||
1, "PyList_Append");
|
||||
|
||||
then we should get output to text sinks similar to the following::
|
||||
|
||||
In function 'make_a_list_of_random_ints_badly':
|
||||
test-warning-with-path.c:30:5: warning: passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter"
|
||||
30 | PyList_Append(list, item);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
make_a_list_of_random_ints_badly': events 1-3
|
||||
26 | list = PyList_New(0);
|
||||
| ^~~~~~~~~~~~~
|
||||
| |
|
||||
| (1) when 'PyList_New' fails, returning NULL
|
||||
27 |
|
||||
28 | for (i = 0; i < count; i++) {
|
||||
| ~~~~~~~~~
|
||||
| |
|
||||
| (2) when 'i < count'
|
||||
29 | item = PyLong_FromLong(random());
|
||||
30 | PyList_Append(list, item);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
| |
|
||||
| (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
|
||||
|
||||
and for SARIF sinks the path will be added as a ``codeFlow`` object
|
||||
(see SARIF 2.1.0 `3.36 codeFlow object <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790990>`_).
|
||||
|
||||
Here's the above example in full:
|
||||
|
||||
.. literalinclude:: ../../../testsuite/libdiagnostics.dg/test-warning-with-path.c
|
||||
:language: c
|
||||
:start-after: begin full example
|
||||
:end-before: end full example
|
||||
|
||||
|
||||
Moving on
|
||||
*********
|
||||
|
||||
That's the end of the tutorial. For more information on libdiagnostics, see
|
||||
the :doc:`topic guide <../topics/index>`.
|
BIN
gcc/doc/libdiagnostics/tutorial/example-1.png
Normal file
BIN
gcc/doc/libdiagnostics/tutorial/example-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
32
gcc/doc/libdiagnostics/tutorial/index.rst
Normal file
32
gcc/doc/libdiagnostics/tutorial/index.rst
Normal file
@ -0,0 +1,32 @@
|
||||
.. Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
This is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
Tutorial
|
||||
========
|
||||
|
||||
The following tutorial gives an overview of how to use libdiagnostics.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
01-hello-world.rst
|
||||
02-physical-locations.rst
|
||||
03-logical-locations.rst
|
||||
04-notes.rst
|
||||
05-warnings.rst
|
||||
06-fix-it-hints.rst
|
||||
07-execution-paths.rst
|
595
gcc/libdiagnostics++.h
Normal file
595
gcc/libdiagnostics++.h
Normal file
@ -0,0 +1,595 @@
|
||||
/* A C++ wrapper API around libdiagnostics.h for emitting diagnostics.
|
||||
Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef LIBDIAGNOSTICSPP_H
|
||||
#define LIBDIAGNOSTICSPP_H
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
|
||||
namespace libdiagnostics {
|
||||
|
||||
typedef diagnostic_line_num_t line_num_t;
|
||||
typedef diagnostic_column_num_t column_num_t;
|
||||
|
||||
class file;
|
||||
class physical_location;
|
||||
class logical_location;
|
||||
class execution_path;
|
||||
class group;
|
||||
class manager;
|
||||
class diagnostic;
|
||||
|
||||
/* Wrapper around a borrowed diagnostic_text_sink *. */
|
||||
|
||||
class text_sink
|
||||
{
|
||||
public:
|
||||
text_sink (diagnostic_text_sink *inner)
|
||||
: m_inner (inner)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
set_source_printing_enabled (int value)
|
||||
{
|
||||
diagnostic_text_sink_set_source_printing_enabled (m_inner, value);
|
||||
}
|
||||
|
||||
void
|
||||
set_colorize (enum diagnostic_colorize colorize)
|
||||
{
|
||||
diagnostic_text_sink_set_colorize (m_inner, colorize);
|
||||
}
|
||||
|
||||
void
|
||||
set_labelled_source_colorization_enabled (int value)
|
||||
{
|
||||
diagnostic_text_sink_set_labelled_source_colorization_enabled (m_inner,
|
||||
value);
|
||||
}
|
||||
|
||||
diagnostic_text_sink *m_inner;
|
||||
};
|
||||
|
||||
/* Wrapper around a const diagnostic_file *. */
|
||||
|
||||
class file
|
||||
{
|
||||
public:
|
||||
file () : m_inner (nullptr) {}
|
||||
file (const diagnostic_file *file) : m_inner (file) {}
|
||||
file (const file &other) : m_inner (other.m_inner) {}
|
||||
file &operator= (const file &other) { m_inner = other.m_inner; return *this; }
|
||||
|
||||
const diagnostic_file * m_inner;
|
||||
};
|
||||
|
||||
/* Wrapper around a const diagnostic_physical_location *. */
|
||||
|
||||
class physical_location
|
||||
{
|
||||
public:
|
||||
physical_location () : m_inner (nullptr) {}
|
||||
|
||||
physical_location (const diagnostic_physical_location *location)
|
||||
: m_inner (location)
|
||||
{}
|
||||
|
||||
const diagnostic_physical_location *m_inner;
|
||||
};
|
||||
|
||||
/* Wrapper around a const diagnostic_logical_location *. */
|
||||
|
||||
class logical_location
|
||||
{
|
||||
public:
|
||||
logical_location () : m_inner (nullptr) {}
|
||||
|
||||
logical_location (const diagnostic_logical_location *logical_loc)
|
||||
: m_inner (logical_loc)
|
||||
{}
|
||||
|
||||
const diagnostic_logical_location *m_inner;
|
||||
};
|
||||
|
||||
/* RAII class around a diagnostic_execution_path *. */
|
||||
|
||||
class execution_path
|
||||
{
|
||||
public:
|
||||
execution_path () : m_inner (nullptr), m_owned (false) {}
|
||||
|
||||
execution_path (diagnostic_execution_path *path)
|
||||
: m_inner (path), m_owned (true)
|
||||
{}
|
||||
|
||||
execution_path (const diagnostic_execution_path *path)
|
||||
: m_inner (const_cast<diagnostic_execution_path *> (path)),
|
||||
m_owned (false)
|
||||
{}
|
||||
|
||||
execution_path (const execution_path &other) = delete;
|
||||
execution_path &operator= (const execution_path &other) = delete;
|
||||
|
||||
execution_path (execution_path &&other)
|
||||
: m_inner (other.m_inner),
|
||||
m_owned (other.m_owned)
|
||||
{
|
||||
other.m_inner = nullptr;
|
||||
other.m_owned = false;
|
||||
}
|
||||
|
||||
execution_path &operator= (execution_path &&other)
|
||||
{
|
||||
m_inner = other.m_inner;
|
||||
m_owned = other.m_owned;
|
||||
other.m_inner = nullptr;
|
||||
other.m_owned = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~execution_path ()
|
||||
{
|
||||
if (m_owned)
|
||||
diagnostic_execution_path_release (m_inner);
|
||||
}
|
||||
|
||||
diagnostic_event_id
|
||||
add_event (physical_location physical_loc,
|
||||
logical_location logical_loc,
|
||||
unsigned stack_depth,
|
||||
const char *fmt, ...)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 6);
|
||||
|
||||
diagnostic_event_id
|
||||
add_event_va (physical_location physical_loc,
|
||||
logical_location logical_loc,
|
||||
unsigned stack_depth,
|
||||
const char *fmt,
|
||||
va_list *args)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 0);
|
||||
|
||||
diagnostic_execution_path *m_inner;
|
||||
bool m_owned;
|
||||
};
|
||||
|
||||
/* RAII class for starting/ending a group within a diagnostic_manager. */
|
||||
|
||||
class group
|
||||
{
|
||||
public:
|
||||
group (manager &mgr);
|
||||
~group ();
|
||||
|
||||
private:
|
||||
manager &m_mgr;
|
||||
};
|
||||
|
||||
/* Wrapper around a diagnostic *. */
|
||||
|
||||
class diagnostic
|
||||
{
|
||||
public:
|
||||
diagnostic (::diagnostic *d) : m_inner (d) {}
|
||||
|
||||
void
|
||||
set_cwe (unsigned cwe_id);
|
||||
|
||||
void
|
||||
add_rule (const char *title, const char *url);
|
||||
|
||||
void
|
||||
set_location (physical_location loc);
|
||||
|
||||
void
|
||||
add_location_with_label (physical_location loc,
|
||||
const char *text);
|
||||
|
||||
void
|
||||
set_logical_location (logical_location loc);
|
||||
|
||||
void
|
||||
add_fix_it_hint_insert_before (physical_location loc,
|
||||
const char *addition);
|
||||
void
|
||||
add_fix_it_hint_insert_after (physical_location loc,
|
||||
const char *addition);
|
||||
void
|
||||
add_fix_it_hint_replace (physical_location loc,
|
||||
const char *replacement);
|
||||
void
|
||||
add_fix_it_hint_delete (physical_location loc);
|
||||
|
||||
void
|
||||
take_execution_path (execution_path path);
|
||||
|
||||
void
|
||||
finish (const char *fmt, ...)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3);
|
||||
|
||||
void
|
||||
finish_va (const char *fmt, va_list *args)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0);
|
||||
|
||||
::diagnostic * const m_inner;
|
||||
};
|
||||
|
||||
/* Wrapper around a diagnostic_manager *, possibly with ownership. */
|
||||
|
||||
class manager
|
||||
{
|
||||
public:
|
||||
manager ()
|
||||
: m_inner (diagnostic_manager_new ()),
|
||||
m_owned (true)
|
||||
{
|
||||
}
|
||||
manager (diagnostic_manager *inner, bool owned)
|
||||
: m_inner (inner),
|
||||
m_owned (owned)
|
||||
{
|
||||
}
|
||||
~manager ()
|
||||
{
|
||||
if (m_owned)
|
||||
diagnostic_manager_release (m_inner);
|
||||
}
|
||||
|
||||
manager (const manager &other) = delete;
|
||||
manager (manager &&other)
|
||||
: m_inner (other.m_inner),
|
||||
m_owned (other.m_owned)
|
||||
{
|
||||
other.m_inner = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
set_tool_name (const char *value)
|
||||
{
|
||||
diagnostic_manager_set_tool_name (m_inner, value);
|
||||
}
|
||||
|
||||
void
|
||||
set_full_name (const char *value)
|
||||
{
|
||||
diagnostic_manager_set_full_name (m_inner, value);
|
||||
}
|
||||
|
||||
void
|
||||
set_version_string (const char *value)
|
||||
{
|
||||
diagnostic_manager_set_version_string (m_inner, value);
|
||||
}
|
||||
|
||||
void
|
||||
set_version_url (const char *value)
|
||||
{
|
||||
diagnostic_manager_set_version_url (m_inner, value);
|
||||
}
|
||||
|
||||
text_sink
|
||||
add_text_sink (FILE *dst_stream,
|
||||
enum diagnostic_colorize colorize)
|
||||
{
|
||||
return text_sink
|
||||
(diagnostic_manager_add_text_sink (m_inner, dst_stream, colorize));
|
||||
}
|
||||
|
||||
void
|
||||
add_sarif_sink (FILE *dst_stream,
|
||||
file main_input_file,
|
||||
enum diagnostic_sarif_version version)
|
||||
{
|
||||
diagnostic_manager_add_sarif_sink (m_inner, dst_stream,
|
||||
main_input_file.m_inner,
|
||||
version);
|
||||
}
|
||||
|
||||
void
|
||||
write_patch (FILE *dst_stream)
|
||||
{
|
||||
diagnostic_manager_write_patch (m_inner, dst_stream);
|
||||
}
|
||||
|
||||
/* Location management. */
|
||||
|
||||
file
|
||||
new_file (const char *name,
|
||||
const char *sarif_source_language)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3);
|
||||
|
||||
void
|
||||
debug_dump (file f,
|
||||
FILE *out);
|
||||
|
||||
physical_location
|
||||
new_location_from_file_and_line (file f, diagnostic_line_num_t line_num);
|
||||
|
||||
physical_location
|
||||
new_location_from_file_line_column (file f,
|
||||
line_num_t line_num,
|
||||
column_num_t column_num);
|
||||
|
||||
physical_location
|
||||
new_location_from_range (physical_location loc_caret,
|
||||
physical_location loc_start,
|
||||
physical_location loc_end);
|
||||
|
||||
void
|
||||
debug_dump (physical_location loc,
|
||||
FILE *out);
|
||||
|
||||
logical_location
|
||||
new_logical_location (enum diagnostic_logical_location_kind_t kind,
|
||||
logical_location parent,
|
||||
const char *short_name,
|
||||
const char *fully_qualified_name,
|
||||
const char *decorated_name);
|
||||
|
||||
void
|
||||
debug_dump (logical_location loc,
|
||||
FILE *out);
|
||||
|
||||
execution_path
|
||||
new_execution_path ();
|
||||
|
||||
diagnostic
|
||||
begin_diagnostic (enum diagnostic_level level);
|
||||
|
||||
|
||||
diagnostic_manager *m_inner;
|
||||
bool m_owned;
|
||||
};
|
||||
|
||||
// Implementation
|
||||
|
||||
// class execution_path
|
||||
|
||||
inline diagnostic_event_id
|
||||
execution_path::add_event (physical_location physical_loc,
|
||||
logical_location logical_loc,
|
||||
unsigned stack_depth,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
diagnostic_event_id result = add_event_va (physical_loc,
|
||||
logical_loc,
|
||||
stack_depth,
|
||||
fmt, &args);
|
||||
va_end (args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline diagnostic_event_id
|
||||
execution_path::add_event_va (physical_location physical_loc,
|
||||
logical_location logical_loc,
|
||||
unsigned stack_depth,
|
||||
const char *fmt,
|
||||
va_list *args)
|
||||
{
|
||||
return diagnostic_execution_path_add_event_va (m_inner,
|
||||
physical_loc.m_inner,
|
||||
logical_loc.m_inner,
|
||||
stack_depth,
|
||||
fmt,
|
||||
args);
|
||||
}
|
||||
|
||||
// class group
|
||||
|
||||
inline
|
||||
group::group (manager &mgr)
|
||||
: m_mgr (mgr)
|
||||
{
|
||||
diagnostic_manager_begin_group (m_mgr.m_inner);
|
||||
}
|
||||
|
||||
inline
|
||||
group::~group ()
|
||||
{
|
||||
diagnostic_manager_end_group (m_mgr.m_inner);
|
||||
}
|
||||
|
||||
// class diagnostic
|
||||
|
||||
inline void
|
||||
diagnostic::set_cwe (unsigned cwe_id)
|
||||
{
|
||||
diagnostic_set_cwe (m_inner, cwe_id);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::add_rule (const char *title, const char *url)
|
||||
{
|
||||
diagnostic_add_rule (m_inner, title, url);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::set_location (physical_location loc)
|
||||
{
|
||||
diagnostic_set_location (m_inner, loc.m_inner);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::add_location_with_label (physical_location loc,
|
||||
const char *text)
|
||||
{
|
||||
diagnostic_add_location_with_label (m_inner, loc.m_inner, text);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::set_logical_location (logical_location loc)
|
||||
{
|
||||
diagnostic_set_logical_location (m_inner, loc.m_inner);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::add_fix_it_hint_insert_before (physical_location loc,
|
||||
const char *addition)
|
||||
{
|
||||
diagnostic_add_fix_it_hint_insert_before (m_inner,
|
||||
loc.m_inner,
|
||||
addition);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::add_fix_it_hint_insert_after (physical_location loc,
|
||||
const char *addition)
|
||||
{
|
||||
diagnostic_add_fix_it_hint_insert_after (m_inner,
|
||||
loc.m_inner,
|
||||
addition);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::add_fix_it_hint_replace (physical_location loc,
|
||||
const char *replacement)
|
||||
{
|
||||
diagnostic_add_fix_it_hint_replace (m_inner,
|
||||
loc.m_inner,
|
||||
replacement);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::add_fix_it_hint_delete (physical_location loc)
|
||||
{
|
||||
diagnostic_add_fix_it_hint_delete (m_inner,
|
||||
loc.m_inner);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::take_execution_path (execution_path path)
|
||||
{
|
||||
diagnostic_take_execution_path (m_inner,
|
||||
path.m_inner);
|
||||
path.m_owned = false;
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::finish (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
diagnostic_finish_va (m_inner, fmt, &ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic::finish_va (const char *fmt, va_list *args)
|
||||
{
|
||||
diagnostic_finish_va (m_inner, fmt, args);
|
||||
}
|
||||
|
||||
// class manager
|
||||
|
||||
inline file
|
||||
manager::new_file (const char *name,
|
||||
const char *sarif_source_language)
|
||||
{
|
||||
return file
|
||||
(diagnostic_manager_new_file (m_inner, name, sarif_source_language));
|
||||
}
|
||||
|
||||
inline physical_location
|
||||
manager::new_location_from_file_and_line (file f,
|
||||
diagnostic_line_num_t line_num)
|
||||
{
|
||||
return physical_location
|
||||
(diagnostic_manager_new_location_from_file_and_line (m_inner,
|
||||
f.m_inner,
|
||||
line_num));
|
||||
}
|
||||
|
||||
inline physical_location
|
||||
manager::new_location_from_file_line_column (file f,
|
||||
line_num_t line_num,
|
||||
column_num_t column_num)
|
||||
{
|
||||
return physical_location
|
||||
(diagnostic_manager_new_location_from_file_line_column (m_inner,
|
||||
f.m_inner,
|
||||
line_num,
|
||||
column_num));
|
||||
}
|
||||
|
||||
inline physical_location
|
||||
manager::new_location_from_range (physical_location loc_caret,
|
||||
physical_location loc_start,
|
||||
physical_location loc_end)
|
||||
{
|
||||
return physical_location
|
||||
(diagnostic_manager_new_location_from_range (m_inner,
|
||||
loc_caret.m_inner,
|
||||
loc_start.m_inner,
|
||||
loc_end.m_inner));
|
||||
}
|
||||
|
||||
inline void
|
||||
manager::debug_dump (physical_location loc,
|
||||
FILE *out)
|
||||
{
|
||||
diagnostic_manager_debug_dump_location (m_inner,
|
||||
loc.m_inner,
|
||||
out);
|
||||
}
|
||||
inline logical_location
|
||||
manager::new_logical_location (enum diagnostic_logical_location_kind_t kind,
|
||||
logical_location parent,
|
||||
const char *short_name,
|
||||
const char *fully_qualified_name,
|
||||
const char *decorated_name)
|
||||
{
|
||||
return logical_location
|
||||
(diagnostic_manager_new_logical_location (m_inner,
|
||||
kind,
|
||||
parent.m_inner,
|
||||
short_name,
|
||||
fully_qualified_name,
|
||||
decorated_name));
|
||||
}
|
||||
|
||||
inline void
|
||||
manager::debug_dump (logical_location loc,
|
||||
FILE *out)
|
||||
{
|
||||
diagnostic_manager_debug_dump_logical_location (m_inner,
|
||||
loc.m_inner,
|
||||
out);
|
||||
}
|
||||
|
||||
inline execution_path
|
||||
manager::new_execution_path ()
|
||||
{
|
||||
return execution_path (diagnostic_manager_new_execution_path (m_inner));
|
||||
}
|
||||
|
||||
inline diagnostic
|
||||
manager::begin_diagnostic (enum diagnostic_level level)
|
||||
{
|
||||
return diagnostic (diagnostic_begin (m_inner, level));
|
||||
}
|
||||
|
||||
} // namespace libdiagnostics
|
||||
|
||||
#endif // #ifndef LIBDIAGNOSTICSPP_H
|
1683
gcc/libdiagnostics.cc
Normal file
1683
gcc/libdiagnostics.cc
Normal file
File diff suppressed because it is too large
Load Diff
691
gcc/libdiagnostics.h
Normal file
691
gcc/libdiagnostics.h
Normal file
@ -0,0 +1,691 @@
|
||||
/* A pure C API for emitting diagnostics.
|
||||
Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef LIBDIAGNOSTICS_H
|
||||
#define LIBDIAGNOSTICS_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**********************************************************************
|
||||
Compatibility macros.
|
||||
**********************************************************************/
|
||||
|
||||
/* This macro simplifies testing whether we are using gcc, and if it
|
||||
is of a particular minimum version. (Both major & minor numbers are
|
||||
significant.) This macro will evaluate to 0 if we are not using
|
||||
gcc at all. */
|
||||
#define LIBDIAGNOSTICS_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
|
||||
|
||||
/**********************************************************************
|
||||
Macros for attributes.
|
||||
**********************************************************************/
|
||||
|
||||
# if (LIBDIAGNOSTICS_GCC_VERSION >= 3003)
|
||||
# define LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL(ARG_NUM) __attribute__ ((__nonnull__ (ARG_NUM)))
|
||||
# else
|
||||
# define LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL(ARG_NUM)
|
||||
# endif /* GNUC >= 3.3 */
|
||||
|
||||
#define LIBDIAGNOSTICS_PARAM_CAN_BE_NULL(ARG_NUM)
|
||||
/* empty; for the human reader */
|
||||
|
||||
#define LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(FMT_ARG_NUM, ARGS_ARG_NUM) \
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (FMT_ARG_NUM)
|
||||
/* In theory we'd also add
|
||||
__attribute__ ((__format__ (__gcc_diag__, FMT_ARG_NUM, ARGS_ARG_NUM)))
|
||||
if LIBDIAGNOSTICS_GCC_VERSION >= 4001
|
||||
However, doing so leads to warnings from -Wformat-diag, which is part
|
||||
of -Wall but undocumented, and much fussier than I'd want to inflict
|
||||
on users of libdiagnostics. */
|
||||
|
||||
/**********************************************************************
|
||||
Data structures and types.
|
||||
All structs within the API are opaque.
|
||||
**********************************************************************/
|
||||
|
||||
/* An opaque bundle of state for a client of the library.
|
||||
Has zero of more "sinks" to which diagnostics are emitted.
|
||||
Responsibilities:
|
||||
- location-management
|
||||
- caching of source file content
|
||||
- patch generation. */
|
||||
typedef struct diagnostic_manager diagnostic_manager;
|
||||
|
||||
/* Types relating to diagnostic output sinks. */
|
||||
|
||||
typedef struct diagnostic_text_sink diagnostic_text_sink;
|
||||
|
||||
/* An enum for determining if we should colorize a text output sink. */
|
||||
enum diagnostic_colorize
|
||||
{
|
||||
DIAGNOSTIC_COLORIZE_IF_TTY,
|
||||
DIAGNOSTIC_COLORIZE_NO,
|
||||
DIAGNOSTIC_COLORIZE_YES
|
||||
};
|
||||
|
||||
/* An enum for choosing the SARIF version for a SARIF output sink. */
|
||||
|
||||
enum diagnostic_sarif_version
|
||||
{
|
||||
DIAGNOSTIC_SARIF_VERSION_2_1_0,
|
||||
DIAGNOSTIC_SARIF_VERSION_2_2_PRERELEASE
|
||||
};
|
||||
|
||||
/* Types relating to "physical" source locations i.e. locations within
|
||||
specific files expressed via line/column. */
|
||||
|
||||
/* Opaque type describing a particular input file. */
|
||||
typedef struct diagnostic_file diagnostic_file;
|
||||
|
||||
/* Opaque type representing a key into a database of source locations within
|
||||
a diagnostic_manager. Locations are created by various API calls into
|
||||
the diagnostic_manager expressing source code points and ranges. They
|
||||
persist until the diagnostic_manager is released, which cleans them
|
||||
up.
|
||||
|
||||
NULL means "UNKNOWN", and can be returned by the manager as a
|
||||
fallback when a problem occurs (e.g. too many locations).
|
||||
|
||||
A diagnostic_location can be a single point within the source code,
|
||||
such as here (at the the '"' at the start of the string literal):
|
||||
|
||||
| int i = "foo";
|
||||
| ^
|
||||
|
||||
or be a range with a start and finish, and a "caret" location.
|
||||
|
||||
| a = (foo && bar)
|
||||
| ~~~~~^~~~~~~
|
||||
|
||||
where the caret here is at the first "&", and the start and finish
|
||||
are at the parentheses. */
|
||||
|
||||
typedef struct diagnostic_physical_location diagnostic_physical_location;
|
||||
|
||||
/* Types for storing line and column information in text files.
|
||||
|
||||
Both libdiagnostics and emacs number source *lines* starting at 1, but
|
||||
they have differing conventions for *columns*.
|
||||
|
||||
libdiagnostics uses a 1-based convention for source columns,
|
||||
whereas Emacs's M-x column-number-mode uses a 0-based convention.
|
||||
|
||||
For example, an error in the initial, left-hand
|
||||
column of source line 3 is reported by libdiagnostics as:
|
||||
|
||||
some-file.c:3:1: error: ...etc...
|
||||
|
||||
On navigating to the location of that error in Emacs
|
||||
(e.g. via "next-error"),
|
||||
the locus is reported in the Mode Line
|
||||
(assuming M-x column-number-mode) as:
|
||||
|
||||
some-file.c 10% (3, 0)
|
||||
|
||||
i.e. "3:1:" in libdiagnostics corresponds to "(3, 0)" in Emacs. */
|
||||
|
||||
typedef unsigned int diagnostic_line_num_t;
|
||||
typedef unsigned int diagnostic_column_num_t;
|
||||
|
||||
/* An opaque type describing a "logical" source location
|
||||
e.g. "within function 'foo'". */
|
||||
|
||||
typedef struct diagnostic_logical_location diagnostic_logical_location;
|
||||
|
||||
/* An enum for discriminating between different kinds of logical location
|
||||
for a diagnostic.
|
||||
|
||||
Roughly corresponds to logicalLocation's "kind" property in SARIF v2.1.0
|
||||
(section 3.33.7). */
|
||||
|
||||
enum diagnostic_logical_location_kind_t
|
||||
{
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE
|
||||
};
|
||||
|
||||
/* A "diagnostic" is an opaque bundle of state for a particular
|
||||
diagnostic that is being constructed in memory.
|
||||
|
||||
A diagnostic has a primary location and zero or more secondary
|
||||
locations. For example:
|
||||
|
||||
| a = (foo && bar)
|
||||
| ~~~~~^~~~~~~
|
||||
|
||||
This diagnostic has a single diagnostic_location, with the caret
|
||||
at the first "&", and the start/finish at the parentheses.
|
||||
|
||||
Contrast with:
|
||||
|
||||
| a = (foo && bar)
|
||||
| ~~~ ^~ ~~~
|
||||
|
||||
This diagnostic has three locations
|
||||
- The primary location (at "&&") has its caret and start location at
|
||||
the first "&" and end at the second "&.
|
||||
- The secondary location for "foo" has its start and finish at the "f"
|
||||
and "o" of "foo"; the caret is not flagged for display, but is perhaps at
|
||||
the "f" of "foo".
|
||||
- Similarly, the other secondary location (for "bar") has its start and
|
||||
finish at the "b" and "r" of "bar"; the caret is not flagged for
|
||||
display, but is perhaps at the"b" of "bar". */
|
||||
typedef struct diagnostic diagnostic;
|
||||
|
||||
enum diagnostic_level
|
||||
{
|
||||
DIAGNOSTIC_LEVEL_ERROR,
|
||||
DIAGNOSTIC_LEVEL_WARNING,
|
||||
DIAGNOSTIC_LEVEL_NOTE,
|
||||
|
||||
/* A problem where the input is valid, but the tool isn't
|
||||
able to handle it. */
|
||||
DIAGNOSTIC_LEVEL_SORRY
|
||||
};
|
||||
|
||||
/* Types for working with execution paths. */
|
||||
typedef struct diagnostic_execution_path diagnostic_execution_path;
|
||||
typedef int diagnostic_event_id;
|
||||
|
||||
/**********************************************************************
|
||||
API entrypoints.
|
||||
**********************************************************************/
|
||||
|
||||
/* Create a new diagnostic_manager.
|
||||
The client needs to call diagnostic_release_manager on it at some
|
||||
point.
|
||||
Note that no output sinks are created by default. */
|
||||
|
||||
extern diagnostic_manager *
|
||||
diagnostic_manager_new (void);
|
||||
|
||||
/* Release a diagnostic_manager.
|
||||
This will flush output to all of the output sinks, and clean up. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_release (diagnostic_manager *)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Optional metadata about the manager. */
|
||||
|
||||
/* Set a string suitable for use as the value of the SARIF "name" property
|
||||
(SARIF v2.1.0 section 3.19.8). */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr,
|
||||
const char *value)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Set a string suitable for use as the value of the SARIF "fullName" property
|
||||
(SARIF v2.1.0 section 3.19.9). */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr,
|
||||
const char *value)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Set a string suitable for use as the value of the SARIF "version" property
|
||||
(SARIF v2.1.0 section 3.19.13). */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr,
|
||||
const char *value)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Set a string suitable for use as the value of the SARIF "informationUri"
|
||||
property (SARIF v2.1.0 section 3.19.17). */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr,
|
||||
const char *value)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Destinations for diagnostics. */
|
||||
|
||||
/* Add a new output sink to DIAG_MGR, which writes GCC-style diagnostics
|
||||
to DST_STREAM.
|
||||
Return a borrowed pointer to the sink, which is cleaned up when DIAG_MGR
|
||||
is released.
|
||||
DST_STREAM is borrowed, and must outlive DIAG_MGR.
|
||||
The output for each diagnostic is written and flushed as each
|
||||
diagnostic is finished. */
|
||||
|
||||
extern diagnostic_text_sink *
|
||||
diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr,
|
||||
FILE *dst_stream,
|
||||
enum diagnostic_colorize colorize)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Functions to manipulate text sinks. */
|
||||
|
||||
/* Enable/disable printing of source text in the text sink.
|
||||
Default: enabled. */
|
||||
|
||||
extern void
|
||||
diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink,
|
||||
int value)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Update colorization of text sink. */
|
||||
|
||||
extern void
|
||||
diagnostic_text_sink_set_colorize (diagnostic_text_sink *text_sink,
|
||||
enum diagnostic_colorize colorize)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Enable/disable colorization of the characters of source text
|
||||
that are underlined.
|
||||
This should be true for clients that generate range information
|
||||
(so that the ranges of code are colorized),
|
||||
and false for clients that merely specify points within the
|
||||
source code (to avoid e.g. colorizing just the first character in
|
||||
a token, which would look strange).
|
||||
Default: enabled. */
|
||||
|
||||
extern void
|
||||
diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink,
|
||||
int value)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Add a new output sink to DIAG_MGR, which writes SARIF of the given
|
||||
version to DST_STREAM.
|
||||
|
||||
The output is not written until DIAG_MGR is released.
|
||||
|
||||
DST_STREAM is borrowed, and must outlive DIAG_MGR.
|
||||
|
||||
For the result to be a valid SARIF file according to the schema,
|
||||
DIAG_MGR must have had diagnostic_manager_set_tool_name called on it. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr,
|
||||
FILE *dst_stream,
|
||||
const diagnostic_file *main_input_file,
|
||||
enum diagnostic_sarif_version version)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
/* Write a patch to DST_STREAM consisting of all fix-it hints
|
||||
on all diagnostics that have been finished on DIAG_MGR. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_write_patch (diagnostic_manager *diag_mgr,
|
||||
FILE *dst_stream)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Location management. */
|
||||
|
||||
/* Create a new diagnostic_file * for file NAME.
|
||||
|
||||
Repeated calls with matching NAMEs will return the
|
||||
same object.
|
||||
|
||||
If SARIF_SOURCE_LANGUAGE is non-NULL, it specifies a "sourceLanguage"
|
||||
value for the file when use when writing SARIF.
|
||||
See SARIF v2.1.0 Appendix J for suggested values for various
|
||||
programmming languages. */
|
||||
|
||||
extern const diagnostic_file *
|
||||
diagnostic_manager_new_file (diagnostic_manager *diag_mgr,
|
||||
const char *name,
|
||||
const char *sarif_source_language)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3);
|
||||
|
||||
/* Write a representation of FILE to OUT, for debugging. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_debug_dump_file (diagnostic_manager *diag_mgr,
|
||||
const diagnostic_file *file,
|
||||
FILE *out)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
/* Attempt to create a diagnostic_location representing
|
||||
FILENAME:LINE_NUM, with no column information
|
||||
(thus "the whole line"). */
|
||||
|
||||
extern const diagnostic_physical_location *
|
||||
diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr,
|
||||
const diagnostic_file *file,
|
||||
diagnostic_line_num_t line_num)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Attempt to create a diagnostic_physical_location representing
|
||||
FILENAME:LINE_NUM:COLUMN_NUM. */
|
||||
|
||||
extern const diagnostic_physical_location *
|
||||
diagnostic_manager_new_location_from_file_line_column (diagnostic_manager *diag_mgr,
|
||||
const diagnostic_file *file,
|
||||
diagnostic_line_num_t line_num,
|
||||
diagnostic_column_num_t column_num)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Attempt to create a diagnostic_physical_location representing a
|
||||
range within a source file, with a highlighted "caret" location.
|
||||
|
||||
All must be within the same file, but they can be on different lines.
|
||||
|
||||
For example, consider the location of the binary expression below:
|
||||
|
||||
...|__________1111111112222222
|
||||
...|12345678901234567890123456
|
||||
...|
|
||||
521|int sum (int foo, int bar)
|
||||
522|{
|
||||
523| return foo + bar;
|
||||
...| ~~~~^~~~~
|
||||
524|}
|
||||
|
||||
The location's caret is at the "+", line 523 column 15, but starts
|
||||
earlier, at the "f" of "foo" at column 11. The finish is at the "r"
|
||||
of "bar" at column 19. */
|
||||
|
||||
extern const diagnostic_physical_location *
|
||||
diagnostic_manager_new_location_from_range (diagnostic_manager *diag_mgr,
|
||||
const diagnostic_physical_location *loc_caret,
|
||||
const diagnostic_physical_location *loc_start,
|
||||
const diagnostic_physical_location *loc_end)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (4);
|
||||
|
||||
/* Write a representation of LOC to OUT, for debugging. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr,
|
||||
const diagnostic_physical_location *loc,
|
||||
FILE *out)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
/* A bundle of state describing a logical location in the user's source,
|
||||
such as "in function 'foo'".
|
||||
|
||||
SHORT_NAME can be NULL, or else a string suitable for use by
|
||||
the SARIF logicalLocation "name" property (SARIF v2.1.0 section 3.33.4).
|
||||
|
||||
FULLY_QUALIFIED_NAME can be NULL or else a string suitable for use by
|
||||
the SARIF logicalLocation "fullyQualifiedName" property
|
||||
(SARIF v2.1.0 section 3.33.5).
|
||||
|
||||
DECORATED_NAME can be NULL or else a string suitable for use by
|
||||
the SARIF logicalLocation "decoratedName" property
|
||||
(SARIF v2.1.0 section 3.33.6). */
|
||||
|
||||
extern const diagnostic_logical_location *
|
||||
diagnostic_manager_new_logical_location (diagnostic_manager *diag_mgr,
|
||||
enum diagnostic_logical_location_kind_t kind,
|
||||
const diagnostic_logical_location *parent,
|
||||
const char *short_name,
|
||||
const char *fully_qualified_name,
|
||||
const char *decorated_name)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (4)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (5)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (6);
|
||||
|
||||
/* Write a representation of LOC to OUT, for debugging. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr,
|
||||
const diagnostic_logical_location *loc,
|
||||
FILE *out)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
/* Diagnostic groups. */
|
||||
|
||||
/* Begin a diagnostic group. All diagnostics emitted within
|
||||
DIAG_MGR after the first one will be treated as notes about
|
||||
the initial diagnostic. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_begin_group (diagnostic_manager *diag_mgr)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Finish a diagnostic group. */
|
||||
|
||||
extern void
|
||||
diagnostic_manager_end_group (diagnostic_manager *diag_mgr)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Step-by-step creation of a diagnostic. */
|
||||
|
||||
extern diagnostic *
|
||||
diagnostic_begin (diagnostic_manager *diag_mgr,
|
||||
enum diagnostic_level level)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Associate this diagnostic with the given ID within
|
||||
the Common Weakness Enumeration. */
|
||||
|
||||
extern void
|
||||
diagnostic_set_cwe (diagnostic *diag,
|
||||
unsigned cwe_id)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Associate this diagnostic with a particular rule that has been violated
|
||||
(such as in a coding standard, or within a specification).
|
||||
The rule must have at least one of a title and a URL, but these
|
||||
can be NULL.
|
||||
A diagnostic can be associated with zero or more rules. */
|
||||
|
||||
extern void
|
||||
diagnostic_add_rule (diagnostic *diag,
|
||||
const char *title,
|
||||
const char *url)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3);
|
||||
|
||||
/* Set the primary location of DIAG. */
|
||||
|
||||
extern void
|
||||
diagnostic_set_location (diagnostic *diag,
|
||||
const diagnostic_physical_location * loc)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2);
|
||||
|
||||
/* Set the primary location of DIAG, with a label. */
|
||||
|
||||
extern void
|
||||
diagnostic_set_location_with_label (diagnostic *diag,
|
||||
const diagnostic_physical_location *loc,
|
||||
const char *fmt, ...)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
/* Add a secondary location to DIAG. */
|
||||
|
||||
extern void
|
||||
diagnostic_add_location (diagnostic *diag,
|
||||
const diagnostic_physical_location * loc)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Add a secondary location to DIAG, with a label. */
|
||||
|
||||
extern void
|
||||
diagnostic_add_location_with_label (diagnostic *diag,
|
||||
const diagnostic_physical_location *loc,
|
||||
const char *text)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
/* Set the logical location of DIAG. */
|
||||
|
||||
extern void
|
||||
diagnostic_set_logical_location (diagnostic *diag,
|
||||
const diagnostic_logical_location *logical_loc)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2);
|
||||
|
||||
/* Fix-it hints. */
|
||||
|
||||
extern void
|
||||
diagnostic_add_fix_it_hint_insert_before (diagnostic *diag,
|
||||
const diagnostic_physical_location *loc,
|
||||
const char *addition)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
extern void
|
||||
diagnostic_add_fix_it_hint_insert_after (diagnostic *diag,
|
||||
const diagnostic_physical_location *loc,
|
||||
const char *addition)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
extern void
|
||||
diagnostic_add_fix_it_hint_replace (diagnostic *diag,
|
||||
const diagnostic_physical_location *loc,
|
||||
const char *replacement)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3);
|
||||
|
||||
extern void
|
||||
diagnostic_add_fix_it_hint_delete (diagnostic *diag,
|
||||
const diagnostic_physical_location *loc)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2);
|
||||
|
||||
/* Create and borrow a pointer to an execution path for DIAG.
|
||||
The path is automatically cleaned up when DIAG is finished. */
|
||||
|
||||
extern diagnostic_execution_path *
|
||||
diagnostic_add_execution_path (diagnostic *diag)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Create a new execution path.
|
||||
This is owned by the called and must have either
|
||||
diagnostic_take_execution_path or diagnostic_execution_path_release
|
||||
called on it. */
|
||||
|
||||
extern diagnostic_execution_path *
|
||||
diagnostic_manager_new_execution_path (diagnostic_manager *manager)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1);
|
||||
|
||||
/* Set DIAG to use PATH as its execution path, taking ownership of PATH. */
|
||||
|
||||
extern void
|
||||
diagnostic_take_execution_path (diagnostic *diag,
|
||||
diagnostic_execution_path *path)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* Release ownership of PATH, which must not have been taken
|
||||
by a diagnostic. */
|
||||
|
||||
extern void
|
||||
diagnostic_execution_path_release (diagnostic_execution_path *path)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (1);
|
||||
|
||||
/* Append an event to the end of PATH. */
|
||||
|
||||
extern diagnostic_event_id
|
||||
diagnostic_execution_path_add_event (diagnostic_execution_path *path,
|
||||
const diagnostic_physical_location *physical_loc,
|
||||
const diagnostic_logical_location *logical_loc,
|
||||
unsigned stack_depth,
|
||||
const char *fmt, ...)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (5)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 6);
|
||||
|
||||
/* Append an event to the end of PATH. */
|
||||
|
||||
extern diagnostic_event_id
|
||||
diagnostic_execution_path_add_event_va (diagnostic_execution_path *path,
|
||||
const diagnostic_physical_location *physical_loc,
|
||||
const diagnostic_logical_location *logical_loc,
|
||||
unsigned stack_depth,
|
||||
const char *fmt,
|
||||
va_list *args)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (5)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (5, 0);
|
||||
|
||||
/* Emit DIAG to all sinks of its manager, and release DIAG.
|
||||
Use FMT for the message.
|
||||
Note that this uses gcc's pretty-print format, which is *not* printf.
|
||||
TODO: who is responsible for putting FMT through gettext? */
|
||||
|
||||
extern void
|
||||
diagnostic_finish (diagnostic *diag, const char *fmt, ...)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3);
|
||||
|
||||
/* As diagnostic_finish, but with a va_list. */
|
||||
|
||||
extern void
|
||||
diagnostic_finish_va (diagnostic *diag, const char *fmt, va_list *args)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0);
|
||||
|
||||
/* DEFERRED:
|
||||
- thread-safety
|
||||
- plural forms
|
||||
- enum about what a "column number" means (bytes, unichars, etc)
|
||||
- locations within binary files
|
||||
- options and URLs for warnings
|
||||
- enable/disable of warnings by kind
|
||||
- plugin metadata. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* LIBDIAGNOSTICS_H */
|
72
gcc/libdiagnostics.map
Normal file
72
gcc/libdiagnostics.map
Normal file
@ -0,0 +1,72 @@
|
||||
# Linker script for libdiagnostics.so
|
||||
# Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
# Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
#
|
||||
# This file is part of GCC.
|
||||
#
|
||||
# GCC is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# GCC is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GCC; see the file COPYING3. If not see
|
||||
# <http://www.gnu.org/licenses/>. */
|
||||
|
||||
# The initial release of the library.
|
||||
LIBDIAGNOSTICS_ABI_0
|
||||
{
|
||||
global:
|
||||
# Keep this list in order of decls in header file.
|
||||
diagnostic_manager_new;
|
||||
diagnostic_manager_release;
|
||||
diagnostic_manager_set_tool_name;
|
||||
diagnostic_manager_set_full_name;
|
||||
diagnostic_manager_set_version_string;
|
||||
diagnostic_manager_set_version_url;
|
||||
diagnostic_manager_add_text_sink;
|
||||
diagnostic_text_sink_set_source_printing_enabled;
|
||||
diagnostic_text_sink_set_colorize;
|
||||
diagnostic_text_sink_set_labelled_source_colorization_enabled;
|
||||
diagnostic_manager_add_sarif_sink;
|
||||
diagnostic_manager_write_patch;
|
||||
diagnostic_manager_new_file;
|
||||
diagnostic_manager_debug_dump_file;
|
||||
diagnostic_manager_new_location_from_file_and_line;
|
||||
diagnostic_manager_new_location_from_file_line_column;
|
||||
diagnostic_manager_new_location_from_range;
|
||||
diagnostic_manager_debug_dump_location;
|
||||
diagnostic_manager_new_logical_location;
|
||||
diagnostic_manager_debug_dump_logical_location;
|
||||
diagnostic_manager_begin_group;
|
||||
diagnostic_manager_end_group;
|
||||
diagnostic_begin;
|
||||
diagnostic_set_cwe;
|
||||
diagnostic_add_rule;
|
||||
diagnostic_set_location;
|
||||
diagnostic_set_location_with_label;
|
||||
diagnostic_add_location;
|
||||
diagnostic_add_location_with_label;
|
||||
diagnostic_set_logical_location;
|
||||
diagnostic_add_fix_it_hint_insert_before;
|
||||
diagnostic_add_fix_it_hint_insert_after;
|
||||
diagnostic_add_fix_it_hint_replace;
|
||||
diagnostic_add_fix_it_hint_delete;
|
||||
|
||||
diagnostic_add_execution_path;
|
||||
diagnostic_manager_new_execution_path;
|
||||
diagnostic_take_execution_path;
|
||||
diagnostic_execution_path_release;
|
||||
diagnostic_execution_path_add_event;
|
||||
diagnostic_execution_path_add_event_va;
|
||||
|
||||
diagnostic_finish;
|
||||
diagnostic_finish_va;
|
||||
|
||||
local: *;
|
||||
};
|
296
gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp
Normal file
296
gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp
Normal file
@ -0,0 +1,296 @@
|
||||
# Test code for libdiagnostics.so
|
||||
#
|
||||
# We will compile each of libdiagnostics.dg/test-*.{c,cc} into an executable
|
||||
# dynamically linked against libdiagnostics.so, and then run each
|
||||
# such executable.
|
||||
#
|
||||
# These executables call into the libdiagnostics.so API to emit diagnostics,
|
||||
# sometimes in text form, and other times in SARIF form.
|
||||
|
||||
# Kludge alert:
|
||||
# We need g++_init so that it can find the stdlib include path.
|
||||
#
|
||||
# g++_init (in lib/g++.exp) uses g++_maybe_build_wrapper,
|
||||
# which normally comes from the definition of
|
||||
# ${tool}_maybe_build_wrapper within lib/wrapper.exp.
|
||||
#
|
||||
# However, for us, ${tool} is "libdiagnostics".
|
||||
# Hence we load wrapper.exp with tool == "g++", so that
|
||||
# g++_maybe_build_wrapper is defined.
|
||||
set tool g++
|
||||
load_lib wrapper.exp
|
||||
set tool libdiagnostics
|
||||
|
||||
load_lib dg.exp
|
||||
load_lib prune.exp
|
||||
load_lib target-supports.exp
|
||||
load_lib gcc-defs.exp
|
||||
load_lib timeout.exp
|
||||
load_lib target-libpath.exp
|
||||
load_lib gcc.exp
|
||||
load_lib g++.exp
|
||||
load_lib dejagnu.exp
|
||||
load_lib target-supports-dg.exp
|
||||
load_lib valgrind.exp
|
||||
load_lib scansarif.exp
|
||||
load_lib dg-test-cleanup.exp
|
||||
|
||||
# The default do-what keyword.
|
||||
set dg-do-what-default compile
|
||||
|
||||
# Adapted from jit.exp.
|
||||
#
|
||||
# Execute the executable file.
|
||||
# Returns:
|
||||
# A "" (empty) string if everything worked, or an error message
|
||||
# if there was a problem.
|
||||
#
|
||||
proc fixed_host_execute {args} {
|
||||
global env
|
||||
global text
|
||||
global spawn_id
|
||||
|
||||
verbose "fixed_host_execute: $args"
|
||||
|
||||
set timeoutmsg "Timed out: Never got started, "
|
||||
set timeout 100
|
||||
set file all
|
||||
set timetol 0
|
||||
set arguments ""
|
||||
|
||||
if { [llength $args] == 0} {
|
||||
set executable $args
|
||||
} else {
|
||||
set executable [lindex $args 0]
|
||||
set params [lindex $args 1]
|
||||
}
|
||||
|
||||
verbose "The executable is $executable" 2
|
||||
if {![file exists ${executable}]} {
|
||||
perror "The executable, \"$executable\" is missing" 0
|
||||
return "No source file found"
|
||||
} elseif {![file executable ${executable}]} {
|
||||
perror "The executable, \"$executable\" is not usable" 0
|
||||
return "Bad executable found"
|
||||
}
|
||||
|
||||
verbose "params: $params" 2
|
||||
|
||||
# spawn the executable and look for the DejaGnu output messages from the
|
||||
# test case.
|
||||
# spawn -noecho -open [open "|./${executable}" "r"]
|
||||
|
||||
# Run under valgrind if RUN_UNDER_VALGRIND is present in the environment.
|
||||
# Note that it's best to configure gcc with --enable-valgrind-annotations
|
||||
# when testing under valgrind.
|
||||
set run_under_valgrind [info exists env(RUN_UNDER_VALGRIND)]
|
||||
if $run_under_valgrind {
|
||||
set valgrind_logfile "${executable}.valgrind.txt"
|
||||
set valgrind_params {"valgrind"}
|
||||
lappend valgrind_params "--leak-check=full"
|
||||
lappend valgrind_params "--log-file=${valgrind_logfile}"
|
||||
} else {
|
||||
set valgrind_params {}
|
||||
}
|
||||
verbose "valgrind_params: $valgrind_params" 2
|
||||
|
||||
set args ${valgrind_params}
|
||||
lappend args "./${executable}"
|
||||
set args [concat $args ${params}]
|
||||
verbose "args: $args" 2
|
||||
|
||||
set status [catch "exec -keepnewline $args" exe_output]
|
||||
verbose "Test program returned $exe_output" 2
|
||||
|
||||
if $run_under_valgrind {
|
||||
upvar 2 name name
|
||||
parse_valgrind_logfile $name $valgrind_logfile fail
|
||||
}
|
||||
|
||||
# We don't do prune_gcc_output here, as we want
|
||||
# to check *exactly* what we get from libdiagnostics
|
||||
|
||||
return $exe_output
|
||||
}
|
||||
|
||||
# (end of code from dejagnu.exp)
|
||||
|
||||
# GCC_UNDER_TEST is needed by gcc_target_compile
|
||||
global GCC_UNDER_TEST
|
||||
if ![info exists GCC_UNDER_TEST] {
|
||||
set GCC_UNDER_TEST "[find_gcc]"
|
||||
}
|
||||
|
||||
g++_init
|
||||
|
||||
# Initialize dg.
|
||||
dg-init
|
||||
|
||||
# Gather a list of all tests.
|
||||
|
||||
# C and C++ tests within the testsuite: gcc/testsuite/libdiagnostics.dg/test-*.{c,c++}
|
||||
set c_tests [find $srcdir/$subdir test-*.c]
|
||||
set cxx_tests [find $srcdir/$subdir test-*.cc]
|
||||
set tests [concat $c_tests $cxx_tests]
|
||||
|
||||
verbose "tests: $tests"
|
||||
|
||||
# Expand "SRCDIR" within ARG to the location of the top-level
|
||||
# src directory
|
||||
|
||||
proc diagnostics-expand-vars {arg} {
|
||||
verbose "diagnostics-expand-vars: $arg"
|
||||
global srcdir
|
||||
verbose " srcdir: $srcdir"
|
||||
# "srcdir" is that of the gcc/testsuite directory, so
|
||||
# we need to go up two levels.
|
||||
set arg [string map [list "SRCDIR" $srcdir/../..] $arg]
|
||||
verbose " new arg: $arg"
|
||||
return $arg
|
||||
}
|
||||
|
||||
# Parameters used when invoking the executables built from the test cases.
|
||||
|
||||
global diagnostics-exe-params
|
||||
set diagnostics-exe-params {}
|
||||
|
||||
# Set "diagnostics-exe-params", expanding "SRCDIR" in each arg to the location of
|
||||
# the top-level srcdir.
|
||||
|
||||
proc dg-diagnostics-set-exe-params { args } {
|
||||
verbose "dg-diagnostics-set-exe-params: $args"
|
||||
|
||||
global diagnostics-exe-params
|
||||
set diagnostics-exe-params {}
|
||||
# Skip initial arg (line number)
|
||||
foreach arg [lrange $args 1 [llength $args] ] {
|
||||
lappend diagnostics-exe-params [diagnostics-expand-vars $arg]
|
||||
}
|
||||
}
|
||||
|
||||
proc libdiagnostics-dg-test { prog do_what extra_tool_flags } {
|
||||
verbose "within libdiagnostics-dg-test..."
|
||||
verbose " prog: $prog"
|
||||
verbose " do_what: $do_what"
|
||||
verbose " extra_tool_flags: $extra_tool_flags"
|
||||
|
||||
global dg-do-what-default
|
||||
set dg-do-what [list ${dg-do-what-default} "" P]
|
||||
|
||||
# If we're not supposed to try this test on this target, we're done.
|
||||
if { [lindex ${dg-do-what} 1] == "N" } {
|
||||
unsupported "$name"
|
||||
verbose "$name not supported on this target, skipping it" 3
|
||||
return
|
||||
}
|
||||
|
||||
# Determine what to name the built executable.
|
||||
#
|
||||
# We simply append .exe to the filename, e.g.
|
||||
# "test-foo.c.exe"
|
||||
# since some testcases exist in both
|
||||
# "test-foo.c" and
|
||||
# "test-foo.cc"
|
||||
# variants, and we don't want them to clobber each other's
|
||||
# executables.
|
||||
#
|
||||
# This also ensures that the source name makes it into the
|
||||
# pass/fail output, so that we can distinguish e.g. which test-foo
|
||||
# is failing.
|
||||
set output_file "[file tail $prog].exe"
|
||||
verbose "output_file: $output_file"
|
||||
|
||||
# Create the test executable:
|
||||
set extension [file extension $prog]
|
||||
if {$extension == ".cc"} {
|
||||
set compilation_function "g++_target_compile"
|
||||
} else {
|
||||
set compilation_function "gcc_target_compile"
|
||||
}
|
||||
set options "{additional_flags=$extra_tool_flags}"
|
||||
verbose "compilation_function=$compilation_function"
|
||||
verbose "options=$options"
|
||||
|
||||
set comp_output [$compilation_function $prog $output_file \
|
||||
"executable" $options]
|
||||
upvar 1 name name
|
||||
if ![libdiagnostics_check_compile "$name" "initial compilation" \
|
||||
$output_file $comp_output] then {
|
||||
return
|
||||
}
|
||||
|
||||
# Run the test executable.
|
||||
|
||||
# We need to set LD_LIBRARY_PATH so that the test files can find
|
||||
# libdiagnostics.so
|
||||
# Do this using set_ld_library_path_env_vars from target-libpath.exp
|
||||
# We will restore the old value later using
|
||||
# restore_ld_library_path_env_vars.
|
||||
|
||||
# Unfortunately this API only supports a single saved value, rather
|
||||
# than a stack, and g++_init has already called into this API,
|
||||
# injecting the appropriate value for LD_LIBRARY_PATH for finding
|
||||
# the built copy of libstdc++.
|
||||
# Hence the call to restore_ld_library_path_env_vars would restore
|
||||
# the *initial* value of LD_LIBRARY_PATH, and attempts to run
|
||||
# a C++ testcase after running any prior testcases would thus look
|
||||
# in the wrong place for libstdc++. This led to failures at startup
|
||||
# of the form:
|
||||
# ./tut01-hello-world.cc.exe: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./tut01-hello-world.cc.exe)
|
||||
# when the built libstdc++ is more recent that the system libstdc++.
|
||||
#
|
||||
# As a workaround, reset the variable "orig_environment_saved" within
|
||||
# target-libpath.exp, so that the {set|restore}_ld_library_path_env_vars
|
||||
# API saves/restores the current value of LD_LIBRARY_PATH (as set up
|
||||
# by g++_init).
|
||||
global orig_environment_saved
|
||||
set orig_environment_saved 0
|
||||
|
||||
global ld_library_path
|
||||
global base_dir
|
||||
set ld_library_path "$base_dir/../../"
|
||||
set_ld_library_path_env_vars
|
||||
|
||||
global diagnostics-exe-params
|
||||
set args ${diagnostics-exe-params}
|
||||
set diagnostics-exe-params {}
|
||||
|
||||
set exe_output [fixed_host_execute $output_file $args ]
|
||||
verbose "exe_output: $exe_output"
|
||||
|
||||
restore_ld_library_path_env_vars
|
||||
|
||||
# Analyze the output from the executable. To some what extent this
|
||||
# is duplicating prune_gcc_output, but we're looking for *precise*
|
||||
# output, so we can't reuse prune_gcc_output.
|
||||
|
||||
global testname_with_flags
|
||||
set testname_with_flags $name
|
||||
|
||||
# Handle any freeform regexps.
|
||||
set exe_output [handle-dg-regexps $exe_output]
|
||||
|
||||
# Call into multiline.exp to handle any multiline output directives.
|
||||
set exe_output [handle-multiline-outputs $exe_output]
|
||||
|
||||
# Normally we would return $exe_output and $output_file to the
|
||||
# caller, which would delete $output_file, the generated executable.
|
||||
# If we need to debug, it's handy to be able to suppress this behavior,
|
||||
# keeping the executable around.
|
||||
|
||||
global env
|
||||
set preserve_executables [info exists env(PRESERVE_EXECUTABLES)]
|
||||
if $preserve_executables {
|
||||
set output_file ""
|
||||
}
|
||||
|
||||
return [list $exe_output $output_file]
|
||||
}
|
||||
|
||||
set DEFAULT_CFLAGS "-I$srcdir/.. -ldiagnostics -g -Wall -Werror"
|
||||
|
||||
# Main loop. This will invoke jig-dg-test on each test-*.c file.
|
||||
dg-runtest $tests "" $DEFAULT_CFLAGS
|
||||
|
||||
# All done.
|
||||
dg-finish
|
23
gcc/testsuite/libdiagnostics.dg/sarif.py
Normal file
23
gcc/testsuite/libdiagnostics.dg/sarif.py
Normal file
@ -0,0 +1,23 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
def sarif_from_env():
|
||||
# return parsed JSON content a SARIF_PATH file
|
||||
json_filename = os.environ['SARIF_PATH']
|
||||
json_filename += '.sarif'
|
||||
print('json_filename: %r' % json_filename)
|
||||
with open(json_filename) as f:
|
||||
json_data = f.read()
|
||||
return json.loads(json_data)
|
||||
|
||||
def get_location_artifact_uri(location):
|
||||
return location['physicalLocation']['artifactLocation']['uri']
|
||||
|
||||
def get_location_physical_region(location):
|
||||
return location['physicalLocation']['region']
|
||||
|
||||
def get_location_snippet_text(location):
|
||||
return location['physicalLocation']['contextRegion']['snippet']['text']
|
||||
|
||||
def get_location_relationships(location):
|
||||
return location['relationships']
|
69
gcc/testsuite/libdiagnostics.dg/test-dump.c
Normal file
69
gcc/testsuite/libdiagnostics.dg/test-dump.c
Normal file
@ -0,0 +1,69 @@
|
||||
/* Usage example of dump API. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
|
||||
const int line_num = 42;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
diagnostic_manager *diag_mgr = diagnostic_manager_new ();
|
||||
|
||||
const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, "foo.c", "c");
|
||||
|
||||
fprintf (stderr, "file: ");
|
||||
diagnostic_manager_debug_dump_file (diag_mgr, file, stderr);
|
||||
fprintf (stderr, "\n");
|
||||
/* { dg-begin-multiline-output "" }
|
||||
file: file(name="foo.c", sarif_source_language="c")
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
fprintf (stderr, "loc_start: ");
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc_start, stderr);
|
||||
fprintf (stderr, "\n");
|
||||
/* { dg-begin-multiline-output "" }
|
||||
loc_start: foo.c:42:8:
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
fprintf (stderr, "loc_end: ");
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc_end, stderr);
|
||||
fprintf (stderr, "\n");
|
||||
/* { dg-begin-multiline-output "" }
|
||||
loc_end: foo.c:42:19:
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
fprintf (stderr, "loc_range: ");
|
||||
diagnostic_manager_debug_dump_location (diag_mgr, loc_range, stderr);
|
||||
fprintf (stderr, "\n");
|
||||
/* { dg-begin-multiline-output "" }
|
||||
loc_range: foo.c:42:8:
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
const diagnostic_logical_location *logical_loc
|
||||
= diagnostic_manager_new_logical_location (diag_mgr,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
|
||||
NULL, /* parent */
|
||||
"test_short_name",
|
||||
"test_qualified_name",
|
||||
"test_decorated_name");
|
||||
|
||||
fprintf (stderr, "logical_loc: ");
|
||||
diagnostic_manager_debug_dump_logical_location (diag_mgr, logical_loc, stderr);
|
||||
fprintf (stderr, "\n");
|
||||
/* { dg-begin-multiline-output "" }
|
||||
logical_loc: logical_location(kind=function, short_name="test_short_name", fully_qualified_name="test_qualified_name", decorated_name="test_decorated_name")
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
diagnostic_manager_release (diag_mgr);
|
||||
return 0;
|
||||
};
|
54
gcc/testsuite/libdiagnostics.dg/test-error-c.py
Normal file
54
gcc/testsuite/libdiagnostics.dg/test-error-c.py
Normal file
@ -0,0 +1,54 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_line_num = 17
|
||||
expected_file_name = 'test-error.c'
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == expected_file_name + '.exe'
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
assert 'workingDirectory' in invocations[0]
|
||||
assert 'startTimeUtc' in invocations[0]
|
||||
assert invocations[0]['executionSuccessful'] == False
|
||||
assert invocations[0]['toolExecutionNotifications'] == []
|
||||
assert 'endTimeUtc' in invocations[0]
|
||||
|
||||
artifacts = run['artifacts']
|
||||
assert len(artifacts) == 1
|
||||
assert artifacts[0]['location']['uri'].endswith(expected_file_name)
|
||||
assert artifacts[0]['sourceLanguage'] == 'c'
|
||||
assert '#include <foo.h>' in artifacts[0]['contents']['text']
|
||||
assert artifacts[0]['roles'] == ["analysisTarget"]
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
assert results[0]['ruleId'] == 'error'
|
||||
assert results[0]['level'] == 'error'
|
||||
assert results[0]['message']['text'] == "can't find 'foo.h'"
|
||||
assert len(results[0]['locations']) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith(expected_file_name)
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 11
|
||||
assert phys_loc['region']['endColumn'] == 16
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== '#include <foo.h>\n'
|
50
gcc/testsuite/libdiagnostics.dg/test-error-with-note-c.py
Normal file
50
gcc/testsuite/libdiagnostics.dg/test-error-with-note-c.py
Normal file
@ -0,0 +1,50 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_line_num = 18
|
||||
|
||||
def test_sarif_output_for_note(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-error-with-note.c.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
assert results[0]['ruleId'] == 'error'
|
||||
assert results[0]['level'] == 'error'
|
||||
assert results[0]['message']['text'] == "can't find 'foo.h'"
|
||||
assert len(results[0]['locations']) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-error-with-note.c')
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 11
|
||||
assert phys_loc['region']['endColumn'] == 16
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== '#include <foo.h>\n'
|
||||
|
||||
assert len(results[0]['relatedLocations']) == 1
|
||||
note = results[0]['relatedLocations'][0]
|
||||
phys_loc = note['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-error-with-note.c')
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 11
|
||||
assert phys_loc['region']['endColumn'] == 16
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== '#include <foo.h>\n'
|
||||
assert note['message']['text'] == 'have you looked behind the couch?'
|
76
gcc/testsuite/libdiagnostics.dg/test-error-with-note.c
Normal file
76
gcc/testsuite/libdiagnostics.dg/test-error-with-note.c
Normal file
@ -0,0 +1,76 @@
|
||||
/* Example of emitting an error with an associated note.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-error-with-note.c:18:11: error: can't find 'foo.h'
|
||||
6 | #include <foo.h>
|
||||
| ^~~~~
|
||||
PATH/test-error-with-note.c:18:11: note: have you looked behind the couch?
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
#include <foo.h>
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-error-with-note.c.exe",
|
||||
"test-error-with-note.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
11);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
15);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
/* begin quoted source */
|
||||
diagnostic_manager_begin_group (diag_mgr);
|
||||
|
||||
diagnostic *err = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (err, loc_range);
|
||||
diagnostic_finish (err, "can't find %qs", "foo.h");
|
||||
|
||||
diagnostic *note = diagnostic_begin (diag_mgr, DIAGNOSTIC_LEVEL_NOTE);
|
||||
diagnostic_set_location (note, loc_range);
|
||||
diagnostic_finish (note, "have you looked behind the couch?");
|
||||
|
||||
diagnostic_manager_end_group (diag_mgr);
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
};
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-error-with-note.c:18:11: error: can't find 'foo.h'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
18 | #include <foo.h>
|
||||
| ^~~~~
|
||||
{ dg-end-multiline-output "" }
|
||||
{ dg-regexp "\[^\n\r\]+test-error-with-note.c:18:11: note: have you looked behind the couch." } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-error-with-note.c "test-error-with-note-c.py" } } */
|
55
gcc/testsuite/libdiagnostics.dg/test-error-with-note.cc
Normal file
55
gcc/testsuite/libdiagnostics.dg/test-error-with-note.cc
Normal file
@ -0,0 +1,55 @@
|
||||
/* C++ example of emitting an error with an associated note.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-error-with-note.c:17:8: error: can't find 'foo'
|
||||
17 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
PATH/test-error-with-note.c:17:8: note: have you looked behind the couch?
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics++.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
libdiagnostics::manager mgr;
|
||||
|
||||
auto file = mgr.new_file (__FILE__, "c");
|
||||
|
||||
mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY);
|
||||
|
||||
auto loc_start = mgr.new_location_from_file_line_column (file, line_num, 8);
|
||||
auto loc_end = mgr.new_location_from_file_line_column (file, line_num, 19);
|
||||
auto loc_range = mgr.new_location_from_range (loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
libdiagnostics::group g (mgr);
|
||||
|
||||
auto err (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR));
|
||||
err.set_location (loc_range);
|
||||
err.finish ("can't find %qs", "foo");
|
||||
|
||||
auto note = mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE);
|
||||
note.set_location (loc_range);
|
||||
note.finish ("have you looked behind the couch?");
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-error-with-note.cc:17:8: error: can't find 'foo'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
17 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" }
|
||||
{ dg-regexp "\[^\n\r\]+test-error-with-note.cc:17:8: note: have you looked behind the couch\\\?" } */
|
61
gcc/testsuite/libdiagnostics.dg/test-error.c
Normal file
61
gcc/testsuite/libdiagnostics.dg/test-error.c
Normal file
@ -0,0 +1,61 @@
|
||||
/* Example of emitting an error.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-error-with-note.c:6:11: error: can't find 'foo.h'
|
||||
6 | #include <foo.h>
|
||||
| ^~~~~
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________1111111
|
||||
1234567890123456
|
||||
#include <foo.h>
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-error.c.exe",
|
||||
"test-error.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
/* begin quoted source */
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, main_file, line_num, 11);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, main_file, line_num, 15);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_range);
|
||||
|
||||
diagnostic_finish (d, "can't find %qs", "foo.h");
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
};
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-error.c:17:11: error: can't find 'foo.h'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
17 | #include <foo.h>
|
||||
| ^~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-error.c "test-error-c.py" } } */
|
47
gcc/testsuite/libdiagnostics.dg/test-error.cc
Normal file
47
gcc/testsuite/libdiagnostics.dg/test-error.cc
Normal file
@ -0,0 +1,47 @@
|
||||
/* C++ example of emitting an error.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-error.cc:16:8: error: can't find 'foo'
|
||||
16 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics++.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
libdiagnostics::manager mgr;
|
||||
|
||||
auto file = mgr.new_file (__FILE__, "c");
|
||||
|
||||
mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY);
|
||||
|
||||
auto loc_start = mgr.new_location_from_file_line_column (file, line_num, 8);
|
||||
auto loc_end = mgr.new_location_from_file_line_column (file, line_num, 19);
|
||||
auto loc_range = mgr.new_location_from_range (loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
libdiagnostics::diagnostic d (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR));
|
||||
d.set_location (loc_range);
|
||||
d.finish ("can't find %qs", "foo");
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-error.cc:16:8: error: can't find 'foo'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
16 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
43
gcc/testsuite/libdiagnostics.dg/test-example-1.c
Normal file
43
gcc/testsuite/libdiagnostics.dg/test-example-1.c
Normal file
@ -0,0 +1,43 @@
|
||||
/* begin quoted source */
|
||||
/* Minimal usage example. */
|
||||
#include "libdiagnostics.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 ();
|
||||
};
|
||||
/* end quoted source */
|
||||
|
||||
/* { dg-regexp "progname: error: I'm sorry Dave, I'm afraid I can't do that" } */
|
46
gcc/testsuite/libdiagnostics.dg/test-fix-it-hint-c.py
Normal file
46
gcc/testsuite/libdiagnostics.dg/test-fix-it-hint-c.py
Normal file
@ -0,0 +1,46 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_sarif_output_with_fixes(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-fix-it-hint.c.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
assert results[0]['ruleId'] == 'error'
|
||||
assert results[0]['level'] == 'error'
|
||||
assert results[0]['message']['text'] == "unknown field 'colour'; did you mean 'color'"
|
||||
assert len(results[0]['locations']) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-fix-it-hint.c')
|
||||
assert phys_loc['region']['startLine'] == 19
|
||||
assert phys_loc['region']['startColumn'] == 13
|
||||
assert phys_loc['region']['endColumn'] == 19
|
||||
assert phys_loc['contextRegion']['startLine'] == 19
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== ' return p->colour;\n'
|
||||
|
||||
assert len(results[0]['fixes']) == 1
|
||||
fix = results[0]['fixes'][0]
|
||||
assert len(fix['artifactChanges']) == 1
|
||||
change = fix['artifactChanges'][0]
|
||||
assert change['artifactLocation']['uri'].endswith('test-fix-it-hint.c')
|
||||
assert len(change['replacements']) == 1
|
||||
replacement = change['replacements'][0]
|
||||
assert replacement['deletedRegion'] == phys_loc['region']
|
||||
assert replacement['insertedContent']['text'] == 'color'
|
83
gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c
Normal file
83
gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c
Normal file
@ -0,0 +1,83 @@
|
||||
/* Example of a fix-it hint, including patch generation.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-fix-it-hint.c:19:13: error: unknown field 'colour'; did you mean 'color'
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
|
||||
along with the equivalent in SARIF, and a generated patch (on stderr) to
|
||||
make the change. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
return p->colour;
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-fix-it-hint.c.exe",
|
||||
"test-fix-it-hint.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
const diagnostic_physical_location *loc_token
|
||||
= make_range (diag_mgr, main_file, line_num, 13, 18);
|
||||
|
||||
/* begin quoted source */
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_token);
|
||||
|
||||
diagnostic_add_fix_it_hint_replace (d, loc_token, "color");
|
||||
|
||||
diagnostic_finish (d, "unknown field %qs; did you mean %qs", "colour", "color");
|
||||
/* end quoted source */
|
||||
|
||||
diagnostic_manager_write_patch (diag_mgr, stderr);
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-fix-it-hint.c:19:13: error: unknown field 'colour'; did you mean 'color'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify the output from diagnostic_manager_write_patch.
|
||||
We expect the patch to begin with a header, containing this
|
||||
source filename, via an absolute path.
|
||||
Given the path, we can only capture it via regexps. */
|
||||
/* { dg-regexp "\\-\\-\\- .*" } */
|
||||
/* { dg-regexp "\\+\\+\\+ .*" } */
|
||||
/* Use #if 0/#endif rather than comments, to allow the text to contain
|
||||
a comment. */
|
||||
#if 0
|
||||
{ dg-begin-multiline-output "" }
|
||||
@@ -16,7 +16,7 @@
|
||||
/*
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
- return p->colour;
|
||||
+ return p->color;
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
{ dg-end-multiline-output "" }
|
||||
#endif
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-fix-it-hint.c "test-fix-it-hint-c.py" } } */
|
74
gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.cc
Normal file
74
gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.cc
Normal file
@ -0,0 +1,74 @@
|
||||
/* C++ example of a fix-it hint, including patch generation.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-fix-it-hint.cc:19:13: error: unknown field 'colour'; did you mean 'color'
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
|
||||
along with the equivalent in SARIF, and a generated patch (on stderr) to
|
||||
make the change. */
|
||||
|
||||
#include "libdiagnostics++.h"
|
||||
#include "test-helpers++.h"
|
||||
|
||||
/*
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
return p->colour;
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
libdiagnostics::manager mgr;
|
||||
|
||||
auto file = mgr.new_file (__FILE__, "c");
|
||||
|
||||
mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY);
|
||||
|
||||
auto loc_token = make_range (mgr, file, line_num, 13, 18);
|
||||
|
||||
auto d = mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR);
|
||||
d.set_location (loc_token);
|
||||
|
||||
d.add_fix_it_hint_replace (loc_token, "color");
|
||||
|
||||
d.finish ("unknown field %qs; did you mean %qs", "colour", "color");
|
||||
|
||||
mgr.write_patch (stderr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-fix-it-hint.cc:19:13: error: unknown field 'colour'; did you mean 'color'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
19 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify the output from diagnostic_manager_write_patch.
|
||||
We expect the patch to begin with a header, containing this
|
||||
source filename, via an absolute path.
|
||||
Given the path, we can only capture it via regexps. */
|
||||
/* { dg-regexp "\\-\\-\\- .*" } */
|
||||
/* { dg-regexp "\\+\\+\\+ .*" } */
|
||||
/* Use #if 0/#endif rather than comments, to allow the text to contain
|
||||
a comment. */
|
||||
#if 0
|
||||
{ dg-begin-multiline-output "" }
|
||||
@@ -16,7 +16,7 @@
|
||||
/*
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
- return p->colour;
|
||||
+ return p->color;
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
{ dg-end-multiline-output "" }
|
||||
#endif
|
28
gcc/testsuite/libdiagnostics.dg/test-helpers++.h
Normal file
28
gcc/testsuite/libdiagnostics.dg/test-helpers++.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* Common utility code shared between test cases. */
|
||||
|
||||
#ifndef TEST_HELPERSPP_H
|
||||
#define TEST_HELPERSPP_H
|
||||
|
||||
namespace libdiagnostics {
|
||||
|
||||
inline physical_location
|
||||
make_range (manager &mgr,
|
||||
file f,
|
||||
line_num_t line_num,
|
||||
column_num_t start_column,
|
||||
column_num_t end_column)
|
||||
{
|
||||
auto loc_start = mgr.new_location_from_file_line_column (f,
|
||||
line_num,
|
||||
start_column);
|
||||
auto loc_end = mgr.new_location_from_file_line_column (f,
|
||||
line_num,
|
||||
end_column);
|
||||
return mgr.new_location_from_range (loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
}
|
||||
|
||||
} // namespace libdiagnostics
|
||||
|
||||
#endif /* #ifndef TEST_HELPERSPP_H */
|
72
gcc/testsuite/libdiagnostics.dg/test-helpers.h
Normal file
72
gcc/testsuite/libdiagnostics.dg/test-helpers.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* Common utility code shared between test cases. */
|
||||
|
||||
#ifndef TEST_HELPERS_H
|
||||
#define TEST_HELPERS_H
|
||||
|
||||
const diagnostic_physical_location *
|
||||
make_range (diagnostic_manager *diag_mgr,
|
||||
const diagnostic_file *file,
|
||||
diagnostic_line_num_t line_num,
|
||||
diagnostic_column_num_t start_column,
|
||||
diagnostic_column_num_t end_column)
|
||||
{
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
file,
|
||||
line_num,
|
||||
start_column);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
file,
|
||||
line_num,
|
||||
end_column);
|
||||
return diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
}
|
||||
|
||||
/* A begin_test/end_test pair to consolidate the code shared by tests:
|
||||
create a diagnostic_manager, a main file, a text sink, and a SARIF sink,
|
||||
and clean these up after emitting zero or more diagnostics. */
|
||||
|
||||
static diagnostic_manager *diag_mgr;
|
||||
static const diagnostic_file *main_file;
|
||||
static FILE *sarif_outfile;
|
||||
|
||||
static void
|
||||
begin_test (const char *tool_name,
|
||||
const char *sarif_output_name,
|
||||
const char *main_file_name,
|
||||
const char *source_language)
|
||||
{
|
||||
diag_mgr = diagnostic_manager_new ();
|
||||
|
||||
/* We need to set this for generated .sarif files to validate
|
||||
against the schema. */
|
||||
diagnostic_manager_set_tool_name (diag_mgr, tool_name);
|
||||
|
||||
main_file = diagnostic_manager_new_file (diag_mgr,
|
||||
main_file_name,
|
||||
source_language);
|
||||
|
||||
diagnostic_manager_add_text_sink (diag_mgr, stderr,
|
||||
DIAGNOSTIC_COLORIZE_IF_TTY);
|
||||
sarif_outfile = fopen (sarif_output_name, "w");
|
||||
if (sarif_outfile)
|
||||
diagnostic_manager_add_sarif_sink (diag_mgr,
|
||||
sarif_outfile,
|
||||
main_file,
|
||||
DIAGNOSTIC_SARIF_VERSION_2_1_0);
|
||||
}
|
||||
|
||||
static int
|
||||
end_test (void)
|
||||
{
|
||||
diagnostic_manager_release (diag_mgr);
|
||||
if (sarif_outfile)
|
||||
fclose (sarif_outfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* #ifndef TEST_HELPERS_H */
|
71
gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c
Normal file
71
gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c
Normal file
@ -0,0 +1,71 @@
|
||||
/* Example of multiple locations, with labelling of ranges.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-labelled-ranges.c:9:6: error: mismatching types: 'int' and 'const char *'
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
42 + "foo"
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-labelled-ranges.c.exe",
|
||||
"test-labelled-ranges.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
const diagnostic_physical_location *loc_operator
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
6);
|
||||
|
||||
/* begin quoted source */
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_operator);
|
||||
diagnostic_add_location_with_label (d,
|
||||
make_range (diag_mgr,
|
||||
main_file,
|
||||
line_num, 3, 4),
|
||||
"int");
|
||||
diagnostic_add_location_with_label (d,
|
||||
make_range (diag_mgr,
|
||||
main_file,
|
||||
line_num, 8, 12),
|
||||
"const char *");
|
||||
|
||||
diagnostic_finish (d, "mismatching types: %qs and %qs", "int", "const char *");
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* Check the output from the text sink. */
|
||||
/* { dg-regexp "\[^\n\r\]+test-labelled-ranges.c:19:6: error: mismatching types: 'int' and 'const char \\*'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-labelled-ranges.c "test-labelled-ranges.py" } } */
|
64
gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.cc
Normal file
64
gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.cc
Normal file
@ -0,0 +1,64 @@
|
||||
/* C++ example of multiple locations, with labelling of ranges.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-labelled-ranges.cc:19:6: error: mismatching types: 'int' and 'const char *'
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics++.h"
|
||||
#include "test-helpers++.h"
|
||||
|
||||
/*
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
42 + "foo"
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
FILE *sarif_outfile;
|
||||
libdiagnostics::manager mgr;
|
||||
mgr.set_tool_name ("test-labelled-ranges.cc.exe");
|
||||
|
||||
libdiagnostics::file file = mgr.new_file (__FILE__, "c");
|
||||
|
||||
mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY);
|
||||
sarif_outfile = fopen ("test-labelled-ranges.cc.sarif", "w");
|
||||
if (sarif_outfile)
|
||||
mgr.add_sarif_sink (sarif_outfile, file, DIAGNOSTIC_SARIF_VERSION_2_1_0);
|
||||
|
||||
auto loc_operator = mgr.new_location_from_file_line_column (file, line_num, 6);
|
||||
|
||||
auto d (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR));
|
||||
d.set_location (loc_operator);
|
||||
d.add_location_with_label (make_range (mgr, file, line_num, 3, 4),
|
||||
"int");
|
||||
d.add_location_with_label (make_range (mgr, file, line_num, 8, 12),
|
||||
"const char *");
|
||||
d.finish ("mismatching types: %qs and %qs", "int", "const char *");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the output from the text sink. */
|
||||
/* { dg-regexp "\[^\n\r\]+test-labelled-ranges.cc:19:6: error: mismatching types: 'int' and 'const char \\*'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
19 | 42 + "foo"
|
||||
| ~~ ^ ~~~~~
|
||||
| | |
|
||||
| int const char *
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-labelled-ranges.cc "test-labelled-ranges.py" } } */
|
48
gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.py
Normal file
48
gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.py
Normal file
@ -0,0 +1,48 @@
|
||||
# Verify the SARIF output of test-labelled-ranges.{c,cc}
|
||||
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
assert results[0]['ruleId'] == 'error'
|
||||
assert results[0]['level'] == 'error'
|
||||
assert results[0]['message']['text'] \
|
||||
== "mismatching types: 'int' and 'const char *'"
|
||||
assert len(results[0]['locations']) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['region']['startLine'] == 19
|
||||
assert phys_loc['region']['startColumn'] == 6
|
||||
assert phys_loc['region']['endColumn'] == 7
|
||||
assert phys_loc['contextRegion']['startLine'] == 19
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== ' 42 + "foo"\n'
|
||||
|
||||
annotations = location['annotations']
|
||||
assert len(annotations) == 2
|
||||
|
||||
assert annotations[0]['startLine'] == 19
|
||||
assert annotations[0]['startColumn'] == 3
|
||||
assert annotations[0]['endColumn'] == 5
|
||||
assert annotations[0]['message']['text'] == 'int'
|
||||
|
||||
assert annotations[1]['startLine'] == 19
|
||||
assert annotations[1]['startColumn'] == 8
|
||||
assert annotations[1]['endColumn'] == 13
|
||||
assert annotations[1]['message']['text'] == 'const char *'
|
37
gcc/testsuite/libdiagnostics.dg/test-logical-location-c.py
Normal file
37
gcc/testsuite/libdiagnostics.dg/test-logical-location-c.py
Normal file
@ -0,0 +1,37 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_sarif_output_with_logical_location(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-logical-location.c.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
|
||||
result = results[0]
|
||||
assert result['ruleId'] == 'error'
|
||||
assert result['level'] == 'error'
|
||||
assert result['message']['text'] == "can't find 'foo'"
|
||||
assert len(result['locations']) == 1
|
||||
location = result['locations'][0]
|
||||
|
||||
assert len(location['logicalLocations']) == 1
|
||||
logical_loc = location['logicalLocations'][0]
|
||||
assert logical_loc['name'] == 'test_short_name'
|
||||
assert logical_loc['fullyQualifiedName'] == 'test_qualified_name'
|
||||
assert logical_loc['decoratedName'] == 'test_decorated_name'
|
||||
assert logical_loc['kind'] == 'function'
|
81
gcc/testsuite/libdiagnostics.dg/test-logical-location.c
Normal file
81
gcc/testsuite/libdiagnostics.dg/test-logical-location.c
Normal file
@ -0,0 +1,81 @@
|
||||
/* Example of using a logical location.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
In function 'test_qualified_name':
|
||||
PATH/test-error-with-note.c:18:8: error: can't find 'foo'
|
||||
18 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/* Placeholder source:
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-logical-location.c.exe",
|
||||
"test-logical-location.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
8);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
19);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
/* begin quoted source */
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_range);
|
||||
|
||||
const diagnostic_logical_location *logical_loc
|
||||
= diagnostic_manager_new_logical_location (diag_mgr,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
|
||||
NULL, /* parent */
|
||||
"test_short_name",
|
||||
"test_qualified_name",
|
||||
"test_decorated_name");
|
||||
|
||||
diagnostic_set_logical_location (d, logical_loc);
|
||||
|
||||
diagnostic_finish (d, "can't find %qs", "foo");
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* Check the output from the text sink. */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
In function 'test_qualified_name':
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-regexp "\[^\n\r\]+test-logical-location.c:18:8: error: can't find 'foo'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
18 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-logical-location.c "test-logical-location-c.py" } } */
|
45
gcc/testsuite/libdiagnostics.dg/test-metadata-c.py
Normal file
45
gcc/testsuite/libdiagnostics.dg/test-metadata-c.py
Normal file
@ -0,0 +1,45 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_sarif_output_metadata(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'FooChecker'
|
||||
assert tool['driver']['fullName'] == 'FooChecker 0.1 (en_US)'
|
||||
assert tool['driver']['version'] == '0.1'
|
||||
assert tool['driver']['informationUri'] == 'https://www.example.com/0.1/'
|
||||
|
||||
taxonomies = run["taxonomies"]
|
||||
assert len(taxonomies) == 1
|
||||
|
||||
cwe = taxonomies[0]
|
||||
assert cwe['name'] == 'CWE'
|
||||
assert cwe['version'] == '4.7'
|
||||
assert cwe['organization'] == 'MITRE'
|
||||
assert cwe['shortDescription']['text'] \
|
||||
== 'The MITRE Common Weakness Enumeration'
|
||||
assert len(cwe['taxa']) == 1
|
||||
assert cwe['taxa'][0]['id'] == '242'
|
||||
assert cwe['taxa'][0]['helpUri'] \
|
||||
== 'https://cwe.mitre.org/data/definitions/242.html'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
|
||||
result = results[0]
|
||||
assert result['ruleId'] == 'warning'
|
||||
assert result['level'] == 'warning'
|
||||
assert result['message']['text'] == "never use 'gets'"
|
61
gcc/testsuite/libdiagnostics.dg/test-metadata.c
Normal file
61
gcc/testsuite/libdiagnostics.dg/test-metadata.c
Normal file
@ -0,0 +1,61 @@
|
||||
/* Example of setting a CWE and adding extra metadata.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-metadata.c:21:3: warning: never use 'gets' [CWE-242] [STR34-C]
|
||||
21 | gets (buf);
|
||||
| ^~~~~~~~~~
|
||||
|
||||
where the metadata tags are linkified in a sufficiently capable terminal,
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/* Placeholder source:
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
void test_cwe (void)
|
||||
{
|
||||
char buf[1024];
|
||||
gets (buf);
|
||||
}
|
||||
*/
|
||||
const int line_num = __LINE__ - 3;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("FooChecker",
|
||||
"test-metadata.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
diagnostic_manager_set_full_name (diag_mgr, "FooChecker 0.1 (en_US)");
|
||||
diagnostic_manager_set_version_string (diag_mgr, "0.1");
|
||||
diagnostic_manager_set_version_url (diag_mgr, "https://www.example.com/0.1/");
|
||||
|
||||
const diagnostic_physical_location *loc_token
|
||||
= make_range (diag_mgr, main_file, line_num, 3, 12);
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_WARNING);
|
||||
diagnostic_set_location (d, loc_token);
|
||||
diagnostic_set_cwe (d, 242); /* CWE-242: Use of Inherently Dangerous Function. */
|
||||
diagnostic_add_rule (d, "STR34-C", "https://example.com/");
|
||||
|
||||
diagnostic_finish (d, "never use %qs", "gets");
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* { dg-regexp "\[^\n\r\]+test-metadata.c:21:3: warning: never use 'gets' \\\[CWE-242\\\] \\\[STR34-C\\\]" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
21 | gets (buf);
|
||||
| ^~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-metadata.c "test-metadata-c.py" } } */
|
83
gcc/testsuite/libdiagnostics.dg/test-multiple-lines-c.py
Normal file
83
gcc/testsuite/libdiagnostics.dg/test-multiple-lines-c.py
Normal file
@ -0,0 +1,83 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-multiple-lines.c.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
result = results[0]
|
||||
assert result['ruleId'] == 'warning'
|
||||
assert result['level'] == 'warning'
|
||||
assert result['message']['text'] == "missing comma"
|
||||
assert len(result['locations']) == 1
|
||||
|
||||
# The primary location should be that of the missing comma
|
||||
location = result['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-multiple-lines.c')
|
||||
assert phys_loc['region']['startLine'] == 23
|
||||
assert phys_loc['region']['startColumn'] == 29
|
||||
assert phys_loc['region']['endColumn'] == 30
|
||||
assert phys_loc['contextRegion']['startLine'] == 23
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== ' "bar"\n'
|
||||
|
||||
assert len(location['relationships']) == 3
|
||||
location['relationships'][0]['target'] == 0
|
||||
location['relationships'][0]['kinds'] == ['relevant']
|
||||
location['relationships'][1]['target'] == 1
|
||||
location['relationships'][1]['kinds'] == ['relevant']
|
||||
location['relationships'][2]['target'] == 2
|
||||
location['relationships'][2]['kinds'] == ['relevant']
|
||||
|
||||
# We should be capturing the secondary locations in relatedLocations
|
||||
assert len(result['relatedLocations']) == 3
|
||||
|
||||
rel_loc_0 = result['relatedLocations'][0]
|
||||
assert get_location_artifact_uri(rel_loc_0) \
|
||||
.endswith('test-multiple-lines.c')
|
||||
assert get_location_snippet_text(rel_loc_0) \
|
||||
== 'const char *strs[3] = {"foo",\n'
|
||||
assert get_location_physical_region(rel_loc_0)['startLine'] == 22
|
||||
assert get_location_physical_region(rel_loc_0)['startColumn'] == 24
|
||||
assert get_location_physical_region(rel_loc_0)['endColumn'] == 29
|
||||
assert rel_loc_0['id'] == 0
|
||||
assert 'relationships' not in rel_loc_0
|
||||
|
||||
rel_loc_1 = result['relatedLocations'][1]
|
||||
assert get_location_artifact_uri(rel_loc_1) \
|
||||
.endswith('test-multiple-lines.c')
|
||||
assert get_location_snippet_text(rel_loc_1) \
|
||||
== ' "bar"\n'
|
||||
assert get_location_physical_region(rel_loc_1)['startLine'] == 23
|
||||
assert get_location_physical_region(rel_loc_1)['startColumn'] == 24
|
||||
assert get_location_physical_region(rel_loc_1)['endColumn'] == 29
|
||||
assert rel_loc_1['id'] == 1
|
||||
assert 'relationships' not in rel_loc_1
|
||||
|
||||
rel_loc_2 = result['relatedLocations'][2]
|
||||
assert get_location_artifact_uri(rel_loc_2) \
|
||||
.endswith('test-multiple-lines.c')
|
||||
assert get_location_snippet_text(rel_loc_2) \
|
||||
== ' "baz"};\n'
|
||||
assert get_location_physical_region(rel_loc_2)['startLine'] == 24
|
||||
assert get_location_physical_region(rel_loc_2)['startColumn'] == 24
|
||||
assert get_location_physical_region(rel_loc_2)['endColumn'] == 29
|
||||
assert rel_loc_2['id'] == 2
|
||||
assert 'relationships' not in rel_loc_2
|
78
gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c
Normal file
78
gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c
Normal file
@ -0,0 +1,78 @@
|
||||
/* Example of a warning with multiple locations in various source lines,
|
||||
with an insertion fix-it hint.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
/PATH/test-multiple-lines.c:23:29: warning: missing comma
|
||||
22 | const char *strs[3] = {"foo",
|
||||
| ~~~~~
|
||||
23 | "bar"
|
||||
| ~~~~~^
|
||||
24 | "baz"};
|
||||
| ~~~~~
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/* Placeholder source (missing comma after "bar"):
|
||||
_________11111111112222222222
|
||||
12345678901234567890123456789
|
||||
const char *strs[3] = {"foo",
|
||||
"bar"
|
||||
"baz"};
|
||||
*/
|
||||
const int foo_line_num = __LINE__ - 4;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-multiple-lines.c.exe",
|
||||
"test-multiple-lines.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
/* begin quoted source */
|
||||
const diagnostic_physical_location *loc_comma
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
foo_line_num + 1,
|
||||
29);
|
||||
const diagnostic_physical_location *loc_foo
|
||||
= make_range (diag_mgr, main_file, foo_line_num, 24, 28);
|
||||
const diagnostic_physical_location *loc_bar
|
||||
= make_range (diag_mgr, main_file, foo_line_num + 1, 24, 28);
|
||||
const diagnostic_physical_location *loc_baz
|
||||
= make_range (diag_mgr, main_file, foo_line_num + 2, 24, 28);
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_WARNING);
|
||||
diagnostic_set_location (d, loc_comma);
|
||||
diagnostic_add_location (d, loc_foo);
|
||||
diagnostic_add_location (d, loc_bar);
|
||||
diagnostic_add_location (d, loc_baz);
|
||||
|
||||
diagnostic_add_fix_it_hint_insert_after (d, loc_bar, ",");
|
||||
|
||||
diagnostic_finish (d, "missing comma");
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
};
|
||||
|
||||
/* { dg-regexp "\[^\n\r\]+test-multiple-lines.c:23:29: warning: missing comma" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
22 | const char *strs[3] = {"foo",
|
||||
| ~~~~~
|
||||
23 | "bar"
|
||||
| ~~~~~^
|
||||
24 | "baz"};
|
||||
| ~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-multiple-lines.c "test-multiple-lines-c.py" } } */
|
35
gcc/testsuite/libdiagnostics.dg/test-no-column-c.py
Normal file
35
gcc/testsuite/libdiagnostics.dg/test-no-column-c.py
Normal file
@ -0,0 +1,35 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_line_num = 16
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-no-column.c.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-no-column.c')
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
# We should have no column properties:
|
||||
assert 'startColumn' not in phys_loc['region']
|
||||
assert 'endColumn' not in phys_loc['region']
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== '#include <foo.h>\n'
|
54
gcc/testsuite/libdiagnostics.dg/test-no-column.c
Normal file
54
gcc/testsuite/libdiagnostics.dg/test-no-column.c
Normal file
@ -0,0 +1,54 @@
|
||||
/* Example of emitting an error without a column number.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
PATH/test-error-with-note.c:6: error: can't find 'foo'
|
||||
6 | #include <foo.h>
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
#include <foo.h>
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-no-column.c.exe",
|
||||
"test-no-column.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
/* begin quoted source */
|
||||
const diagnostic_physical_location *loc
|
||||
= diagnostic_manager_new_location_from_file_and_line (diag_mgr,
|
||||
main_file,
|
||||
line_num);
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc);
|
||||
|
||||
diagnostic_finish (d, "can't find %qs", "foo.h");
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-no-column.c:16: error: can't find 'foo.h'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
16 | #include <foo.h>
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-no-column.c "test-no-column-c.py" } } */
|
42
gcc/testsuite/libdiagnostics.dg/test-no-diagnostics-c.py
Normal file
42
gcc/testsuite/libdiagnostics.dg/test-no-diagnostics-c.py
Normal file
@ -0,0 +1,42 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_file_name = 'test-no-diagnostics.c'
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == expected_file_name + '.exe'
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
assert 'workingDirectory' in invocations[0]
|
||||
assert 'startTimeUtc' in invocations[0]
|
||||
assert invocations[0]['executionSuccessful'] == True
|
||||
assert invocations[0]['toolExecutionNotifications'] == []
|
||||
assert 'endTimeUtc' in invocations[0]
|
||||
|
||||
artifacts = run['artifacts']
|
||||
assert len(artifacts) == 1
|
||||
assert artifacts[0]['location']['uri'].endswith(expected_file_name)
|
||||
assert artifacts[0]['sourceLanguage'] == 'c'
|
||||
# We don't bother capturing the contents if there are
|
||||
# no diagnostics to display
|
||||
assert 'contents' not in artifacts[0]
|
||||
assert artifacts[0]['roles'] == ["analysisTarget"]
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 0
|
25
gcc/testsuite/libdiagnostics.dg/test-no-diagnostics.c
Normal file
25
gcc/testsuite/libdiagnostics.dg/test-no-diagnostics.c
Normal file
@ -0,0 +1,25 @@
|
||||
/* Test of the "no diagnostics are emitted" case. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-no-diagnostics.c.exe",
|
||||
"test-no-diagnostics.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
/* No-op. */
|
||||
|
||||
return end_test ();
|
||||
};
|
||||
|
||||
/* There should be no output from the text sink. */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-no-diagnostics.c "test-no-diagnostics-c.py" } } */
|
@ -0,0 +1,54 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_line_num = 21
|
||||
|
||||
def test_sarif_output_with_fixes(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-note-with-fix-it-hint.c.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
result = results[0]
|
||||
assert result['ruleId'] == 'error'
|
||||
assert result['level'] == 'error'
|
||||
assert result['message']['text'] == "unknown field 'colour'"
|
||||
assert len(result['locations']) == 1
|
||||
location = result['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-note-with-fix-it-hint.c')
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 13
|
||||
assert phys_loc['region']['endColumn'] == 19
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== ' return p->colour;\n'
|
||||
|
||||
assert len(result['relatedLocations']) == 1
|
||||
note = result['relatedLocations'][0]
|
||||
phys_loc = note['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith('test-note-with-fix-it-hint.c')
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 13
|
||||
assert phys_loc['region']['endColumn'] == 19
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== ' return p->colour;\n'
|
||||
assert note['message']['text'] == "did you mean 'color'"
|
||||
|
||||
# TODO: we don't yet capture fix-it hints on notes (PR other/116164)
|
||||
assert 'fixes' not in result
|
69
gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c
Normal file
69
gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c
Normal file
@ -0,0 +1,69 @@
|
||||
/* Example of a grouped error and note, with a fix-it hint on the note.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
/PATH/test-note-with-fix-it-hint.c:21:13: error: unknown field 'colour'
|
||||
21 | return p->colour;
|
||||
| ^~~~~~
|
||||
/PATH/test-note-with-fix-it-hint.c:21:13: note: did you mean 'color'
|
||||
21 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/* Placeholder source:
|
||||
_________11111111112
|
||||
12345678901234567890
|
||||
return p->colour;
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-note-with-fix-it-hint.c.exe",
|
||||
"test-note-with-fix-it-hint.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
const diagnostic_physical_location *loc_token
|
||||
= make_range (diag_mgr, main_file, line_num, 13, 18);
|
||||
|
||||
diagnostic_manager_begin_group (diag_mgr);
|
||||
|
||||
diagnostic *err = diagnostic_begin (diag_mgr, DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (err, loc_token);
|
||||
diagnostic_finish (err, "unknown field %qs", "colour");
|
||||
|
||||
diagnostic *n = diagnostic_begin (diag_mgr, DIAGNOSTIC_LEVEL_NOTE);
|
||||
diagnostic_set_location (n, loc_token);
|
||||
diagnostic_add_fix_it_hint_replace (n, loc_token, "color");
|
||||
diagnostic_finish (n, "did you mean %qs", "color");
|
||||
|
||||
diagnostic_manager_end_group (diag_mgr);
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-note-with-fix-it-hint.c:21:13: error: unknown field 'colour'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
21 | return p->colour;
|
||||
| ^~~~~~
|
||||
{ dg-end-multiline-output "" }
|
||||
{ dg-regexp "\[^\n\r\]+test-note-with-fix-it-hint.c:21:13: note: did you mean 'color'" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
21 | return p->colour;
|
||||
| ^~~~~~
|
||||
| color
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-note-with-fix-it-hint.c "test-note-with-fix-it-hint-c.py" } } */
|
59
gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c
Normal file
59
gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c
Normal file
@ -0,0 +1,59 @@
|
||||
/* Example of controlling options for text sinks,
|
||||
with multiple text sinks,
|
||||
and color output. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
diagnostic_manager *diag_mgr = diagnostic_manager_new ();
|
||||
|
||||
diagnostic_text_sink *sink_1
|
||||
= diagnostic_manager_add_text_sink (diag_mgr, stderr,
|
||||
DIAGNOSTIC_COLORIZE_NO);
|
||||
diagnostic_text_sink_set_source_printing_enabled (sink_1, 0);
|
||||
|
||||
diagnostic_text_sink *sink_2
|
||||
= diagnostic_manager_add_text_sink (diag_mgr, stderr,
|
||||
DIAGNOSTIC_COLORIZE_YES);
|
||||
diagnostic_text_sink_set_labelled_source_colorization_enabled (sink_2, 0);
|
||||
|
||||
const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c");
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_range);
|
||||
|
||||
diagnostic_finish (d, "can't find %qs", "foo");
|
||||
|
||||
diagnostic_manager_release (diag_mgr);
|
||||
return 0;
|
||||
};
|
||||
|
||||
/* Verify the output from text sink 1. */
|
||||
/* { dg-regexp "\[^\n\r\]+test-text-sink-options.c:10:8: error: can't find 'foo'" } */
|
||||
|
||||
/* Verify the output from text sink 2.
|
||||
{ dg-regexp "\[^\n\r\]+test-text-sink-options.c:10:8:" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
[m[K [01;31m[Kerror: [m[Kcan't find '[01m[Kfoo[m[K'
|
||||
10 | PRINT "hello world!";
|
||||
| [01;31m[K^~~~~~~~~~~~[m[K
|
||||
{ dg-end-multiline-output "" } */
|
54
gcc/testsuite/libdiagnostics.dg/test-warning-c.py
Normal file
54
gcc/testsuite/libdiagnostics.dg/test-warning-c.py
Normal file
@ -0,0 +1,54 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_line_num = 17
|
||||
expected_file_name = 'test-warning.c'
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == expected_file_name + '.exe'
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
assert 'workingDirectory' in invocations[0]
|
||||
assert 'startTimeUtc' in invocations[0]
|
||||
assert invocations[0]['executionSuccessful'] == True
|
||||
assert invocations[0]['toolExecutionNotifications'] == []
|
||||
assert 'endTimeUtc' in invocations[0]
|
||||
|
||||
artifacts = run['artifacts']
|
||||
assert len(artifacts) == 1
|
||||
assert artifacts[0]['location']['uri'].endswith(expected_file_name)
|
||||
assert artifacts[0]['sourceLanguage'] == 'c'
|
||||
assert 'PRINT' in artifacts[0]['contents']['text']
|
||||
assert artifacts[0]['roles'] == ["analysisTarget"]
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
assert results[0]['ruleId'] == 'warning'
|
||||
assert results[0]['level'] == 'warning'
|
||||
assert results[0]['message']['text'] == "this is a warning"
|
||||
assert len(results[0]['locations']) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith(expected_file_name)
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 8
|
||||
assert phys_loc['region']['endColumn'] == 20
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== 'PRINT "hello world!";\n'
|
108
gcc/testsuite/libdiagnostics.dg/test-warning-with-path-c.py
Normal file
108
gcc/testsuite/libdiagnostics.dg/test-warning-with-path-c.py
Normal file
@ -0,0 +1,108 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
final_line_num = 34
|
||||
|
||||
line_num_call_to_PyList_New = final_line_num - 7;
|
||||
line_num_for_loop = final_line_num - 5;
|
||||
line_num_call_to_PyList_Append = final_line_num - 3;
|
||||
|
||||
expected_file_name = 'test-warning-with-path.c'
|
||||
|
||||
def test_sarif_output_for_warning_with_path(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == expected_file_name + '.exe'
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
|
||||
result = results[0]
|
||||
assert result['ruleId'] == 'warning'
|
||||
assert result['level'] == 'warning'
|
||||
assert result['message']['text'] \
|
||||
== "passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter"
|
||||
assert len(result['locations']) == 1
|
||||
location = result['locations'][0]
|
||||
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'].endswith(expected_file_name)
|
||||
assert phys_loc['region']['startLine'] \
|
||||
== line_num_call_to_PyList_Append
|
||||
assert phys_loc['region']['startColumn'] == 5
|
||||
assert phys_loc['region']['endColumn'] == 30
|
||||
assert phys_loc['contextRegion']['startLine'] \
|
||||
== line_num_call_to_PyList_Append
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== ' PyList_Append(list, item);\n'
|
||||
|
||||
assert len(location['logicalLocations']) == 1
|
||||
logical_loc = location['logicalLocations'][0]
|
||||
assert logical_loc['name'] == 'make_a_list_of_random_ints_badly'
|
||||
assert logical_loc['fullyQualifiedName'] == 'make_a_list_of_random_ints_badly'
|
||||
assert logical_loc['decoratedName'] == 'make_a_list_of_random_ints_badly'
|
||||
assert logical_loc['kind'] == 'function'
|
||||
|
||||
assert len(result['codeFlows']) == 1
|
||||
assert len(result['codeFlows'][0]['threadFlows']) == 1
|
||||
thread_flow = result['codeFlows'][0]['threadFlows'][0]
|
||||
|
||||
assert len(thread_flow['locations']) == 3
|
||||
|
||||
tfl_0 = thread_flow['locations'][0]
|
||||
tfl_0_loc = tfl_0['location']
|
||||
assert get_location_artifact_uri(tfl_0_loc).endswith(expected_file_name)
|
||||
assert get_location_physical_region(tfl_0_loc)['startLine'] \
|
||||
== line_num_call_to_PyList_New
|
||||
assert get_location_physical_region(tfl_0_loc)['startColumn'] == 10
|
||||
assert get_location_physical_region(tfl_0_loc)['endColumn'] == 23
|
||||
assert get_location_snippet_text(tfl_0_loc) \
|
||||
== ' list = PyList_New(0);\n'
|
||||
assert tfl_0_loc['logicalLocations'] == location['logicalLocations']
|
||||
assert tfl_0_loc['message']['text'] \
|
||||
== "when 'PyList_New' fails, returning NULL"
|
||||
assert tfl_0['nestingLevel'] == 0
|
||||
assert tfl_0['executionOrder'] == 1
|
||||
|
||||
tfl_1 = thread_flow['locations'][1]
|
||||
tfl_1_loc = tfl_1['location']
|
||||
assert get_location_artifact_uri(tfl_1_loc).endswith(expected_file_name)
|
||||
assert get_location_physical_region(tfl_1_loc)['startLine'] \
|
||||
== line_num_for_loop
|
||||
assert get_location_physical_region(tfl_1_loc)['startColumn'] == 15
|
||||
assert get_location_physical_region(tfl_1_loc)['endColumn'] == 24
|
||||
assert get_location_snippet_text(tfl_1_loc) \
|
||||
== ' for (i = 0; i < count; i++) {\n'
|
||||
assert tfl_1_loc['logicalLocations'] == location['logicalLocations']
|
||||
assert tfl_1_loc['message']['text'] \
|
||||
== "when 'i < count'"
|
||||
assert tfl_1['nestingLevel'] == 0
|
||||
assert tfl_1['executionOrder'] == 2
|
||||
|
||||
tfl_2 = thread_flow['locations'][2]
|
||||
tfl_2_loc = tfl_2['location']
|
||||
assert get_location_artifact_uri(tfl_2_loc).endswith(expected_file_name)
|
||||
assert get_location_physical_region(tfl_2_loc)['startLine'] \
|
||||
== line_num_call_to_PyList_Append
|
||||
assert get_location_physical_region(tfl_2_loc)['startColumn'] == 5
|
||||
assert get_location_physical_region(tfl_2_loc)['endColumn'] == 30
|
||||
assert get_location_snippet_text(tfl_2_loc) \
|
||||
== ' PyList_Append(list, item);\n'
|
||||
assert tfl_2_loc['logicalLocations'] == location['logicalLocations']
|
||||
assert tfl_2_loc['message']['text'] \
|
||||
== "when calling 'PyList_Append', passing NULL from (1) as argument 1"
|
||||
assert tfl_2['nestingLevel'] == 0
|
||||
assert tfl_2['executionOrder'] == 3
|
138
gcc/testsuite/libdiagnostics.dg/test-warning-with-path.c
Normal file
138
gcc/testsuite/libdiagnostics.dg/test-warning-with-path.c
Normal file
@ -0,0 +1,138 @@
|
||||
/* Example of emitting a warning with an execution path.
|
||||
|
||||
TODO:
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________111111111122222222223333333333444444444455555555556
|
||||
123456789012345678901234567890123456789012345678901234567890
|
||||
begin fake source
|
||||
PyObject *
|
||||
make_a_list_of_random_ints_badly(PyObject *self,
|
||||
PyObject *args)
|
||||
{
|
||||
PyObject *list, *item;
|
||||
long count, i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i", &count)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list = PyList_New(0);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
item = PyLong_FromLong(random());
|
||||
PyList_Append(list, item);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
end fake source
|
||||
*/
|
||||
const int final_line_num = __LINE__ - 4; /* line of "return list;" */
|
||||
|
||||
/* begin line consts */
|
||||
const int line_num_call_to_PyList_New = final_line_num - 7;
|
||||
const int line_num_for_loop = final_line_num - 5;
|
||||
const int line_num_call_to_PyList_Append = final_line_num - 3;
|
||||
/* end line consts */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-warning-with-path.c.exe",
|
||||
"test-warning-with-path.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
/* begin full example */
|
||||
/* begin create phys locs */
|
||||
const diagnostic_physical_location *loc_call_to_PyList_New
|
||||
= make_range (diag_mgr, main_file, line_num_call_to_PyList_New, 10, 22);
|
||||
const diagnostic_physical_location *loc_for_cond
|
||||
= make_range (diag_mgr, main_file, line_num_for_loop, 15, 23);
|
||||
const diagnostic_physical_location *loc_call_to_PyList_Append
|
||||
= make_range (diag_mgr, main_file, line_num_call_to_PyList_Append, 5, 29);
|
||||
/* end create phys locs */
|
||||
|
||||
/* begin create logical locs */
|
||||
const char *funcname = "make_a_list_of_random_ints_badly";
|
||||
const diagnostic_logical_location *logical_loc
|
||||
= diagnostic_manager_new_logical_location (diag_mgr,
|
||||
DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
|
||||
NULL, /* parent */
|
||||
funcname,
|
||||
funcname,
|
||||
funcname);
|
||||
/* end create logical locs */
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_WARNING);
|
||||
diagnostic_set_location (d, loc_call_to_PyList_Append);
|
||||
diagnostic_set_logical_location (d, logical_loc);
|
||||
|
||||
/* begin path creation */
|
||||
diagnostic_execution_path *path = diagnostic_add_execution_path (d);
|
||||
|
||||
diagnostic_event_id alloc_event_id
|
||||
= diagnostic_execution_path_add_event (path,
|
||||
loc_call_to_PyList_New,
|
||||
logical_loc, 0,
|
||||
"when %qs fails, returning NULL",
|
||||
"PyList_New");
|
||||
diagnostic_execution_path_add_event (path,
|
||||
loc_for_cond,
|
||||
logical_loc, 0,
|
||||
"when %qs", "i < count");
|
||||
diagnostic_execution_path_add_event (path,
|
||||
loc_call_to_PyList_Append,
|
||||
logical_loc, 0,
|
||||
"when calling %qs, passing NULL from %@ as argument %i",
|
||||
"PyList_Append", &alloc_event_id, 1);
|
||||
/* end path creation */
|
||||
|
||||
diagnostic_finish (d,
|
||||
"passing NULL as argument %i to %qs"
|
||||
" which requires a non-NULL parameter",
|
||||
1, "PyList_Append");
|
||||
/* end full example */
|
||||
|
||||
return end_test ();
|
||||
};
|
||||
|
||||
/* Check the output from the text sink.
|
||||
{ dg-begin-multiline-output "" }
|
||||
In function 'make_a_list_of_random_ints_badly':
|
||||
{ dg-end-multiline-output "" }
|
||||
{ dg-regexp "\[^\n\r\]+test-warning-with-path.c:31:5: warning: passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
31 | PyList_Append(list, item);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
'make_a_list_of_random_ints_badly': events 1-3
|
||||
27 | list = PyList_New(0);
|
||||
| ^~~~~~~~~~~~~
|
||||
| |
|
||||
| (1) when 'PyList_New' fails, returning NULL
|
||||
28 |
|
||||
29 | for (i = 0; i < count; i++) {
|
||||
| ~~~~~~~~~
|
||||
| |
|
||||
| (2) when 'i < count'
|
||||
30 | item = PyLong_FromLong(random());
|
||||
31 | PyList_Append(list, item);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
| |
|
||||
| (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-warning-with-path.c "test-warning-with-path-c.py" } } */
|
67
gcc/testsuite/libdiagnostics.dg/test-warning.c
Normal file
67
gcc/testsuite/libdiagnostics.dg/test-warning.c
Normal file
@ -0,0 +1,67 @@
|
||||
/* Example of emitting a warning.
|
||||
|
||||
Intended output is similar to:
|
||||
|
||||
/PATH/test-warning.c:17:8: warning: this is a warning
|
||||
17 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
|
||||
along with the equivalent in SARIF. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
#include "test-helpers.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
begin_test ("test-warning.c.exe",
|
||||
"test-warning.c.sarif",
|
||||
__FILE__, "c");
|
||||
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
8);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr,
|
||||
main_file,
|
||||
line_num,
|
||||
19);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
/* begin quoted source */
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_WARNING);
|
||||
diagnostic_set_location (d, loc_range);
|
||||
|
||||
diagnostic_finish (d, "this is a warning");
|
||||
/* end quoted source */
|
||||
|
||||
return end_test ();
|
||||
}
|
||||
|
||||
/* Verify the output from the text sink.
|
||||
{ dg-regexp "\[^\n\r\]+test-warning.c:17:8: warning: this is a warning" }
|
||||
{ dg-begin-multiline-output "" }
|
||||
17 | PRINT "hello world!";
|
||||
| ^~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-warning.c "test-warning-c.py" } } */
|
@ -0,0 +1,55 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
expected_line_num = 8
|
||||
|
||||
def test_sarif_output(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
|
||||
|
||||
version = sarif['version']
|
||||
assert version == '2.1.0'
|
||||
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
tool = run['tool']
|
||||
assert tool['driver']['name'] == 'test-write-sarif-to-file.c.exe'
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
assert 'workingDirectory' in invocations[0]
|
||||
assert 'startTimeUtc' in invocations[0]
|
||||
assert invocations[0]['executionSuccessful'] == False
|
||||
assert invocations[0]['toolExecutionNotifications'] == []
|
||||
assert 'endTimeUtc' in invocations[0]
|
||||
|
||||
artifacts = run['artifacts']
|
||||
assert len(artifacts) == 1
|
||||
assert artifacts[0]['location']['uri'] \
|
||||
.endswith('test-write-sarif-to-file.c')
|
||||
assert artifacts[0]['sourceLanguage'] == 'c'
|
||||
assert 'PRINT' in artifacts[0]['contents']['text']
|
||||
assert artifacts[0]['roles'] == ["analysisTarget"]
|
||||
|
||||
results = run['results']
|
||||
assert len(results) == 1
|
||||
assert results[0]['ruleId'] == 'error'
|
||||
assert results[0]['level'] == 'error'
|
||||
assert results[0]['message']['text'] == "can't find 'foo'"
|
||||
assert len(results[0]['locations']) == 1
|
||||
location = results[0]['locations'][0]
|
||||
phys_loc = location['physicalLocation']
|
||||
assert phys_loc['artifactLocation']['uri'] \
|
||||
.endswith('test-write-sarif-to-file.c')
|
||||
assert phys_loc['region']['startLine'] == expected_line_num
|
||||
assert phys_loc['region']['startColumn'] == 8
|
||||
assert phys_loc['region']['endColumn'] == 20
|
||||
assert phys_loc['contextRegion']['startLine'] == expected_line_num
|
||||
assert phys_loc['contextRegion']['snippet']['text'] \
|
||||
== 'PRINT "hello world!";\n'
|
55
gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c
Normal file
55
gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* Example of writing diagnostics as SARIF to a file. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
FILE *sarif_outfile = fopen ("test-write-sarif-to-file.c.sarif", "w");
|
||||
if (!sarif_outfile)
|
||||
return -1;
|
||||
|
||||
diagnostic_manager *diag_mgr = diagnostic_manager_new ();
|
||||
diagnostic_manager_set_tool_name (diag_mgr, "test-write-sarif-to-file.c.exe");
|
||||
|
||||
const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c");
|
||||
|
||||
diagnostic_manager_add_sarif_sink (diag_mgr, sarif_outfile, file,
|
||||
DIAGNOSTIC_SARIF_VERSION_2_1_0);
|
||||
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_range);
|
||||
|
||||
diagnostic_finish (d, "can't find %qs", "foo");
|
||||
|
||||
diagnostic_manager_release (diag_mgr);
|
||||
|
||||
fclose (sarif_outfile);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name:
|
||||
{ dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest test-write-sarif-to-file.c "test-write-sarif-to-file-c.py" } } */
|
47
gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c
Normal file
47
gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c
Normal file
@ -0,0 +1,47 @@
|
||||
/* Example of writing diagnostics in text form, but to a file,
|
||||
rather than stderr. */
|
||||
|
||||
#include "libdiagnostics.h"
|
||||
|
||||
/*
|
||||
_________111111111122
|
||||
123456789012345678901
|
||||
PRINT "hello world!";
|
||||
*/
|
||||
const int line_num = __LINE__ - 2;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
FILE *outfile = fopen ("test.txt", "w");
|
||||
if (!outfile)
|
||||
return -1;
|
||||
|
||||
diagnostic_manager *diag_mgr = diagnostic_manager_new ();
|
||||
|
||||
diagnostic_manager_add_text_sink (diag_mgr, outfile,
|
||||
DIAGNOSTIC_COLORIZE_NO);
|
||||
|
||||
const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c");
|
||||
const diagnostic_physical_location *loc_start
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8);
|
||||
const diagnostic_physical_location *loc_end
|
||||
= diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19);
|
||||
const diagnostic_physical_location *loc_range
|
||||
= diagnostic_manager_new_location_from_range (diag_mgr,
|
||||
loc_start,
|
||||
loc_start,
|
||||
loc_end);
|
||||
|
||||
diagnostic *d = diagnostic_begin (diag_mgr,
|
||||
DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_set_location (d, loc_range);
|
||||
|
||||
diagnostic_finish (d, "can't find %qs", "foo");
|
||||
|
||||
diagnostic_manager_release (diag_mgr);
|
||||
|
||||
fclose (outfile);
|
||||
|
||||
return 0;
|
||||
};
|
Loading…
Reference in New Issue
Block a user