By manually investigating the accompanying test case of each bug, we identified what specific language features are involved in each test case. We group these language features into eight general categories:
Standard Language Features
Object-Oriented Programming (OOP) Features
Functional Programming Features
Parametric Polymorphism
Type Inference Features
Type System-Related Features
Standard Library
Other
We discuss these categories and their enclosing language features below.
This category includes features that can be found in every modern programming language (e.g., method calls, arithmetic expressions, binary operations, assignments, type casting, etc.).
Below you can find some standard features that we encountered in the bug-revealing test cases.
The test case imports another source file.
Example:
class A {}
import myprocect.A;
class B {}
The test case declares an enumeration.
Example:
enum Animal {
DOG,
CAT
}
The test case involves arithmetic expressions.
Example:
fun test(a: Int, b: Int) {
val x = a + b
}
The test case contains an augmented assignment operator (x += 1).
Example:
fun test() {
var x: Int = 1
x += 1
}
The test cases declares a method with variable arguments.
Example:
class Test {
void test(Integer x...) {}
}
The test case contains a cast expression.
Example:
class Test {
void test() {
Long x = (long) 1;
}
}
The test case declares a variable, parameter or field whose type is an array.
Example:
class Test {
void test() {
Integer[] x = new Integer[] {1};
}
}
The test case involves conditionals (e.g., if, switch, ternary operator).
Example:
open class A
class B: A()
fun test() =
if (true) A() else B()
The test case contains loops (e.g., for, while).
Example:
class Test {
void test(List<Integer> list) {
for (Integer x: list) {}
}
}
The test case contains try / catch statements or handles exceptions.
Example:
class Test {
void test() {
try {
// something
} catch (Exception e) {
// something else
}
}
}
This category includes features that are related to object-oriented programming, e.g., classes, fields, methods, inheritance, object initialization, overriding, etc.
Below you can find some OOP features that we encountered in the bug-revealing test cases.
The test case declares a class that inherits from another.
Example:
open class A
class B: A()
The test case declares a class that implements more than one interfaces.
Example:
interface A
interface B
class B: A, B
The test case uses access modifiers keywords (e.g., private)
class Test {
private Test() {}
}
The test case contains a class which is declared inside the body of another class.
class A {
class B {}
}
The test case declares an anonymous class.
interface Foo {
String foo();
}
class Test {
void test() {
Foo x = new Foo() {
public String foo() { return "v"; }
};
}
}
The test case contains a class that overrides a specific method or field.
Example:
open class A {
open fun foo() = "A"
}
class B: A() {
override fun foo() = "B"
}
The test case contains overloaded methods.
Example:
class A {
fun test(): String = "test A"
fun test(x: String) = x
}
The test case declares a singleton object (Scala and Kotlin only).
Example:
object A {}
The test case declares a static method (Groovy and Java only).
Example:
class Test {
public static void test() {}
}
The test case declares a secondary constructor (Kotlin only).
Example:
class A {
constructor(x: Int) {}
}
The test case declares a sealed class.
Example:
sealed class A {}
The test case declares a data class (Kotlin only).
Example:
data class A(val x: Int)
The test case declares a case class (Scala only).
Example:
case class A(x: String)
The test case declares a value class (Scala only).
Example:
class A(val x: String) extends AnyVal
The test case uses the this expression.
Example:
class A {
constructor(x: Int): this() {}
}
The test case uses self types (Scala only).
Example:
trait A {
def x: String
}
trait B {
this: A => // reassign this
def foo() = ???
}
The test case contains a reference to a property of class.
Example:
class A(val x: Int)
fun test() {
val x = A()
x::x
}
The test case uses the delegation functionality (Kotlin only).
Example:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
This category includes features related to functional programming and the use of functions as first-class citizens. For example, use of lambdas, declaration of higher-order functions, use of function types, etc.
Below you can find some functional programming features that we encountered in the bug-revealing test cases.
The test case uses a lambda expression.
Example:
fun test() {
val x = x: Int -> x
}
The test case involves a function reference.
Example:
class A {
fun m() = ""
}
fun test() {
val x = A()
x::m
}
The test case declares a parameter, field, variable whose type is a function type.
Example:
class A {
fun m() = ""
}
fun test() {
val x = A()
val y: () => String = x::m
}
The test case declares a Single Abstract Method (SAM) interface which is implemented by a lambda or function reference.
interface I {
int m();
}
class Test {
int m2(I x) {
return x.m();
}
void test() {
m2 { -> 1 };
}
}
The test case involves the eta expansion technology (Scala only).
object Test {
def m(x: Int) = x
def test() {
val x = m _
}
}
This category includes features related to parametric polymorphism, e.g., declaration of parameterized classes / functions, use of parameterized types, etc.
Below you can find some features related to parametric polymorphism that we encountered in the bug-revealing test cases.
The test case declares a class that receives type parameters.
Example:
class A<T>
The test case declares a field, parameter or variable whose type is parameterized.
Example:
class A<T>
class B(val x: A<String>)
The test case declares a function that receives type parameters.
Example:
class A {
fun <T> m(x: T) = x
}
The test case defines a type parameter with a bound.
Example:
class A<T: Number>
The test case declares a type constructor with variant type parameters.
Example:
class A<out T> // covariant type parameter
The test cases uses a parameterized type with variant type arguments (Kotlin, Groovy and Java only).
Example:
class A<T>
fun test() {
val x: A<out Number> = A<Int>()
}
The test case declares a type constructor that receives another type constructor as a type parameter (Scala only).
Example:
class B[T]
class A[B[_]]
This category includes features related to type inference. For example, the input program declares a function whose return type is omitted and inferred by the compiler.
Below we present some features related to type inference that we encountered in the studied test cases.
The test case makes use of implicit casts made by the compiler.
Example:
fun test(x: Any) =
if (x is String)
x // here the inferred type of x is String
else
"val"
The test case omits the type arguments of parameterized type.
Example:
class A<T>
fun bar(A<String>) {}
fun test() {
bar(A()) // omitted type arguments here
}
The test case declares a variable whose declared type is omitted.
Example:
fun test() {
val x = "val"
}
The test case declares a function or a lambda with parameters whose declared types are omitted.
Example:
fun bar(x: Int => Int) {}
fun test() {
bar(x -> x)
}
The test case declares a function whose return type is omitted.
Example:
fun test() = "val"
The test case involves builder-style type inference (Kotlin only).
Example:
fun <T> sequence(@BuilderInference block: suspend SequenceScope<T>.() -> Unit): Sequence<T>
fun test() {
val result = sequence { yield("result") }
}
The test case uses the function API from the standard library of Java.
The test case us the reflection API.
Example:
class A { }
val x = new A()
x.getClass()
The test case uses the collection API (e.g., it uses list types, it creates sets, and more).
The test case uses the Stream API from the standard library of Java.
The test case uses the Coroutines API (Kotlin only).
The test case uses the Delegation API from the standard library of Groovy.
The test case contains annotations.
The test case is written in a language other than Java, but uses part of code written in Java (e.g., a library, imports a Java class, uses the standard library of Java, etc.)
The test case uses implicits (Scala only).
Example:
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc")
The test case contains erased parameters (Scala only).
Example:
def methodWithErasedEv(erased ev: Ev): Int = 42
The test case contains call-by-name arguments (Scala only).
Example:
def calculate(input: => Int) = input * 37
The test case contains a default initializer (Scala only).
Example:
class Socket(var timeout: Int = 2000, var linger: Int = 3000) { }
The test case declares a variable, parameter or field with an option type (Scala only).
Example:
object Test {
def test(x: Option[String]) = x match {
case None => ???
case Some(str) => ???
}
}
The test case contains pattern matching (Scala only).
Example:
val x: Int = Random.nextInt(10)
x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case _ => "other"
}
The test case uses the inline keyword (Scala and Kotlin only).
Example:
inline fun <T> lock(lock: Lock, body: () -> T): T { ... }
The test case contains a function that takes named arguments (Kotlin, Scala, and Groovy only).
Example:
String foo(String x, String y = "foo")
The test case defines an extension function or property (Kotlin only).
Example:
fun MutableList<Int>.swap(index1: Int, index2: Int) { }
The test case contains an elvis expression (Kotlin, Groovy only).
Example:
val list = mutableList ?: mutableListOf()
The test case contains a null assertion expression (Kotlin only).
Example:
val answer = "42"
answer!!.toInt()
The test case performs multiple assignments through the with pattern (Groovy only).
Example:
Foo foo = new Foo()
foo.with {
name = 'Foo'
age = 2
}
The test case contains a template sting (Groovy, Kotlin, Scala only).
Example:
String greet(String otherPerson) {
"Hello ${otherPerson}"
}