Skip to content

Commit 6bc999d

Browse files
rnveachromani
authored andcommitted
Issue #6717: add support to RegexpMultiline to match across lines
1 parent 638f601 commit 6bc999d

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

src/main/java/com/puppycrawl/tools/checkstyle/checks/regexp/RegexpMultilineCheck.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class RegexpMultilineCheck extends AbstractFileSetCheck {
4343
private int maximum;
4444
/** Whether to ignore case when matching. */
4545
private boolean ignoreCase;
46+
/** Whether to match across multiple lines. */
47+
private boolean matchAcrossLines;
4648

4749
/** The detector to use. */
4850
private MultilineDetector detector;
@@ -51,7 +53,7 @@ public class RegexpMultilineCheck extends AbstractFileSetCheck {
5153
public void beginProcessing(String charset) {
5254
final DetectorOptions options = DetectorOptions.newBuilder()
5355
.reporter(this)
54-
.compileFlags(Pattern.MULTILINE)
56+
.compileFlags(getRegexCompileFlags())
5557
.format(format)
5658
.message(message)
5759
.minimum(minimum)
@@ -66,6 +68,24 @@ protected void processFiltered(File file, FileText fileText) {
6668
detector.processLines(fileText);
6769
}
6870

71+
/**
72+
* Retrieves the compile flags for the regular expression being built based
73+
* on {@code matchAcrossLines}.
74+
* @return The compile flags.
75+
*/
76+
private int getRegexCompileFlags() {
77+
final int result;
78+
79+
if (matchAcrossLines) {
80+
result = Pattern.DOTALL;
81+
}
82+
else {
83+
result = Pattern.MULTILINE;
84+
}
85+
86+
return result;
87+
}
88+
6989
/**
7090
* Sets the format of the regular expression to match.
7191
* @param format the format of the regular expression to match.
@@ -106,4 +126,12 @@ public void setIgnoreCase(boolean ignoreCase) {
106126
this.ignoreCase = ignoreCase;
107127
}
108128

129+
/**
130+
* Sets whether to match across multiple lines.
131+
* @param matchAcrossLines whether to match across multiple lines.
132+
*/
133+
public void setMatchAcrossLines(boolean matchAcrossLines) {
134+
this.matchAcrossLines = matchAcrossLines;
135+
}
136+
109137
}

src/test/java/com/puppycrawl/tools/checkstyle/checks/regexp/RegexpMultilineCheckTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,27 @@ public void testGoodLimit() throws Exception {
255255
verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
256256
}
257257

258+
@Test
259+
public void testMultilineSupport() throws Exception {
260+
final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
261+
checkConfig.addAttribute("format", "abc.*def");
262+
checkConfig.addAttribute("matchAcrossLines", "true");
263+
final String[] expected = {
264+
"9: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "abc.*def"),
265+
};
266+
verify(checkConfig, getPath("InputRegexpMultilineMultilineSupport.java"), expected);
267+
}
268+
269+
@Test
270+
public void testMultilineSupportNotGreedy() throws Exception {
271+
final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
272+
checkConfig.addAttribute("format", "abc.*?def");
273+
checkConfig.addAttribute("matchAcrossLines", "true");
274+
final String[] expected = {
275+
"9: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "abc.*?def"),
276+
"11: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "abc.*?def"),
277+
};
278+
verify(checkConfig, getPath("InputRegexpMultilineMultilineSupport2.java"), expected);
279+
}
280+
258281
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.puppycrawl.tools.checkstyle.checks.regexp.regexpmultiline;
2+
3+
/**
4+
* Config format = 'ABC.*DEF'(lowercased)
5+
* matchAcrossLines = true
6+
*/
7+
public class InputRegexpMultilineMultilineSupport {
8+
void method() {
9+
// abc - violation
10+
// def
11+
// abc
12+
}
13+
14+
void method2() {
15+
// def
16+
// abc
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.puppycrawl.tools.checkstyle.checks.regexp.regexpmultiline;
2+
3+
/**
4+
* Config format = 'ABC.*?DEF'(lowercased)
5+
* matchAcrossLines = true
6+
*/
7+
public class InputRegexpMultilineMultilineSupport2 {
8+
void method() {
9+
// abc - violation
10+
// def
11+
// abc - violation
12+
}
13+
14+
void method2() {
15+
// def
16+
// abc
17+
}
18+
}

src/xdocs/config_regexp.xml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,13 @@ public static final int A_SETPOINT = 1;
550550
<td><code>0</code></td>
551551
<td>5.0</td>
552552
</tr>
553+
<tr>
554+
<td>matchAcrossLines</td>
555+
<td>Controls whether to match expressions across multiple lines.</td>
556+
<td><a href="property_types.html#boolean">Boolean</a></td>
557+
<td><code>false</code></td>
558+
<td>8.25</td>
559+
</tr>
553560
<tr>
554561
<td>fileExtensions</td>
555562
<td>file type extension of files to process</td>
@@ -570,6 +577,36 @@ public static final int A_SETPOINT = 1;
570577
value=&quot;System\.(out)|(err)\.print(ln)?\(&quot;/&gt;
571578
&lt;/module&gt;
572579
</source>
580+
581+
<p>
582+
To configure the check to match text that spans multiple lines,
583+
like normal code in a Java file:
584+
</p>
585+
<source>
586+
&lt;module name=&quot;RegexpMultiline&quot;&gt;
587+
&lt;property name=&quot;matchAcrossLines&quot; value=&quot;true&quot;/&gt;
588+
&lt;property name=&quot;format&quot; value=&quot;System\.out.*print\(&quot;/&gt;
589+
&lt;/module&gt;
590+
</source>
591+
<p>
592+
Example of violation from the above config:
593+
</p>
594+
<source>
595+
void method() {
596+
System.out. // violation
597+
print("Example");
598+
System.out.
599+
print("Example");
600+
}
601+
</source>
602+
<p>
603+
Note: Beware of the greedy regular expression used in the above example.
604+
<code>.*</code> will match as much as possible and not produce multiple violations in
605+
the file if multiple groups of lines could match the expression. To prevent an
606+
expression being too greedy, avoid overusing matching all text or allow it to be
607+
optional, like <code>.*?</code>. Changing the example expression to not be greedy
608+
will allow multiple violations in the example to be found in the same file.
609+
</p>
573610
</subsection>
574611

575612
<subsection name="Example of Usage" id="RegexpMultiline_Example_of_Usage">

0 commit comments

Comments
 (0)