import argparse
from dataclasses import dataclass, field
from typing import Set
[docs]
KNOWN_WARNINGS = {
'implicit-trunc',
'shift-overflow',
'shift-signed',
'implicit-extend',
'sign-compare',
'unused-value',
'bit-op-missmatch',
}
@dataclass
[docs]
class WarningsInfo:
# known: Set[str] = field(default_factory=set)
[docs]
known: Set[str] = field(default_factory=lambda: set(KNOWN_WARNINGS))
# defaults: Set[str] = field(default_factory=set)
[docs]
defaults: Set[str] = field(default_factory=lambda: set(KNOWN_WARNINGS))
[docs]
enabled: Set[str] = field(default_factory=set)
[docs]
disabled: Set[str] = field(default_factory=set)
[docs]
as_error: Set[str] = field(default_factory=set)
[docs]
all_as_error: bool = False
@property
[docs]
def warnings(self):
return (self.defaults - self.disabled) | self.enabled
@property
[docs]
def errors(self):
return self.as_error if not self.all_as_error else self.warnings
[docs]
class WarningFlagAction(argparse.Action):
[docs]
def __call__(self, parser, namespace, values, option_string=None):
warnings_info = getattr(namespace, 'warnings', None)
if warnings_info is None:
warnings_info = WarningsInfo()
for val in values:
if val == 'no-error':
warnings_info.all_as_error = False
elif val.startswith('no-'):
warn = val[3:]
assert warn in warnings_info.known, f"Unknown warning: {warn}"
warnings_info.disabled.add(warn)
elif val.startswith('error='):
warn = val[6:]
assert warn in warnings_info.known, f"Unknown warning: {warn}"
warnings_info.as_error.add(warn)
elif val == 'error':
warnings_info.all_as_error = True
elif val == 'all':
warnings_info.enabled.update(warnings_info.known)
else:
warn = val[3:]
assert warn in warnings_info.known, f"Unknown warning: {val}"
warnings_info.enabled.add(val)
# No need for -Wall as all warnings are enabled by default
setattr(namespace, 'warnings', warnings_info)
[docs]
def add_warnings_flags(parser, known_warnings: Set[str], default_warnings: Set[str]):
parser.add_argument(
'-W',
dest='warnings',
metavar='warning',
action=WarningFlagAction,
nargs='+',
help=(
"Enable/disable warnings like -Wfoo or -Wno-foo; "
"Make fatal with -Werror or -Werror=foo"
),
)
# Defaults
warnings_info = WarningsInfo(known=known_warnings, defaults=default_warnings)
parser.set_defaults(warnings=warnings_info)
[docs]
class WarningsManager:
def __init__(self, warnings_info: WarningsInfo):
[docs]
self.warnings_info = warnings_info
# TODO: warnings as errors?
[docs]
def emit_warning(self, msg, name=None, logger=None, line_info=None):
log_warn_f = logging.warning if logger is None else logger.warning
log_err_f = logging.error if logger is None else logger.error
if self.warnings_info is None:
return # ignore
assert name in self.warnings_info.known, f"Unknown warning: {name}"
is_err = name in self.warnings_info.errors
if name not in self.warnings_info.warnings:
# do nothing
return
log_f = log_err_f if is_err else log_warn_f
if name is not None:
msg += f" [-W{name}]"
if line_info is not None:
line_info_str = f"{line_info.file_path}:{line_info.start_line_no}"
msg += f" @ {line_info_str}"
log_f(msg)
if is_err:
raise RuntimeError(msg)