diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2020-10-23 00:42:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-23 00:42:51 (GMT) |
commit | 345cd37abe324ad4f60f80e2c3133b8849e54e9b (patch) | |
tree | 5d965e662dca9dcac19e7eddd63a3d9d0b816fed /Tools/c-analyzer/c_parser/preprocessor/errors.py | |
parent | ec388cfb4ede56dace2bb78851ff6f38fa2a6abe (diff) | |
download | cpython-345cd37abe324ad4f60f80e2c3133b8849e54e9b.zip cpython-345cd37abe324ad4f60f80e2c3133b8849e54e9b.tar.gz cpython-345cd37abe324ad4f60f80e2c3133b8849e54e9b.tar.bz2 |
bpo-36876: Fix the C analyzer tool. (GH-22841)
The original tool wasn't working right and it was simpler to create a new one, partially re-using some of the old code. At this point the tool runs properly on the master. (Try: ./python Tools/c-analyzer/c-analyzer.py analyze.) It take ~40 seconds on my machine to analyze the full CPython code base.
Note that we'll need to iron out some OS-specific stuff (e.g. preprocessor). We're okay though since this tool isn't used yet in our workflow. We will also need to verify the analysis results in detail before activating the check in CI, though I'm pretty sure it's close.
https://bugs.python.org/issue36876
Diffstat (limited to 'Tools/c-analyzer/c_parser/preprocessor/errors.py')
-rw-r--r-- | Tools/c-analyzer/c_parser/preprocessor/errors.py | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_parser/preprocessor/errors.py b/Tools/c-analyzer/c_parser/preprocessor/errors.py new file mode 100644 index 0000000..9b66801 --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/errors.py @@ -0,0 +1,110 @@ +import sys + + +OS = sys.platform + + +def _as_tuple(items): + if isinstance(items, str): + return tuple(items.strip().replace(',', ' ').split()) + elif items: + return tuple(items) + else: + return () + + +class PreprocessorError(Exception): + """Something preprocessor-related went wrong.""" + + @classmethod + def _msg(cls, filename, reason, **ignored): + msg = 'failure while preprocessing' + if reason: + msg = f'{msg} ({reason})' + return msg + + def __init__(self, filename, preprocessor=None, reason=None): + if isinstance(reason, str): + reason = reason.strip() + + self.filename = filename + self.preprocessor = preprocessor or None + self.reason = str(reason) if reason else None + + msg = self._msg(**vars(self)) + msg = f'({filename}) {msg}' + if preprocessor: + msg = f'[{preprocessor}] {msg}' + super().__init__(msg) + + +class PreprocessorFailure(PreprocessorError): + """The preprocessor command failed.""" + + @classmethod + def _msg(cls, error, **ignored): + msg = 'preprocessor command failed' + if error: + msg = f'{msg} {error}' + return msg + + def __init__(self, filename, argv, error=None, preprocessor=None): + exitcode = -1 + if isinstance(error, tuple): + if len(error) == 2: + error, exitcode = error + else: + error = str(error) + if isinstance(error, str): + error = error.strip() + + self.argv = _as_tuple(argv) or None + self.error = error if error else None + self.exitcode = exitcode + + reason = str(self.error) + super().__init__(filename, preprocessor, reason) + + +class ErrorDirectiveError(PreprocessorFailure): + """The file hit a #error directive.""" + + @classmethod + def _msg(cls, error, **ignored): + return f'#error directive hit ({error})' + + def __init__(self, filename, argv, error, *args, **kwargs): + super().__init__(filename, argv, error, *args, **kwargs) + + +class MissingDependenciesError(PreprocessorFailure): + """The preprocessor did not have access to all the target's dependencies.""" + + @classmethod + def _msg(cls, missing, **ignored): + msg = 'preprocessing failed due to missing dependencies' + if missing: + msg = f'{msg} ({", ".join(missing)})' + return msg + + def __init__(self, filename, missing=None, *args, **kwargs): + self.missing = _as_tuple(missing) or None + + super().__init__(filename, *args, **kwargs) + + +class OSMismatchError(MissingDependenciesError): + """The target is not compatible with the host OS.""" + + @classmethod + def _msg(cls, expected, **ignored): + return f'OS is {OS} but expected {expected or "???"}' + + def __init__(self, filename, expected=None, *args, **kwargs): + if isinstance(expected, str): + expected = expected.strip() + + self.actual = OS + self.expected = expected if expected else None + + super().__init__(filename, None, *args, **kwargs) |