From bc3597635a708cd91d742c91c6050829cfb4062a Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 29 Nov 2024 18:13:22 -0500 Subject: Rename "libdiagnostics" to "libgdiagnostics" "libdiagnostics" clashes with an existing soname in Debian, as per: https://gcc.gnu.org/pipermail/gcc/2024-November/245175.html Rename it to "libgdiagnostics" for uniqueness. I am being deliberately vague about what the "g" stands for: it could be "gnu", "gcc", or "gpl-licensed" as the reader desires. ChangeLog: * configure.ac: Rename "libdiagnostics" to "libgdiagnostics". * configure: Regenerate. gcc/ChangeLog: * Makefile.in: Rename "libdiagnostics" to "libgdiagnostics". * configure.ac: Likewise. * configure: Regenerate. * doc/install.texi: Rename "libdiagnostics" to "libgdiagnostics". * doc/libdiagnostics/*: Rename to doc/libgdiagnostics, renaming "libdiagnostics" to "libgdiagnostics" throughout. * libdiagnostics++.h: Rename to... * libgdiagnostics++.h: ...this, renaming "libdiagnostics" to "libgdiagnostics" throughout. * libdiagnostics.cc: Rename to... * libgdiagnostics.cc: ...this, renaming "libdiagnostics" to "libgdiagnostics" throughout. * libdiagnostics.h: Rename to... * libgdiagnostics.h: ...this, renaming "libdiagnostics" to "libgdiagnostics" throughout. * libdiagnostics.map: Rename to... * libgdiagnostics.map: ...this, renaming "libdiagnostics" to "libgdiagnostics" throughout. * libsarifreplay.cc: Update for renaming of "libdiagnostics" to "libgdiagnostics". * libsarifreplay.h: Likewise. * sarif-replay.cc: Likewise. gcc/testsuite/ChangeLog: * libdiagnostics.dg/*: Rename to libgdiagnostics.dg, renaming "libdiagnostics" to "libgdiagnostics" throughout. Signed-off-by: David Malcolm --- gcc/Makefile.in | 186 +-- gcc/configure | 29 +- gcc/configure.ac | 20 +- gcc/doc/install.texi | 6 +- gcc/doc/libdiagnostics/Makefile | 20 - gcc/doc/libdiagnostics/conf.py | 27 - gcc/doc/libdiagnostics/index.rst | 113 -- gcc/doc/libdiagnostics/make.bat | 35 - .../libdiagnostics/topics/diagnostic-manager.rst | 58 - gcc/doc/libdiagnostics/topics/diagnostics.rst | 127 -- gcc/doc/libdiagnostics/topics/execution-paths.rst | 93 -- gcc/doc/libdiagnostics/topics/fix-it-hints.rst | 135 -- gcc/doc/libdiagnostics/topics/index.rst | 38 - .../libdiagnostics/topics/logical-locations.rst | 109 -- .../libdiagnostics/topics/message-formatting.rst | 224 --- gcc/doc/libdiagnostics/topics/metadata.rst | 149 -- .../libdiagnostics/topics/physical-locations.rst | 281 ---- gcc/doc/libdiagnostics/topics/retrofitting.rst | 23 - gcc/doc/libdiagnostics/topics/sarif.rst | 51 - gcc/doc/libdiagnostics/topics/text-output.rst | 87 - gcc/doc/libdiagnostics/topics/ux.rst | 26 - gcc/doc/libdiagnostics/tutorial/01-hello-world.rst | 173 -- .../tutorial/02-physical-locations.rst | 260 --- .../tutorial/03-logical-locations.rst | 60 - gcc/doc/libdiagnostics/tutorial/04-notes.rst | 66 - gcc/doc/libdiagnostics/tutorial/05-warnings.rst | 44 - .../libdiagnostics/tutorial/06-fix-it-hints.rst | 61 - .../libdiagnostics/tutorial/07-execution-paths.rst | 141 -- gcc/doc/libdiagnostics/tutorial/example-1.png | Bin 5646 -> 0 bytes gcc/doc/libdiagnostics/tutorial/index.rst | 32 - gcc/doc/libgdiagnostics/Makefile | 20 + gcc/doc/libgdiagnostics/conf.py | 27 + gcc/doc/libgdiagnostics/index.rst | 113 ++ gcc/doc/libgdiagnostics/make.bat | 35 + .../libgdiagnostics/topics/diagnostic-manager.rst | 58 + gcc/doc/libgdiagnostics/topics/diagnostics.rst | 127 ++ gcc/doc/libgdiagnostics/topics/execution-paths.rst | 93 ++ gcc/doc/libgdiagnostics/topics/fix-it-hints.rst | 135 ++ gcc/doc/libgdiagnostics/topics/index.rst | 38 + .../libgdiagnostics/topics/logical-locations.rst | 109 ++ .../libgdiagnostics/topics/message-formatting.rst | 224 +++ gcc/doc/libgdiagnostics/topics/metadata.rst | 149 ++ .../libgdiagnostics/topics/physical-locations.rst | 281 ++++ gcc/doc/libgdiagnostics/topics/retrofitting.rst | 23 + gcc/doc/libgdiagnostics/topics/sarif.rst | 51 + gcc/doc/libgdiagnostics/topics/text-output.rst | 87 + gcc/doc/libgdiagnostics/topics/ux.rst | 26 + .../libgdiagnostics/tutorial/01-hello-world.rst | 173 ++ .../tutorial/02-physical-locations.rst | 260 +++ .../tutorial/03-logical-locations.rst | 60 + gcc/doc/libgdiagnostics/tutorial/04-notes.rst | 66 + gcc/doc/libgdiagnostics/tutorial/05-warnings.rst | 44 + .../libgdiagnostics/tutorial/06-fix-it-hints.rst | 61 + .../tutorial/07-execution-paths.rst | 141 ++ gcc/doc/libgdiagnostics/tutorial/example-1.png | Bin 0 -> 5646 bytes gcc/doc/libgdiagnostics/tutorial/index.rst | 32 + gcc/libdiagnostics++.h | 595 ------- gcc/libdiagnostics.cc | 1682 -------------------- gcc/libdiagnostics.h | 691 -------- gcc/libdiagnostics.map | 72 - gcc/libgdiagnostics++.h | 595 +++++++ gcc/libgdiagnostics.cc | 1682 ++++++++++++++++++++ gcc/libgdiagnostics.h | 691 ++++++++ gcc/libgdiagnostics.map | 72 + gcc/libsarifreplay.cc | 106 +- gcc/libsarifreplay.h | 10 +- gcc/sarif-replay.cc | 12 +- gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp | 296 ---- gcc/testsuite/libdiagnostics.dg/sarif.py | 23 - gcc/testsuite/libdiagnostics.dg/test-dump.c | 69 - gcc/testsuite/libdiagnostics.dg/test-error-c.py | 54 - .../libdiagnostics.dg/test-error-with-note-c.py | 50 - .../libdiagnostics.dg/test-error-with-note.c | 76 - .../libdiagnostics.dg/test-error-with-note.cc | 55 - gcc/testsuite/libdiagnostics.dg/test-error.c | 61 - gcc/testsuite/libdiagnostics.dg/test-error.cc | 47 - gcc/testsuite/libdiagnostics.dg/test-example-1.c | 43 - .../libdiagnostics.dg/test-fix-it-hint-c.py | 46 - gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c | 83 - .../libdiagnostics.dg/test-fix-it-hint.cc | 74 - gcc/testsuite/libdiagnostics.dg/test-helpers++.h | 28 - gcc/testsuite/libdiagnostics.dg/test-helpers.h | 72 - .../libdiagnostics.dg/test-labelled-ranges.c | 71 - .../libdiagnostics.dg/test-labelled-ranges.cc | 64 - .../libdiagnostics.dg/test-labelled-ranges.py | 48 - .../libdiagnostics.dg/test-logical-location-c.py | 37 - .../libdiagnostics.dg/test-logical-location.c | 81 - gcc/testsuite/libdiagnostics.dg/test-metadata-c.py | 45 - gcc/testsuite/libdiagnostics.dg/test-metadata.c | 61 - .../libdiagnostics.dg/test-multiple-lines-c.py | 83 - .../libdiagnostics.dg/test-multiple-lines.c | 78 - .../libdiagnostics.dg/test-no-column-c.py | 35 - gcc/testsuite/libdiagnostics.dg/test-no-column.c | 54 - .../libdiagnostics.dg/test-no-diagnostics-c.py | 42 - .../libdiagnostics.dg/test-no-diagnostics.c | 25 - .../test-note-with-fix-it-hint-c.py | 54 - .../libdiagnostics.dg/test-note-with-fix-it-hint.c | 69 - .../libdiagnostics.dg/test-text-sink-options.c | 59 - gcc/testsuite/libdiagnostics.dg/test-warning-c.py | 54 - .../libdiagnostics.dg/test-warning-with-path-c.py | 108 -- .../libdiagnostics.dg/test-warning-with-path.c | 138 -- gcc/testsuite/libdiagnostics.dg/test-warning.c | 67 - .../test-write-sarif-to-file-c.py | 55 - .../libdiagnostics.dg/test-write-sarif-to-file.c | 55 - .../libdiagnostics.dg/test-write-text-to-file.c | 47 - .../libgdiagnostics.dg/libgdiagnostics.exp | 296 ++++ gcc/testsuite/libgdiagnostics.dg/sarif.py | 23 + gcc/testsuite/libgdiagnostics.dg/test-dump.c | 69 + gcc/testsuite/libgdiagnostics.dg/test-error-c.py | 54 + .../libgdiagnostics.dg/test-error-with-note-c.py | 50 + .../libgdiagnostics.dg/test-error-with-note.c | 76 + .../libgdiagnostics.dg/test-error-with-note.cc | 55 + gcc/testsuite/libgdiagnostics.dg/test-error.c | 61 + gcc/testsuite/libgdiagnostics.dg/test-error.cc | 47 + gcc/testsuite/libgdiagnostics.dg/test-example-1.c | 43 + .../libgdiagnostics.dg/test-fix-it-hint-c.py | 46 + .../libgdiagnostics.dg/test-fix-it-hint.c | 83 + .../libgdiagnostics.dg/test-fix-it-hint.cc | 74 + gcc/testsuite/libgdiagnostics.dg/test-helpers++.h | 28 + gcc/testsuite/libgdiagnostics.dg/test-helpers.h | 72 + .../libgdiagnostics.dg/test-labelled-ranges.c | 71 + .../libgdiagnostics.dg/test-labelled-ranges.cc | 64 + .../libgdiagnostics.dg/test-labelled-ranges.py | 48 + .../libgdiagnostics.dg/test-logical-location-c.py | 37 + .../libgdiagnostics.dg/test-logical-location.c | 81 + .../libgdiagnostics.dg/test-metadata-c.py | 45 + gcc/testsuite/libgdiagnostics.dg/test-metadata.c | 61 + .../libgdiagnostics.dg/test-multiple-lines-c.py | 83 + .../libgdiagnostics.dg/test-multiple-lines.c | 78 + .../libgdiagnostics.dg/test-no-column-c.py | 35 + gcc/testsuite/libgdiagnostics.dg/test-no-column.c | 54 + .../libgdiagnostics.dg/test-no-diagnostics-c.py | 42 + .../libgdiagnostics.dg/test-no-diagnostics.c | 25 + .../test-note-with-fix-it-hint-c.py | 54 + .../test-note-with-fix-it-hint.c | 69 + .../libgdiagnostics.dg/test-text-sink-options.c | 59 + gcc/testsuite/libgdiagnostics.dg/test-warning-c.py | 54 + .../libgdiagnostics.dg/test-warning-with-path-c.py | 108 ++ .../libgdiagnostics.dg/test-warning-with-path.c | 138 ++ gcc/testsuite/libgdiagnostics.dg/test-warning.c | 67 + .../test-write-sarif-to-file-c.py | 55 + .../libgdiagnostics.dg/test-write-sarif-to-file.c | 55 + .../libgdiagnostics.dg/test-write-text-to-file.c | 47 + 143 files changed, 8165 insertions(+), 8164 deletions(-) delete mode 100644 gcc/doc/libdiagnostics/Makefile delete mode 100644 gcc/doc/libdiagnostics/conf.py delete mode 100644 gcc/doc/libdiagnostics/index.rst delete mode 100644 gcc/doc/libdiagnostics/make.bat delete mode 100644 gcc/doc/libdiagnostics/topics/diagnostic-manager.rst delete mode 100644 gcc/doc/libdiagnostics/topics/diagnostics.rst delete mode 100644 gcc/doc/libdiagnostics/topics/execution-paths.rst delete mode 100644 gcc/doc/libdiagnostics/topics/fix-it-hints.rst delete mode 100644 gcc/doc/libdiagnostics/topics/index.rst delete mode 100644 gcc/doc/libdiagnostics/topics/logical-locations.rst delete mode 100644 gcc/doc/libdiagnostics/topics/message-formatting.rst delete mode 100644 gcc/doc/libdiagnostics/topics/metadata.rst delete mode 100644 gcc/doc/libdiagnostics/topics/physical-locations.rst delete mode 100644 gcc/doc/libdiagnostics/topics/retrofitting.rst delete mode 100644 gcc/doc/libdiagnostics/topics/sarif.rst delete mode 100644 gcc/doc/libdiagnostics/topics/text-output.rst delete mode 100644 gcc/doc/libdiagnostics/topics/ux.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/01-hello-world.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/04-notes.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/05-warnings.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst delete mode 100644 gcc/doc/libdiagnostics/tutorial/example-1.png delete mode 100644 gcc/doc/libdiagnostics/tutorial/index.rst create mode 100644 gcc/doc/libgdiagnostics/Makefile create mode 100644 gcc/doc/libgdiagnostics/conf.py create mode 100644 gcc/doc/libgdiagnostics/index.rst create mode 100644 gcc/doc/libgdiagnostics/make.bat create mode 100644 gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst create mode 100644 gcc/doc/libgdiagnostics/topics/diagnostics.rst create mode 100644 gcc/doc/libgdiagnostics/topics/execution-paths.rst create mode 100644 gcc/doc/libgdiagnostics/topics/fix-it-hints.rst create mode 100644 gcc/doc/libgdiagnostics/topics/index.rst create mode 100644 gcc/doc/libgdiagnostics/topics/logical-locations.rst create mode 100644 gcc/doc/libgdiagnostics/topics/message-formatting.rst create mode 100644 gcc/doc/libgdiagnostics/topics/metadata.rst create mode 100644 gcc/doc/libgdiagnostics/topics/physical-locations.rst create mode 100644 gcc/doc/libgdiagnostics/topics/retrofitting.rst create mode 100644 gcc/doc/libgdiagnostics/topics/sarif.rst create mode 100644 gcc/doc/libgdiagnostics/topics/text-output.rst create mode 100644 gcc/doc/libgdiagnostics/topics/ux.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/01-hello-world.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/03-logical-locations.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/04-notes.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/05-warnings.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/06-fix-it-hints.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/07-execution-paths.rst create mode 100644 gcc/doc/libgdiagnostics/tutorial/example-1.png create mode 100644 gcc/doc/libgdiagnostics/tutorial/index.rst delete mode 100644 gcc/libdiagnostics++.h delete mode 100644 gcc/libdiagnostics.cc delete mode 100644 gcc/libdiagnostics.h delete mode 100644 gcc/libdiagnostics.map create mode 100644 gcc/libgdiagnostics++.h create mode 100644 gcc/libgdiagnostics.cc create mode 100644 gcc/libgdiagnostics.h create mode 100644 gcc/libgdiagnostics.map delete mode 100644 gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp delete mode 100644 gcc/testsuite/libdiagnostics.dg/sarif.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-dump.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-error-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-error-with-note-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-error-with-note.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-error-with-note.cc delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-error.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-error.cc delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-example-1.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-fix-it-hint-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.cc delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-helpers++.h delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-helpers.h delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.cc delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-logical-location-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-logical-location.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-metadata-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-metadata.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-multiple-lines-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-no-column-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-no-column.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-no-diagnostics-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-no-diagnostics.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-warning-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-warning-with-path-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-warning-with-path.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-warning.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file-c.py delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c delete mode 100644 gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/libgdiagnostics.exp create mode 100644 gcc/testsuite/libgdiagnostics.dg/sarif.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-dump.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-error-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-error-with-note-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-error-with-note.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-error-with-note.cc create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-error.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-error.cc create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-example-1.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.cc create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-helpers++.h create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-helpers.h create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.cc create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-logical-location.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-metadata-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-metadata.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-multiple-lines-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-no-column-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-no-column.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-text-sink-options.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-warning-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-warning-with-path.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-warning.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file-c.py create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file.c create mode 100644 gcc/testsuite/libgdiagnostics.dg/test-write-text-to-file.c (limited to 'gcc') diff --git a/gcc/Makefile.in b/gcc/Makefile.in index ead8d2e..d2fe821 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -436,7 +436,7 @@ endif enable_host_shared = @enable_host_shared@ -enable_libdiagnostics = @enable_libdiagnostics@ +enable_libgdiagnostics = @enable_libgdiagnostics@ enable_as_accelerator = @enable_as_accelerator@ @@ -620,8 +620,8 @@ xm_include_list=@xm_include_list@ xm_defines=@xm_defines@ lang_checks= lang_checks_parallelized= -ifeq (@enable_libdiagnostics@,yes) -lang_checks += check-libdiagnostics check-sarif-replay +ifeq (@enable_libgdiagnostics@,yes) +lang_checks += check-libgdiagnostics check-sarif-replay endif lang_opt_files=@lang_opt_files@ $(srcdir)/c-family/c.opt $(srcdir)/common.opt $(srcdir)/params.opt $(srcdir)/analyzer/analyzer.opt lang_specs_files=@lang_specs_files@ @@ -1888,8 +1888,8 @@ endif # compilation or not. ALL_HOST_OBJS = $(ALL_HOST_FRONTEND_OBJS) $(ALL_HOST_BACKEND_OBJS) -ifeq (@enable_libdiagnostics@,yes) -ALL_HOST_OBJS += $(libdiagnostics_OBJS) $(SARIF_REPLAY_OBJS) +ifeq (@enable_libgdiagnostics@,yes) +ALL_HOST_OBJS += $(libgdiagnostics_OBJS) $(SARIF_REPLAY_OBJS) endif BACKEND = libbackend.a main.o libcommon-target.a libcommon.a \ @@ -2198,7 +2198,7 @@ all.cross: native gcc-cross$(exeext) cpp$(exeext) specs \ libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra # This is what must be made before installing GCC and converting libraries. start.encap: native xgcc$(exeext) cpp$(exeext) specs \ - libgcc-support lang.start.encap @LIBDIAGNOSTICS@ @GENINSRC@ srcextra + libgcc-support lang.start.encap @LIBGDIAGNOSTICS@ @GENINSRC@ srcextra # These can't be made until after GCC can run. rest.encap: lang.rest.encap # This is what is made with the host's compiler @@ -2288,133 +2288,133 @@ cpp$(exeext): $(GCC_OBJS) c-family/cppspec.o libcommon-target.a $(LIBDEPS) \ $(EXTRA_GCC_LIBS) $(LIBS) -libdiagnostics_OBJS = libdiagnostics.o \ +libgdiagnostics_OBJS = libgdiagnostics.o \ libcommon.a -# libdiagnostics +# libgdiagnostics -LIBDIAGNOSTICS_VERSION_NUM = 0 -LIBDIAGNOSTICS_MINOR_NUM = 0 -LIBDIAGNOSTICS_RELEASE_NUM = 1 +LIBGDIAGNOSTICS_VERSION_NUM = 0 +LIBGDIAGNOSTICS_MINOR_NUM = 0 +LIBGDIAGNOSTICS_RELEASE_NUM = 1 ifneq (,$(findstring mingw,$(target))) -LIBDIAGNOSTICS_FILENAME = libdiagnostics-$(LIBDIAGNOSTICS_VERSION_NUM).dll -LIBDIAGNOSTICS_IMPORT_LIB = libdiagnostics.dll.a +LIBGDIAGNOSTICS_FILENAME = libgdiagnostics-$(LIBGDIAGNOSTICS_VERSION_NUM).dll +LIBGDIAGNOSTICS_IMPORT_LIB = libgdiagnostics.dll.a -libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) +libgdiagnostics: $(LIBGDIAGNOSTICS_FILENAME) else ifneq (,$(findstring darwin,$(host))) -LIBDIAGNOSTICS_AGE = 1 -LIBDIAGNOSTICS_BASENAME = libdiagnostics +LIBGDIAGNOSTICS_AGE = 1 +LIBGDIAGNOSTICS_BASENAME = libgdiagnostics -LIBDIAGNOSTICS_SONAME = \ - ${libdir}/$(LIBDIAGNOSTICS_BASENAME).$(LIBDIAGNOSTICS_VERSION_NUM).dylib -LIBDIAGNOSTICS_FILENAME = $(LIBDIAGNOSTICS_BASENAME).$(LIBDIAGNOSTICS_VERSION_NUM).dylib -LIBDIAGNOSTICS_LINKER_NAME = $(LIBDIAGNOSTICS_BASENAME).dylib +LIBGDIAGNOSTICS_SONAME = \ + ${libdir}/$(LIBGDIAGNOSTICS_BASENAME).$(LIBGDIAGNOSTICS_VERSION_NUM).dylib +LIBGDIAGNOSTICS_FILENAME = $(LIBGDIAGNOSTICS_BASENAME).$(LIBGDIAGNOSTICS_VERSION_NUM).dylib +LIBGDIAGNOSTICS_LINKER_NAME = $(LIBGDIAGNOSTICS_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" +# We have to define a LIBGDIAGNOSTICS_COMMA here, otherwise the commas in the "true" # result are treated as separators by the $(if). -LIBDIAGNOSTICS_COMMA := , -LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION = \ +LIBGDIAGNOSTICS_COMMA := , +LIBGDIAGNOSTICS_VERSION_SCRIPT_OPTION = \ $(if $(LD_VERSION_SCRIPT_OPTION),\ - -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBDIAGNOSTICS_COMMA)$(srcdir)/libdiagnostics.map) + -Wl$(LIBGDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBGDIAGNOSTICS_COMMA)$(srcdir)/libgdiagnostics.map) -LIBDIAGNOSTICS_SONAME_OPTION = \ +LIBGDIAGNOSTICS_SONAME_OPTION = \ $(if $(LD_SONAME_OPTION), \ - -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBDIAGNOSTICS_COMMA)$(LIBDIAGNOSTICS_SONAME)) + -Wl$(LIBGDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBGDIAGNOSTICS_COMMA)$(LIBGDIAGNOSTICS_SONAME)) -LIBDIAGNOSTICS_SONAME_SYMLINK = $(LIBDIAGNOSTICS_FILENAME) -LIBDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBDIAGNOSTICS_LINKER_NAME) +LIBGDIAGNOSTICS_SONAME_SYMLINK = $(LIBGDIAGNOSTICS_FILENAME) +LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBGDIAGNOSTICS_LINKER_NAME) -libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \ - $(LIBDIAGNOSTICS_SYMLINK) \ - $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) +libgdiagnostics: $(LIBGDIAGNOSTICS_FILENAME) \ + $(LIBGDIAGNOSTICS_SYMLINK) \ + $(LIBGDIAGNOSTICS_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) +LIBGDIAGNOSTICS_LINKER_NAME = libgdiagnostics.so +LIBGDIAGNOSTICS_SONAME = $(LIBGDIAGNOSTICS_LINKER_NAME).$(LIBGDIAGNOSTICS_VERSION_NUM) +LIBGDIAGNOSTICS_FILENAME = \ + $(LIBGDIAGNOSTICS_SONAME).$(LIBGDIAGNOSTICS_MINOR_NUM).$(LIBGDIAGNOSTICS_RELEASE_NUM) -LIBDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBDIAGNOSTICS_LINKER_NAME) -LIBDIAGNOSTICS_SONAME_SYMLINK = $(LIBDIAGNOSTICS_SONAME) +LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBGDIAGNOSTICS_LINKER_NAME) +LIBGDIAGNOSTICS_SONAME_SYMLINK = $(LIBGDIAGNOSTICS_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" +# We have to define a LIBGDIAGNOSTICS_COMMA here, otherwise the commas in the "true" # result are treated as separators by the $(if). -LIBDIAGNOSTICS_COMMA := , -LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION = \ +LIBGDIAGNOSTICS_COMMA := , +LIBGDIAGNOSTICS_VERSION_SCRIPT_OPTION = \ $(if $(LD_VERSION_SCRIPT_OPTION),\ - -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBDIAGNOSTICS_COMMA)$(srcdir)/libdiagnostics.map) + -Wl$(LIBGDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBGDIAGNOSTICS_COMMA)$(srcdir)/libgdiagnostics.map) -LIBDIAGNOSTICS_SONAME_OPTION = \ +LIBGDIAGNOSTICS_SONAME_OPTION = \ $(if $(LD_SONAME_OPTION), \ - -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBDIAGNOSTICS_COMMA)$(LIBDIAGNOSTICS_SONAME)) + -Wl$(LIBGDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBGDIAGNOSTICS_COMMA)$(LIBGDIAGNOSTICS_SONAME)) -libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \ - $(LIBDIAGNOSTICS_SYMLINK) \ - $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) +libgdiagnostics: $(LIBGDIAGNOSTICS_FILENAME) \ + $(LIBGDIAGNOSTICS_SYMLINK) \ + $(LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK) endif endif -libdiagnostics.serial = $(LIBDIAGNOSTICS_FILENAME) +libgdiagnostics.serial = $(LIBGDIAGNOSTICS_FILENAME) # Tell GNU make to ignore these if they exist. -.PHONY: libdiagnostics +.PHONY: libgdiagnostics ifneq (,$(findstring mingw,$(target))) # Create import library -LIBDIAGNOSTICS_EXTRA_OPTS = -Wl,--out-implib,$(LIBDIAGNOSTICS_IMPORT_LIB) +LIBGDIAGNOSTICS_EXTRA_OPTS = -Wl,--out-implib,$(LIBGDIAGNOSTICS_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) +LIBGDIAGNOSTICS_EXTRA_OPTS = -Wl,-compatibility_version,$(LIBGDIAGNOSTICS_VERSION_NUM) \ + -Wl,-current_version,$(LIBGDIAGNOSTICS_VERSION_NUM).$(LIBGDIAGNOSTICS_MINOR_NUM).$(LIBGDIAGNOSTICS_AGE) \ + $(LIBGDIAGNOSTICS_VERSION_SCRIPT_OPTION) \ + $(LIBGDIAGNOSTICS_SONAME_OPTION) else -LIBDIAGNOSTICS_EXTRA_OPTS = $(LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION) \ - $(LIBDIAGNOSTICS_SONAME_OPTION) +LIBGDIAGNOSTICS_EXTRA_OPTS = $(LIBGDIAGNOSTICS_VERSION_SCRIPT_OPTION) \ + $(LIBGDIAGNOSTICS_SONAME_OPTION) endif endif -$(LIBDIAGNOSTICS_FILENAME): $(libdiagnostics_OBJS) $(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \ - $(LIBDEPS) $(srcdir)/libdiagnostics.map - @$(call LINK_PROGRESS,$(INDEX.libdiagnostics),start) +$(LIBGDIAGNOSTICS_FILENAME): $(libgdiagnostics_OBJS) $(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \ + $(LIBDEPS) $(srcdir)/libgdiagnostics.map + @$(call LINK_PROGRESS,$(INDEX.libgdiagnostics),start) +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ -shared \ - $(libdiagnostics_OBJS) \ + $(libgdiagnostics_OBJS) \ $(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \ - $(LIBDIAGNOSTICS_EXTRA_OPTS) - @$(call LINK_PROGRESS,$(INDEX.libdiagnostics),end) + $(LIBGDIAGNOSTICS_EXTRA_OPTS) + @$(call LINK_PROGRESS,$(INDEX.libgdiagnostics),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) +$(LIBGDIAGNOSTICS_SONAME_SYMLINK): $(LIBGDIAGNOSTICS_FILENAME) + ln -sf $(LIBGDIAGNOSTICS_FILENAME) $(LIBGDIAGNOSTICS_SONAME_SYMLINK) endif -$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK): $(LIBDIAGNOSTICS_SONAME_SYMLINK) - ln -sf $(LIBDIAGNOSTICS_SONAME_SYMLINK) $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) +$(LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK): $(LIBGDIAGNOSTICS_SONAME_SYMLINK) + ln -sf $(LIBGDIAGNOSTICS_SONAME_SYMLINK) $(LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK) endif -# sarif-replay: a command-line tool that uses libdiagnostics to +# sarif-replay: a command-line tool that uses libgdiagnostics to # replay SARIF files -sarif-replay: $(SARIF_REPLAY_OBJS) $(LIBDIAGNOSTICS_FILENAME) +sarif-replay: $(SARIF_REPLAY_OBJS) $(LIBGDIAGNOSTICS_FILENAME) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ - $(SARIF_REPLAY_OBJS) $(LIBDIAGNOSTICS_FILENAME) $(LIBS) + $(SARIF_REPLAY_OBJS) $(LIBGDIAGNOSTICS_FILENAME) $(LIBS) # Dump a specs file to make -B./ read these specs over installed ones. $(SPECS): xgcc$(exeext) @@ -3957,8 +3957,8 @@ ifeq ($(enable_plugin),yes) install: install-plugin endif -ifeq ($(enable_libdiagnostics),yes) -install: install-libdiagnostics +ifeq ($(enable_libgdiagnostics),yes) +install: install-libgdiagnostics endif install-strip: override INSTALL_PROGRAM = $(INSTALL_STRIP_PROGRAM) @@ -4137,46 +4137,46 @@ install-driver: installdirs xgcc$(exeext) fi; \ fi -libdiagnostics.install-headers: installdirs - $(INSTALL_DATA) $(srcdir)/libdiagnostics.h \ - $(DESTDIR)$(includedir)/libdiagnostics.h - $(INSTALL_DATA) $(srcdir)/libdiagnostics++.h \ - $(DESTDIR)$(includedir)/libdiagnostics++.h +libgdiagnostics.install-headers: installdirs + $(INSTALL_DATA) $(srcdir)/libgdiagnostics.h \ + $(DESTDIR)$(includedir)/libgdiagnostics.h + $(INSTALL_DATA) $(srcdir)/libgdiagnostics++.h \ + $(DESTDIR)$(includedir)/libgdiagnostics++.h ifneq (,$(findstring mingw,$(target))) -libdiagnostics.install-common: installdirs libdiagnostics.install-headers +libgdiagnostics.install-common: installdirs libgdiagnostics.install-headers # Install import library - $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_IMPORT_LIB) \ - $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_IMPORT_LIB) + $(INSTALL_PROGRAM) $(LIBGDIAGNOSTICS_IMPORT_LIB) \ + $(DESTDIR)$(libdir)/$(LIBGDIAGNOSTICS_IMPORT_LIB) # Install DLL file - $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \ - $(DESTDIR)$(bindir)/$(LIBDIAGNOSTICS_FILENAME) + $(INSTALL_PROGRAM) $(LIBGDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(bindir)/$(LIBGDIAGNOSTICS_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) +libgdiagnostics.install-common: installdirs libgdiagnostics.install-headers + $(INSTALL_PROGRAM) $(LIBGDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(libdir)/$(LIBGDIAGNOSTICS_FILENAME) ln -sf \ - $(LIBDIAGNOSTICS_SONAME_SYMLINK)\ - $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) + $(LIBGDIAGNOSTICS_SONAME_SYMLINK)\ + $(DESTDIR)$(libdir)/$(LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK) else -libdiagnostics.install-common: installdirs libdiagnostics.install-headers - $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \ - $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_FILENAME) +libgdiagnostics.install-common: installdirs libgdiagnostics.install-headers + $(INSTALL_PROGRAM) $(LIBGDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(libdir)/$(LIBGDIAGNOSTICS_FILENAME) ln -sf \ - $(LIBDIAGNOSTICS_FILENAME) \ - $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_SONAME_SYMLINK) + $(LIBGDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(libdir)/$(LIBGDIAGNOSTICS_SONAME_SYMLINK) ln -sf \ - $(LIBDIAGNOSTICS_SONAME_SYMLINK)\ - $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) + $(LIBGDIAGNOSTICS_SONAME_SYMLINK)\ + $(DESTDIR)$(libdir)/$(LIBGDIAGNOSTICS_LINKER_NAME_SYMLINK) endif endif -install-libdiagnostics: libdiagnostics.install-common sarif-replay +install-libgdiagnostics: libgdiagnostics.install-common sarif-replay -rm -f $(DESTDIR)$(bindir)/sarif-replay -$(INSTALL_PROGRAM) sarif-replay $(DESTDIR)$(bindir)/sarif-replay diff --git a/gcc/configure b/gcc/configure index 019ded4..8bb71cf 100755 --- a/gcc/configure +++ b/gcc/configure @@ -637,8 +637,8 @@ LD_PICFLAG PICFLAG enable_default_pie enable_host_bind_now -LIBDIAGNOSTICS -enable_libdiagnostics +LIBGDIAGNOSTICS +enable_libgdiagnostics enable_host_pie enable_host_shared enable_plugin @@ -1053,7 +1053,7 @@ enable_version_specific_runtime_libs enable_plugin enable_host_shared enable_host_pie -enable_libdiagnostics +enable_libgdiagnostics enable_host_bind_now enable_libquadmath_support with_linker_hash_style @@ -1829,7 +1829,8 @@ Optional Features: --enable-plugin enable plugin support --enable-host-shared build host code as shared libraries --enable-host-pie build host code as PIE - --enable-libdiagnostics build libdiagnostics shared library + --enable-libgdiagnostics + build libgdiagnostics shared library --enable-host-bind-now link host code as BIND_NOW --disable-libquadmath-support disable libquadmath support for Fortran @@ -21460,7 +21461,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 21463 "configure" +#line 21464 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -21566,7 +21567,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 21569 "configure" +#line 21570 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -33800,8 +33801,8 @@ for language in $all_selected_languages do check_languages="$check_languages check-$language" done -if test x$enable_libdiagnostics = xyes; then - check_languages="$check_languages check-libdiagnostics check-sarif-replay" +if test x$enable_libgdiagnostics = xyes; then + check_languages="$check_languages check-libgdiagnostics check-sarif-replay" fi selftest_languages= @@ -34241,17 +34242,17 @@ fi -# Check whether --enable-libdiagnostics was given. -if test "${enable_libdiagnostics+set}" = set; then : - enableval=$enable_libdiagnostics; +# Check whether --enable-libgdiagnostics was given. +if test "${enable_libgdiagnostics+set}" = set; then : + enableval=$enable_libgdiagnostics; fi -if test "$enable_libdiagnostics" = "yes"; then - LIBDIAGNOSTICS='libdiagnostics sarif-replay' +if test "$enable_libgdiagnostics" = "yes"; then + LIBGDIAGNOSTICS='libgdiagnostics sarif-replay' else - LIBDIAGNOSTICS='' + LIBGDIAGNOSTICS='' fi diff --git a/gcc/configure.ac b/gcc/configure.ac index c546432..88a1a44 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -7364,8 +7364,8 @@ for language in $all_selected_languages do check_languages="$check_languages check-$language" done -if test x$enable_libdiagnostics = xyes; then - check_languages="$check_languages check-libdiagnostics check-sarif-replay" +if test x$enable_libgdiagnostics = xyes; then + check_languages="$check_languages check-libgdiagnostics check-sarif-replay" fi selftest_languages= @@ -7599,17 +7599,17 @@ AC_ARG_ENABLE(host-pie, [build host code as PIE])]) AC_SUBST(enable_host_pie) -AC_ARG_ENABLE(libdiagnostics, -[AS_HELP_STRING([--enable-libdiagnostics], - [build libdiagnostics shared library])]) -AC_SUBST(enable_libdiagnostics) +AC_ARG_ENABLE(libgdiagnostics, +[AS_HELP_STRING([--enable-libgdiagnostics], + [build libgdiagnostics shared library])]) +AC_SUBST(enable_libgdiagnostics) -if test "$enable_libdiagnostics" = "yes"; then - LIBDIAGNOSTICS='libdiagnostics sarif-replay' +if test "$enable_libgdiagnostics" = "yes"; then + LIBGDIAGNOSTICS='libgdiagnostics sarif-replay' else - LIBDIAGNOSTICS='' + LIBGDIAGNOSTICS='' fi -AC_SUBST(LIBDIAGNOSTICS) +AC_SUBST(LIBGDIAGNOSTICS) # Enable --enable-host-bind-now diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi index 03575c2..97d9aaf 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -1231,8 +1231,8 @@ virtual calls in verifiable mode at all. However the libvtv library will still be built (see @option{--disable-libvtv} to turn off building libvtv). @option{--disable-vtable-verify} is the default. -@item --enable-libdiagnostics -Specify whether to build @code{libdiagnostics}, a shared library exposing +@item --enable-libgdiagnostics +Specify whether to build @code{libgdiagnostics}, a shared library exposing GCC's diagnostics capabilities via a C API, and a C++ wrapper API adding ``syntactic sugar''. @@ -1241,7 +1241,7 @@ This option requires @option{--enable-host-shared} on non-Windows hosts. This option also enables @code{sarif-replay}, a command-line tool for viewing @uref{https://sarif.info/,,SARIF files}. @code{sarif-replay} takes one or more @code{.sarif} files as input and attempts to replay any -diagnostics within them to stderr (via @code{libdiagnostics}) in the style +diagnostics within them to stderr (via @code{libgdiagnostics}) in the style of GCC's diagnostics. @item --disable-gcov diff --git a/gcc/doc/libdiagnostics/Makefile b/gcc/doc/libdiagnostics/Makefile deleted file mode 100644 index d4bb2cb..0000000 --- a/gcc/doc/libdiagnostics/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# 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) diff --git a/gcc/doc/libdiagnostics/conf.py b/gcc/doc/libdiagnostics/conf.py deleted file mode 100644 index 1ff7552..0000000 --- a/gcc/doc/libdiagnostics/conf.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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'] diff --git a/gcc/doc/libdiagnostics/index.rst b/gcc/doc/libdiagnostics/index.rst deleted file mode 100644 index a05eb4e..0000000 --- a/gcc/doc/libdiagnostics/index.rst +++ /dev/null @@ -1,113 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -libdiagnostics -============== - -This document describes `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 `, - 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 `:: - - 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` 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 ` - -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` diff --git a/gcc/doc/libdiagnostics/make.bat b/gcc/doc/libdiagnostics/make.bat deleted file mode 100644 index 954237b..0000000 --- a/gcc/doc/libdiagnostics/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@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 diff --git a/gcc/doc/libdiagnostics/topics/diagnostic-manager.rst b/gcc/doc/libdiagnostics/topics/diagnostic-manager.rst deleted file mode 100644 index 7f86f6b..0000000 --- a/gcc/doc/libdiagnostics/topics/diagnostic-manager.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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. diff --git a/gcc/doc/libdiagnostics/topics/diagnostics.rst b/gcc/doc/libdiagnostics/topics/diagnostics.rst deleted file mode 100644 index 66f0a25..0000000 --- a/gcc/doc/libdiagnostics/topics/diagnostics.rst +++ /dev/null @@ -1,127 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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. diff --git a/gcc/doc/libdiagnostics/topics/execution-paths.rst b/gcc/doc/libdiagnostics/topics/execution-paths.rst deleted file mode 100644 index 3f4109c..0000000 --- a/gcc/doc/libdiagnostics/topics/execution-paths.rst +++ /dev/null @@ -1,93 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `_). diff --git a/gcc/doc/libdiagnostics/topics/fix-it-hints.rst b/gcc/doc/libdiagnostics/topics/fix-it-hints.rst deleted file mode 100644 index 08acb71..0000000 --- a/gcc/doc/libdiagnostics/topics/fix-it-hints.rst +++ /dev/null @@ -1,135 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `_). - -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. diff --git a/gcc/doc/libdiagnostics/topics/index.rst b/gcc/doc/libdiagnostics/topics/index.rst deleted file mode 100644 index 064340b..0000000 --- a/gcc/doc/libdiagnostics/topics/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 diff --git a/gcc/doc/libdiagnostics/topics/logical-locations.rst b/gcc/doc/libdiagnostics/topics/logical-locations.rst deleted file mode 100644 index 85900b6..0000000 --- a/gcc/doc/libdiagnostics/topics/logical-locations.rst +++ /dev/null @@ -1,109 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `, 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 `_). - - .. 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 `_). - - ``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 `_). - - ``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 `_). - -.. 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. diff --git a/gcc/doc/libdiagnostics/topics/message-formatting.rst b/gcc/doc/libdiagnostics/topics/message-formatting.rst deleted file mode 100644 index 9d42f89..0000000 --- a/gcc/doc/libdiagnostics/topics/message-formatting.rst +++ /dev/null @@ -1,224 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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. */ - - diff --git a/gcc/doc/libdiagnostics/topics/metadata.rst b/gcc/doc/libdiagnostics/topics/metadata.rst deleted file mode 100644 index c62792a..0000000 --- a/gcc/doc/libdiagnostics/topics/metadata.rst +++ /dev/null @@ -1,149 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `, 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 ` 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 ` as the value for the - ``name`` property of the ``driver`` - (`SARIF v2.1.0 §3.19.8 `_). - -.. 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 ` as - the value for the ``fullName`` property of the ``driver`` - (`SARIF v2.1.0 §3.19.9 `_). - - 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 `_):: - - 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 `_):: - - 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 `_:: - - /* 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); - | ^~~~~~~~~~ diff --git a/gcc/doc/libdiagnostics/topics/physical-locations.rst b/gcc/doc/libdiagnostics/topics/physical-locations.rst deleted file mode 100644 index bad2b8d..0000000 --- a/gcc/doc/libdiagnostics/topics/physical-locations.rst +++ /dev/null @@ -1,281 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `, -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 v2.1.0 §3.24.10 `_). - See - `SARIF v2.1.0 Appendix J `_ - 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 * diff --git a/gcc/doc/libdiagnostics/topics/retrofitting.rst b/gcc/doc/libdiagnostics/topics/retrofitting.rst deleted file mode 100644 index d034057..0000000 --- a/gcc/doc/libdiagnostics/topics/retrofitting.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. default-domain:: c - -Adding libdiagnostics to an existing project -============================================ - -TODO diff --git a/gcc/doc/libdiagnostics/topics/sarif.rst b/gcc/doc/libdiagnostics/topics/sarif.rst deleted file mode 100644 index 3fd75ed..0000000 --- a/gcc/doc/libdiagnostics/topics/sarif.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. default-domain:: c - -SARIF support -============= - -`SARIF `_ 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 diff --git a/gcc/doc/libdiagnostics/topics/text-output.rst b/gcc/doc/libdiagnostics/topics/text-output.rst deleted file mode 100644 index 32b2a54..0000000 --- a/gcc/doc/libdiagnostics/topics/text-output.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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. diff --git a/gcc/doc/libdiagnostics/topics/ux.rst b/gcc/doc/libdiagnostics/topics/ux.rst deleted file mode 100644 index fc96e17..0000000 --- a/gcc/doc/libdiagnostics/topics/ux.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. default-domain:: c - -User Experience -=============== - -Refer to -`GCC's user experience guidelines `_ -for notes on -`what makes a good diagnostic `_. diff --git a/gcc/doc/libdiagnostics/tutorial/01-hello-world.rst b/gcc/doc/libdiagnostics/tutorial/01-hello-world.rst deleted file mode 100644 index 4635687..0000000 --- a/gcc/doc/libdiagnostics/tutorial/01-hello-world.rst +++ /dev/null @@ -1,173 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `_). - -.. 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. diff --git a/gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst b/gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst deleted file mode 100644 index 2e429de..0000000 --- a/gcc/doc/libdiagnostics/tutorial/02-physical-locations.rst +++ /dev/null @@ -1,260 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 - -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 - -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 - | ^~~~~ - -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 - | ^~~~~ - -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. diff --git a/gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst b/gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst deleted file mode 100644 index d36ac09..0000000 --- a/gcc/doc/libdiagnostics/tutorial/03-logical-locations.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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`. diff --git a/gcc/doc/libdiagnostics/tutorial/04-notes.rst b/gcc/doc/libdiagnostics/tutorial/04-notes.rst deleted file mode 100644 index 117eb6f..0000000 --- a/gcc/doc/libdiagnostics/tutorial/04-notes.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 - | ^~~~~ - 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 - | ^~~~~ - 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 `_). - -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. diff --git a/gcc/doc/libdiagnostics/tutorial/05-warnings.rst b/gcc/doc/libdiagnostics/tutorial/05-warnings.rst deleted file mode 100644 index 1512ae7..0000000 --- a/gcc/doc/libdiagnostics/tutorial/05-warnings.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 - | ^~~~~ - -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. diff --git a/gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst b/gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst deleted file mode 100644 index 9486ab7..0000000 --- a/gcc/doc/libdiagnostics/tutorial/06-fix-it-hints.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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>`. diff --git a/gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst b/gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst deleted file mode 100644 index 0fbbed2..0000000 --- a/gcc/doc/libdiagnostics/tutorial/07-execution-paths.rst +++ /dev/null @@ -1,141 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -.. 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 `_. - -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" `_). - -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 `_). - -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>`. diff --git a/gcc/doc/libdiagnostics/tutorial/example-1.png b/gcc/doc/libdiagnostics/tutorial/example-1.png deleted file mode 100644 index d637103..0000000 Binary files a/gcc/doc/libdiagnostics/tutorial/example-1.png and /dev/null differ diff --git a/gcc/doc/libdiagnostics/tutorial/index.rst b/gcc/doc/libdiagnostics/tutorial/index.rst deleted file mode 100644 index 6ea6866..0000000 --- a/gcc/doc/libdiagnostics/tutorial/index.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. Copyright (C) 2024 Free Software Foundation, Inc. - Originally contributed by David Malcolm - - 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 - . - -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 diff --git a/gcc/doc/libgdiagnostics/Makefile b/gcc/doc/libgdiagnostics/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/gcc/doc/libgdiagnostics/Makefile @@ -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) diff --git a/gcc/doc/libgdiagnostics/conf.py b/gcc/doc/libgdiagnostics/conf.py new file mode 100644 index 0000000..8e92e12 --- /dev/null +++ b/gcc/doc/libgdiagnostics/conf.py @@ -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 = 'libgdiagnostics' +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'] diff --git a/gcc/doc/libgdiagnostics/index.rst b/gcc/doc/libgdiagnostics/index.rst new file mode 100644 index 0000000..853331d --- /dev/null +++ b/gcc/doc/libgdiagnostics/index.rst @@ -0,0 +1,113 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +libgdiagnostics +=============== + +This document describes `libgdiagnostics `_, +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 `, + 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 `:: + + 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` 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 ` + +There are actually two APIs for the library: + +* a pure C API: ``libgdiagnostics.h`` + +* a C++ wrapper API: ``libgdiagnostics+.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 + +libgdiagnostics 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` diff --git a/gcc/doc/libgdiagnostics/make.bat b/gcc/doc/libgdiagnostics/make.bat new file mode 100644 index 0000000..954237b --- /dev/null +++ b/gcc/doc/libgdiagnostics/make.bat @@ -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 diff --git a/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst b/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst new file mode 100644 index 0000000..e370cfc --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst @@ -0,0 +1,58 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +Diagnostic Managers +=================== + +.. type:: diagnostic_manager; + +A :type:`diagnostic_manager` is an opaque bundle of state for a client of +libgdiagnostics. + +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. diff --git a/gcc/doc/libgdiagnostics/topics/diagnostics.rst b/gcc/doc/libgdiagnostics/topics/diagnostics.rst new file mode 100644 index 0000000..66f0a25 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/diagnostics.rst @@ -0,0 +1,127 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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. diff --git a/gcc/doc/libgdiagnostics/topics/execution-paths.rst b/gcc/doc/libgdiagnostics/topics/execution-paths.rst new file mode 100644 index 0000000..3f4109c --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/execution-paths.rst @@ -0,0 +1,93 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 `_). diff --git a/gcc/doc/libgdiagnostics/topics/fix-it-hints.rst b/gcc/doc/libgdiagnostics/topics/fix-it-hints.rst new file mode 100644 index 0000000..08acb71 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/fix-it-hints.rst @@ -0,0 +1,135 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 `_). + +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. diff --git a/gcc/doc/libgdiagnostics/topics/index.rst b/gcc/doc/libgdiagnostics/topics/index.rst new file mode 100644 index 0000000..064340b --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/index.rst @@ -0,0 +1,38 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 diff --git a/gcc/doc/libgdiagnostics/topics/logical-locations.rst b/gcc/doc/libgdiagnostics/topics/logical-locations.rst new file mode 100644 index 0000000..85900b6 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/logical-locations.rst @@ -0,0 +1,109 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 `, 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 `_). + + .. 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 `_). + + ``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 `_). + + ``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 `_). + +.. 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. diff --git a/gcc/doc/libgdiagnostics/topics/message-formatting.rst b/gcc/doc/libgdiagnostics/topics/message-formatting.rst new file mode 100644 index 0000000..086e894 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/message-formatting.rst @@ -0,0 +1,224 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +Message formatting +================== + +Various libgdiagnostics 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. */ + + diff --git a/gcc/doc/libgdiagnostics/topics/metadata.rst b/gcc/doc/libgdiagnostics/topics/metadata.rst new file mode 100644 index 0000000..c62792a --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/metadata.rst @@ -0,0 +1,149 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 `, 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 ` 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 ` as the value for the + ``name`` property of the ``driver`` + (`SARIF v2.1.0 §3.19.8 `_). + +.. 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 ` as + the value for the ``fullName`` property of the ``driver`` + (`SARIF v2.1.0 §3.19.9 `_). + + 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 `_):: + + 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 `_):: + + 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 `_:: + + /* 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); + | ^~~~~~~~~~ diff --git a/gcc/doc/libgdiagnostics/topics/physical-locations.rst b/gcc/doc/libgdiagnostics/topics/physical-locations.rst new file mode 100644 index 0000000..cf10f53 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/physical-locations.rst @@ -0,0 +1,281 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 `, +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 v2.1.0 §3.24.10 `_). + See + `SARIF v2.1.0 Appendix J `_ + 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. libgdiagnostics 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. libgdiagnostics treats the first column of a text line +as column 1, **not** column 0. + +.. note:: + + Both libgdiagnostics and Emacs number source *lines* starting at 1, but + they have differing conventions for *columns*. + + libgdiagnostics 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 libgdiagnostics 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 libgdiagnostics 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/libgdiagnostics.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 * diff --git a/gcc/doc/libgdiagnostics/topics/retrofitting.rst b/gcc/doc/libgdiagnostics/topics/retrofitting.rst new file mode 100644 index 0000000..3c7198d --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/retrofitting.rst @@ -0,0 +1,23 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +Adding libgdiagnostics to an existing project +============================================= + +TODO diff --git a/gcc/doc/libgdiagnostics/topics/sarif.rst b/gcc/doc/libgdiagnostics/topics/sarif.rst new file mode 100644 index 0000000..3fd75ed --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/sarif.rst @@ -0,0 +1,51 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +SARIF support +============= + +`SARIF `_ 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 diff --git a/gcc/doc/libgdiagnostics/topics/text-output.rst b/gcc/doc/libgdiagnostics/topics/text-output.rst new file mode 100644 index 0000000..32b2a54 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/text-output.rst @@ -0,0 +1,87 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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. diff --git a/gcc/doc/libgdiagnostics/topics/ux.rst b/gcc/doc/libgdiagnostics/topics/ux.rst new file mode 100644 index 0000000..fc96e17 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/ux.rst @@ -0,0 +1,26 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +User Experience +=============== + +Refer to +`GCC's user experience guidelines `_ +for notes on +`what makes a good diagnostic `_. diff --git a/gcc/doc/libgdiagnostics/tutorial/01-hello-world.rst b/gcc/doc/libgdiagnostics/tutorial/01-hello-world.rst new file mode 100644 index 0000000..67f54c0 --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/01-hello-world.rst @@ -0,0 +1,173 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 libgdiagnostics to emit an error message +to stderr. + + .. literalinclude:: ../../../testsuite/libgdiagnostics.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 libgdiagnostics 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 `_). + +.. 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 libgdiagnostics will +quote the pertinent parts of the file, underlining them, which is less trivial +to reimplement. libgdiagnostics gives us many other such abilities, such as +fix-it hints and execution paths, which we'll cover in the following +tutorials. Also, once a program's diagnostics are using libgdiagnostics, +it is trivial to add support for outputting them in +machine-readable form as :doc:`SARIF <../topics/sarif>`. + + +Structure +********* + +The above example shows the typical structure of a program using +libgdiagnostics: + +* **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 libgdiagnostics. See :doc:`02-physical-locations` for +more information. + + +Formatted messages +****************** + +The above example uses :func:`diagnostic_finish`, which takes a format +string and arguments. libgdiagnostics 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 libgdiagnostics to + ``erreur``, + +* locale-specific quoting has been used (``«`` and ``»`` rather than + ``‘`` and ``’``), + +* ``foo`` hasn't been localized - you would typically use quoted strings + for referring to identifiers in the input language (such as function names + in code, property names in JSON, etc), + +* the message itself hasn't been localized: you are responsible for + passing a translated format string to :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 libgdiagnostics <../../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. diff --git a/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst b/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst new file mode 100644 index 0000000..71d836ed --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst @@ -0,0 +1,260 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +Tutorial part 2: physical locations +=================================== + +libgdiagnostics 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 + +we could complain about it via libgdiagnostics via: + +.. literalinclude:: ../../../testsuite/libgdiagnostics.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 + +where libgdiagnostics will attempt to load the source file and +quote the pertinent line. + +If libgdiagnostics 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 + | ^~~~~ + +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/libgdiagnostics.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 + | ^~~~~ + +where libgdiagnostics will attempt to load the source file and +underling the pertinent part of the given line. + +If libgdiagnostics 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/libgdiagnostics.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/libgdiagnostics.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. diff --git a/gcc/doc/libgdiagnostics/tutorial/03-logical-locations.rst b/gcc/doc/libgdiagnostics/tutorial/03-logical-locations.rst new file mode 100644 index 0000000..d36ac09 --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/03-logical-locations.rst @@ -0,0 +1,60 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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`. diff --git a/gcc/doc/libgdiagnostics/tutorial/04-notes.rst b/gcc/doc/libgdiagnostics/tutorial/04-notes.rst new file mode 100644 index 0000000..81755b2 --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/04-notes.rst @@ -0,0 +1,66 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 + | ^~~~~ + 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 libgdiagnostics 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/libgdiagnostics.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 + | ^~~~~ + 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 `_). + +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. diff --git a/gcc/doc/libgdiagnostics/tutorial/05-warnings.rst b/gcc/doc/libgdiagnostics/tutorial/05-warnings.rst new file mode 100644 index 0000000..8362318 --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/05-warnings.rst @@ -0,0 +1,44 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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/libgdiagnostics.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 + | ^~~~~ + +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. diff --git a/gcc/doc/libgdiagnostics/tutorial/06-fix-it-hints.rst b/gcc/doc/libgdiagnostics/tutorial/06-fix-it-hints.rst new file mode 100644 index 0000000..f3c32ad --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/06-fix-it-hints.rst @@ -0,0 +1,61 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. default-domain:: c + +Tutorial part 6: fix-it hints +============================= + +libgdiagnostics 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/libgdiagnostics.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>`. diff --git a/gcc/doc/libgdiagnostics/tutorial/07-execution-paths.rst b/gcc/doc/libgdiagnostics/tutorial/07-execution-paths.rst new file mode 100644 index 0000000..3c0ba71 --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/07-execution-paths.rst @@ -0,0 +1,141 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +.. 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 `_. + +Let's say we're analyzing this code: + +.. literalinclude:: ../../../testsuite/libgdiagnostics.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" `_). + +Let's add an event between these describing control flow, creating three +events in all: + +.. literalinclude:: ../../../testsuite/libgdiagnostics.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/libgdiagnostics.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 `_). + +Here's the above example in full: + +.. literalinclude:: ../../../testsuite/libgdiagnostics.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 libgdiagnostics, see +the :doc:`topic guide <../topics/index>`. diff --git a/gcc/doc/libgdiagnostics/tutorial/example-1.png b/gcc/doc/libgdiagnostics/tutorial/example-1.png new file mode 100644 index 0000000..d637103 Binary files /dev/null and b/gcc/doc/libgdiagnostics/tutorial/example-1.png differ diff --git a/gcc/doc/libgdiagnostics/tutorial/index.rst b/gcc/doc/libgdiagnostics/tutorial/index.rst new file mode 100644 index 0000000..0bad00e --- /dev/null +++ b/gcc/doc/libgdiagnostics/tutorial/index.rst @@ -0,0 +1,32 @@ +.. Copyright (C) 2024 Free Software Foundation, Inc. + Originally contributed by David Malcolm + + 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 + . + +Tutorial +======== + +The following tutorial gives an overview of how to use libgdiagnostics. + +.. 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 diff --git a/gcc/libdiagnostics++.h b/gcc/libdiagnostics++.h deleted file mode 100644 index 14c8493..0000000 --- a/gcc/libdiagnostics++.h +++ /dev/null @@ -1,595 +0,0 @@ -/* 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 -. */ - -#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 (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 diff --git a/gcc/libdiagnostics.cc b/gcc/libdiagnostics.cc deleted file mode 100644 index 5037b87..0000000 --- a/gcc/libdiagnostics.cc +++ /dev/null @@ -1,1682 +0,0 @@ -/* C++ implementation of 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 -. */ - -#include "config.h" -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "intl.h" -#include "diagnostic.h" -#include "diagnostic-color.h" -#include "diagnostic-url.h" -#include "diagnostic-metadata.h" -#include "diagnostic-path.h" -#include "diagnostic-client-data-hooks.h" -#include "diagnostic-format-sarif.h" -#include "diagnostic-format-text.h" -#include "logical-location.h" -#include "edit-context.h" -#include "make-unique.h" -#include "libdiagnostics.h" - -class owned_nullable_string -{ -public: - owned_nullable_string () : m_str (nullptr) {} - owned_nullable_string (const char *str) - : m_str (str ? ::xstrdup (str) : nullptr) - { - } - - ~owned_nullable_string () - { - free (m_str); - } - - void set (const char *str) - { - free (m_str); - m_str = str ? ::xstrdup (str) : nullptr; - } - - const char *get_str () const { return m_str; } - - char *xstrdup () const - { - return m_str ? ::xstrdup (m_str) : nullptr; - } - -private: - char *m_str; -}; - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic_file -{ - diagnostic_file (const char *name, const char *sarif_source_language) - : m_name (name), m_sarif_source_language (sarif_source_language) - { - } - - const char *get_name () const { return m_name.get_str (); } - const char *get_sarif_source_language () const - { - return m_sarif_source_language.get_str (); - } - -private: - owned_nullable_string m_name; - owned_nullable_string m_sarif_source_language; -}; - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic_physical_location -{ - diagnostic_physical_location (diagnostic_manager *mgr, - location_t inner) - : m_mgr (mgr), - m_inner (inner) - {} - - diagnostic_manager *m_mgr; - location_t m_inner; -}; - -static location_t -as_location_t (const diagnostic_physical_location *loc) -{ - if (!loc) - return UNKNOWN_LOCATION; - return loc->m_inner; -} - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic_logical_location : public logical_location -{ - diagnostic_logical_location (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) - : m_kind (kind), - m_parent (parent), - m_short_name (short_name), - m_fully_qualified_name (fully_qualified_name), - m_decorated_name (decorated_name) - { - } - - const char *get_short_name () const final override - { - return m_short_name.get_str (); - } - const char *get_name_with_scope () const final override - { - return m_fully_qualified_name.get_str (); - } - const char *get_internal_name () const final override - { - return m_decorated_name.get_str (); - } - enum logical_location_kind get_kind () const final override - { - switch (m_kind) - { - default: - gcc_unreachable (); - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: - return LOGICAL_LOCATION_KIND_FUNCTION; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: - return LOGICAL_LOCATION_KIND_MEMBER; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: - return LOGICAL_LOCATION_KIND_MODULE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: - return LOGICAL_LOCATION_KIND_NAMESPACE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: - return LOGICAL_LOCATION_KIND_TYPE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: - return LOGICAL_LOCATION_KIND_RETURN_TYPE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: - return LOGICAL_LOCATION_KIND_PARAMETER; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: - return LOGICAL_LOCATION_KIND_VARIABLE; - } - } - - enum diagnostic_logical_location_kind_t get_external_kind () const - { - return m_kind; - } - - const diagnostic_logical_location *get_parent () const { return m_parent; } - - label_text get_name_for_path_output () const - { - return label_text::borrow (m_short_name.get_str ()); - } - -private: - enum diagnostic_logical_location_kind_t m_kind; - const diagnostic_logical_location *m_parent; - owned_nullable_string m_short_name; - owned_nullable_string m_fully_qualified_name; - owned_nullable_string m_decorated_name; -}; - -static diagnostic_event_id -as_diagnostic_event_id (diagnostic_event_id_t id) -{ - return id.zero_based (); -} - -class sink -{ -public: - virtual ~sink (); - - void begin_group () - { - m_dc.begin_group (); - } - void end_group () - { - m_dc.end_group (); - } - - void emit (diagnostic &diag, const char *msgid, va_list *args) - LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (3, 0); - -protected: - sink (diagnostic_manager &mgr); - - diagnostic_manager &m_mgr; - - /* One context per sink. */ - diagnostic_context m_dc; -}; - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic_text_sink : public sink -{ -public: - diagnostic_text_sink (diagnostic_manager &mgr, - FILE *dst_stream, - enum diagnostic_colorize colorize); - - void - on_begin_text_diagnostic (diagnostic_text_output_format &text_format, - const diagnostic_info *info); - - diagnostic_source_printing_options &get_source_printing_options () - { - return m_dc.m_source_printing; - } - - void - set_colorize (enum diagnostic_colorize colorize); - -private: - const diagnostic_logical_location *m_current_logical_loc; -}; - -class sarif_sink : public sink -{ -public: - sarif_sink (diagnostic_manager &mgr, - FILE *dst_stream, - const diagnostic_file *main_input_file, - enum sarif_version version); -}; - -/* Helper for the linemap code. */ - -static size_t -round_alloc_size (size_t s) -{ - return s; -} - -class impl_diagnostic_client_data_hooks : public diagnostic_client_data_hooks -{ -public: - impl_diagnostic_client_data_hooks (diagnostic_manager &mgr) - : m_mgr (mgr) - {} - - const client_version_info *get_any_version_info () const final override; - const logical_location *get_current_logical_location () const final override; - const char * maybe_get_sarif_source_language (const char *filename) - const final override; - void add_sarif_invocation_properties (sarif_object &invocation_obj) - const final override; - -private: - diagnostic_manager &m_mgr; -}; - -class impl_client_version_info : public client_version_info -{ -public: - const char *get_tool_name () const final override - { - return m_name.get_str (); - } - - char *maybe_make_full_name () const final override - { - return m_full_name.xstrdup (); - } - - const char *get_version_string () const final override - { - return m_version.get_str (); - } - - char *maybe_make_version_url () const final override - { - return m_version_url.xstrdup (); - } - - void for_each_plugin (plugin_visitor &) const final override - { - // No-op. - } - - owned_nullable_string m_name; - owned_nullable_string m_full_name; - owned_nullable_string m_version; - owned_nullable_string m_version_url; -}; - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic_manager -{ -public: - diagnostic_manager () - : m_current_diag (nullptr), - m_edit_context (m_file_cache) - { - linemap_init (&m_line_table, BUILTINS_LOCATION); - m_line_table.m_reallocator = xrealloc; - m_line_table.m_round_alloc_size = round_alloc_size; - m_line_table.default_range_bits = 5; - } - ~diagnostic_manager () - { - /* Clean up sinks first, as they can use other fields. */ - for (size_t i = 0; i < m_sinks.size (); i++) - m_sinks[i] = nullptr; - - for (auto iter : m_str_to_file_map) - delete iter.second; - - for (auto iter :m_location_t_map) - delete iter.second; - - free (m_line_table.m_location_adhoc_data_map.data); - free (m_line_table.info_ordinary.maps); - } - - line_maps *get_line_table () { return &m_line_table; } - file_cache *get_file_cache () { return &m_file_cache; } - - void write_patch (FILE *dst_stream); - - void add_sink (std::unique_ptr sink) - { - m_sinks.push_back (std::move (sink)); - } - - void emit (diagnostic &diag, const char *msgid, va_list *args) - LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0); - - const diagnostic_file * - new_file (const char *name, - const char *sarif_source_language) - { - if (diagnostic_file **slot = m_str_to_file_map.get (name)) - return *slot; - diagnostic_file *file = new diagnostic_file (name, sarif_source_language); - m_str_to_file_map.put (file->get_name (), file); - return file; - } - - const diagnostic_physical_location * - new_location_from_file_and_line (const diagnostic_file *file, - diagnostic_line_num_t line_num) - { - ensure_linemap_for_file_and_line (file, line_num); - location_t loc = linemap_position_for_column (&m_line_table, 0); - return new_location (loc); - } - - const diagnostic_physical_location * - new_location_from_file_line_column (const diagnostic_file *file, - diagnostic_line_num_t line_num, - diagnostic_column_num_t column_num) - { - ensure_linemap_for_file_and_line (file, line_num); - location_t loc = linemap_position_for_column (&m_line_table, column_num); - return new_location (loc); - } - - const diagnostic_physical_location * - new_location_from_range (const diagnostic_physical_location *loc_caret, - const diagnostic_physical_location *loc_start, - const diagnostic_physical_location *loc_end) - { - return new_location - (m_line_table.make_location (as_location_t (loc_caret), - as_location_t (loc_start), - as_location_t (loc_end))); - } - - const diagnostic_logical_location * - new_logical_location (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) - { - std::unique_ptr logical_loc - = ::make_unique (kind, - parent, - short_name, - fully_qualified_name, - decorated_name); - const diagnostic_logical_location *result = logical_loc.get (); - m_logical_locs.push_back (std::move (logical_loc)); - return result; - } - - diagnostic_execution_path * - new_execution_path (); - - void begin_group () - { - for (auto &sink : m_sinks) - sink->begin_group (); - } - - void end_group () - { - for (auto &sink : m_sinks) - sink->end_group (); - } - - const char * - maybe_get_sarif_source_language (const char *filename) - { - if (diagnostic_file **slot = m_str_to_file_map.get (filename)) - { - gcc_assert (*slot); - return (*slot)->get_sarif_source_language (); - } - return nullptr; - } - - const diagnostic *get_current_diag () { return m_current_diag; } - - const client_version_info *get_client_version_info () const - { - return &m_client_version_info; - } - impl_client_version_info *get_client_version_info () - { - return &m_client_version_info; - } - - void - assert_valid_diagnostic_physical_location (const diagnostic_physical_location *loc) const - { - if (!loc) - return; - gcc_assert (loc->m_mgr == this); - } - - /* TODO: Various things still use the "line_table" global variable. - Set it to be this diagnostic_manager's m_line_table. - Ideally we should eliminate this global (and this function). */ - void set_line_table_global () const - { - line_table = const_cast (&m_line_table); - } - -private: - void - ensure_linemap_for_file_and_line (const diagnostic_file *file, - diagnostic_line_num_t linenum) - { - /* Build a simple linemap describing some locations. */ - if (LINEMAPS_ORDINARY_USED (&m_line_table) == 0) - linemap_add (&m_line_table, LC_ENTER, false, file->get_name (), 0); - else - { - line_map *map - = const_cast - (linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, - file->get_name (), 0)); - ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; - } - linemap_line_start (&m_line_table, linenum, 100); - } - - const diagnostic_physical_location * - new_location (location_t loc) - { - if (loc == UNKNOWN_LOCATION) - return nullptr; - if (diagnostic_physical_location **slot = m_location_t_map.get (loc)) - return *slot; - diagnostic_physical_location *phys_loc - = new diagnostic_physical_location (this, loc); - m_location_t_map.put (loc, phys_loc); - return phys_loc; - } - - line_maps m_line_table; - file_cache m_file_cache; - impl_client_version_info m_client_version_info; - std::vector> m_sinks; - hash_map m_str_to_file_map; - hash_map, - diagnostic_physical_location *> m_location_t_map; - std::vector> m_logical_locs; - const diagnostic *m_current_diag; - edit_context m_edit_context; -}; - -class impl_rich_location : public rich_location -{ -public: - impl_rich_location (line_maps *set) - : rich_location (set, UNKNOWN_LOCATION) - {} -}; - -class impl_range_label : public range_label -{ -public: - impl_range_label (const char *text) - : m_text (xstrdup (text)) - {} - - ~impl_range_label () { free (m_text); } - - label_text get_text (unsigned) const final override - { - return label_text::borrow (m_text); - } - -private: - char *m_text; -}; - -class impl_rule : public diagnostic_metadata::rule -{ -public: - impl_rule (const char *title, const char *url) - : m_title (title), - m_url (url) - { - } - - virtual ~impl_rule () {} - - char *make_description () const final override - { - return m_title.xstrdup (); - } - - char *make_url () const final override - { - return m_url.xstrdup (); - } - -private: - owned_nullable_string m_title; - owned_nullable_string m_url; -}; - -class libdiagnostics_path_event : public diagnostic_event -{ -public: - libdiagnostics_path_event (const diagnostic_physical_location *physical_loc, - const diagnostic_logical_location *logical_loc, - unsigned stack_depth, - const char *gmsgid, - va_list *args) - : m_physical_loc (physical_loc), - m_logical_loc (logical_loc), - m_stack_depth (stack_depth) - { - m_desc_uncolored = make_desc (gmsgid, args, false); - m_desc_colored = make_desc (gmsgid, args, true); - } - - /* diagnostic_event vfunc implementations. */ - - location_t get_location () const final override - { - return as_location_t (m_physical_loc); - } - - int get_stack_depth () const final override - { - return m_stack_depth; - } - - void print_desc (pretty_printer &pp) const final override - { - if (pp_show_color (&pp)) - pp_string (&pp, m_desc_colored.get ()); - else - pp_string (&pp, m_desc_uncolored.get ()); - } - - const logical_location *get_logical_location () const - { - return m_logical_loc; - } - - meaning get_meaning () const final override - { - return meaning (); - } - - bool connect_to_next_event_p () const final override - { - return false; // TODO - } - - diagnostic_thread_id_t get_thread_id () const final override - { - return 0; - } - -private: - static label_text make_desc (const char *gmsgid, - va_list *args, - bool colorize) - { - va_list copy_of_args; - va_copy (copy_of_args, *args); - - // TODO: when should localization happen? - text_info text (gmsgid, ©_of_args, errno); - pretty_printer pp; - pp_show_color (&pp) = colorize; - pp.set_output_stream (nullptr); - pp_format (&pp, &text); - pp_output_formatted_text (&pp, nullptr); - label_text result = label_text::take (xstrdup (pp_formatted_text (&pp))); - - va_end (copy_of_args); - - return result; - } - - const diagnostic_physical_location *m_physical_loc; - const diagnostic_logical_location *m_logical_loc; - unsigned m_stack_depth; - label_text m_desc_uncolored; - label_text m_desc_colored; -}; - -class libdiagnostics_path_thread : public diagnostic_thread -{ -public: - libdiagnostics_path_thread (const char *name) : m_name (name) {} - label_text get_name (bool) const final override - { - return label_text::borrow (m_name); - } - -private: - const char *m_name; // has been i18n-ed and formatted -}; - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic_execution_path : public diagnostic_path -{ - diagnostic_execution_path () - : m_thread ("") - { - } - - diagnostic_event_id_t - add_event_va (const diagnostic_physical_location *physical_loc, - const diagnostic_logical_location *logical_loc, - unsigned stack_depth, - const char *gmsgid, - va_list *args) - { - m_events.push_back (::make_unique (physical_loc, - logical_loc, - stack_depth, - gmsgid, - args)); - return m_events.size () - 1; - } - - /* diagnostic_path vfunc implementations. */ - - unsigned num_events () const final override - { - return m_events.size (); - } - const diagnostic_event & get_event (int idx) const final override - { - return *m_events[idx]; - } - unsigned num_threads () const final override { return 1; } - const diagnostic_thread & - get_thread (diagnostic_thread_id_t) const final override - { - return m_thread; - } - - bool - same_function_p (int event_idx_a, - int event_idx_b) const final override - { - const logical_location *logical_loc_a - = m_events[event_idx_a]->get_logical_location (); - const logical_location *logical_loc_b - = m_events[event_idx_b]->get_logical_location (); - - // TODO: - /* Pointer equality, so we may want to uniqify logical loc ptrs. */ - return logical_loc_a == logical_loc_b; - } - -private: - libdiagnostics_path_thread m_thread; - std::vector> m_events; -}; - -/* This has to be a "struct" as it is exposed in the C API. */ - -struct diagnostic -{ -public: - diagnostic (diagnostic_manager &diag_mgr, - enum diagnostic_level level) - : m_diag_mgr (diag_mgr), - m_level (level), - m_rich_loc (diag_mgr.get_line_table ()), - m_logical_loc (nullptr), - m_path (nullptr) - {} - - diagnostic_manager &get_manager () const - { - return m_diag_mgr; - } - - enum diagnostic_level get_level () const { return m_level; } - - rich_location *get_rich_location () { return &m_rich_loc; } - const diagnostic_metadata *get_metadata () { return &m_metadata; } - - void set_cwe (unsigned cwe_id) - { - m_metadata.add_cwe (cwe_id); - } - - void add_rule (const char *title, - const char *url) - { - std::unique_ptr rule = ::make_unique (title, url); - m_metadata.add_rule (*rule.get ()); - m_rules.push_back (std::move (rule)); - } - - void set_location (const diagnostic_physical_location *loc) - { - m_rich_loc.set_range (0, as_location_t (loc), SHOW_RANGE_WITH_CARET); - } - - void - add_location (const diagnostic_physical_location *loc) - { - m_rich_loc.add_range (as_location_t (loc), - SHOW_RANGE_WITHOUT_CARET); - } - - void - add_location_with_label (const diagnostic_physical_location *loc, - const char *text) - { - std::unique_ptr label - = ::make_unique (text); - m_rich_loc.add_range (as_location_t (loc), - SHOW_RANGE_WITHOUT_CARET, - label.get ()); - m_labels.push_back (std::move (label)); - } - - void - set_logical_location (const diagnostic_logical_location *logical_loc) - { - m_logical_loc = logical_loc; - } - const diagnostic_logical_location *get_logical_location () const - { - return m_logical_loc; - } - - diagnostic_execution_path * - add_execution_path () - { - m_path = ::make_unique (); - m_rich_loc.set_path (m_path.get ()); - return m_path.get (); - } - - void - take_execution_path (diagnostic_execution_path *path) - { - m_path = std::unique_ptr (path); - m_rich_loc.set_path (path); - } - -private: - diagnostic_manager &m_diag_mgr; - enum diagnostic_level m_level; - impl_rich_location m_rich_loc; - const diagnostic_logical_location *m_logical_loc; - diagnostic_metadata m_metadata; - std::vector> m_labels; - std::vector> m_rules; - std::unique_ptr m_path; -}; - -static diagnostic_t -diagnostic_t_from_diagnostic_level (enum diagnostic_level level) -{ - switch (level) - { - default: - gcc_unreachable (); - case DIAGNOSTIC_LEVEL_ERROR: - return DK_ERROR; - case DIAGNOSTIC_LEVEL_WARNING: - return DK_WARNING; - case DIAGNOSTIC_LEVEL_NOTE: - return DK_NOTE; - case DIAGNOSTIC_LEVEL_SORRY: - return DK_SORRY; - } -} - -/* class impl_diagnostic_client_data_hooks. */ - -const client_version_info * -impl_diagnostic_client_data_hooks::get_any_version_info () const -{ - return m_mgr.get_client_version_info (); -} - -const logical_location * -impl_diagnostic_client_data_hooks::get_current_logical_location () const -{ - gcc_assert (m_mgr.get_current_diag ()); - - return m_mgr.get_current_diag ()->get_logical_location (); -} - -const char * -impl_diagnostic_client_data_hooks:: -maybe_get_sarif_source_language (const char *filename) const -{ - return m_mgr.maybe_get_sarif_source_language (filename); -} - -void -impl_diagnostic_client_data_hooks:: -add_sarif_invocation_properties (sarif_object &) const -{ - // No-op. -} - -/* class sink. */ - -void -sink::emit (diagnostic &diag, const char *msgid, va_list *args) -{ - diagnostic_info info; -GCC_DIAGNOSTIC_PUSH_IGNORED(-Wsuggest-attribute=format) - diagnostic_set_info (&info, msgid, args, diag.get_rich_location (), - diagnostic_t_from_diagnostic_level (diag.get_level ())); -GCC_DIAGNOSTIC_POP - info.metadata = diag.get_metadata (); - diagnostic_report_diagnostic (&m_dc, &info); -} - -sink::sink (diagnostic_manager &mgr) -: m_mgr (mgr) -{ - diagnostic_initialize (&m_dc, 0); - m_dc.m_client_aux_data = this; - m_dc.set_client_data_hooks - (::make_unique (mgr)); -} - -sink::~sink () -{ - diagnostic_finish (&m_dc); -} - -/* struct diagnostic_text_sink : public sink. */ - -static diagnostic_color_rule_t -get_color_rule (enum diagnostic_colorize colorize) -{ - switch (colorize) - { - default: - gcc_unreachable (); - case DIAGNOSTIC_COLORIZE_IF_TTY: - return DIAGNOSTICS_COLOR_AUTO; - break; - case DIAGNOSTIC_COLORIZE_NO: - return DIAGNOSTICS_COLOR_NO; - break; - case DIAGNOSTIC_COLORIZE_YES: - return DIAGNOSTICS_COLOR_YES; - break; - } -} - -diagnostic_text_sink::diagnostic_text_sink (diagnostic_manager &mgr, - FILE *dst_stream, - enum diagnostic_colorize colorize) -: sink (mgr), - m_current_logical_loc (nullptr) -{ - m_dc.set_show_cwe (true); - m_dc.set_show_rules (true); - - diagnostic_color_init (&m_dc, get_color_rule (colorize)); - diagnostic_urls_init (&m_dc); - - auto text_format = ::make_unique (m_dc, true); - text_format->get_printer ()->set_output_stream (dst_stream); - m_dc.set_output_format (std::move (text_format)); - diagnostic_text_starter (&m_dc) - = [] (diagnostic_text_output_format &text_format, - const diagnostic_info *info) - { - diagnostic_context &dc = text_format.get_context (); - diagnostic_text_sink *sink - = static_cast (dc.m_client_aux_data); - sink->on_begin_text_diagnostic (text_format, info); - }; - m_dc.set_show_cwe (true); - m_dc.set_show_rules (true); - m_dc.m_show_column = true; - m_dc.m_source_printing.enabled = true; - m_dc.m_source_printing.colorize_source_p = true; - - /* We don't currently expose a way for clients to manipulate the - following. */ - m_dc.m_source_printing.show_labels_p = true; - m_dc.m_source_printing.show_line_numbers_p = true; - m_dc.m_source_printing.min_margin_width = 6; - m_dc.set_path_format (DPF_INLINE_EVENTS); -} - -void -diagnostic_text_sink::set_colorize (enum diagnostic_colorize colorize) -{ - diagnostic_color_init (&m_dc, get_color_rule (colorize)); -} - -void -diagnostic_text_sink:: -on_begin_text_diagnostic (diagnostic_text_output_format &text_format, - const diagnostic_info *info) -{ - const diagnostic *diag = m_mgr.get_current_diag (); - gcc_assert (diag); - pretty_printer *pp = text_format.get_printer (); - const diagnostic_logical_location *diag_logical_loc - = diag->get_logical_location (); - if (m_current_logical_loc != diag_logical_loc) - { - m_current_logical_loc = diag_logical_loc; - if (m_current_logical_loc) - { - pp_set_prefix (pp, nullptr); - switch (m_current_logical_loc->get_kind ()) - { - default: - break; - case LOGICAL_LOCATION_KIND_FUNCTION: - if (const char *name - = m_current_logical_loc->get_name_with_scope ()) - { - pp_printf (pp, _("In function %qs"), name); - pp_character (pp, ':'); - pp_newline (pp); - } - break; - // TODO: handle other cases - } - } - } - pp_set_prefix (pp, - text_format.build_prefix (*info)); -} - -/* class sarif_sink : public sink. */ - -sarif_sink::sarif_sink (diagnostic_manager &mgr, - FILE *dst_stream, - const diagnostic_file *main_input_file, - enum sarif_version version) -: sink (mgr) -{ - const char *main_input_filename = main_input_file->get_name (); - diagnostic_output_format_init_sarif_stream (m_dc, - mgr.get_line_table (), - main_input_filename, - true, - version, - dst_stream); -} - -/* struct diagnostic_manager. */ - -void -diagnostic_manager::write_patch (FILE *dst_stream) -{ - pretty_printer pp; - pp.set_output_stream (dst_stream); - m_edit_context.print_diff (&pp, true); - pp_flush (&pp); -} - -void -diagnostic_manager::emit (diagnostic &diag, const char *msgid, va_list *args) -{ - set_line_table_global (); - - m_current_diag = &diag; - for (auto &sink : m_sinks) - { - va_list arg_copy; - va_copy (arg_copy, *args); - sink->emit (diag, msgid, &arg_copy); - } - - rich_location *rich_loc = diag.get_rich_location (); - if (rich_loc->fixits_can_be_auto_applied_p ()) - m_edit_context.add_fixits (rich_loc); - - m_current_diag = nullptr; -} - -diagnostic_execution_path * -diagnostic_manager::new_execution_path () -{ - return new diagnostic_execution_path (); -} - -/* Error-checking at the API boundary. */ - -#define FAIL_IF_NULL(PTR_ARG) \ - do { \ - volatile const void *p = (PTR_ARG); \ - if (!p) { \ - fprintf (stderr, "%s: %s must be non-NULL\n", \ - __func__, #PTR_ARG); \ - abort (); \ - } \ - } while (0) - -/* Public entrypoints. */ - -/* Public entrypoint for clients to acquire a diagnostic_manager. */ - -diagnostic_manager * -diagnostic_manager_new (void) -{ - return new diagnostic_manager (); -} - -/* Public entrypoint for clients to release a diagnostic_manager. */ - -void -diagnostic_manager_release (diagnostic_manager *diag_mgr) -{ - delete diag_mgr; -} - -/* Public entrypoint. */ - -void -diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr, - const char *value) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (value); - - diag_mgr->get_client_version_info ()->m_name.set (value); -} - -/* Public entrypoint. */ - -void -diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr, - const char *value) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (value); - - diag_mgr->get_client_version_info ()->m_full_name.set (value); -} - -/* Public entrypoint. */ - -void -diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr, - const char *value) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (value); - - diag_mgr->get_client_version_info ()->m_version.set (value); -} - -/* Public entrypoint. */ - -void -diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr, - const char *value) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (value); - - diag_mgr->get_client_version_info ()->m_version_url.set (value); -} - -/* Public entrypoint. */ - -diagnostic_text_sink * -diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr, - FILE *dst_stream, - enum diagnostic_colorize colorize) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (dst_stream); - - diagnostic_text_sink *result - = new diagnostic_text_sink (*diag_mgr, dst_stream, colorize); - diag_mgr->add_sink (std::unique_ptr (result)); - return result; -} - -/* Public entrypoint. */ - -void -diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink, - int value) -{ - FAIL_IF_NULL (text_sink); - - text_sink->get_source_printing_options ().enabled = value; -} - -/* Public entrypoint. */ - -void -diagnostic_text_sink_set_colorize (diagnostic_text_sink *text_sink, - enum diagnostic_colorize colorize) -{ - FAIL_IF_NULL (text_sink); - - text_sink->set_colorize (colorize); -} - -/* Public entrypoint. */ - -void -diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink, - int value) -{ - FAIL_IF_NULL (text_sink); - - text_sink->get_source_printing_options ().colorize_source_p = value; -} - - -/* Public entrypoint. */ - -void -diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, - FILE *dst_stream, - const diagnostic_file *main_input_file, - enum diagnostic_sarif_version version) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (dst_stream); - FAIL_IF_NULL (main_input_file); - - enum sarif_version internal_version; - switch (version) - { - default: - fprintf (stderr, "%s: unrecognized value for version: %i\n", - __func__, (int)version); - abort (); - case DIAGNOSTIC_SARIF_VERSION_2_1_0: - internal_version = sarif_version::v2_1_0; - break; - case DIAGNOSTIC_SARIF_VERSION_2_2_PRERELEASE: - internal_version = sarif_version::v2_2_prerelease_2024_08_08; - break; - } - - diag_mgr->add_sink (make_unique (*diag_mgr, - dst_stream, - main_input_file, - internal_version)); -} - -/* Public entrypoint. */ - -void -diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, - FILE *dst_stream) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (dst_stream); - - diag_mgr->write_patch (dst_stream); -} - -/* Public entrypoint. */ - -const diagnostic_file * -diagnostic_manager_new_file (diagnostic_manager *diag_mgr, - const char *name, - const char *sarif_source_language) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (name); - - return diag_mgr->new_file (name, sarif_source_language); -} - -void -diagnostic_manager_debug_dump_file (diagnostic_manager *, - const diagnostic_file *file, - FILE *out) -{ - FAIL_IF_NULL (out); - if (file) - { - if (file->get_sarif_source_language ()) - { - fprintf (out, "file(name=\"%s\", sarif_source_language=\"%s\")", - file->get_name (), - file->get_sarif_source_language ()); - } - else - { - fprintf (out, "file(name=\"%s\")", - file->get_name ()); - } - } - else - fprintf (out, "(null)"); -} - - -/* Public entrypoint. */ - -const diagnostic_physical_location * -diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, - const diagnostic_file *file, - diagnostic_line_num_t linenum) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (file); - - return diag_mgr->new_location_from_file_and_line (file, linenum); -} - -/* Public entrypoint. */ - -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) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (file); - - return diag_mgr->new_location_from_file_line_column (file, - line_num, - column_num); -} - -/* Public entrypoint. */ - -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) -{ - FAIL_IF_NULL (diag_mgr); - - return diag_mgr->new_location_from_range (loc_caret, - loc_start, - loc_end); -} - -/* Public entrypoint. */ - -void -diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr, - const diagnostic_physical_location *loc, - FILE *out) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (out); - - if (loc) - { - const location_t cpplib_loc = as_location_t (loc); - diag_mgr->set_line_table_global (); - const expanded_location exp_loc (expand_location (cpplib_loc)); - - diagnostic_context dc; - diagnostic_initialize (&dc, 0); - dc.m_show_column = true; - - diagnostic_text_output_format text_format (dc); - label_text loc_text = text_format.get_location_text (exp_loc); - fprintf (out, "%s", loc_text.get ()); - - diagnostic_finish (&dc); - } - else - fprintf (out, "(null)"); -} - -/* Public entrypoint. */ - -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) -{ - FAIL_IF_NULL (diag_mgr); - - return diag_mgr->new_logical_location (kind, - parent, - short_name, - fully_qualified_name, - decorated_name); -} - -void -diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, - const diagnostic_logical_location *loc, - FILE *out) -{ - FAIL_IF_NULL (diag_mgr); - FAIL_IF_NULL (out); - - if (loc) - { - fprintf (out, "logical_location(kind="); - switch (loc->get_external_kind ()) - { - default: - gcc_unreachable (); - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: - fprintf (out, "function"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: - fprintf (out, "member"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: - fprintf (out, "module"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: - fprintf (out, "namespace"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: - fprintf (out, "file"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: - fprintf (out, "return_type"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: - fprintf (out, "parameter"); - break; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: - fprintf (out, "variable"); - break; - } - if (const diagnostic_logical_location *parent = loc->get_parent ()) - diagnostic_manager_debug_dump_logical_location (diag_mgr, - parent, - out); - if (const char *val = loc->get_short_name ()) - fprintf (out, ", short_name=\"%s\"", val); - if (const char *val = loc->get_name_with_scope ()) - fprintf (out, ", fully_qualified_name=\"%s\"", val); - if (const char *val = loc->get_internal_name ()) - fprintf (out, ", decorated_name=\"%s\"", val); - fprintf (out, ")"); - } - else - fprintf (out, "(null)"); -} - -/* Public entrypoint. */ - -void -diagnostic_manager_begin_group (diagnostic_manager *diag_mgr) -{ - FAIL_IF_NULL (diag_mgr); - diag_mgr->begin_group (); -} - -/* Public entrypoint. */ - -extern void -diagnostic_manager_end_group (diagnostic_manager *diag_mgr) -{ - FAIL_IF_NULL (diag_mgr); - diag_mgr->end_group (); -} - -/* Public entrypoint. */ - -diagnostic * -diagnostic_begin (diagnostic_manager *diag_mgr, - enum diagnostic_level level) -{ - FAIL_IF_NULL (diag_mgr); - - return new diagnostic (*diag_mgr, level); -} - -/* Public entrypoint. */ - -void -diagnostic_set_cwe (diagnostic *diag, - unsigned cwe_id) -{ - FAIL_IF_NULL (diag); - - diag->set_cwe (cwe_id); -} - -/* Public entrypoint. */ - -void -diagnostic_add_rule (diagnostic *diag, - const char *title, - const char *url) -{ - FAIL_IF_NULL (diag); - - diag->add_rule (title, url); -} - -/* Public entrypoint. */ - -void -diagnostic_set_location (diagnostic *diag, - const diagnostic_physical_location *loc) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - - diag->set_location (loc); -} - -/* Public entrypoint. */ - -void -diagnostic_add_location (diagnostic *diag, - const diagnostic_physical_location *loc) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - - diag->add_location (loc); -} - -/* Public entrypoint. */ - -void -diagnostic_add_location_with_label (diagnostic *diag, - const diagnostic_physical_location *loc, - const char *text) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - FAIL_IF_NULL (text); - - diag->add_location_with_label (loc, text); -} - -/* Public entrypoint. */ - -void -diagnostic_set_logical_location (diagnostic *diag, - const diagnostic_logical_location *logical_loc) -{ - FAIL_IF_NULL (diag); - - diag->set_logical_location (logical_loc); -} - -/* Public entrypoint. */ - -void -diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, - const diagnostic_physical_location *loc, - const char *addition) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - FAIL_IF_NULL (addition); - - diag->get_manager ().set_line_table_global (); - diag->get_rich_location ()->add_fixit_insert_before (as_location_t (loc), - addition); -} - -/* Public entrypoint. */ - -void -diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, - const diagnostic_physical_location *loc, - const char *addition) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - FAIL_IF_NULL (addition); - - diag->get_manager ().set_line_table_global (); - diag->get_rich_location ()->add_fixit_insert_after (as_location_t (loc), - addition); -} - -/* Public entrypoint. */ - -void -diagnostic_add_fix_it_hint_replace (diagnostic *diag, - const diagnostic_physical_location *loc, - const char *replacement) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - FAIL_IF_NULL (replacement); - - diag->get_manager ().set_line_table_global (); - diag->get_rich_location ()->add_fixit_replace (as_location_t (loc), - replacement); -} - -/* Public entrypoint. */ - -void -diagnostic_add_fix_it_hint_delete (diagnostic *diag, - const diagnostic_physical_location *loc) -{ - FAIL_IF_NULL (diag); - diag->get_manager ().assert_valid_diagnostic_physical_location (loc); - - diag->get_manager ().set_line_table_global (); - diag->get_rich_location ()->add_fixit_remove (as_location_t (loc)); -} - -/* Public entrypoint. */ - -diagnostic_execution_path * -diagnostic_add_execution_path (diagnostic *diag) -{ - FAIL_IF_NULL (diag); - - return diag->add_execution_path (); -} - -/* Public entrypoint. */ - -diagnostic_execution_path * -diagnostic_manager_new_execution_path (diagnostic_manager *manager) -{ - FAIL_IF_NULL (manager); - - return manager->new_execution_path (); -} - -/* Public entrypoint. */ - -extern void -diagnostic_take_execution_path (diagnostic *diag, - diagnostic_execution_path *path) -{ - FAIL_IF_NULL (diag); - FAIL_IF_NULL (path); - - return diag->take_execution_path (path); -} - -/* Public entrypoint. */ - -void -diagnostic_execution_path_release (diagnostic_execution_path *path) -{ - delete path; -} - -/* Public entrypoint. */ - -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 *gmsgid, ...) -{ - FAIL_IF_NULL (path); - FAIL_IF_NULL (gmsgid); - - va_list args; - va_start (args, gmsgid); - diagnostic_event_id_t result = path->add_event_va (physical_loc, - logical_loc, - stack_depth, - gmsgid, &args); - va_end (args); - - return as_diagnostic_event_id (result); -} - -/* Public entrypoint. */ - -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 *gmsgid, - va_list *args) -{ - FAIL_IF_NULL (path); - FAIL_IF_NULL (gmsgid); - - diagnostic_event_id_t result = path->add_event_va (physical_loc, - logical_loc, - stack_depth, - gmsgid, args); - return as_diagnostic_event_id (result); -} - -/* Public entrypoint. */ - -void -diagnostic_finish (diagnostic *diag, const char *gmsgid, ...) -{ - FAIL_IF_NULL (diag); - - va_list args; - va_start (args, gmsgid); - diagnostic_finish_va (diag, gmsgid, &args); - va_end (args); -} - -/* Public entrypoint. */ - -void -diagnostic_finish_va (diagnostic *diag, const char *gmsgid, va_list *args) -{ - FAIL_IF_NULL (diag); - - if (const char *tool_name - = diag->get_manager ().get_client_version_info ()->m_name.get_str ()) - progname = tool_name; - else - progname = "progname"; - auto_diagnostic_group d; - diag->get_manager ().emit (*diag, gmsgid, args); - delete diag; -} diff --git a/gcc/libdiagnostics.h b/gcc/libdiagnostics.h deleted file mode 100644 index 96086bc..0000000 --- a/gcc/libdiagnostics.h +++ /dev/null @@ -1,691 +0,0 @@ -/* 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 -. */ - -#ifndef LIBDIAGNOSTICS_H -#define LIBDIAGNOSTICS_H - -#include -#include - -#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 */ diff --git a/gcc/libdiagnostics.map b/gcc/libdiagnostics.map deleted file mode 100644 index cc32b76..0000000 --- a/gcc/libdiagnostics.map +++ /dev/null @@ -1,72 +0,0 @@ -# Linker script for libdiagnostics.so -# Copyright (C) 2023-2024 Free Software Foundation, Inc. -# Contributed by David Malcolm . -# -# 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 -# . */ - -# 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: *; -}; diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h new file mode 100644 index 0000000..018f737 --- /dev/null +++ b/gcc/libgdiagnostics++.h @@ -0,0 +1,595 @@ +/* A C++ wrapper API around libgdiagnostics.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 +. */ + +#ifndef LIBGDIAGNOSTICSPP_H +#define LIBGDIAGNOSTICSPP_H + +#include "libgdiagnostics.h" + +namespace libgdiagnostics { + +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 (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, ...) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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, ...) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3); + + void + finish_va (const char *fmt, va_list *args) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_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 libgdiagnostics + +#endif // #ifndef LIBGDIAGNOSTICSPP_H diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc new file mode 100644 index 0000000..e5cee09 --- /dev/null +++ b/gcc/libgdiagnostics.cc @@ -0,0 +1,1682 @@ +/* C++ implementation of 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 +. */ + +#include "config.h" +#define INCLUDE_VECTOR +#include "system.h" +#include "coretypes.h" +#include "intl.h" +#include "diagnostic.h" +#include "diagnostic-color.h" +#include "diagnostic-url.h" +#include "diagnostic-metadata.h" +#include "diagnostic-path.h" +#include "diagnostic-client-data-hooks.h" +#include "diagnostic-format-sarif.h" +#include "diagnostic-format-text.h" +#include "logical-location.h" +#include "edit-context.h" +#include "make-unique.h" +#include "libgdiagnostics.h" + +class owned_nullable_string +{ +public: + owned_nullable_string () : m_str (nullptr) {} + owned_nullable_string (const char *str) + : m_str (str ? ::xstrdup (str) : nullptr) + { + } + + ~owned_nullable_string () + { + free (m_str); + } + + void set (const char *str) + { + free (m_str); + m_str = str ? ::xstrdup (str) : nullptr; + } + + const char *get_str () const { return m_str; } + + char *xstrdup () const + { + return m_str ? ::xstrdup (m_str) : nullptr; + } + +private: + char *m_str; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_file +{ + diagnostic_file (const char *name, const char *sarif_source_language) + : m_name (name), m_sarif_source_language (sarif_source_language) + { + } + + const char *get_name () const { return m_name.get_str (); } + const char *get_sarif_source_language () const + { + return m_sarif_source_language.get_str (); + } + +private: + owned_nullable_string m_name; + owned_nullable_string m_sarif_source_language; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_physical_location +{ + diagnostic_physical_location (diagnostic_manager *mgr, + location_t inner) + : m_mgr (mgr), + m_inner (inner) + {} + + diagnostic_manager *m_mgr; + location_t m_inner; +}; + +static location_t +as_location_t (const diagnostic_physical_location *loc) +{ + if (!loc) + return UNKNOWN_LOCATION; + return loc->m_inner; +} + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_logical_location : public logical_location +{ + diagnostic_logical_location (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) + : m_kind (kind), + m_parent (parent), + m_short_name (short_name), + m_fully_qualified_name (fully_qualified_name), + m_decorated_name (decorated_name) + { + } + + const char *get_short_name () const final override + { + return m_short_name.get_str (); + } + const char *get_name_with_scope () const final override + { + return m_fully_qualified_name.get_str (); + } + const char *get_internal_name () const final override + { + return m_decorated_name.get_str (); + } + enum logical_location_kind get_kind () const final override + { + switch (m_kind) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: + return LOGICAL_LOCATION_KIND_FUNCTION; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: + return LOGICAL_LOCATION_KIND_MEMBER; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: + return LOGICAL_LOCATION_KIND_MODULE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: + return LOGICAL_LOCATION_KIND_NAMESPACE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: + return LOGICAL_LOCATION_KIND_TYPE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: + return LOGICAL_LOCATION_KIND_RETURN_TYPE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: + return LOGICAL_LOCATION_KIND_PARAMETER; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: + return LOGICAL_LOCATION_KIND_VARIABLE; + } + } + + enum diagnostic_logical_location_kind_t get_external_kind () const + { + return m_kind; + } + + const diagnostic_logical_location *get_parent () const { return m_parent; } + + label_text get_name_for_path_output () const + { + return label_text::borrow (m_short_name.get_str ()); + } + +private: + enum diagnostic_logical_location_kind_t m_kind; + const diagnostic_logical_location *m_parent; + owned_nullable_string m_short_name; + owned_nullable_string m_fully_qualified_name; + owned_nullable_string m_decorated_name; +}; + +static diagnostic_event_id +as_diagnostic_event_id (diagnostic_event_id_t id) +{ + return id.zero_based (); +} + +class sink +{ +public: + virtual ~sink (); + + void begin_group () + { + m_dc.begin_group (); + } + void end_group () + { + m_dc.end_group (); + } + + void emit (diagnostic &diag, const char *msgid, va_list *args) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (3, 0); + +protected: + sink (diagnostic_manager &mgr); + + diagnostic_manager &m_mgr; + + /* One context per sink. */ + diagnostic_context m_dc; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_text_sink : public sink +{ +public: + diagnostic_text_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize); + + void + on_begin_text_diagnostic (diagnostic_text_output_format &text_format, + const diagnostic_info *info); + + diagnostic_source_printing_options &get_source_printing_options () + { + return m_dc.m_source_printing; + } + + void + set_colorize (enum diagnostic_colorize colorize); + +private: + const diagnostic_logical_location *m_current_logical_loc; +}; + +class sarif_sink : public sink +{ +public: + sarif_sink (diagnostic_manager &mgr, + FILE *dst_stream, + const diagnostic_file *main_input_file, + enum sarif_version version); +}; + +/* Helper for the linemap code. */ + +static size_t +round_alloc_size (size_t s) +{ + return s; +} + +class impl_diagnostic_client_data_hooks : public diagnostic_client_data_hooks +{ +public: + impl_diagnostic_client_data_hooks (diagnostic_manager &mgr) + : m_mgr (mgr) + {} + + const client_version_info *get_any_version_info () const final override; + const logical_location *get_current_logical_location () const final override; + const char * maybe_get_sarif_source_language (const char *filename) + const final override; + void add_sarif_invocation_properties (sarif_object &invocation_obj) + const final override; + +private: + diagnostic_manager &m_mgr; +}; + +class impl_client_version_info : public client_version_info +{ +public: + const char *get_tool_name () const final override + { + return m_name.get_str (); + } + + char *maybe_make_full_name () const final override + { + return m_full_name.xstrdup (); + } + + const char *get_version_string () const final override + { + return m_version.get_str (); + } + + char *maybe_make_version_url () const final override + { + return m_version_url.xstrdup (); + } + + void for_each_plugin (plugin_visitor &) const final override + { + // No-op. + } + + owned_nullable_string m_name; + owned_nullable_string m_full_name; + owned_nullable_string m_version; + owned_nullable_string m_version_url; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_manager +{ +public: + diagnostic_manager () + : m_current_diag (nullptr), + m_edit_context (m_file_cache) + { + linemap_init (&m_line_table, BUILTINS_LOCATION); + m_line_table.m_reallocator = xrealloc; + m_line_table.m_round_alloc_size = round_alloc_size; + m_line_table.default_range_bits = 5; + } + ~diagnostic_manager () + { + /* Clean up sinks first, as they can use other fields. */ + for (size_t i = 0; i < m_sinks.size (); i++) + m_sinks[i] = nullptr; + + for (auto iter : m_str_to_file_map) + delete iter.second; + + for (auto iter :m_location_t_map) + delete iter.second; + + free (m_line_table.m_location_adhoc_data_map.data); + free (m_line_table.info_ordinary.maps); + } + + line_maps *get_line_table () { return &m_line_table; } + file_cache *get_file_cache () { return &m_file_cache; } + + void write_patch (FILE *dst_stream); + + void add_sink (std::unique_ptr sink) + { + m_sinks.push_back (std::move (sink)); + } + + void emit (diagnostic &diag, const char *msgid, va_list *args) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0); + + const diagnostic_file * + new_file (const char *name, + const char *sarif_source_language) + { + if (diagnostic_file **slot = m_str_to_file_map.get (name)) + return *slot; + diagnostic_file *file = new diagnostic_file (name, sarif_source_language); + m_str_to_file_map.put (file->get_name (), file); + return file; + } + + const diagnostic_physical_location * + new_location_from_file_and_line (const diagnostic_file *file, + diagnostic_line_num_t line_num) + { + ensure_linemap_for_file_and_line (file, line_num); + location_t loc = linemap_position_for_column (&m_line_table, 0); + return new_location (loc); + } + + const diagnostic_physical_location * + new_location_from_file_line_column (const diagnostic_file *file, + diagnostic_line_num_t line_num, + diagnostic_column_num_t column_num) + { + ensure_linemap_for_file_and_line (file, line_num); + location_t loc = linemap_position_for_column (&m_line_table, column_num); + return new_location (loc); + } + + const diagnostic_physical_location * + new_location_from_range (const diagnostic_physical_location *loc_caret, + const diagnostic_physical_location *loc_start, + const diagnostic_physical_location *loc_end) + { + return new_location + (m_line_table.make_location (as_location_t (loc_caret), + as_location_t (loc_start), + as_location_t (loc_end))); + } + + const diagnostic_logical_location * + new_logical_location (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) + { + std::unique_ptr logical_loc + = ::make_unique (kind, + parent, + short_name, + fully_qualified_name, + decorated_name); + const diagnostic_logical_location *result = logical_loc.get (); + m_logical_locs.push_back (std::move (logical_loc)); + return result; + } + + diagnostic_execution_path * + new_execution_path (); + + void begin_group () + { + for (auto &sink : m_sinks) + sink->begin_group (); + } + + void end_group () + { + for (auto &sink : m_sinks) + sink->end_group (); + } + + const char * + maybe_get_sarif_source_language (const char *filename) + { + if (diagnostic_file **slot = m_str_to_file_map.get (filename)) + { + gcc_assert (*slot); + return (*slot)->get_sarif_source_language (); + } + return nullptr; + } + + const diagnostic *get_current_diag () { return m_current_diag; } + + const client_version_info *get_client_version_info () const + { + return &m_client_version_info; + } + impl_client_version_info *get_client_version_info () + { + return &m_client_version_info; + } + + void + assert_valid_diagnostic_physical_location (const diagnostic_physical_location *loc) const + { + if (!loc) + return; + gcc_assert (loc->m_mgr == this); + } + + /* TODO: Various things still use the "line_table" global variable. + Set it to be this diagnostic_manager's m_line_table. + Ideally we should eliminate this global (and this function). */ + void set_line_table_global () const + { + line_table = const_cast (&m_line_table); + } + +private: + void + ensure_linemap_for_file_and_line (const diagnostic_file *file, + diagnostic_line_num_t linenum) + { + /* Build a simple linemap describing some locations. */ + if (LINEMAPS_ORDINARY_USED (&m_line_table) == 0) + linemap_add (&m_line_table, LC_ENTER, false, file->get_name (), 0); + else + { + line_map *map + = const_cast + (linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, + file->get_name (), 0)); + ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; + } + linemap_line_start (&m_line_table, linenum, 100); + } + + const diagnostic_physical_location * + new_location (location_t loc) + { + if (loc == UNKNOWN_LOCATION) + return nullptr; + if (diagnostic_physical_location **slot = m_location_t_map.get (loc)) + return *slot; + diagnostic_physical_location *phys_loc + = new diagnostic_physical_location (this, loc); + m_location_t_map.put (loc, phys_loc); + return phys_loc; + } + + line_maps m_line_table; + file_cache m_file_cache; + impl_client_version_info m_client_version_info; + std::vector> m_sinks; + hash_map m_str_to_file_map; + hash_map, + diagnostic_physical_location *> m_location_t_map; + std::vector> m_logical_locs; + const diagnostic *m_current_diag; + edit_context m_edit_context; +}; + +class impl_rich_location : public rich_location +{ +public: + impl_rich_location (line_maps *set) + : rich_location (set, UNKNOWN_LOCATION) + {} +}; + +class impl_range_label : public range_label +{ +public: + impl_range_label (const char *text) + : m_text (xstrdup (text)) + {} + + ~impl_range_label () { free (m_text); } + + label_text get_text (unsigned) const final override + { + return label_text::borrow (m_text); + } + +private: + char *m_text; +}; + +class impl_rule : public diagnostic_metadata::rule +{ +public: + impl_rule (const char *title, const char *url) + : m_title (title), + m_url (url) + { + } + + virtual ~impl_rule () {} + + char *make_description () const final override + { + return m_title.xstrdup (); + } + + char *make_url () const final override + { + return m_url.xstrdup (); + } + +private: + owned_nullable_string m_title; + owned_nullable_string m_url; +}; + +class libgdiagnostics_path_event : public diagnostic_event +{ +public: + libgdiagnostics_path_event (const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + const char *gmsgid, + va_list *args) + : m_physical_loc (physical_loc), + m_logical_loc (logical_loc), + m_stack_depth (stack_depth) + { + m_desc_uncolored = make_desc (gmsgid, args, false); + m_desc_colored = make_desc (gmsgid, args, true); + } + + /* diagnostic_event vfunc implementations. */ + + location_t get_location () const final override + { + return as_location_t (m_physical_loc); + } + + int get_stack_depth () const final override + { + return m_stack_depth; + } + + void print_desc (pretty_printer &pp) const final override + { + if (pp_show_color (&pp)) + pp_string (&pp, m_desc_colored.get ()); + else + pp_string (&pp, m_desc_uncolored.get ()); + } + + const logical_location *get_logical_location () const + { + return m_logical_loc; + } + + meaning get_meaning () const final override + { + return meaning (); + } + + bool connect_to_next_event_p () const final override + { + return false; // TODO + } + + diagnostic_thread_id_t get_thread_id () const final override + { + return 0; + } + +private: + static label_text make_desc (const char *gmsgid, + va_list *args, + bool colorize) + { + va_list copy_of_args; + va_copy (copy_of_args, *args); + + // TODO: when should localization happen? + text_info text (gmsgid, ©_of_args, errno); + pretty_printer pp; + pp_show_color (&pp) = colorize; + pp.set_output_stream (nullptr); + pp_format (&pp, &text); + pp_output_formatted_text (&pp, nullptr); + label_text result = label_text::take (xstrdup (pp_formatted_text (&pp))); + + va_end (copy_of_args); + + return result; + } + + const diagnostic_physical_location *m_physical_loc; + const diagnostic_logical_location *m_logical_loc; + unsigned m_stack_depth; + label_text m_desc_uncolored; + label_text m_desc_colored; +}; + +class libgdiagnostics_path_thread : public diagnostic_thread +{ +public: + libgdiagnostics_path_thread (const char *name) : m_name (name) {} + label_text get_name (bool) const final override + { + return label_text::borrow (m_name); + } + +private: + const char *m_name; // has been i18n-ed and formatted +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_execution_path : public diagnostic_path +{ + diagnostic_execution_path () + : m_thread ("") + { + } + + diagnostic_event_id_t + add_event_va (const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + const char *gmsgid, + va_list *args) + { + m_events.push_back (::make_unique (physical_loc, + logical_loc, + stack_depth, + gmsgid, + args)); + return m_events.size () - 1; + } + + /* diagnostic_path vfunc implementations. */ + + unsigned num_events () const final override + { + return m_events.size (); + } + const diagnostic_event & get_event (int idx) const final override + { + return *m_events[idx]; + } + unsigned num_threads () const final override { return 1; } + const diagnostic_thread & + get_thread (diagnostic_thread_id_t) const final override + { + return m_thread; + } + + bool + same_function_p (int event_idx_a, + int event_idx_b) const final override + { + const logical_location *logical_loc_a + = m_events[event_idx_a]->get_logical_location (); + const logical_location *logical_loc_b + = m_events[event_idx_b]->get_logical_location (); + + // TODO: + /* Pointer equality, so we may want to uniqify logical loc ptrs. */ + return logical_loc_a == logical_loc_b; + } + +private: + libgdiagnostics_path_thread m_thread; + std::vector> m_events; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic +{ +public: + diagnostic (diagnostic_manager &diag_mgr, + enum diagnostic_level level) + : m_diag_mgr (diag_mgr), + m_level (level), + m_rich_loc (diag_mgr.get_line_table ()), + m_logical_loc (nullptr), + m_path (nullptr) + {} + + diagnostic_manager &get_manager () const + { + return m_diag_mgr; + } + + enum diagnostic_level get_level () const { return m_level; } + + rich_location *get_rich_location () { return &m_rich_loc; } + const diagnostic_metadata *get_metadata () { return &m_metadata; } + + void set_cwe (unsigned cwe_id) + { + m_metadata.add_cwe (cwe_id); + } + + void add_rule (const char *title, + const char *url) + { + std::unique_ptr rule = ::make_unique (title, url); + m_metadata.add_rule (*rule.get ()); + m_rules.push_back (std::move (rule)); + } + + void set_location (const diagnostic_physical_location *loc) + { + m_rich_loc.set_range (0, as_location_t (loc), SHOW_RANGE_WITH_CARET); + } + + void + add_location (const diagnostic_physical_location *loc) + { + m_rich_loc.add_range (as_location_t (loc), + SHOW_RANGE_WITHOUT_CARET); + } + + void + add_location_with_label (const diagnostic_physical_location *loc, + const char *text) + { + std::unique_ptr label + = ::make_unique (text); + m_rich_loc.add_range (as_location_t (loc), + SHOW_RANGE_WITHOUT_CARET, + label.get ()); + m_labels.push_back (std::move (label)); + } + + void + set_logical_location (const diagnostic_logical_location *logical_loc) + { + m_logical_loc = logical_loc; + } + const diagnostic_logical_location *get_logical_location () const + { + return m_logical_loc; + } + + diagnostic_execution_path * + add_execution_path () + { + m_path = ::make_unique (); + m_rich_loc.set_path (m_path.get ()); + return m_path.get (); + } + + void + take_execution_path (diagnostic_execution_path *path) + { + m_path = std::unique_ptr (path); + m_rich_loc.set_path (path); + } + +private: + diagnostic_manager &m_diag_mgr; + enum diagnostic_level m_level; + impl_rich_location m_rich_loc; + const diagnostic_logical_location *m_logical_loc; + diagnostic_metadata m_metadata; + std::vector> m_labels; + std::vector> m_rules; + std::unique_ptr m_path; +}; + +static diagnostic_t +diagnostic_t_from_diagnostic_level (enum diagnostic_level level) +{ + switch (level) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_LEVEL_ERROR: + return DK_ERROR; + case DIAGNOSTIC_LEVEL_WARNING: + return DK_WARNING; + case DIAGNOSTIC_LEVEL_NOTE: + return DK_NOTE; + case DIAGNOSTIC_LEVEL_SORRY: + return DK_SORRY; + } +} + +/* class impl_diagnostic_client_data_hooks. */ + +const client_version_info * +impl_diagnostic_client_data_hooks::get_any_version_info () const +{ + return m_mgr.get_client_version_info (); +} + +const logical_location * +impl_diagnostic_client_data_hooks::get_current_logical_location () const +{ + gcc_assert (m_mgr.get_current_diag ()); + + return m_mgr.get_current_diag ()->get_logical_location (); +} + +const char * +impl_diagnostic_client_data_hooks:: +maybe_get_sarif_source_language (const char *filename) const +{ + return m_mgr.maybe_get_sarif_source_language (filename); +} + +void +impl_diagnostic_client_data_hooks:: +add_sarif_invocation_properties (sarif_object &) const +{ + // No-op. +} + +/* class sink. */ + +void +sink::emit (diagnostic &diag, const char *msgid, va_list *args) +{ + diagnostic_info info; +GCC_DIAGNOSTIC_PUSH_IGNORED(-Wsuggest-attribute=format) + diagnostic_set_info (&info, msgid, args, diag.get_rich_location (), + diagnostic_t_from_diagnostic_level (diag.get_level ())); +GCC_DIAGNOSTIC_POP + info.metadata = diag.get_metadata (); + diagnostic_report_diagnostic (&m_dc, &info); +} + +sink::sink (diagnostic_manager &mgr) +: m_mgr (mgr) +{ + diagnostic_initialize (&m_dc, 0); + m_dc.m_client_aux_data = this; + m_dc.set_client_data_hooks + (::make_unique (mgr)); +} + +sink::~sink () +{ + diagnostic_finish (&m_dc); +} + +/* struct diagnostic_text_sink : public sink. */ + +static diagnostic_color_rule_t +get_color_rule (enum diagnostic_colorize colorize) +{ + switch (colorize) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_COLORIZE_IF_TTY: + return DIAGNOSTICS_COLOR_AUTO; + break; + case DIAGNOSTIC_COLORIZE_NO: + return DIAGNOSTICS_COLOR_NO; + break; + case DIAGNOSTIC_COLORIZE_YES: + return DIAGNOSTICS_COLOR_YES; + break; + } +} + +diagnostic_text_sink::diagnostic_text_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) +: sink (mgr), + m_current_logical_loc (nullptr) +{ + m_dc.set_show_cwe (true); + m_dc.set_show_rules (true); + + diagnostic_color_init (&m_dc, get_color_rule (colorize)); + diagnostic_urls_init (&m_dc); + + auto text_format = ::make_unique (m_dc, true); + text_format->get_printer ()->set_output_stream (dst_stream); + m_dc.set_output_format (std::move (text_format)); + diagnostic_text_starter (&m_dc) + = [] (diagnostic_text_output_format &text_format, + const diagnostic_info *info) + { + diagnostic_context &dc = text_format.get_context (); + diagnostic_text_sink *sink + = static_cast (dc.m_client_aux_data); + sink->on_begin_text_diagnostic (text_format, info); + }; + m_dc.set_show_cwe (true); + m_dc.set_show_rules (true); + m_dc.m_show_column = true; + m_dc.m_source_printing.enabled = true; + m_dc.m_source_printing.colorize_source_p = true; + + /* We don't currently expose a way for clients to manipulate the + following. */ + m_dc.m_source_printing.show_labels_p = true; + m_dc.m_source_printing.show_line_numbers_p = true; + m_dc.m_source_printing.min_margin_width = 6; + m_dc.set_path_format (DPF_INLINE_EVENTS); +} + +void +diagnostic_text_sink::set_colorize (enum diagnostic_colorize colorize) +{ + diagnostic_color_init (&m_dc, get_color_rule (colorize)); +} + +void +diagnostic_text_sink:: +on_begin_text_diagnostic (diagnostic_text_output_format &text_format, + const diagnostic_info *info) +{ + const diagnostic *diag = m_mgr.get_current_diag (); + gcc_assert (diag); + pretty_printer *pp = text_format.get_printer (); + const diagnostic_logical_location *diag_logical_loc + = diag->get_logical_location (); + if (m_current_logical_loc != diag_logical_loc) + { + m_current_logical_loc = diag_logical_loc; + if (m_current_logical_loc) + { + pp_set_prefix (pp, nullptr); + switch (m_current_logical_loc->get_kind ()) + { + default: + break; + case LOGICAL_LOCATION_KIND_FUNCTION: + if (const char *name + = m_current_logical_loc->get_name_with_scope ()) + { + pp_printf (pp, _("In function %qs"), name); + pp_character (pp, ':'); + pp_newline (pp); + } + break; + // TODO: handle other cases + } + } + } + pp_set_prefix (pp, + text_format.build_prefix (*info)); +} + +/* class sarif_sink : public sink. */ + +sarif_sink::sarif_sink (diagnostic_manager &mgr, + FILE *dst_stream, + const diagnostic_file *main_input_file, + enum sarif_version version) +: sink (mgr) +{ + const char *main_input_filename = main_input_file->get_name (); + diagnostic_output_format_init_sarif_stream (m_dc, + mgr.get_line_table (), + main_input_filename, + true, + version, + dst_stream); +} + +/* struct diagnostic_manager. */ + +void +diagnostic_manager::write_patch (FILE *dst_stream) +{ + pretty_printer pp; + pp.set_output_stream (dst_stream); + m_edit_context.print_diff (&pp, true); + pp_flush (&pp); +} + +void +diagnostic_manager::emit (diagnostic &diag, const char *msgid, va_list *args) +{ + set_line_table_global (); + + m_current_diag = &diag; + for (auto &sink : m_sinks) + { + va_list arg_copy; + va_copy (arg_copy, *args); + sink->emit (diag, msgid, &arg_copy); + } + + rich_location *rich_loc = diag.get_rich_location (); + if (rich_loc->fixits_can_be_auto_applied_p ()) + m_edit_context.add_fixits (rich_loc); + + m_current_diag = nullptr; +} + +diagnostic_execution_path * +diagnostic_manager::new_execution_path () +{ + return new diagnostic_execution_path (); +} + +/* Error-checking at the API boundary. */ + +#define FAIL_IF_NULL(PTR_ARG) \ + do { \ + volatile const void *p = (PTR_ARG); \ + if (!p) { \ + fprintf (stderr, "%s: %s must be non-NULL\n", \ + __func__, #PTR_ARG); \ + abort (); \ + } \ + } while (0) + +/* Public entrypoints. */ + +/* Public entrypoint for clients to acquire a diagnostic_manager. */ + +diagnostic_manager * +diagnostic_manager_new (void) +{ + return new diagnostic_manager (); +} + +/* Public entrypoint for clients to release a diagnostic_manager. */ + +void +diagnostic_manager_release (diagnostic_manager *diag_mgr) +{ + delete diag_mgr; +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr, + const char *value) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (value); + + diag_mgr->get_client_version_info ()->m_name.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr, + const char *value) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (value); + + diag_mgr->get_client_version_info ()->m_full_name.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr, + const char *value) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (value); + + diag_mgr->get_client_version_info ()->m_version.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr, + const char *value) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (value); + + diag_mgr->get_client_version_info ()->m_version_url.set (value); +} + +/* Public entrypoint. */ + +diagnostic_text_sink * +diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (dst_stream); + + diagnostic_text_sink *result + = new diagnostic_text_sink (*diag_mgr, dst_stream, colorize); + diag_mgr->add_sink (std::unique_ptr (result)); + return result; +} + +/* Public entrypoint. */ + +void +diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink, + int value) +{ + FAIL_IF_NULL (text_sink); + + text_sink->get_source_printing_options ().enabled = value; +} + +/* Public entrypoint. */ + +void +diagnostic_text_sink_set_colorize (diagnostic_text_sink *text_sink, + enum diagnostic_colorize colorize) +{ + FAIL_IF_NULL (text_sink); + + text_sink->set_colorize (colorize); +} + +/* Public entrypoint. */ + +void +diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink, + int value) +{ + FAIL_IF_NULL (text_sink); + + text_sink->get_source_printing_options ().colorize_source_p = value; +} + + +/* Public entrypoint. */ + +void +diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + const diagnostic_file *main_input_file, + enum diagnostic_sarif_version version) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (dst_stream); + FAIL_IF_NULL (main_input_file); + + enum sarif_version internal_version; + switch (version) + { + default: + fprintf (stderr, "%s: unrecognized value for version: %i\n", + __func__, (int)version); + abort (); + case DIAGNOSTIC_SARIF_VERSION_2_1_0: + internal_version = sarif_version::v2_1_0; + break; + case DIAGNOSTIC_SARIF_VERSION_2_2_PRERELEASE: + internal_version = sarif_version::v2_2_prerelease_2024_08_08; + break; + } + + diag_mgr->add_sink (make_unique (*diag_mgr, + dst_stream, + main_input_file, + internal_version)); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, + FILE *dst_stream) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (dst_stream); + + diag_mgr->write_patch (dst_stream); +} + +/* Public entrypoint. */ + +const diagnostic_file * +diagnostic_manager_new_file (diagnostic_manager *diag_mgr, + const char *name, + const char *sarif_source_language) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (name); + + return diag_mgr->new_file (name, sarif_source_language); +} + +void +diagnostic_manager_debug_dump_file (diagnostic_manager *, + const diagnostic_file *file, + FILE *out) +{ + FAIL_IF_NULL (out); + if (file) + { + if (file->get_sarif_source_language ()) + { + fprintf (out, "file(name=\"%s\", sarif_source_language=\"%s\")", + file->get_name (), + file->get_sarif_source_language ()); + } + else + { + fprintf (out, "file(name=\"%s\")", + file->get_name ()); + } + } + else + fprintf (out, "(null)"); +} + + +/* Public entrypoint. */ + +const diagnostic_physical_location * +diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t linenum) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (file); + + return diag_mgr->new_location_from_file_and_line (file, linenum); +} + +/* Public entrypoint. */ + +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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (file); + + return diag_mgr->new_location_from_file_line_column (file, + line_num, + column_num); +} + +/* Public entrypoint. */ + +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) +{ + FAIL_IF_NULL (diag_mgr); + + return diag_mgr->new_location_from_range (loc_caret, + loc_start, + loc_end); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr, + const diagnostic_physical_location *loc, + FILE *out) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (out); + + if (loc) + { + const location_t cpplib_loc = as_location_t (loc); + diag_mgr->set_line_table_global (); + const expanded_location exp_loc (expand_location (cpplib_loc)); + + diagnostic_context dc; + diagnostic_initialize (&dc, 0); + dc.m_show_column = true; + + diagnostic_text_output_format text_format (dc); + label_text loc_text = text_format.get_location_text (exp_loc); + fprintf (out, "%s", loc_text.get ()); + + diagnostic_finish (&dc); + } + else + fprintf (out, "(null)"); +} + +/* Public entrypoint. */ + +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) +{ + FAIL_IF_NULL (diag_mgr); + + return diag_mgr->new_logical_location (kind, + parent, + short_name, + fully_qualified_name, + decorated_name); +} + +void +diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, + const diagnostic_logical_location *loc, + FILE *out) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (out); + + if (loc) + { + fprintf (out, "logical_location(kind="); + switch (loc->get_external_kind ()) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: + fprintf (out, "function"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: + fprintf (out, "member"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: + fprintf (out, "module"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: + fprintf (out, "namespace"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: + fprintf (out, "file"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: + fprintf (out, "return_type"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: + fprintf (out, "parameter"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: + fprintf (out, "variable"); + break; + } + if (const diagnostic_logical_location *parent = loc->get_parent ()) + diagnostic_manager_debug_dump_logical_location (diag_mgr, + parent, + out); + if (const char *val = loc->get_short_name ()) + fprintf (out, ", short_name=\"%s\"", val); + if (const char *val = loc->get_name_with_scope ()) + fprintf (out, ", fully_qualified_name=\"%s\"", val); + if (const char *val = loc->get_internal_name ()) + fprintf (out, ", decorated_name=\"%s\"", val); + fprintf (out, ")"); + } + else + fprintf (out, "(null)"); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_begin_group (diagnostic_manager *diag_mgr) +{ + FAIL_IF_NULL (diag_mgr); + diag_mgr->begin_group (); +} + +/* Public entrypoint. */ + +extern void +diagnostic_manager_end_group (diagnostic_manager *diag_mgr) +{ + FAIL_IF_NULL (diag_mgr); + diag_mgr->end_group (); +} + +/* Public entrypoint. */ + +diagnostic * +diagnostic_begin (diagnostic_manager *diag_mgr, + enum diagnostic_level level) +{ + FAIL_IF_NULL (diag_mgr); + + return new diagnostic (*diag_mgr, level); +} + +/* Public entrypoint. */ + +void +diagnostic_set_cwe (diagnostic *diag, + unsigned cwe_id) +{ + FAIL_IF_NULL (diag); + + diag->set_cwe (cwe_id); +} + +/* Public entrypoint. */ + +void +diagnostic_add_rule (diagnostic *diag, + const char *title, + const char *url) +{ + FAIL_IF_NULL (diag); + + diag->add_rule (title, url); +} + +/* Public entrypoint. */ + +void +diagnostic_set_location (diagnostic *diag, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + + diag->set_location (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_location (diagnostic *diag, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + + diag->add_location (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_location_with_label (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *text) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (text); + + diag->add_location_with_label (loc, text); +} + +/* Public entrypoint. */ + +void +diagnostic_set_logical_location (diagnostic *diag, + const diagnostic_logical_location *logical_loc) +{ + FAIL_IF_NULL (diag); + + diag->set_logical_location (logical_loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *addition) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (addition); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_insert_before (as_location_t (loc), + addition); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *addition) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (addition); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_insert_after (as_location_t (loc), + addition); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_replace (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *replacement) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (replacement); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_replace (as_location_t (loc), + replacement); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_delete (diagnostic *diag, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_remove (as_location_t (loc)); +} + +/* Public entrypoint. */ + +diagnostic_execution_path * +diagnostic_add_execution_path (diagnostic *diag) +{ + FAIL_IF_NULL (diag); + + return diag->add_execution_path (); +} + +/* Public entrypoint. */ + +diagnostic_execution_path * +diagnostic_manager_new_execution_path (diagnostic_manager *manager) +{ + FAIL_IF_NULL (manager); + + return manager->new_execution_path (); +} + +/* Public entrypoint. */ + +extern void +diagnostic_take_execution_path (diagnostic *diag, + diagnostic_execution_path *path) +{ + FAIL_IF_NULL (diag); + FAIL_IF_NULL (path); + + return diag->take_execution_path (path); +} + +/* Public entrypoint. */ + +void +diagnostic_execution_path_release (diagnostic_execution_path *path) +{ + delete path; +} + +/* Public entrypoint. */ + +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 *gmsgid, ...) +{ + FAIL_IF_NULL (path); + FAIL_IF_NULL (gmsgid); + + va_list args; + va_start (args, gmsgid); + diagnostic_event_id_t result = path->add_event_va (physical_loc, + logical_loc, + stack_depth, + gmsgid, &args); + va_end (args); + + return as_diagnostic_event_id (result); +} + +/* Public entrypoint. */ + +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 *gmsgid, + va_list *args) +{ + FAIL_IF_NULL (path); + FAIL_IF_NULL (gmsgid); + + diagnostic_event_id_t result = path->add_event_va (physical_loc, + logical_loc, + stack_depth, + gmsgid, args); + return as_diagnostic_event_id (result); +} + +/* Public entrypoint. */ + +void +diagnostic_finish (diagnostic *diag, const char *gmsgid, ...) +{ + FAIL_IF_NULL (diag); + + va_list args; + va_start (args, gmsgid); + diagnostic_finish_va (diag, gmsgid, &args); + va_end (args); +} + +/* Public entrypoint. */ + +void +diagnostic_finish_va (diagnostic *diag, const char *gmsgid, va_list *args) +{ + FAIL_IF_NULL (diag); + + if (const char *tool_name + = diag->get_manager ().get_client_version_info ()->m_name.get_str ()) + progname = tool_name; + else + progname = "progname"; + auto_diagnostic_group d; + diag->get_manager ().emit (*diag, gmsgid, args); + delete diag; +} diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h new file mode 100644 index 0000000..a6dc298 --- /dev/null +++ b/gcc/libgdiagnostics.h @@ -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 +. */ + +#ifndef LIBGDIAGNOSTICS_H +#define LIBGDIAGNOSTICS_H + +#include +#include + +#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 LIBGDIAGNOSTICS_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) + +/********************************************************************** + Macros for attributes. + **********************************************************************/ + +# if (LIBGDIAGNOSTICS_GCC_VERSION >= 3003) +# define LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL(ARG_NUM) __attribute__ ((__nonnull__ (ARG_NUM))) +# else +# define LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL(ARG_NUM) +# endif /* GNUC >= 3.3 */ + +#define LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL(ARG_NUM) + /* empty; for the human reader */ + +#define LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(FMT_ARG_NUM, ARGS_ARG_NUM) \ + LIBGDIAGNOSTICS_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 LIBGDIAGNOSTICS_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 libgdiagnostics. */ + +/********************************************************************** + 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 libgdiagnostics and emacs number source *lines* starting at 1, but + they have differing conventions for *columns*. + + libgdiagnostics 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 libgdiagnostics 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 libgdiagnostics 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 *) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (4) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Finish a diagnostic group. */ + +extern void +diagnostic_manager_end_group (diagnostic_manager *diag_mgr) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3); + +/* Set the primary location of DIAG. */ + +extern void +diagnostic_set_location (diagnostic *diag, + const diagnostic_physical_location * loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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, ...) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +/* Add a secondary location to DIAG. */ + +extern void +diagnostic_add_location (diagnostic *diag, + const diagnostic_physical_location * loc) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +extern void +diagnostic_add_fix_it_hint_replace (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *replacement) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +extern void +diagnostic_add_fix_it_hint_delete (diagnostic *diag, + const diagnostic_physical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_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, ...) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (5) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (5) + LIBGDIAGNOSTICS_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, ...) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_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) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_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 /* LIBGDIAGNOSTICS_H */ diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map new file mode 100644 index 0000000..52bff6a --- /dev/null +++ b/gcc/libgdiagnostics.map @@ -0,0 +1,72 @@ +# Linker script for libgdiagnostics.so +# Copyright (C) 2023-2024 Free Software Foundation, Inc. +# Contributed by David Malcolm . +# +# 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 +# . */ + +# The initial release of the library. +LIBGDIAGNOSTICS_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: *; +}; diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc index 5228683..9a053f7 100644 --- a/gcc/libsarifreplay.cc +++ b/gcc/libsarifreplay.cc @@ -1,5 +1,5 @@ /* A library for re-emitting diagnostics saved in SARIF form - via libdiagnostics. + via libgdiagnostics. Copyright (C) 2022-2024 Free Software Foundation, Inc. Contributed by David Malcolm . @@ -26,7 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "make-unique.h" -#include "libdiagnostics++.h" +#include "libgdiagnostics++.h" #include "json-parsing.h" #include "intl.h" #include "sarif-spec-urls.def" @@ -39,7 +39,7 @@ namespace { Issue an error to MGR and return nullptr if there are any problems. */ static std::unique_ptr> -read_file (const char *path, libdiagnostics::manager &mgr) +read_file (const char *path, libgdiagnostics::manager &mgr) { FILE *f_in = fopen (path, "r"); if (!f_in) @@ -83,26 +83,26 @@ read_file (const char *path, libdiagnostics::manager &mgr) return result; } -static libdiagnostics::physical_location -make_physical_location (libdiagnostics::manager &mgr, - libdiagnostics::file f, +static libgdiagnostics::physical_location +make_physical_location (libgdiagnostics::manager &mgr, + libgdiagnostics::file f, const json::location_map::point &point) { /* json::location_map::point uses 0-based columns, - whereas libdiagnostics uses 1-based columns. */ + whereas libgdiagnostics uses 1-based columns. */ return mgr.new_location_from_file_line_column (f, point.m_line, point.m_column + 1); } -static libdiagnostics::physical_location -make_physical_location (libdiagnostics::manager &mgr, - libdiagnostics::file f, +static libgdiagnostics::physical_location +make_physical_location (libgdiagnostics::manager &mgr, + libgdiagnostics::file f, const json::location_map::range &range) { - libdiagnostics::physical_location start + libgdiagnostics::physical_location start = make_physical_location (mgr, f, range.m_start); - libdiagnostics::physical_location end + libgdiagnostics::physical_location end = make_physical_location (mgr, f, range.m_end); return mgr.new_location_from_range (start, start, end); } @@ -202,8 +202,8 @@ struct string_property_value class sarif_replayer { public: - sarif_replayer (libdiagnostics::manager &&output_manager, - libdiagnostics::manager &&control_manager) + sarif_replayer (libgdiagnostics::manager &&output_manager, + libgdiagnostics::manager &&control_manager) : m_output_mgr (std::move (output_manager)), m_control_mgr (std::move (control_manager)), m_driver_obj (nullptr), @@ -249,7 +249,7 @@ private: // "artifactLocation" object (§3.4) enum status handle_artifact_location_object (const json::object &artifact_loc, - libdiagnostics::file &out); + libgdiagnostics::file &out); // Message string lookup algorithm (§3.11.7) const char * @@ -280,34 +280,34 @@ private: // "location" object (§3.28) enum status handle_location_object (const json::object &location_obj, - libdiagnostics::physical_location &out_physical_loc, - libdiagnostics::logical_location &out_logical_loc); + libgdiagnostics::physical_location &out_physical_loc, + libgdiagnostics::logical_location &out_logical_loc); // "physicalLocation" object (§3.29) enum status handle_physical_location_object (const json::object &phys_loc_obj, - libdiagnostics::physical_location &out); + libgdiagnostics::physical_location &out); // "region" object (§3.30) enum status handle_region_object (const json::object ®ion_obj, - libdiagnostics::file file, - libdiagnostics::physical_location &out); + libgdiagnostics::file file, + libgdiagnostics::physical_location &out); // "logicalLocation" object (§3.33) enum status handle_logical_location_object (const json::object &logical_loc_obj, - libdiagnostics::logical_location &out); + libgdiagnostics::logical_location &out); // "threadFlow" object (§3.37) enum status handle_thread_flow_object (const json::object &thread_flow_obj, - libdiagnostics::execution_path &out); + libgdiagnostics::execution_path &out); // "threadFlowLocation" object (§3.38) enum status handle_thread_flow_location_object (const json::object &tflow_loc_obj, - libdiagnostics::execution_path &out); + libgdiagnostics::execution_path &out); // reportingDescriptor lookup (§3.52.3) const json::object * @@ -328,7 +328,7 @@ private: report_invalid_sarif (const json::value &jv, const spec_ref &ref, const char *gmsgid, ...) - LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5) { va_list ap; va_start (ap, gmsgid); @@ -344,7 +344,7 @@ private: report_unhandled_sarif (const json::value &jv, const spec_ref &ref, const char *gmsgid, ...) - LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5) { va_list ap; va_start (ap, gmsgid); @@ -359,7 +359,7 @@ private: const char *gmsgid, va_list *args, enum diagnostic_level level) - LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 0) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 0) { auto diag (m_control_mgr.begin_diagnostic (level)); @@ -535,13 +535,13 @@ private: size_t num_values); /* The manager to replay the SARIF files to. */ - libdiagnostics::manager m_output_mgr; + libgdiagnostics::manager m_output_mgr; /* The manager for reporting issues loading SARIF files. */ - libdiagnostics::manager m_control_mgr; + libgdiagnostics::manager m_control_mgr; /* The file within m_control_mgr representing the .sarif file. */ - libdiagnostics::file m_loaded_file; + libgdiagnostics::file m_loaded_file; replayer_location_map m_json_location_map; @@ -946,8 +946,8 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, return status::err_invalid_sarif; // §3.27.12 "locations" property - libdiagnostics::physical_location physical_loc; - libdiagnostics::logical_location logical_loc; + libgdiagnostics::physical_location physical_loc; + libgdiagnostics::logical_location logical_loc; const property_spec_ref locations_prop ("result", "locations", "3.27.12"); const json::array *locations_arr = get_required_property (result_obj, locations_prop); @@ -969,7 +969,7 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, } // §3.27.18 "codeFlows" property - libdiagnostics::execution_path path; + libgdiagnostics::execution_path path; const property_spec_ref code_flows ("result", "codeFlows", "3.27.18"); if (auto code_flows_arr = get_optional_property (result_obj, code_flows)) @@ -1001,7 +1001,7 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, } } - libdiagnostics::group g (m_output_mgr); + libgdiagnostics::group g (m_output_mgr); auto err (m_output_mgr.begin_diagnostic (level)); if (rule_id) err.add_rule (rule_id->get_string (), nullptr); @@ -1020,8 +1020,8 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, { for (auto rel_loc : *related_locations_arr) { - libdiagnostics::physical_location physical_loc; - libdiagnostics::logical_location logical_loc; + libgdiagnostics::physical_location physical_loc; + libgdiagnostics::logical_location logical_loc; const json::object *location_obj = require_object_for_element (*rel_loc, prop_related_locations); @@ -1274,7 +1274,7 @@ lookup_plain_text_within_result_message (const json::object *tool_component_obj, enum status sarif_replayer::handle_thread_flow_object (const json::object &thread_flow_obj, - libdiagnostics::execution_path &out) + libgdiagnostics::execution_path &out) { const property_spec_ref locations ("threadFlow", "locations", "3.37.6"); const json::array *locations_arr @@ -1302,10 +1302,10 @@ sarif_replayer::handle_thread_flow_object (const json::object &thread_flow_obj, enum status sarif_replayer:: handle_thread_flow_location_object (const json::object &tflow_loc_obj, - libdiagnostics::execution_path &path) + libgdiagnostics::execution_path &path) { - libdiagnostics::physical_location physical_loc; - libdiagnostics::logical_location logical_loc; + libgdiagnostics::physical_location physical_loc; + libgdiagnostics::logical_location logical_loc; label_text message; int stack_depth = 0; @@ -1349,7 +1349,7 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj, kind_strs.push_back (kind_str->get_string ()); // TODO: handle meaning? /* TOOD: probably just want to add sarif kinds to - the libdiagnostics event, and have libdiagnostics + the libgdiagnostics event, and have libgdiagnostics turn that back into a "meaning". */ } } @@ -1388,8 +1388,8 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj, enum status sarif_replayer:: handle_location_object (const json::object &location_obj, - libdiagnostics::physical_location &out_physical_loc, - libdiagnostics::logical_location &out_logical_loc) + libgdiagnostics::physical_location &out_physical_loc, + libgdiagnostics::logical_location &out_logical_loc) { // §3.28.3 "physicalLocation" property { @@ -1438,9 +1438,9 @@ handle_location_object (const json::object &location_obj, enum status sarif_replayer:: handle_physical_location_object (const json::object &phys_loc_obj, - libdiagnostics::physical_location &out) + libgdiagnostics::physical_location &out) { - libdiagnostics::file artifact_file; + libgdiagnostics::file artifact_file; // §3.29.3 "artifactLocation" property const property_spec_ref artifact_location_prop @@ -1487,7 +1487,7 @@ handle_physical_location_object (const json::object &phys_loc_obj, enum status sarif_replayer::handle_artifact_location_object (const json::object &artifact_loc, - libdiagnostics::file &out) + libgdiagnostics::file &out) { // §3.4.3 "uri" property const property_spec_ref uri_prop ("artifactLocation", "uri", "3.4.3"); @@ -1521,15 +1521,15 @@ sarif_replayer::handle_artifact_location_object (const json::object &artifact_lo enum status sarif_replayer:: handle_region_object (const json::object ®ion_obj, - libdiagnostics::file file, - libdiagnostics::physical_location &out) + libgdiagnostics::file file, + libgdiagnostics::physical_location &out) { gcc_assert (file.m_inner); // §3.30.5 "startLine" property const property_spec_ref start_line_prop ("region", "startLine", "3.30.5"); - libdiagnostics::physical_location start; - libdiagnostics::physical_location end; + libgdiagnostics::physical_location start; + libgdiagnostics::physical_location end; if (auto start_line_jnum = get_optional_property (region_obj, start_line_prop)) @@ -1586,7 +1586,7 @@ handle_region_object (const json::object ®ion_obj, enum status sarif_replayer:: handle_logical_location_object (const json::object &logical_loc_obj, - libdiagnostics::logical_location &out) + libgdiagnostics::logical_location &out) { const property_spec_ref name_prop ("logicalLocation", "name", "3.33.4"); const char *short_name = nullptr; @@ -1643,7 +1643,7 @@ handle_logical_location_object (const json::object &logical_loc_obj, kind = result.m_val; } - libdiagnostics::logical_location parent; + libgdiagnostics::logical_location parent; out = m_output_mgr.new_logical_location (kind, parent, short_name, @@ -1740,7 +1740,7 @@ sarif_replay_path (const char *sarif_file, FAIL_IF_NULL (control_manager); FAIL_IF_NULL (options); - sarif_replayer r (libdiagnostics::manager (output_manager, false), - libdiagnostics::manager (control_manager, false)); + sarif_replayer r (libgdiagnostics::manager (output_manager, false), + libgdiagnostics::manager (control_manager, false)); return (int)r.replay_file (sarif_file, *options); } diff --git a/gcc/libsarifreplay.h b/gcc/libsarifreplay.h index 4c6b255..c92ae8a 100644 --- a/gcc/libsarifreplay.h +++ b/gcc/libsarifreplay.h @@ -20,7 +20,7 @@ along with GCC; see the file COPYING3. If not see #ifndef LIBSARIFREPLAY_H #define LIBSARIFREPLAY_H -#include "libdiagnostics.h" +#include "libgdiagnostics.h" #ifdef __cplusplus extern "C" { @@ -47,10 +47,10 @@ sarif_replay_path (const char *sarif_file, diagnostic_manager *output_manager, diagnostic_manager *control_manager, const replay_options *options) - LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) - LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) - LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3) - LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (4); + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (4); #ifdef __cplusplus } diff --git a/gcc/sarif-replay.cc b/gcc/sarif-replay.cc index e33457e..e0e7dadd 100644 --- a/gcc/sarif-replay.cc +++ b/gcc/sarif-replay.cc @@ -24,7 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "version.h" #include "intl.h" -#include "libdiagnostics++.h" +#include "libgdiagnostics++.h" #include "libsarifreplay.h" static const char *progname; @@ -98,9 +98,9 @@ print_usage () static bool parse_options (int argc, char **argv, options &opts, - libdiagnostics::text_sink control_text_sink) + libgdiagnostics::text_sink control_text_sink) { - libdiagnostics::manager options_mgr; + libgdiagnostics::manager options_mgr; options_mgr.set_tool_name ("sarif-replay"); options_mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_NO/*IF_TTY*/); @@ -196,11 +196,11 @@ main (int argc, char **argv) progname = get_progname (argv[0]); xmalloc_set_program_name (progname); - libdiagnostics::manager control_mgr; + libgdiagnostics::manager control_mgr; control_mgr.set_tool_name (progname); - libdiagnostics::text_sink control_text_sink + libgdiagnostics::text_sink control_text_sink = control_mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY); options opts; @@ -218,7 +218,7 @@ main (int argc, char **argv) auto note = control_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE); note.finish ("about to replay %qs...", filename); } - libdiagnostics::manager playback_mgr; + libgdiagnostics::manager playback_mgr; playback_mgr.add_text_sink (stderr, opts.m_replay_opts.m_diagnostics_colorize); diff --git a/gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp b/gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp deleted file mode 100644 index d29a469..0000000 --- a/gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp +++ /dev/null @@ -1,296 +0,0 @@ -# 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 diff --git a/gcc/testsuite/libdiagnostics.dg/sarif.py b/gcc/testsuite/libdiagnostics.dg/sarif.py deleted file mode 100644 index 7daf35b..0000000 --- a/gcc/testsuite/libdiagnostics.dg/sarif.py +++ /dev/null @@ -1,23 +0,0 @@ -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'] diff --git a/gcc/testsuite/libdiagnostics.dg/test-dump.c b/gcc/testsuite/libdiagnostics.dg/test-dump.c deleted file mode 100644 index 9f0576d..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-dump.c +++ /dev/null @@ -1,69 +0,0 @@ -/* 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; -}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-error-c.py b/gcc/testsuite/libdiagnostics.dg/test-error-c.py deleted file mode 100644 index 1206be8..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-error-c.py +++ /dev/null @@ -1,54 +0,0 @@ -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 ' 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 \n' diff --git a/gcc/testsuite/libdiagnostics.dg/test-error-with-note-c.py b/gcc/testsuite/libdiagnostics.dg/test-error-with-note-c.py deleted file mode 100644 index bed21ca..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-error-with-note-c.py +++ /dev/null @@ -1,50 +0,0 @@ -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 \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 \n' - assert note['message']['text'] == 'have you looked behind the couch?' diff --git a/gcc/testsuite/libdiagnostics.dg/test-error-with-note.c b/gcc/testsuite/libdiagnostics.dg/test-error-with-note.c deleted file mode 100644 index f406743..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-error-with-note.c +++ /dev/null @@ -1,76 +0,0 @@ -/* 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 - | ^~~~~ -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 -*/ -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 - | ^~~~~ - { 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-error-with-note.cc b/gcc/testsuite/libdiagnostics.dg/test-error-with-note.cc deleted file mode 100644 index e211297..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-error-with-note.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* 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\\\?" } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-error.c b/gcc/testsuite/libdiagnostics.dg/test-error.c deleted file mode 100644 index 6f17ce2..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-error.c +++ /dev/null @@ -1,61 +0,0 @@ -/* 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 - | ^~~~~ - - along with the equivalent in SARIF. */ - -#include "libdiagnostics.h" -#include "test-helpers.h" - -/* -_________1111111 -1234567890123456 -#include -*/ -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 - | ^~~~~ - { 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-error.cc b/gcc/testsuite/libdiagnostics.dg/test-error.cc deleted file mode 100644 index 6f919e4..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-error.cc +++ /dev/null @@ -1,47 +0,0 @@ -/* 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 "" } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-example-1.c b/gcc/testsuite/libdiagnostics.dg/test-example-1.c deleted file mode 100644 index 58a146b..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-example-1.c +++ /dev/null @@ -1,43 +0,0 @@ -/* 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" } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint-c.py b/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint-c.py deleted file mode 100644 index f3dc71c..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint-c.py +++ /dev/null @@ -1,46 +0,0 @@ -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' diff --git a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c b/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c deleted file mode 100644 index 0d8ee46..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c +++ /dev/null @@ -1,83 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.cc b/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.cc deleted file mode 100644 index 92c7f07..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* 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 diff --git a/gcc/testsuite/libdiagnostics.dg/test-helpers++.h b/gcc/testsuite/libdiagnostics.dg/test-helpers++.h deleted file mode 100644 index c8ff2de..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-helpers++.h +++ /dev/null @@ -1,28 +0,0 @@ -/* 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 */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-helpers.h b/gcc/testsuite/libdiagnostics.dg/test-helpers.h deleted file mode 100644 index a578850..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-helpers.h +++ /dev/null @@ -1,72 +0,0 @@ -/* 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 */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c b/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c deleted file mode 100644 index 39978b2..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c +++ /dev/null @@ -1,71 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.cc b/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.cc deleted file mode 100644 index 1c1c050..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.py b/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.py deleted file mode 100644 index dce404f..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 *' diff --git a/gcc/testsuite/libdiagnostics.dg/test-logical-location-c.py b/gcc/testsuite/libdiagnostics.dg/test-logical-location-c.py deleted file mode 100644 index 7448a1e..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-logical-location-c.py +++ /dev/null @@ -1,37 +0,0 @@ -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' diff --git a/gcc/testsuite/libdiagnostics.dg/test-logical-location.c b/gcc/testsuite/libdiagnostics.dg/test-logical-location.c deleted file mode 100644 index 7d0bed9..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-logical-location.c +++ /dev/null @@ -1,81 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-metadata-c.py b/gcc/testsuite/libdiagnostics.dg/test-metadata-c.py deleted file mode 100644 index fc83658..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-metadata-c.py +++ /dev/null @@ -1,45 +0,0 @@ -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'" diff --git a/gcc/testsuite/libdiagnostics.dg/test-metadata.c b/gcc/testsuite/libdiagnostics.dg/test-metadata.c deleted file mode 100644 index 7881c9e..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-metadata.c +++ /dev/null @@ -1,61 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-multiple-lines-c.py b/gcc/testsuite/libdiagnostics.dg/test-multiple-lines-c.py deleted file mode 100644 index 3189fcf..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-multiple-lines-c.py +++ /dev/null @@ -1,83 +0,0 @@ -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 diff --git a/gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c b/gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c deleted file mode 100644 index 888897a..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c +++ /dev/null @@ -1,78 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-no-column-c.py b/gcc/testsuite/libdiagnostics.dg/test-no-column-c.py deleted file mode 100644 index afef984..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-no-column-c.py +++ /dev/null @@ -1,35 +0,0 @@ -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 \n' diff --git a/gcc/testsuite/libdiagnostics.dg/test-no-column.c b/gcc/testsuite/libdiagnostics.dg/test-no-column.c deleted file mode 100644 index 5354f75..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-no-column.c +++ /dev/null @@ -1,54 +0,0 @@ -/* 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 - - along with the equivalent in SARIF. */ - -#include "libdiagnostics.h" -#include "test-helpers.h" - -/* -_________111111111122 -123456789012345678901 -#include -*/ -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 - { 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-no-diagnostics-c.py b/gcc/testsuite/libdiagnostics.dg/test-no-diagnostics-c.py deleted file mode 100644 index 9ce1c2a..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-no-diagnostics-c.py +++ /dev/null @@ -1,42 +0,0 @@ -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 diff --git a/gcc/testsuite/libdiagnostics.dg/test-no-diagnostics.c b/gcc/testsuite/libdiagnostics.dg/test-no-diagnostics.c deleted file mode 100644 index 78e186a..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-no-diagnostics.c +++ /dev/null @@ -1,25 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint-c.py b/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint-c.py deleted file mode 100644 index cd4e6e2..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint-c.py +++ /dev/null @@ -1,54 +0,0 @@ -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 diff --git a/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c b/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c deleted file mode 100644 index 19fa7c1..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c +++ /dev/null @@ -1,69 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c b/gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c deleted file mode 100644 index c1468553..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c +++ /dev/null @@ -1,59 +0,0 @@ -/* 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 "" } - error: can't find 'foo' - 10 | PRINT "hello world!"; - | ^~~~~~~~~~~~ - { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-warning-c.py b/gcc/testsuite/libdiagnostics.dg/test-warning-c.py deleted file mode 100644 index c6e3752..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-warning-c.py +++ /dev/null @@ -1,54 +0,0 @@ -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' diff --git a/gcc/testsuite/libdiagnostics.dg/test-warning-with-path-c.py b/gcc/testsuite/libdiagnostics.dg/test-warning-with-path-c.py deleted file mode 100644 index 5d3bbc4..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-warning-with-path-c.py +++ /dev/null @@ -1,108 +0,0 @@ -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 diff --git a/gcc/testsuite/libdiagnostics.dg/test-warning-with-path.c b/gcc/testsuite/libdiagnostics.dg/test-warning-with-path.c deleted file mode 100644 index 4ff5548..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-warning-with-path.c +++ /dev/null @@ -1,138 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-warning.c b/gcc/testsuite/libdiagnostics.dg/test-warning.c deleted file mode 100644 index 252f646..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-warning.c +++ /dev/null @@ -1,67 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file-c.py b/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file-c.py deleted file mode 100644 index 2d5ebe9..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file-c.py +++ /dev/null @@ -1,55 +0,0 @@ -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' diff --git a/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c b/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c deleted file mode 100644 index 637935e..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c +++ /dev/null @@ -1,55 +0,0 @@ -/* 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" } } */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c b/gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c deleted file mode 100644 index 8ad448c..0000000 --- a/gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c +++ /dev/null @@ -1,47 +0,0 @@ -/* 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; -}; diff --git a/gcc/testsuite/libgdiagnostics.dg/libgdiagnostics.exp b/gcc/testsuite/libgdiagnostics.dg/libgdiagnostics.exp new file mode 100644 index 0000000..8e202a6 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/libgdiagnostics.exp @@ -0,0 +1,296 @@ +# Test code for libgdiagnostics.so +# +# We will compile each of libgdiagnostics.dg/test-*.{c,cc} into an executable +# dynamically linked against libgdiagnostics.so, and then run each +# such executable. +# +# These executables call into the libgdiagnostics.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 "libgdiagnostics". +# 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 libgdiagnostics + +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 libgdiagnostics + + 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/libgdiagnostics.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 libgdiagnostics-dg-test { prog do_what extra_tool_flags } { + verbose "within libgdiagnostics-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 ![libgdiagnostics_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 + # libgdiagnostics.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/.. -lgdiagnostics -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 diff --git a/gcc/testsuite/libgdiagnostics.dg/sarif.py b/gcc/testsuite/libgdiagnostics.dg/sarif.py new file mode 100644 index 0000000..7daf35b --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/sarif.py @@ -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'] diff --git a/gcc/testsuite/libgdiagnostics.dg/test-dump.c b/gcc/testsuite/libgdiagnostics.dg/test-dump.c new file mode 100644 index 0000000..c586f56 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-dump.c @@ -0,0 +1,69 @@ +/* Usage example of dump API. */ + +#include "libgdiagnostics.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; +}; diff --git a/gcc/testsuite/libgdiagnostics.dg/test-error-c.py b/gcc/testsuite/libgdiagnostics.dg/test-error-c.py new file mode 100644 index 0000000..1206be8 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-error-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 = 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 ' 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 \n' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-error-with-note-c.py b/gcc/testsuite/libgdiagnostics.dg/test-error-with-note-c.py new file mode 100644 index 0000000..bed21ca --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-error-with-note-c.py @@ -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 \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 \n' + assert note['message']['text'] == 'have you looked behind the couch?' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-error-with-note.c b/gcc/testsuite/libgdiagnostics.dg/test-error-with-note.c new file mode 100644 index 0000000..6a9b7de --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-error-with-note.c @@ -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 + | ^~~~~ +PATH/test-error-with-note.c:18:11: note: have you looked behind the couch? + + along with the equivalent in SARIF. */ + +#include "libgdiagnostics.h" +#include "test-helpers.h" + +/* +_________111111111122 +123456789012345678901 +#include +*/ +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 + | ^~~~~ + { 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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-error-with-note.cc b/gcc/testsuite/libgdiagnostics.dg/test-error-with-note.cc new file mode 100644 index 0000000..913abb6 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-error-with-note.cc @@ -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 "libgdiagnostics++.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + libgdiagnostics::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); + + libgdiagnostics::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\\\?" } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-error.c b/gcc/testsuite/libgdiagnostics.dg/test-error.c new file mode 100644 index 0000000..54501d0 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-error.c @@ -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 + | ^~~~~ + + along with the equivalent in SARIF. */ + +#include "libgdiagnostics.h" +#include "test-helpers.h" + +/* +_________1111111 +1234567890123456 +#include +*/ +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 + | ^~~~~ + { 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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-error.cc b/gcc/testsuite/libgdiagnostics.dg/test-error.cc new file mode 100644 index 0000000..bf1d464 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-error.cc @@ -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 "libgdiagnostics++.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + libgdiagnostics::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); + + libgdiagnostics::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 "" } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-example-1.c b/gcc/testsuite/libgdiagnostics.dg/test-example-1.c new file mode 100644 index 0000000..86b16b1 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-example-1.c @@ -0,0 +1,43 @@ +/* begin quoted source */ +/* Minimal usage example. */ +#include "libgdiagnostics.h" + +static diagnostic_manager *diag_mgr; + +static void +init_diagnostics (void) +{ + diag_mgr = diagnostic_manager_new (); + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); +} + +static void +finish_diagnostics (void) +{ + diagnostic_manager_release (diag_mgr); +} + +static void +do_stuff (void) +{ + const char *username = "Dave"; + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_finish (d, + "I'm sorry %s, I'm afraid I can't do that", + username); +} + +int +main () +{ + init_diagnostics (); + + do_stuff (); + + finish_diagnostics (); +}; +/* end quoted source */ + +/* { dg-regexp "progname: error: I'm sorry Dave, I'm afraid I can't do that" } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint-c.py b/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint-c.py new file mode 100644 index 0000000..f3dc71c --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint-c.py @@ -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' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.c b/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.c new file mode 100644 index 0000000..505142c --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.cc b/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.cc new file mode 100644 index 0000000..506c095 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-fix-it-hint.cc @@ -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 "libgdiagnostics++.h" +#include "test-helpers++.h" + +/* +_________11111111112 +12345678901234567890 + return p->colour; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + libgdiagnostics::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 diff --git a/gcc/testsuite/libgdiagnostics.dg/test-helpers++.h b/gcc/testsuite/libgdiagnostics.dg/test-helpers++.h new file mode 100644 index 0000000..1d9eb96 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-helpers++.h @@ -0,0 +1,28 @@ +/* Common utility code shared between test cases. */ + +#ifndef TEST_HELPERSPP_H +#define TEST_HELPERSPP_H + +namespace libgdiagnostics { + +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 libgdiagnostics + +#endif /* #ifndef TEST_HELPERSPP_H */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-helpers.h b/gcc/testsuite/libgdiagnostics.dg/test-helpers.h new file mode 100644 index 0000000..a578850 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-helpers.h @@ -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 */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.c b/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.c new file mode 100644 index 0000000..0befe0b --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.cc b/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.cc new file mode 100644 index 0000000..fb6262b --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.cc @@ -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 "libgdiagnostics++.h" +#include "test-helpers++.h" + +/* +_________11111111112 +12345678901234567890 + 42 + "foo" +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + FILE *sarif_outfile; + libgdiagnostics::manager mgr; + mgr.set_tool_name ("test-labelled-ranges.cc.exe"); + + libgdiagnostics::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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.py b/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.py new file mode 100644 index 0000000..dce404f --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-labelled-ranges.py @@ -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 *' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py b/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py new file mode 100644 index 0000000..7448a1e --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py @@ -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' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c new file mode 100644 index 0000000..d853983 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-metadata-c.py b/gcc/testsuite/libgdiagnostics.dg/test-metadata-c.py new file mode 100644 index 0000000..fc83658 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-metadata-c.py @@ -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'" diff --git a/gcc/testsuite/libgdiagnostics.dg/test-metadata.c b/gcc/testsuite/libgdiagnostics.dg/test-metadata.c new file mode 100644 index 0000000..064a05c --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-metadata.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines-c.py b/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines-c.py new file mode 100644 index 0000000..3189fcf --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines-c.py @@ -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 diff --git a/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c b/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c new file mode 100644 index 0000000..e761110 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-no-column-c.py b/gcc/testsuite/libgdiagnostics.dg/test-no-column-c.py new file mode 100644 index 0000000..afef984 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-no-column-c.py @@ -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 \n' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-no-column.c b/gcc/testsuite/libgdiagnostics.dg/test-no-column.c new file mode 100644 index 0000000..6533b25 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-no-column.c @@ -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 + + along with the equivalent in SARIF. */ + +#include "libgdiagnostics.h" +#include "test-helpers.h" + +/* +_________111111111122 +123456789012345678901 +#include +*/ +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 + { 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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics-c.py b/gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics-c.py new file mode 100644 index 0000000..9ce1c2a --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics-c.py @@ -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 diff --git a/gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics.c b/gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics.c new file mode 100644 index 0000000..d4805f4 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-no-diagnostics.c @@ -0,0 +1,25 @@ +/* Test of the "no diagnostics are emitted" case. */ + +#include "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint-c.py b/gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint-c.py new file mode 100644 index 0000000..cd4e6e2 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint-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 diff --git a/gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint.c b/gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint.c new file mode 100644 index 0000000..7017915 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-note-with-fix-it-hint.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-text-sink-options.c b/gcc/testsuite/libgdiagnostics.dg/test-text-sink-options.c new file mode 100644 index 0000000..29e576c --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-text-sink-options.c @@ -0,0 +1,59 @@ +/* Example of controlling options for text sinks, + with multiple text sinks, + and color output. */ + +#include "libgdiagnostics.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 "" } + error: can't find 'foo' + 10 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-warning-c.py b/gcc/testsuite/libgdiagnostics.dg/test-warning-c.py new file mode 100644 index 0000000..c6e3752 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-warning-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 = 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' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py b/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py new file mode 100644 index 0000000..5d3bbc4 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py @@ -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 diff --git a/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path.c b/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path.c new file mode 100644 index 0000000..58bd4de --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-warning.c b/gcc/testsuite/libgdiagnostics.dg/test-warning.c new file mode 100644 index 0000000..29f76b1 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-warning.c @@ -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 "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file-c.py b/gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file-c.py new file mode 100644 index 0000000..2d5ebe9 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file-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' diff --git a/gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file.c b/gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file.c new file mode 100644 index 0000000..b783d14 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-write-sarif-to-file.c @@ -0,0 +1,55 @@ +/* Example of writing diagnostics as SARIF to a file. */ + +#include "libgdiagnostics.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" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-write-text-to-file.c b/gcc/testsuite/libgdiagnostics.dg/test-write-text-to-file.c new file mode 100644 index 0000000..c5075d4 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-write-text-to-file.c @@ -0,0 +1,47 @@ +/* Example of writing diagnostics in text form, but to a file, + rather than stderr. */ + +#include "libgdiagnostics.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; +}; -- cgit v1.1