From d3ef02b2e4d312ac604c0045ec97bf35a121bab5 Mon Sep 17 00:00:00 2001 From: Charles Brunet Date: Tue, 20 Aug 2024 17:09:57 -0400 Subject: [PATCH] mformat: detect invalid config - detect unknown config keys in format config - add test for detection of invalid config values - detect invalid .editorconfig values Fixes #13569 --- mesonbuild/mformat.py | 12 +++++++--- unittests/platformagnostictests.py | 37 ++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/mesonbuild/mformat.py b/mesonbuild/mformat.py index 6a28684d6..d87674bc7 100644 --- a/mesonbuild/mformat.py +++ b/mesonbuild/mformat.py @@ -839,7 +839,10 @@ class Formatter: getter = f.metadata['getter'] for section in sections: - value = getter(cp, section, f.name, fallback=None) + try: + value = getter(cp, section, f.name, fallback=None) + except ValueError as e: + raise MesonException(f'Invalid type for key "{f.name}" in "{editorconfig_file}" file:\n{e}') from e if value is not None: setattr(config, f.name, value) @@ -858,6 +861,10 @@ class Formatter: except ParsingError as e: raise MesonException(f'Unable to parse configuration file "{configuration_file}":\n{e}') from e + extra_keys = sorted(set(cp.defaults()).difference(f.name for f in fields(config))) + if extra_keys: + raise MesonException(f'Unknown config keys: "{", ".join(extra_keys)}" in configuration file "{configuration_file}"') + for f in fields(config): getter = f.metadata['getter'] try: @@ -992,9 +999,8 @@ def run(options: argparse.Namespace) -> int: # TODO: remove empty newlines when more than N (2...) # TODO: magic comment to prevent formatting -# TODO: handle meson.options ? # TODO: split long lines on binary operators -# TODO: align series of assignements +# TODO: align series of assignments # TODO: align comments # TODO: move comments on long lines diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 7d6ffa0f9..3ee0b458c 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -15,7 +15,7 @@ from pathlib import Path from .baseplatformtests import BasePlatformTests from .helpers import is_ci -from mesonbuild.mesonlib import EnvironmentVariables, ExecutableSerialisation, MesonException, is_linux, python_command +from mesonbuild.mesonlib import EnvironmentVariables, ExecutableSerialisation, MesonException, is_linux, python_command, windows_proof_rmtree from mesonbuild.mformat import Formatter, match_path from mesonbuild.optinterpreter import OptionInterpreter, OptionException from mesonbuild.options import OptionStore @@ -336,7 +336,40 @@ class PlatformAgnosticTests(BasePlatformTests): for filename, pattern, expected in cases: self.assertTrue(match_path(filename, pattern) is expected, f'{filename} -> {pattern}') - + + def test_format_invalid_config_key(self) -> None: + fd, fname = tempfile.mkstemp(suffix='.ini', text=True) + self.addCleanup(os.unlink, fname) + + with os.fdopen(fd, 'w', encoding='utf-8') as handle: + handle.write('not_an_option = 42\n') + + with self.assertRaises(MesonException): + Formatter(Path(fname), use_editor_config=False, fetch_subdirs=False) + + def test_format_invalid_config_value(self) -> None: + fd, fname = tempfile.mkstemp(suffix='.ini', text=True) + self.addCleanup(os.unlink, fname) + + with os.fdopen(fd, 'w', encoding='utf-8') as handle: + handle.write('max_line_length = string\n') + + with self.assertRaises(MesonException): + Formatter(Path(fname), use_editor_config=False, fetch_subdirs=False) + + def test_format_invalid_editorconfig_value(self) -> None: + dirpath = tempfile.mkdtemp() + self.addCleanup(windows_proof_rmtree, dirpath) + + editorconfig = Path(dirpath, '.editorconfig') + with open(editorconfig, 'w', encoding='utf-8') as handle: + handle.write('[*]\n') + handle.write('indent_size = string\n') + + formatter = Formatter(None, use_editor_config=True, fetch_subdirs=False) + with self.assertRaises(MesonException): + formatter.load_editor_config(editorconfig) + def test_format_empty_file(self) -> None: formatter = Formatter(None, use_editor_config=False, fetch_subdirs=False) for code in ('', '\n'):