From 8b483579de587d9f9c7f0add2ef36dfcd22dfafe Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Sun, 21 Jun 2026 08:54:54 -0700 Subject: [PATCH] fix: enumerate RestrictAddressFamilies= AF_ names instead of loose regex Replace RegexTerminal("AF_[A-Z0-9_]+") with a FlexibleLiteralChoiceTerminal of the 48 address families systemd's af_from_name accepts (the AF_* macros from , minus AF_UNSPEC/AF_MAX). Unknown names like AF_BOGUS are now correctly rejected, and the exact name set is the foundation for grammar-based completion and a future deprecation annotator for kernel-removed families (AF_DECnet, AF_IRDA, AF_ECONET, AF_WANPIPE). FlexibleLiteralChoiceTerminal keeps loose syntactic matching (so error localization still highlights the offending token) but requires an exact choice to be semantically valid. Adds a golden test suite (valid families incl. alias/mixed-case/~-inversion, plus a raw-value regression guard that unknown AF_ names are flagged) to anchor the upcoming grammar-engine rework. Refs #467 #345 #343 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../ConfigParseAddressFamiliesOptionValue.kt | 82 +++++++++++++++++-- ...nfigParseAddressFamiliesOptionValueTest.kt | 44 ++++++++++ 2 files changed, 119 insertions(+), 7 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/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) + } }