Skip to content

Commit 78999e2

Browse files
Steve Ramageclaude
andcommitted
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 <sys/socket.h> (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) <noreply@anthropic.com>
1 parent a8dcb19 commit 78999e2

3 files changed

Lines changed: 123 additions & 10 deletions

File tree

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.gram
1010
* in src/shared/parse-helpers.c:90. Accepts:
1111
* - "none" (clears the set, sets allowlist)
1212
* - optional leading "~" (invert / denylist mode), followed by a whitespace-separated list
13-
* of address family names from af_from_name (AF_UNIX, AF_INET, AF_INET6, AF_NETLINK,
14-
* AF_PACKET, …)
13+
* of address family names resolved by af_from_name
1514
*
16-
* The grammar matches the "AF_" prefix loosely (any uppercase/digit/underscore tail); unknown
17-
* names slip past the grammar but fail at runtime. This is the same tradeoff as syscall_errno.
15+
* The name set is the list of AF_* macros systemd's af_from_name knows about, which is
16+
* generated (src/basic/generate-af-list.sh) from the AF_* #defines in <sys/socket.h>, minus
17+
* AF_UNSPEC and AF_MAX. It can be reproduced by preprocessing <sys/socket.h> with
18+
* `cpp -dM`, keeping the `#define AF_*` lines (dropping AF_UNSPEC/AF_MAX), and taking the macro
19+
* names.
20+
*
21+
* Enumerating the names exactly (rather than the old loose RegexTerminal("AF_[A-Z0-9_]+"))
22+
* makes validation correct (AF_BOGUS is now rejected) and sets up grammar-based completion.
23+
*
24+
* Note: some of these names still resolve via af_from_name (the macro exists in libc headers)
25+
* even though the kernel removed the protocol — AF_DECnet (Linux 6.1), AF_IRDA (4.17),
26+
* AF_ECONET (3.5), AF_WANPIPE (2.6.21). These are valid-but-removed and are intended targets
27+
* of a future deprecation annotator (see GitHub #467 / address_families(7)).
1828
*/
1929
class ConfigParseAddressFamiliesOptionValue : SimpleGrammarOptionValues(
2030
"config_parse_address_families",
@@ -23,13 +33,71 @@ class ConfigParseAddressFamiliesOptionValue : SimpleGrammarOptionValues(
2333
LiteralChoiceTerminal("none"),
2434
SequenceCombinator(
2535
ZeroOrOne(LiteralChoiceTerminal("~")),
26-
RegexTerminal("AF_[A-Z0-9_]+", "AF_[A-Z0-9_]+"),
36+
ADDRESS_FAMILY,
2737
ZeroOrMore(SequenceCombinator(
2838
WhitespaceTerminal(),
29-
RegexTerminal("AF_[A-Z0-9_]+", "AF_[A-Z0-9_]+")
39+
ADDRESS_FAMILY
3040
))
3141
)
3242
),
3343
EOF()
3444
)
35-
)
45+
) {
46+
companion object {
47+
/**
48+
* The AF_* names systemd's af_from_name accepts. FlexibleLiteralChoiceTerminal matches
49+
* loosely for syntax (so coloring / error localization still work) but requires an exact
50+
* choice to be semantically valid.
51+
*/
52+
private val ADDRESS_FAMILY = FlexibleLiteralChoiceTerminal(
53+
"AF_ALG",
54+
"AF_APPLETALK",
55+
"AF_ASH",
56+
"AF_ATMPVC",
57+
"AF_ATMSVC",
58+
"AF_AX25",
59+
"AF_BLUETOOTH",
60+
"AF_BRIDGE",
61+
"AF_CAIF",
62+
"AF_CAN",
63+
"AF_DECnet",
64+
"AF_ECONET",
65+
"AF_FILE",
66+
"AF_IB",
67+
"AF_IEEE802154",
68+
"AF_INET",
69+
"AF_INET6",
70+
"AF_IPX",
71+
"AF_IRDA",
72+
"AF_ISDN",
73+
"AF_IUCV",
74+
"AF_KCM",
75+
"AF_KEY",
76+
"AF_LLC",
77+
"AF_LOCAL",
78+
"AF_MCTP",
79+
"AF_MPLS",
80+
"AF_NETBEUI",
81+
"AF_NETLINK",
82+
"AF_NETROM",
83+
"AF_NFC",
84+
"AF_PACKET",
85+
"AF_PHONET",
86+
"AF_PPPOX",
87+
"AF_QIPCRTR",
88+
"AF_RDS",
89+
"AF_ROSE",
90+
"AF_ROUTE",
91+
"AF_RXRPC",
92+
"AF_SECURITY",
93+
"AF_SMC",
94+
"AF_SNA",
95+
"AF_TIPC",
96+
"AF_UNIX",
97+
"AF_VSOCK",
98+
"AF_WANPIPE",
99+
"AF_X25",
100+
"AF_XDP"
101+
)
102+
}
103+
}

