mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
268 lines
9.4 KiB
Python
268 lines
9.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Python bindings for libgdiagnostics, using ctypes
|
|
|
|
# Contributed by David Malcolm <dmalcolm@redhat.com>
|
|
#
|
|
# Copyright (C) 2025-2026 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
|
|
|
|
cdll.diagnostic_manager_add_sink_from_spec.argtypes \
|
|
= [c_diagnostic_manager_ptr,
|
|
ctypes.c_char_p,
|
|
ctypes.c_char_p,
|
|
c_diagnostic_manager_ptr]
|
|
cdll.diagnostic_manager_add_sink_from_spec.restype = ctypes.c_int
|
|
|
|
# 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 add_sink_from_spec(self, option_name: str, scheme: str, control_mgr):
|
|
assert self.c_mgr
|
|
assert control_mgr.c_mgr
|
|
res = cdll.diagnostic_manager_add_sink_from_spec (self.c_mgr,
|
|
_to_utf8(option_name),
|
|
_to_utf8(scheme),
|
|
control_mgr.c_mgr)
|
|
if res:
|
|
raise RuntimeError()
|
|
|
|
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)
|