Skip to content

Commit 2dfa563

Browse files
committed
fix: do not lex types starting with a '!'
Closes: #161
1 parent 326d323 commit 2dfa563

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

src/main/kotlin/com/github/lppedd/cc/language/lexer/conventionalCommit.flex

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,23 @@ FooterType = [^:\s]+ | BREAKING\ CHANGE
7979
}
8080

8181
<TYPE> {
82-
^[^(:\r\n]+\!? {
82+
// The commit type must be at the beginning of the line, and cannot start with a '!'
83+
^[^!(:\r\n][^(:\r\n]*\!? {
8384
if (yycharat(yylength() - 1) == '!') {
8485
yypushback(1);
8586
}
8687

8788
return ConventionalCommitTokenType.TYPE;
8889
}
8990

91+
// This catches the case where we have a lone '!'.
92+
// We lex it as BREAKING_CHANGE so that we do not fail lexing
93+
// and then let the parser or inspections reason about it.
94+
// Note that this rule must have lower priority than '\! / :'.
95+
\! {
96+
return ConventionalCommitTokenType.BREAKING_CHANGE;
97+
}
98+
9099
\( {
91100
yybegin(SCOPE);
92101
return ConventionalCommitTokenType.SCOPE_OPEN_PAREN;

src/main/kotlin/com/github/lppedd/cc/parser/strictConventionalCommit.flex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ FooterType = [^\s:][^:\r\n]*
7878
}
7979

8080
<TYPE> {
81-
// The commit type must be at the start of the line
82-
^[^(:\s]+\!? {
81+
// The commit type must be at the beginning of the line, and cannot start with a '!'
82+
^[^!(:\s][^(:\s]*\!? {
8383
if (yycharat(yylength() - 1) == '!') {
8484
yypushback(1);
8585
}

src/test/kotlin/com/github/lppedd/cc/parser/ConventionalCommitParserTest.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ import kotlin.contracts.contract
77
class ConventionalCommitParserTest {
88
@Test
99
fun `fail incomplete`() {
10-
var result = parseConventionalCommit("fix: ")
10+
var result = parseConventionalCommit("f!x")
11+
assertIs<ParseResult.Error>(result)
12+
assertEquals("The ':' separator is missing after the type/scope", result.message)
13+
14+
result = parseConventionalCommit("!fix")
15+
assertIs<ParseResult.Error>(result)
16+
assertEquals("The commit type is missing or invalid", result.message)
17+
18+
result = parseConventionalCommit("fix: ")
1119
assertIs<ParseResult.Error>(result)
1220
assertEquals("The commit subject is missing or invalid", result.message)
1321

@@ -56,6 +64,17 @@ class ConventionalCommitParserTest {
5664
assertEquals("foo", message.subject.trim())
5765
}
5866

67+
@Test
68+
fun `parse unusual type`() {
69+
val result = parseConventionalCommit("bui!ld!: foo")
70+
assertIs<ParseResult.Success>(result)
71+
72+
val message = result.message
73+
assertEquals("bui!ld", message.type)
74+
assertTrue(message.isBreakingChange)
75+
assertEquals("foo", message.subject.trim())
76+
}
77+
5978
@Test
6079
fun `parse empty scope`() {
6180
var result = parseConventionalCommit("build(): bar")

0 commit comments

Comments
 (0)