aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/ChangeLog8
-rwxr-xr-xcontrib/dg-lint/dg-lint404
-rw-r--r--contrib/dg-lint/libgdiagnostics.py250
-rw-r--r--contrib/dg-lint/test-1.c41
-rw-r--r--contrib/dg-lint/test-2.c8
5 files changed, 711 insertions, 0 deletions
diff --git a/contrib/ChangeLog b/contrib/ChangeLog
index 3b66733..c78c0b7 100644
--- a/contrib/ChangeLog
+++ b/contrib/ChangeLog
@@ -1,3 +1,11 @@
+2025-03-27 David Malcolm <dmalcolm@redhat.com>
+
+ PR testsuite/116163
+ * dg-lint/dg-lint: New file.
+ * dg-lint/libgdiagnostics.py: New file.
+ * dg-lint/test-1.c: New file.
+ * dg-lint/test-2.c: New file.
+
2025-03-17 Sahil Yeole <sahilyeole93@gmail.com>
* update-copyright.py: Add libgrust folder.
diff --git a/contrib/dg-lint/dg-lint b/contrib/dg-lint/dg-lint
new file mode 100755
index 0000000..01d58d7
--- /dev/null
+++ b/contrib/dg-lint/dg-lint
@@ -0,0 +1,404 @@
+#!/usr/bin/env python3
+
+# Script to detect common mistakes in DejaGnu tests.
+
+# Contributed by David Malcolm <dmalcolm@redhat.com>
+#
+# Copyright (C) 2024-2025 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 COPYING. If not, write to
+# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+import argparse
+import difflib
+import os
+import pathlib
+import re
+import sys
+
+import libgdiagnostics
+
+KNOWN_DIRECTIVES = {
+ # Directives, organized by HTML documentation file:
+
+ # https://gcc.gnu.org/onlinedocs/gccint/Directives.html
+ 'dg-do',
+ 'dg-options',
+ 'dg-add-options',
+ 'dg-remove-options',
+ 'dg-additional-options',
+ 'dg-timeout',
+ 'dg-timeout-factor',
+ 'dg-do-if',
+ 'dg-skip-if',
+ 'dg-require-effective-target',
+ 'dg-require-support',
+ 'dg-xfail-if',
+ 'dg-xfail-run-if',
+ 'dg-ice',
+ 'dg-shouldfail',
+ 'dg-error',
+ 'dg-warning',
+ 'dg-message',
+ 'dg-note',
+ 'dg-bogus',
+ 'dg-line',
+ 'dg-excess-errors',
+ 'dg-prune-output',
+ 'dg-output',
+ 'dg-output-file',
+ 'dg-set-compiler-env-var',
+ 'dg-set-target-env-var',
+ 'dg-additional-files',
+ 'dg-final',
+
+ # https://gcc.gnu.org/onlinedocs/gccint/Require-Support.html
+ 'dg-require-iconv',
+ 'dg-require-profiling',
+ 'dg-require-stack-check',
+ 'dg-require-stack-size',
+ 'dg-require-visibility',
+ 'dg-require-alias',
+ 'dg-require-ascii-locale',
+ 'dg-require-compat-dfp',
+ 'dg-require-cxa-atexit',
+ 'dg-require-dll',
+ 'dg-require-dot',
+ 'dg-require-fork',
+ 'dg-require-gc-sections',
+ 'dg-require-host-local',
+ 'dg-require-linker-plugin',
+ 'dg-require-mkfifo',
+ 'dg-require-named-sections',
+ 'dg-require-weak',
+ 'dg-require-weak-override',
+
+ # https://gcc.gnu.org/onlinedocs/gccint/LTO-Testing.html
+ 'dg-lto-do',
+ 'dg-lto-options',
+ 'dg-extra-ld-options',
+ 'dg-suppress-ld-options',
+
+ # https://gcc.gnu.org/onlinedocs/gccint/profopt-Testing.html
+ 'dg-final-generate',
+ 'dg-final-use',
+
+ # https://gcc.gnu.org/onlinedocs/gccint/Final-Actions.html
+ 'dg-keep-saved-temps',
+
+ # Other directives I couldn't find docs for,
+ # organized by implementation file:
+
+ # gcc/testsuite/lib/target-supports-dg.exp
+ 'dg-require-ifunc',
+ 'dg-require-python-h',
+
+ # gcc/testsuite/lib/multiline.exp:
+ 'dg-begin-multiline-output',
+ 'dg-end-multiline-output',
+ 'dg-enable-nn-line-numbers',
+
+ # gcc/testsuite/lib/gcc-dg.exp:
+ 'dg-allow-blank-lines-in-output',
+ 'dg-locus',
+ 'dg-optimized',
+ 'dg-missed',
+
+ # In gcc/testsuite/lib/gcc-defs.exp:
+ 'dg-additional-sources',
+ 'dg-regexp',
+
+ 'dg-compile-aux-modules',
+ # implemented in various places:
+ # gcc/testsuite/gcc.target/powerpc/ppc-fortran/ppc-fortran.exp
+ # gcc/testsuite/gfortran.dg/c-interop/c-interop.exp
+ # gcc/testsuite/gfortran.dg/coarray/caf.exp
+ # gcc/testsuite/gfortran.dg/dg.exp
+ # gcc/testsuite/gfortran.dg/f202y/f202y.exp
+ # gcc/testsuite/gfortran.dg/goacc/goacc.exp
+
+ # gcc/testsuite/lib/lto.exp
+ 'dg-lto-warning',
+ 'dg-lto-message',
+ 'dg-lto-note',
+
+ # gcc/testsuite/lib/profopt.exp
+ 'dg-final-use-autofdo',
+ 'dg-final-use-not-autofdo',
+
+ # gcc/testsuite/lib/scanasm.exp
+ 'dg-function-on-line',
+
+ # gcc/testsuite/g++.dg/modules/modules.exp:
+ 'dg-module-cmi',
+ 'dg-module-do',
+
+ # gcc/testsuite/gcc.target/bfin/bfin.exp
+ 'dg-bfin-options',
+ 'dg-bfin-processors',
+
+ # gcc/testsuite/gcc.target/csky/csky.exp
+ 'dg-csky-options',
+
+ # libstdc++-v3/testsuite/lib/dg-options.exp
+ 'dg-require-c-std',
+ 'dg-require-debug-mode',
+ 'dg-require-profile-mode',
+ 'dg-require-normal-mode',
+ 'dg-require-normal-namespace',
+ 'dg-require-parallel-mode',
+ 'dg-require-fileio',
+ 'dg-require-namedlocale',
+ 'dg-require-sharedlib',
+ 'dg-require-time',
+ 'dg-require-cstdint',
+ 'dg-require-cmath',
+ 'dg-require-thread-fence',
+ 'dg-require-atomic-cmpxchg-word',
+ 'dg-require-atomic-builtins',
+ 'dg-require-gthreads',
+ 'dg-require-gthreads-timed',
+ 'dg-require-sleep',
+ 'dg-require-sched-yield',
+ 'dg-require-string-conversions',
+ 'dg-require-swprintf',
+ 'dg-require-binary-io',
+ 'dg-require-nprocs',
+ 'dg-require-static-libstdcxx',
+ 'dg-require-little-endian',
+ 'dg-require-filesystem-ts',
+ 'dg-require-target-fs-symlinks',
+ 'dg-require-target-fs-space',
+ 'dg-require-target-fs-lwt',
+ 'dg-require-cpp-feature-test'
+}
+
+class Directive:
+ @staticmethod
+ def from_match(path, line_num, line, m):
+ return Directive(path, line_num,
+ m.group(1), m.span(1),
+ m.group(2), m.span(2))
+
+ def __init__(self, path, line_num,
+ name, name_span,
+ args_str, args_str_span):
+ self.path = path
+ self.line_num = line_num
+ self.name = name
+ self.name_span = name_span
+ self.args_str = args_str
+ self.args_str_span = args_str_span
+
+class FileState:
+ def __init__(self):
+ # Map from directive name to list of directives
+ self.directives = {}
+
+ def add_directive(self, d):
+ if d.name not in self.directives:
+ self.directives[d.name] = []
+ self.directives[d.name].append(d)
+
+class Context:
+ def __init__(self):
+ self.mgr = libgdiagnostics.Manager()
+ self.mgr.set_tool_name('dg-lint')
+ self.mgr.add_text_sink()
+ self.num_files_checked = 0
+
+ def check_file(self, f_in):
+ file_state = FileState()
+ try:
+ for line_idx, line in enumerate(f_in):
+ self.check_line(file_state, f_in.name, line_idx + 1, line)
+ self.num_files_checked += 1
+ except UnicodeDecodeError:
+ return # FIXME
+
+ def check_line(self, file_state, path, line_num, line):
+ if 0:
+ phys_loc = self.get_file_and_line(path, line_num)
+ self.report_warning(phys_loc, "line = '%s'", line.rstrip())
+
+ # Look for directives
+ # Compare with dg.exp's dg-get-options
+ m = re.search(r"{[ \t]+(dg-[-a-z]+)[ \t]+(.*)[ \t]+}", line)
+ if m:
+ d = Directive.from_match(path, line_num, line, m)
+ self.on_directive(file_state, d)
+ else:
+ # Look for things that look like attempts to use directives,
+ # but didn't match the regexp
+ m = re.search(r"(dg-[-a-z]+)", line)
+ if m:
+ phys_loc \
+ = self.get_file_line_and_range_for_match_span(path,
+ line_num,
+ m.span(1))
+ self.make_warning() \
+ .at(phys_loc) \
+ .emit('directive %qs appears not to match dg.exp\'s regexp',
+ m.group(1))
+ else:
+ # Look for things that look like misspellings of directives,
+ # via underscores rather than dashes
+ m = re.search(r"{[ \t]+(dg[-_][-_a-z]+)(.*)", line)
+ if m:
+ bogus_d = Directive.from_match(path, line_num, line, m)
+ self.report_unknown_directive(bogus_d)
+
+ def on_directive(self, file_state, d):
+ if 0:
+ phys_loc = self.get_file_and_line(d.path, d.line_num)
+ self.make_note() \
+ .at(phys_loc) \
+ .emit('directive %qs with arguments %qs',
+ d.name, d.args_str)
+
+ if d.name not in KNOWN_DIRECTIVES:
+ self.report_unknown_directive(d)
+
+ # Apparently we can't have a dg-do after a dg-require;
+ # see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116163#c5
+ if d.name == 'dg-do':
+ for existing_name in file_state.directives:
+ if existing_name.startswith('dg-require'):
+ other_d = file_state.directives[existing_name][0]
+ self.complain_about_order(other_d, d)
+
+ file_state.add_directive(d)
+
+ def report_unknown_directive(self, d):
+ phys_loc = self.get_phys_loc_for_directive(d)
+ w = self.make_warning() \
+ .at(phys_loc)
+ candidates = difflib.get_close_matches(d.name, KNOWN_DIRECTIVES)
+ if candidates:
+ suggestion = candidates[0]
+ w.with_fix_it_replace (phys_loc, suggestion) \
+ .emit("unknown directive: %qs; did you mean %qs?",
+ d.name, suggestion)
+ else:
+ w.emit("unknown directive: %qs",
+ d.name)
+
+ def complain_about_order(self, existing_d: Directive, new_d: Directive):
+ """
+ Complain about new_d occurring after existing_d.
+ """
+ with self.mgr.make_group() as g:
+ self.make_warning() \
+ .at(self.get_phys_loc_for_directive(new_d))\
+ .emit("%qs after %qs", new_d.name, existing_d.name)
+ self.make_note() \
+ .at(self.get_phys_loc_for_directive(existing_d))\
+ .emit("%qs was here", existing_d.name)
+
+ def get_file_and_line(self,
+ path: str,
+ line_num: int):
+ """
+ Get a libgdiagnostics.c_diagnostic_physical_location_ptr for the given line.
+ """
+ c_diag_file = self.mgr.get_file(path)
+ return self.mgr.get_file_and_line(c_diag_file, line_num)
+
+ def get_phys_loc_for_directive(self, d: Directive) \
+ -> libgdiagnostics.c_diagnostic_physical_location_ptr:
+ return self.get_file_line_and_range_for_match_span(d.path,
+ d.line_num,
+ d.name_span)
+
+ def get_file_line_and_range_for_match_span(self,
+ path: str,
+ line_num: int,
+ span):
+ return self.get_file_line_and_range(path, line_num,
+ start_column=span[0] + 1,
+ end_column=span[1])
+
+ def get_file_line_and_range(self,
+ path: str,
+ line_num: int,
+ start_column: int,
+ end_column: int):
+ """
+ Get a libgdiagnostics.c_diagnostic_physical_location_ptr for the range
+ of columns on the given line.
+ """
+ c_diag_file = self.mgr.get_file(path)
+ start_loc = self.mgr.get_file_line_and_column(c_diag_file, line_num, start_column)
+ end_loc = self.mgr.get_file_line_and_column(c_diag_file, line_num, end_column)
+ return self.mgr.get_range(start_loc, start_loc, end_loc)
+
+ def report_warning(self, phys_loc, msg, *args):
+ diag = libgdiagnostics.Diagnostic(self.mgr,
+ libgdiagnostics.DIAGNOSTIC_LEVEL_WARNING)
+ diag.set_location (phys_loc)
+ diag.finish(msg, *args)
+
+ def make_warning(self):
+ return libgdiagnostics.Diagnostic(self.mgr,
+ libgdiagnostics.DIAGNOSTIC_LEVEL_WARNING)
+
+ def make_note(self):
+ return libgdiagnostics.Diagnostic(self.mgr,
+ libgdiagnostics.DIAGNOSTIC_LEVEL_NOTE)
+
+ def report_stats(self):
+ self.make_note ()\
+ .emit('%i files checked', self.num_files_checked)
+
+def skip_file(filename):
+ if 'ChangeLog' in filename:
+ return True
+ if 'README' in filename:
+ return True
+ if filename.endswith('Makefile.am') or filename.endswith('Makefile.in'):
+ return True
+ if filename.endswith('.exp'):
+ return True
+ if 'gen_directive_tests' in filename:
+ return True
+ return False
+
+def main(argv):
+ parser = argparse.ArgumentParser()#usage=__doc__)
+ parser.add_argument('paths', nargs='+', type=pathlib.Path)
+ opts = parser.parse_args(argv[1:])
+
+ ctxt = Context()
+ for path in opts.paths:
+ if path.is_dir():
+ for dirpath, dirnames, filenames in os.walk(path):
+ for f in filenames:
+ if skip_file(f):
+ continue
+ p = os.path.join(dirpath, f)
+ with open(p) as f:
+ ctxt.check_file(f)
+ else:
+ with open(path) as f:
+ ctxt.check_file(f)
+ ctxt.report_stats()
+
+# TODO: how to unit test?
+
+if __name__ == '__main__':
+ retval = main(sys.argv)
+ sys.exit(retval)
diff --git a/contrib/dg-lint/libgdiagnostics.py b/contrib/dg-lint/libgdiagnostics.py
new file mode 100644
index 0000000..03a6440
--- /dev/null
+++ b/contrib/dg-lint/libgdiagnostics.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python3
+
+# Python bindings for libgdiagnostics, using ctypes
+
+# Contributed by David Malcolm <dmalcolm@redhat.com>
+#
+# Copyright (C) 2025 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 COPYING. If not, write to
+# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+from contextlib import contextmanager
+import ctypes
+
+# Lower-level API: use ctypes and FFI to wrap the C API directly
+
+cdll = ctypes.cdll.LoadLibrary('libgdiagnostics.so')
+
+libc = ctypes.CDLL(None)
+c_stderr = ctypes.c_void_p.in_dll(libc, 'stderr')
+
+# Opaque structs
+
+class c_diagnostic_manager(ctypes.Structure):
+ pass
+c_diagnostic_manager_ptr = ctypes.POINTER(c_diagnostic_manager)
+
+class c_diagnostic(ctypes.Structure):
+ pass
+c_diagnostic_ptr = ctypes.POINTER(c_diagnostic)
+
+class c_diagnostic_file(ctypes.Structure):
+ pass
+c_diagnostic_file_ptr = ctypes.POINTER(c_diagnostic_file)
+
+class c_diagnostic_physical_location(ctypes.Structure):
+ pass
+c_diagnostic_physical_location_ptr = ctypes.POINTER(c_diagnostic_physical_location)
+
+# Enums
+
+DIAGNOSTIC_COLORIZE_IF_TTY = 0
+
+DIAGNOSTIC_LEVEL_ERROR = 0
+DIAGNOSTIC_LEVEL_WARNING = 1
+DIAGNOSTIC_LEVEL_NOTE = 2
+DIAGNOSTIC_LEVEL_SORRY = 3
+
+# Entrypoints
+
+cdll.diagnostic_manager_new.argtypes = []
+cdll.diagnostic_manager_new.restype = c_diagnostic_manager_ptr
+
+cdll.diagnostic_manager_release.argtypes = [c_diagnostic_manager_ptr]
+cdll.diagnostic_manager_release.restype = None
+
+cdll.diagnostic_manager_set_tool_name.argtypes = [c_diagnostic_manager_ptr,
+ ctypes.c_char_p]
+cdll.diagnostic_manager_set_tool_name.restype = None
+
+cdll.diagnostic_manager_begin_group.argtypes = [c_diagnostic_manager_ptr]
+cdll.diagnostic_manager_begin_group.restype = None
+
+cdll.diagnostic_manager_end_group.argtypes = [c_diagnostic_manager_ptr]
+cdll.diagnostic_manager_end_group.restype = None
+
+cdll.diagnostic_begin.argtypes = [c_diagnostic_manager_ptr,
+ ctypes.c_int]
+cdll.diagnostic_begin.restype = c_diagnostic_ptr
+
+cdll.diagnostic_finish.argtypes = [c_diagnostic_ptr,
+ ctypes.c_char_p]#, ctypes.c_char_p] # FIXME: should be variadic
+cdll.diagnostic_finish.restype = None
+
+cdll.diagnostic_manager_new_file.argtypes = [c_diagnostic_manager_ptr,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+cdll.diagnostic_manager_new_file.restype = c_diagnostic_file_ptr
+
+cdll.diagnostic_manager_new_location_from_file_and_line.argtypes \
+ = [c_diagnostic_manager_ptr,
+ c_diagnostic_file_ptr,
+ ctypes.c_int]
+cdll.diagnostic_manager_new_location_from_file_and_line.restype \
+ = c_diagnostic_physical_location_ptr
+
+cdll.diagnostic_manager_new_location_from_file_line_column.argtypes \
+ = [c_diagnostic_manager_ptr,
+ c_diagnostic_file_ptr,
+ ctypes.c_int,
+ ctypes.c_int]
+cdll.diagnostic_manager_new_location_from_file_line_column.restype \
+ = c_diagnostic_physical_location_ptr
+
+cdll.diagnostic_manager_new_location_from_range.argtypes\
+ = [c_diagnostic_manager_ptr,
+ c_diagnostic_physical_location_ptr,
+ c_diagnostic_physical_location_ptr,
+ c_diagnostic_physical_location_ptr]
+cdll.diagnostic_manager_new_location_from_range.restype \
+ = c_diagnostic_physical_location_ptr
+
+cdll.diagnostic_set_location.argtypes = [c_diagnostic_ptr,
+ c_diagnostic_physical_location_ptr]
+cdll.diagnostic_set_location.restype = None
+
+cdll.diagnostic_add_fix_it_hint_replace.argtypes \
+ = [c_diagnostic_ptr,
+ c_diagnostic_physical_location_ptr,
+ ctypes.c_char_p]
+cdll.diagnostic_add_fix_it_hint_replace.restype = None
+
+# Helper functions
+
+def _to_utf8(s: str):
+ if s is None:
+ return None
+ return s.encode('utf-8')
+
+# Higher-level API, a more pythonic approach, with classes
+
+class Manager:
+ def __init__(self):
+ self.c_mgr = cdll.diagnostic_manager_new()
+ if 0:
+ print('__init__: %r' % self.c_mgr)
+
+ def __del__(self):
+ if 0:
+ print('__del__: %r' % self.c_mgr)
+ self.c_mgr = cdll.diagnostic_manager_release(self.c_mgr)
+
+ def set_tool_name(self, name: str):
+ assert self.c_mgr
+ assert str
+ cdll.diagnostic_manager_set_tool_name (self.c_mgr, _to_utf8(name))
+
+ def add_text_sink(self):
+ assert self.c_mgr
+ # FIXME: hardcode the args for now
+ cdll.diagnostic_manager_add_text_sink (self.c_mgr,
+ c_stderr,
+ DIAGNOSTIC_COLORIZE_IF_TTY)
+
+ def get_file(self, path: str, sarif_lang: str = None):
+ assert self.c_mgr
+ assert path
+ c_file = cdll.diagnostic_manager_new_file (self.c_mgr,
+ _to_utf8(path),
+ _to_utf8(sarif_lang))
+ return c_file
+
+ def get_file_and_line(self,
+ c_file: c_diagnostic_file_ptr,
+ line_num: int):
+ assert self.c_mgr
+ assert c_file
+ c_phys_loc = cdll.diagnostic_manager_new_location_from_file_and_line (self.c_mgr,
+ c_file,
+ line_num)
+ return c_phys_loc
+
+ def get_file_line_and_column(self,
+ c_file: c_diagnostic_file_ptr,
+ line_num: int,
+ column_num: int):
+ assert self.c_mgr
+ assert c_file
+ c_phys_loc = cdll.diagnostic_manager_new_location_from_file_line_column (self.c_mgr,
+ c_file,
+ line_num,
+ column_num)
+ return c_phys_loc
+
+ def get_range(self,
+ caret: c_diagnostic_physical_location_ptr,
+ start: c_diagnostic_physical_location_ptr,
+ finish: c_diagnostic_physical_location_ptr):
+ assert self.c_mgr
+ c_phys_loc = cdll.diagnostic_manager_new_location_from_range (self.c_mgr,
+ caret,
+ start,
+ finish)
+ return c_phys_loc
+
+ @contextmanager
+ def make_group(self):
+ assert self.c_mgr
+ cdll.diagnostic_manager_begin_group (self.c_mgr)
+ try:
+ yield
+ finally:
+ assert self.c_mgr
+ cdll.diagnostic_manager_end_group (self.c_mgr)
+
+class Diagnostic:
+ def __init__(self, mgr: Manager, level: int):
+ self.c_diag = cdll.diagnostic_begin (mgr.c_mgr, level)
+ if 0:
+ print('__init__: %r' % self.c_diag)
+
+ def finish(self, fmt: str, *args):
+ assert self.c_diag
+ c_args = []
+ for arg in args:
+ if type(arg) is str:
+ arg = _to_utf8(arg)
+ c_args.append(arg)
+ cdll.diagnostic_finish (self.c_diag, _to_utf8(fmt), *c_args)
+ self.c_diagnostic = None
+
+ def set_location(self,
+ phys_loc: c_diagnostic_physical_location_ptr):
+ assert self.c_diag
+ cdll.diagnostic_set_location(self.c_diag, phys_loc)
+
+
+ # Chaining-style API
+
+ def at(self,
+ phys_loc: c_diagnostic_physical_location_ptr):
+ self.set_location(phys_loc)
+ return self
+
+ def with_fix_it_replace(self,
+ phys_loc: c_diagnostic_physical_location_ptr,
+ replacement: str):
+ assert self.c_diag
+ assert replacement
+ cdll.diagnostic_add_fix_it_hint_replace(self.c_diag,
+ phys_loc,
+ _to_utf8(replacement))
+ return self
+
+ def emit(self, fmt: str, *args):
+ self.finish(fmt, *args)
diff --git a/contrib/dg-lint/test-1.c b/contrib/dg-lint/test-1.c
new file mode 100644
index 0000000..38a2a85
--- /dev/null
+++ b/contrib/dg-lint/test-1.c
@@ -0,0 +1,41 @@
+/* Various misspelled DejaGnu directives. */
+
+/* Underscore rather than dash.
+ Example taken from gcc/testsuite/gcc.target/powerpc/vsx-builtin-msum.c
+ fixed in r15-3878-gd5864b95ce94d9. */
+/* { dg_final { scan_assembler_times "vmsumudm" 2 } } */
+
+
+/* Correct directive. */
+/* { dg-final { scan_assembler_times "vmsumudm" 2 } } */
+
+/* Malformed uses of directives.
+
+ Missing braces
+ dg-output-file "m4.out"
+
+ Missing leading brace
+ dg-output-file "m4.out" }
+
+ Missing trailing brace
+ { dg-output-file "m4.out"
+
+ Missing whitespace.
+ {dg-output-file "m4.out"}
+
+ Trailing comma.
+ { dg-output-file, "m4.out" }
+*/
+
+/* Unrecognized directives.
+
+ { dg-whatever }
+ { dg-whatever }
+ { dg-whatever "" }
+*/
+
+/* Misspelled directive
+
+ { dg-oupyt-file "m4.out" }
+
+ */
diff --git a/contrib/dg-lint/test-2.c b/contrib/dg-lint/test-2.c
new file mode 100644
index 0000000..147f5b0
--- /dev/null
+++ b/contrib/dg-lint/test-2.c
@@ -0,0 +1,8 @@
+/* Bad directive ordering; see
+ https://gcc.gnu.org/onlinedocs/gccint/Directives.html
+ "This directive must appear after any dg-do directive in the test and
+ before any dg-additional-sources directive." for
+ dg-require-effective-target. */
+
+/* { dg-require-effective-target c++11 } */
+/* { dg-do compile } */