From 78999e28cdf051bb468050ada91e4fd5c75b1953 Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Wed, 24 Jun 2026 17:55:33 -0700 Subject: [PATCH] fix: enumerate RestrictAddressFamilies= AF_ names instead of loose regex config_parse_address_families resolves each name through af_from_name, whose set is generated from the AF_* macros in (minus AF_UNSPEC/AF_MAX). The validator matched the loose RegexTerminal("AF_[A-Z0-9_]+"), so any AF_-prefixed token (AF_BOGUS, AF_INETZ, AF_DECNET) passed validation incorrectly. Replace it with a FlexibleLiteralChoiceTerminal enumerating the real names: it still matches the shape leniently (so coloring / error localization keep working) but is semantically valid only for an exact name. This is a correctness fix on the default path (both engines), and it sets up grammar-based completion and the valid-but-kernel-removed deprecation annotator (AF_DECnet, AF_IRDA, ...). Tests: adds enumerated-valid and unknown-rejected cases to the validator test; now that AF_BOGUS is honestly invalid, the grammar-engine e2e test uses it to cover the SemanticError -> highlight mapping (replacing a 242.x-safe workaround). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../ConfigParseAddressFamiliesOptionValue.kt | 82 +++++++++++++++++-- .../GrammarParseEngineInspectionTest.kt | 7 +- ...nfigParseAddressFamiliesOptionValueTest.kt | 44 ++++++++++ 3 files changed, 123 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt index 186890f..ee814d4 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt @@ -10,11 +10,21 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.gram * in src/shared/parse-helpers.c:90. Accepts: * - "none" (clears the set, sets allowlist) * - optional leading "~" (invert / denylist mode), followed by a whitespace-separated list - * of address family names from af_from_name (AF_UNIX, AF_INET, AF_INET6, AF_NETLINK, - * AF_PACKET, …) + * of address family names resolved by af_from_name * - * The grammar matches the "AF_" prefix loosely (any uppercase/digit/underscore tail); unknown - * names slip past the grammar but fail at runtime. This is the same tradeoff as syscall_errno. + * The name set is the list of AF_* macros systemd's af_from_name knows about, which is + * generated (src/basic/generate-af-list.sh) from the AF_* #defines in , minus + * AF_UNSPEC and AF_MAX. It can be reproduced by preprocessing with + * `cpp -dM`, keeping the `#define AF_*` lines (dropping AF_UNSPEC/AF_MAX), and taking the macro + * names. + * + * Enumerating the names exactly (rather than the old loose RegexTerminal("AF_[A-Z0-9_]+")) + * makes validation correct (AF_BOGUS is now rejected) and sets up grammar-based completion. + * + * Note: some of these names still resolve via af_from_name (the macro exists in libc headers) + * even though the kernel removed the protocol — AF_DECnet (Linux 6.1), AF_IRDA (4.17), + * AF_ECONET (3.5), AF_WANPIPE (2.6.21). These are valid-but-removed and are intended targets + * of a future deprecation annotator (see GitHub #467 / address_families(7)). */ class ConfigParseAddressFamiliesOptionValue : SimpleGrammarOptionValues( "config_parse_address_families", @@ -23,13 +33,71 @@ class ConfigParseAddressFamiliesOptionValue : SimpleGrammarOptionValues( LiteralChoiceTerminal("none"), SequenceCombinator( ZeroOrOne(LiteralChoiceTerminal("~")), - RegexTerminal("AF_[A-Z0-9_]+", "AF_[A-Z0-9_]+"), + ADDRESS_FAMILY, ZeroOrMore(SequenceCombinator( WhitespaceTerminal(), - RegexTerminal("AF_[A-Z0-9_]+", "AF_[A-Z0-9_]+") + ADDRESS_FAMILY )) ) ), EOF() ) -) +) { + companion object { + /** + * The AF_* names systemd's af_from_name accepts. FlexibleLiteralChoiceTerminal matches + * loosely for syntax (so coloring / error localization still work) but requires an exact + * choice to be semantically valid. + */ + private val ADDRESS_FAMILY = FlexibleLiteralChoiceTerminal( + "AF_ALG", + "AF_APPLETALK", + "AF_ASH", + "AF_ATMPVC", + "AF_ATMSVC", + "AF_AX25", + "AF_BLUETOOTH", + "AF_BRIDGE", + "AF_CAIF", + "AF_CAN", + "AF_DECnet", + "AF_ECONET", + "AF_FILE", + "AF_IB", + "AF_IEEE802154", + "AF_INET", + "AF_INET6", + "AF_IPX", + "AF_IRDA", + "AF_ISDN", + "AF_IUCV", + "AF_KCM", + "AF_KEY", + "AF_LLC", + "AF_LOCAL", + "AF_MCTP", + "AF_MPLS", + "AF_NETBEUI", + "AF_NETLINK", + "AF_NETROM", + "AF_NFC", + "AF_PACKET", + "AF_PHONET", + "AF_PPPOX", + "AF_QIPCRTR", + "AF_RDS", + "AF_ROSE", + "AF_ROUTE", + "AF_RXRPC", + "AF_SECURITY", + "AF_SMC", + "AF_SNA", + "AF_TIPC", + "AF_UNIX", + "AF_VSOCK", + "AF_WANPIPE", + "AF_X25", + "AF_XDP" + ) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt index 3478b25..992a431 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt @@ -48,14 +48,15 @@ class GrammarParseEngineInspectionTest : AbstractUnitFileTest() { @Test fun testInvalidAddressFamiliesUnderNewEngine() { enableNewEngine() - // Three malformed lists, each ill-formed against the grammar's shape -> one highlight each: - // a stray comma, a name without the AF_ prefix, and a lowercase tail the AF_* regex rejects. + // One highlight each, exercising both ParseOutcome failure kinds now that AF names are + // enumerated: AF_BOGUS is well-formed but unknown (SemanticError), while the comma and the + // bare non-AF token are ill-formed against the grammar shape (SyntaxError). // language="unit file (systemd)" val file = """ [Service] + RestrictAddressFamilies=AF_BOGUS RestrictAddressFamilies=AF_INET, AF_INET6 RestrictAddressFamilies=inet - RestrictAddressFamilies=AF_inet """.trimIndent() setupFileInEditor("file.service", file) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt index 8d37381..4bc8d5f 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt @@ -27,6 +27,27 @@ class ConfigParseAddressFamiliesOptionValueTest : AbstractUnitFileTest() { assertSize(0, highlights) } + @Test + fun testValidEnumeratedFamilies() { + // Names that the previous loose RegexTerminal happened to accept but that we now want to + // keep accepting: an alias (AF_LOCAL), a mixed-case real name (AF_DECnet), the newest + // families, and a long whitespace-separated list including the inversion prefix. + // language="unit file (systemd)" + val file = """ + [Service] + RestrictAddressFamilies=AF_LOCAL + RestrictAddressFamilies=AF_DECnet + RestrictAddressFamilies=AF_VSOCK AF_XDP AF_MCTP + RestrictAddressFamilies=~AF_UNIX AF_INET AF_INET6 AF_NETLINK AF_PACKET + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + @Test fun testInvalidValues() { // language="unit file (systemd)" @@ -45,4 +66,27 @@ class ConfigParseAddressFamiliesOptionValueTest : AbstractUnitFileTest() { assertSize(5, highlights) } + + @Test + fun testUnknownFamiliesAreNowRejected() { + // Regression guard for the grammar fix: the old RegexTerminal("AF_[A-Z0-9_]+") accepted + // any AF_-prefixed token, so these passed validation incorrectly. With the enumerated + // family set they must be flagged (syntactically well-formed, semantically invalid). + // Raw values (no markup) so the guard does not depend on markup stripping; each + // invalid property contributes exactly one highlight. + // language="unit file (systemd)" + val file = """ + [Service] + RestrictAddressFamilies=AF_BOGUS + RestrictAddressFamilies=AF_INETZ + RestrictAddressFamilies=AF_INET AF_MADEUP + RestrictAddressFamilies=AF_DECNET + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(4, highlights) + } }