Skip to content

Commit cff95bd

Browse files
authored
Merge pull request #178 from BennyHill/topic/sym-excl
Add excludedSymbols with reasonable defaults
2 parents 9171bc1 + 0282cf6 commit cff95bd

File tree

3 files changed

+69
-18
lines changed

3 files changed

+69
-18
lines changed

scalac-scoverage-plugin/src/main/scala/scoverage/CoverageFilter.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ trait CoverageFilter {
1212
def isClassIncluded(className: String): Boolean
1313
def isFileIncluded(file: SourceFile): Boolean
1414
def isLineIncluded(position: Position): Boolean
15+
def isSymbolIncluded(symbolName: String): Boolean
1516
def getExcludedLineNumbers(sourceFile: SourceFile): List[Range]
1617
}
1718

@@ -20,13 +21,16 @@ object AllCoverageFilter extends CoverageFilter {
2021
override def isLineIncluded(position: Position): Boolean = true
2122
override def isClassIncluded(className: String): Boolean = true
2223
override def isFileIncluded(file: SourceFile): Boolean = true
24+
override def isSymbolIncluded(symbolName: String): Boolean = true
2325
}
2426

2527
class RegexCoverageFilter(excludedPackages: Seq[String],
26-
excludedFiles: Seq[String]) extends CoverageFilter {
28+
excludedFiles: Seq[String],
29+
excludedSymbols: Seq[String]) extends CoverageFilter {
2730

2831
val excludedClassNamePatterns = excludedPackages.map(_.r.pattern)
2932
val excludedFilePatterns = excludedFiles.map(_.r.pattern)
33+
val excludedSymbolPatterns = excludedSymbols.map(_.r.pattern)
3034

3135
/**
3236
* We cache the excluded ranges to avoid scanning the source code files
@@ -64,6 +68,10 @@ class RegexCoverageFilter(excludedPackages: Seq[String],
6468
}
6569
}
6670

71+
override def isSymbolIncluded(symbolName: String): Boolean = {
72+
excludedSymbolPatterns.isEmpty || !excludedSymbolPatterns.exists(_.matcher(symbolName).matches)
73+
}
74+
6775
/**
6876
* Checks the given sourceFile for any magic comments which exclude lines
6977
* from coverage. Returns a list of Ranges of lines that should be excluded.

scalac-scoverage-plugin/src/main/scala/scoverage/plugin.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class ScoveragePlugin(val global: Global) extends Plugin {
2525
options.excludedPackages = opt.substring("excludedPackages:".length).split(";").map(_.trim).filterNot(_.isEmpty)
2626
} else if (opt.startsWith("excludedFiles:")) {
2727
options.excludedFiles = opt.substring("excludedFiles:".length).split(";").map(_.trim).filterNot(_.isEmpty)
28+
} else if (opt.startsWith("excludedSymbols:")) {
29+
options.excludedSymbols = opt.substring("excludedSymbols:".length).split(";").map(_.trim).filterNot(_.isEmpty)
2830
} else if (opt.startsWith("dataDir:")) {
2931
options.dataDir = opt.substring("dataDir:".length)
3032
} else if (opt.startsWith("extraAfterPhase:") || opt.startsWith("extraBeforePhase:")){
@@ -42,6 +44,7 @@ class ScoveragePlugin(val global: Global) extends Plugin {
4244
"-P:scoverage:dataDir:<pathtodatadir> where the coverage files should be written\n",
4345
"-P:scoverage:excludedPackages:<regex>;<regex> semicolon separated list of regexs for packages to exclude",
4446
"-P:scoverage:excludedFiles:<regex>;<regex> semicolon separated list of regexs for paths to exclude",
47+
"-P:scoverage:excludedSymbols:<regex>;<regex> semicolon separated list of regexs for symbols to exclude",
4548
"-P:scoverage:extraAfterPhase:<phaseName> phase after which scoverage phase runs (must be after typer phase)",
4649
"-P:scoverage:extraBeforePhase:<phaseName> phase before which scoverage phase runs (must be before patmat phase)",
4750
" Any classes whose fully qualified name matches the regex will",
@@ -73,6 +76,7 @@ class ScoveragePlugin(val global: Global) extends Plugin {
7376
class ScoverageOptions {
7477
var excludedPackages: Seq[String] = Nil
7578
var excludedFiles: Seq[String] = Nil
79+
var excludedSymbols: Seq[String] = Seq("scala.reflect.api.Exprs.Expr", "scala.reflect.api.Trees.Tree", "scala.reflect.macros.Universe.Tree")
7680
var dataDir: String = IOUtils.getTempPath
7781
}
7882

@@ -101,7 +105,7 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
101105

102106
def setOptions(options: ScoverageOptions): Unit = {
103107
this.options = options
104-
coverageFilter = new RegexCoverageFilter(options.excludedPackages, options.excludedFiles)
108+
coverageFilter = new RegexCoverageFilter(options.excludedPackages, options.excludedFiles, options.excludedSymbols)
105109
new File(options.dataDir).mkdirs() // ensure data directory is created
106110
}
107111

@@ -221,6 +225,7 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
221225
def isClassIncluded(symbol: Symbol): Boolean = coverageFilter.isClassIncluded(symbol.fullNameString)
222226
def isFileIncluded(source: SourceFile): Boolean = coverageFilter.isFileIncluded(source)
223227
def isStatementIncluded(pos: Position): Boolean = coverageFilter.isLineIncluded(pos)
228+
def isSymbolIncluded(symbol: Symbol): Boolean = coverageFilter.isSymbolIncluded(symbol.fullNameString)
224229

225230
def updateLocation(t: Tree) {
226231
Location(global)(t) match {
@@ -371,7 +376,7 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
371376

372377
// will catch macro implementations, as they must end with Expr, however will catch
373378
// any method that ends in Expr. // todo add way of allowing methods that return Expr
374-
case d: DefDef if d.symbol != null && d.tpt.symbol.fullNameString == "scala.reflect.api.Exprs.Expr" =>
379+
case d: DefDef if d.symbol != null && !isSymbolIncluded(d.tpt.symbol) =>
375380
tree
376381

377382
// we can ignore primary constructors because they are just empty at this stage, the body is added later.

scalac-scoverage-plugin/src/test/scala/scoverage/RegexCoverageFilterTest.scala

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,51 +12,89 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
1212
"isClassIncluded" - {
1313

1414
"should return true for empty excludes" in {
15-
assert(new RegexCoverageFilter(Nil, Nil).isClassIncluded("x"))
15+
assert(new RegexCoverageFilter(Nil, Nil, Nil).isClassIncluded("x"))
1616
}
1717

1818
"should not crash for empty input" in {
19-
assert(new RegexCoverageFilter(Nil, Nil).isClassIncluded(""))
19+
assert(new RegexCoverageFilter(Nil, Nil, Nil).isClassIncluded(""))
2020
}
2121

2222
"should exclude scoverage -> scoverage" in {
23-
assert(!new RegexCoverageFilter(Seq("scoverage"), Nil).isClassIncluded("scoverage"))
23+
assert(!new RegexCoverageFilter(Seq("scoverage"), Nil, Nil).isClassIncluded("scoverage"))
2424
}
2525

2626
"should include scoverage -> scoverageeee" in {
27-
assert(new RegexCoverageFilter(Seq("scoverage"), Nil).isClassIncluded("scoverageeee"))
27+
assert(new RegexCoverageFilter(Seq("scoverage"), Nil, Nil).isClassIncluded("scoverageeee"))
2828
}
2929

3030
"should exclude scoverage* -> scoverageeee" in {
31-
assert(!new RegexCoverageFilter(Seq("scoverage*"), Nil).isClassIncluded("scoverageeee"))
31+
assert(!new RegexCoverageFilter(Seq("scoverage*"), Nil, Nil).isClassIncluded("scoverageeee"))
3232
}
3333

3434
"should include eee -> scoverageeee" in {
35-
assert(new RegexCoverageFilter(Seq("eee"), Nil).isClassIncluded("scoverageeee"))
35+
assert(new RegexCoverageFilter(Seq("eee"), Nil, Nil).isClassIncluded("scoverageeee"))
3636
}
3737

3838
"should exclude .*eee -> scoverageeee" in {
39-
assert(!new RegexCoverageFilter(Seq(".*eee"), Nil).isClassIncluded("scoverageeee"))
39+
assert(!new RegexCoverageFilter(Seq(".*eee"), Nil, Nil).isClassIncluded("scoverageeee"))
4040
}
4141
}
4242
"isFileIncluded" - {
4343
val abstractFile = mock[AbstractFile]
4444
Mockito.when(abstractFile.path).thenReturn("sammy.scala")
4545
"should return true for empty excludes" in {
4646
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
47-
new RegexCoverageFilter(Nil, Nil).isFileIncluded(file) shouldBe true
47+
new RegexCoverageFilter(Nil, Nil, Nil).isFileIncluded(file) shouldBe true
4848
}
4949
"should exclude by filename" in {
5050
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
51-
new RegexCoverageFilter(Nil, Seq("sammy")).isFileIncluded(file) shouldBe false
51+
new RegexCoverageFilter(Nil, Seq("sammy"), Nil).isFileIncluded(file) shouldBe false
5252
}
5353
"should exclude by regex wildcard" in {
5454
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
55-
new RegexCoverageFilter(Nil, Seq("sam.*")).isFileIncluded(file) shouldBe false
55+
new RegexCoverageFilter(Nil, Seq("sam.*"), Nil).isFileIncluded(file) shouldBe false
5656
}
5757
"should not exclude non matching regex" in {
5858
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
59-
new RegexCoverageFilter(Nil, Seq("qweqeqwe")).isFileIncluded(file) shouldBe true
59+
new RegexCoverageFilter(Nil, Seq("qweqeqwe"), Nil).isFileIncluded(file) shouldBe true
60+
}
61+
}
62+
"isSymbolIncluded" - {
63+
val options = new ScoverageOptions()
64+
"should return true for empty excludes" in {
65+
assert(new RegexCoverageFilter(Nil, Nil, Nil).isSymbolIncluded("x"))
66+
}
67+
68+
"should not crash for empty input" in {
69+
assert(new RegexCoverageFilter(Nil, Nil, Nil).isSymbolIncluded(""))
70+
}
71+
72+
"should exclude scoverage -> scoverage" in {
73+
assert(!new RegexCoverageFilter(Nil, Nil, Seq("scoverage")).isSymbolIncluded("scoverage"))
74+
}
75+
76+
"should include scoverage -> scoverageeee" in {
77+
assert(new RegexCoverageFilter(Nil, Nil, Seq("scoverage")).isSymbolIncluded("scoverageeee"))
78+
}
79+
"should exclude scoverage* -> scoverageeee" in {
80+
assert(!new RegexCoverageFilter(Nil, Nil, Seq("scoverage*")).isSymbolIncluded("scoverageeee"))
81+
}
82+
83+
"should include eee -> scoverageeee" in {
84+
assert(new RegexCoverageFilter(Nil, Nil, Seq("eee")).isSymbolIncluded("scoverageeee"))
85+
}
86+
87+
"should exclude .*eee -> scoverageeee" in {
88+
assert(!new RegexCoverageFilter(Nil, Nil, Seq(".*eee")).isSymbolIncluded("scoverageeee"))
89+
}
90+
"should exclude scala.reflect.api.Exprs.Expr" in {
91+
assert(!new RegexCoverageFilter(Nil, Nil, options.excludedSymbols).isSymbolIncluded("scala.reflect.api.Exprs.Expr"))
92+
}
93+
"should exclude scala.reflect.macros.Universe.Tree" in {
94+
assert(!new RegexCoverageFilter(Nil, Nil, options.excludedSymbols).isSymbolIncluded("scala.reflect.macros.Universe.Tree"))
95+
}
96+
"should exclude scala.reflect.api.Trees.Tree" in {
97+
assert(!new RegexCoverageFilter(Nil, Nil, options.excludedSymbols).isSymbolIncluded("scala.reflect.api.Trees.Tree"))
6098
}
6199
}
62100
"getExcludedLineNumbers" - {
@@ -72,7 +110,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
72110
|8
73111
""".stripMargin
74112

75-
val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
113+
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
76114
numbers === List.empty
77115
}
78116
"should exclude lines between magic comments" in {
@@ -95,7 +133,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
95133
|16
96134
""".stripMargin
97135

98-
val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
136+
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
99137
numbers === List(Range(4, 9), Range(12, 14))
100138
}
101139
"should exclude all lines after an upaired magic comment" in {
@@ -117,7 +155,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
117155
|15
118156
""".stripMargin
119157

120-
val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
158+
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
121159
numbers === List(Range(4, 9), Range(12, 16))
122160
}
123161
"should allow text comments on the same line as the markers" in {
@@ -139,7 +177,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
139177
|15
140178
""".stripMargin
141179

142-
val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
180+
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
143181
numbers === List(Range(4, 9), Range(12, 16))
144182
}
145183
}

0 commit comments

Comments
 (0)