src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ class GrammarParseEngineInspectionTest : AbstractUnitFileTest() {
4848
@Test
4949
fun testInvalidAddressFamiliesUnderNewEngine() {
5050
enableNewEngine()
51-
// Three malformed lists, each ill-formed against the grammar's shape -> one highlight each:
52-
// a stray comma, a name without the AF_ prefix, and a lowercase tail the AF_* regex rejects.
51+
// One highlight each, exercising both ParseOutcome failure kinds now that AF names are
52+
// enumerated: AF_BOGUS is well-formed but unknown (SemanticError), while the comma and the
53+
// bare non-AF token are ill-formed against the grammar shape (SyntaxError).
5354
// language="unit file (systemd)"
5455
val file = """
5556
[Service]
57+
RestrictAddressFamilies=AF_BOGUS
5658
RestrictAddressFamilies=AF_INET, AF_INET6
5759
RestrictAddressFamilies=inet
58-
RestrictAddressFamilies=AF_inet
5960
""".trimIndent()
6061

6162
setupFileInEditor("file.service", file)

src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@ class ConfigParseAddressFamiliesOptionValueTest : AbstractUnitFileTest() {
2727
assertSize(0, highlights)
2828
}
2929

30+
@Test
31+
fun testValidEnumeratedFamilies() {
32+
// Names that the previous loose RegexTerminal happened to accept but that we now want to
33+
// keep accepting: an alias (AF_LOCAL), a mixed-case real name (AF_DECnet), the newest
34+
// families, and a long whitespace-separated list including the inversion prefix.
35+
// language="unit file (systemd)"
36+
val file = """
37+
[Service]
38+
RestrictAddressFamilies=AF_LOCAL
39+
RestrictAddressFamilies=AF_DECnet
40+
RestrictAddressFamilies=AF_VSOCK AF_XDP AF_MCTP
41+
RestrictAddressFamilies=~AF_UNIX AF_INET AF_INET6 AF_NETLINK AF_PACKET
42+
""".trimIndent()
43+
44+
setupFileInEditor("file.service", file)
45+
enableInspection(InvalidValueInspection::class.java)
46+
val highlights = myFixture.doHighlighting()
47+
48+
assertSize(0, highlights)
49+
}
50+
3051
@Test
3152
fun testInvalidValues() {
3253
// language="unit file (systemd)"
@@ -45,4 +66,27 @@ class ConfigParseAddressFamiliesOptionValueTest : AbstractUnitFileTest() {
4566

4667
assertSize(5, highlights)
4768
}
69+
70+
@Test
71+
fun testUnknownFamiliesAreNowRejected() {
72+
// Regression guard for the grammar fix: the old RegexTerminal("AF_[A-Z0-9_]+") accepted
73+
// any AF_-prefixed token, so these passed validation incorrectly. With the enumerated
74+
// family set they must be flagged (syntactically well-formed, semantically invalid).
75+
// Raw values (no <error> markup) so the guard does not depend on markup stripping; each
76+
// invalid property contributes exactly one highlight.
77+
// language="unit file (systemd)"
78+
val file = """
79+
[Service]
80+
RestrictAddressFamilies=AF_BOGUS
81+
RestrictAddressFamilies=AF_INETZ
82+
RestrictAddressFamilies=AF_INET AF_MADEUP
83+
RestrictAddressFamilies=AF_DECNET
84+
""".trimIndent()
85+
86+
setupFileInEditor("file.service", file)
87+
enableInspection(InvalidValueInspection::class.java)
88+
val highlights = myFixture.doHighlighting()
89+
90+
assertSize(4, highlights)
91+
}
4892
}

0 commit comments

Comments
 (0)