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
|
stage1_languages
|
||||||
host_libs_picflag
|
host_libs_picflag
|
||||||
CRAB1_LIBS
|
CRAB1_LIBS
|
||||||
|
enable_libdiagnostics
|
||||||
PICFLAG
|
PICFLAG
|
||||||
host_shared
|
host_shared
|
||||||
gcc_host_pie
|
gcc_host_pie
|
||||||
@ -844,6 +845,7 @@ enable_linker_plugin_configure_flags
|
|||||||
enable_linker_plugin_flags
|
enable_linker_plugin_flags
|
||||||
enable_host_pie
|
enable_host_pie
|
||||||
enable_host_shared
|
enable_host_shared
|
||||||
|
enable_libdiagnostics
|
||||||
enable_stage1_languages
|
enable_stage1_languages
|
||||||
enable_objc_gc
|
enable_objc_gc
|
||||||
with_target_bdw_gc
|
with_target_bdw_gc
|
||||||
@ -1578,6 +1580,7 @@ Optional Features:
|
|||||||
plugins [none]
|
plugins [none]
|
||||||
--enable-host-pie build position independent host executables
|
--enable-host-pie build position independent host executables
|
||||||
--enable-host-shared build host code as shared libraries
|
--enable-host-shared build host code as shared libraries
|
||||||
|
--enable-libdiagnostics build libdiagnostics shared library
|
||||||
--enable-stage1-languages[=all]
|
--enable-stage1-languages[=all]
|
||||||
choose additional languages to build during stage1.
|
choose additional languages to build during stage1.
|
||||||
Mostly useful for compiler development
|
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
|
# 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
|
# 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
|
# 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)
|
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
|
# 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
|
# 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
|
# 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_host_shared = @enable_host_shared@
|
||||||
|
|
||||||
|
enable_libdiagnostics = @enable_libdiagnostics@
|
||||||
|
|
||||||
enable_as_accelerator = @enable_as_accelerator@
|
enable_as_accelerator = @enable_as_accelerator@
|
||||||
|
|
||||||
CPPLIB = ../libcpp/libcpp.a
|
CPPLIB = ../libcpp/libcpp.a
|
||||||
@ -618,6 +620,9 @@ xm_include_list=@xm_include_list@
|
|||||||
xm_defines=@xm_defines@
|
xm_defines=@xm_defines@
|
||||||
lang_checks=
|
lang_checks=
|
||||||
lang_checks_parallelized=
|
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_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_specs_files=@lang_specs_files@
|
||||||
lang_tree_files=@lang_tree_files@
|
lang_tree_files=@lang_tree_files@
|
||||||
@ -1880,6 +1885,10 @@ endif
|
|||||||
# compilation or not.
|
# compilation or not.
|
||||||
ALL_HOST_OBJS = $(ALL_HOST_FRONTEND_OBJS) $(ALL_HOST_BACKEND_OBJS)
|
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 \
|
BACKEND = libbackend.a main.o libcommon-target.a libcommon.a \
|
||||||
$(CPPLIB) $(LIBDECNUMBER)
|
$(CPPLIB) $(LIBDECNUMBER)
|
||||||
|
|
||||||
@ -2186,7 +2195,7 @@ all.cross: native gcc-cross$(exeext) cpp$(exeext) specs \
|
|||||||
libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra
|
libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra
|
||||||
# This is what must be made before installing GCC and converting libraries.
|
# This is what must be made before installing GCC and converting libraries.
|
||||||
start.encap: native xgcc$(exeext) cpp$(exeext) specs \
|
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.
|
# These can't be made until after GCC can run.
|
||||||
rest.encap: lang.rest.encap
|
rest.encap: lang.rest.encap
|
||||||
# This is what is made with the host's compiler
|
# 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 \
|
c-family/cppspec.o $(EXTRA_GCC_OBJS) libcommon-target.a \
|
||||||
$(EXTRA_GCC_LIBS) $(LIBS)
|
$(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.
|
# Dump a specs file to make -B./ read these specs over installed ones.
|
||||||
$(SPECS): xgcc$(exeext)
|
$(SPECS): xgcc$(exeext)
|
||||||
$(GCC_FOR_TARGET) -dumpspecs > tmp-specs
|
$(GCC_FOR_TARGET) -dumpspecs > tmp-specs
|
||||||
@ -3816,6 +3948,10 @@ ifeq ($(enable_plugin),yes)
|
|||||||
install: install-plugin
|
install: install-plugin
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(enable_libdiagnostics),yes)
|
||||||
|
install: install-libdiagnostics
|
||||||
|
endif
|
||||||
|
|
||||||
install-strip: override INSTALL_PROGRAM = $(INSTALL_STRIP_PROGRAM)
|
install-strip: override INSTALL_PROGRAM = $(INSTALL_STRIP_PROGRAM)
|
||||||
ifneq ($(STRIP),)
|
ifneq ($(STRIP),)
|
||||||
install-strip: STRIPPROG = $(STRIP)
|
install-strip: STRIPPROG = $(STRIP)
|
||||||
@ -3992,6 +4128,47 @@ install-driver: installdirs xgcc$(exeext)
|
|||||||
fi; \
|
fi; \
|
||||||
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 the info files.
|
||||||
# $(INSTALL_DATA) might be a relative pathname, so we can't cd into srcdir
|
# $(INSTALL_DATA) might be a relative pathname, so we can't cd into srcdir
|
||||||
# to do the install.
|
# to do the install.
|
||||||
|
26
gcc/configure
vendored
26
gcc/configure
vendored
@ -637,6 +637,8 @@ LD_PICFLAG
|
|||||||
PICFLAG
|
PICFLAG
|
||||||
enable_default_pie
|
enable_default_pie
|
||||||
enable_host_bind_now
|
enable_host_bind_now
|
||||||
|
LIBDIAGNOSTICS
|
||||||
|
enable_libdiagnostics
|
||||||
enable_host_pie
|
enable_host_pie
|
||||||
enable_host_shared
|
enable_host_shared
|
||||||
enable_plugin
|
enable_plugin
|
||||||
@ -1051,6 +1053,7 @@ enable_version_specific_runtime_libs
|
|||||||
enable_plugin
|
enable_plugin
|
||||||
enable_host_shared
|
enable_host_shared
|
||||||
enable_host_pie
|
enable_host_pie
|
||||||
|
enable_libdiagnostics
|
||||||
enable_host_bind_now
|
enable_host_bind_now
|
||||||
enable_libquadmath_support
|
enable_libquadmath_support
|
||||||
with_linker_hash_style
|
with_linker_hash_style
|
||||||
@ -1826,6 +1829,7 @@ Optional Features:
|
|||||||
--enable-plugin enable plugin support
|
--enable-plugin enable plugin support
|
||||||
--enable-host-shared build host code as shared libraries
|
--enable-host-shared build host code as shared libraries
|
||||||
--enable-host-pie build host code as PIE
|
--enable-host-pie build host code as PIE
|
||||||
|
--enable-libdiagnostics build libdiagnostics shared library
|
||||||
--enable-host-bind-now link host code as BIND_NOW
|
--enable-host-bind-now link host code as BIND_NOW
|
||||||
--disable-libquadmath-support
|
--disable-libquadmath-support
|
||||||
disable libquadmath support for Fortran
|
disable libquadmath support for Fortran
|
||||||
@ -21456,7 +21460,7 @@ else
|
|||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 21459 "configure"
|
#line 21463 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
@ -21562,7 +21566,7 @@ else
|
|||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 21565 "configure"
|
#line 21569 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
@ -33838,6 +33842,9 @@ for language in $all_selected_languages
|
|||||||
do
|
do
|
||||||
check_languages="$check_languages check-$language"
|
check_languages="$check_languages check-$language"
|
||||||
done
|
done
|
||||||
|
if test x$enable_libdiagnostics = xyes; then
|
||||||
|
check_languages="$check_languages check-libdiagnostics"
|
||||||
|
fi
|
||||||
|
|
||||||
selftest_languages=
|
selftest_languages=
|
||||||
for language in $all_selected_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
|
# Enable --enable-host-bind-now
|
||||||
# Check whether --enable-host-bind-now was given.
|
# Check whether --enable-host-bind-now was given.
|
||||||
if test "${enable_host_bind_now+set}" = set; then :
|
if test "${enable_host_bind_now+set}" = set; then :
|
||||||
|
@ -7380,6 +7380,9 @@ for language in $all_selected_languages
|
|||||||
do
|
do
|
||||||
check_languages="$check_languages check-$language"
|
check_languages="$check_languages check-$language"
|
||||||
done
|
done
|
||||||
|
if test x$enable_libdiagnostics = xyes; then
|
||||||
|
check_languages="$check_languages check-libdiagnostics"
|
||||||
|
fi
|
||||||
|
|
||||||
selftest_languages=
|
selftest_languages=
|
||||||
for language in $all_selected_languages
|
for language in $all_selected_languages
|
||||||
@ -7612,6 +7615,19 @@ AC_ARG_ENABLE(host-pie,
|
|||||||
[build host code as PIE])])
|
[build host code as PIE])])
|
||||||
AC_SUBST(enable_host_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
|
# Enable --enable-host-bind-now
|
||||||
AC_ARG_ENABLE(host-bind-now,
|
AC_ARG_ENABLE(host-bind-now,
|
||||||
[AS_HELP_STRING([--enable-host-bind-now],
|
[AS_HELP_STRING([--enable-host-bind-now],
|
||||||
|
@ -103,13 +103,14 @@ public:
|
|||||||
m_show_nesting_levels = show_nesting_levels;
|
m_show_nesting_levels = show_nesting_levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label_text get_location_text (const expanded_location &s) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void print_any_cwe (const diagnostic_info &diagnostic);
|
void print_any_cwe (const diagnostic_info &diagnostic);
|
||||||
void print_any_rules (const diagnostic_info &diagnostic);
|
void print_any_rules (const diagnostic_info &diagnostic);
|
||||||
void print_option_information (const diagnostic_info &diagnostic,
|
void print_option_information (const diagnostic_info &diagnostic,
|
||||||
diagnostic_t orig_diag_kind);
|
diagnostic_t orig_diag_kind);
|
||||||
|
|
||||||
label_text get_location_text (const expanded_location &s) const;
|
|
||||||
bool includes_seen_p (const line_map_ordinary *map);
|
bool includes_seen_p (const line_map_ordinary *map);
|
||||||
|
|
||||||
/* For handling diagnostic_buffer. */
|
/* 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).
|
still be built (see @option{--disable-libvtv} to turn off building libvtv).
|
||||||
@option{--disable-vtable-verify} is the default.
|
@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
|
@item --disable-gcov
|
||||||
Specify that the run-time library used for coverage analysis
|
Specify that the run-time library used for coverage analysis
|
||||||
and associated host tools should not be built.
|
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