Skip to content

Instantly share code, notes, and snippets.

@bvenners
Last active July 3, 2016 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bvenners/ebc3d1ad1e0be5a3f36f3d1c7b302fbf to your computer and use it in GitHub Desktop.
Save bvenners/ebc3d1ad1e0be5a3f36f3d1c7b302fbf to your computer and use it in GitHub Desktop.
// Proposed ehancement to ScalaTest to surface more info about compiler errors
// Existing syntax
// Assertions:
assertDoesNotCompile("val i: String = 1") // Expects either a type or parse error
assertTypeError("val i: String = 1") // Expects just a type error (not a parse error)
assertCompiles("val i: Int = 1") // Expects no error during compilation
// Matchers:
"val i: String = 1" shouldNot compile // Expects either a type or parse error
"val i: String = 1" shouldNot typeCheck // Expects either a type error
"val i: String = 1" should compile // Expects no error during compilation
// For assertions, I'd like to make something symetric with assertThrows (new in 3.0, which
// returns Succeeded) and intercept (which returns the caught exception for further inspection)
// I could either do this:
val typeError = interceptTypeError("val i: String = 1")
assert(typeError.message.indexOf("type mismatch") >= 0)
// Which would require I add this kind of ADT to the library, which would
// surface all the info. Not sure it is worth it if people only need in
// practice to check error messages.
sealed abstract class CompilerError extends Product with Serializable {
val message: String
val fileName: String
val filePathName: String
val lineNumber: Int
val columnNumber: Int
val point: Int
}
case class TypeError(
message: String,
fileName: String,
filePathName: String,
lineNumber: Int,
columnNumber: Int,
point: Int
) extends CompilerError
case class ParseError(
message: String,
fileName: String,
filePathName: String,
lineNumber: Int,
columnNumber: Int,
point: Int
) extends CompilerError
// Otherwise, we could do this:
val errMsg: String = interceptTypeErrorMessage("val i: String = 1")
assert(errMsg.indexOf("type mismatch") >= 0)
// Maybe intercept is the wrong word, though an exception is actually
// being caught at compile time. So I think intercept is probably the
// best choice. It means "catch and return (for a touchdown, if possible)"
// For now I'll flesh out the syntax for surfacing all the info.
// Assertions
assertParseError("val i; Int = 1") // Could add this
interceptParseError("val i; Int = 1") // Returns a ParseError instance for further inspection
assertTypeError("val i: String = 1") // This exists
interceptTypeError("val i: String = 1") // Returns a TypeError instance for further inspection
// Could stop there, or add
assertCompilerError("val i: String = 1") // Could deprecate assertDoesNotCompile in favor of this name, for consistency with:
interceptCompilerError("val i: String = 1") // Which returns an instance of CompilerError
// Could also just deprecate assertDoesNotCompile and make people pick between assertTypeError
// and assertParseError. But I think assertCompilerError balances assertCompiles nicely.
// Matchers:
theParseError producedBy "val i; Int = 1" // Returns a ParseError instance for further inspection
theTypeError producedBy "val i: String = 1" // Returns a TypeError instance for further inspection
theCompilerError producedBy "val i: String = 1" // Returns a CompilerError instance for further inspection
// The above is similar to the [IllegalArgumentException] thrownBy { ... } syntax for exceptions
// I.e. you could do things like
theTypeError producedBy """NonEmptyString("")""" should have message "Cannot create a NonEmptyString with the empty string"
// Or:
theTypeError.producedBy("""NonEmptyString("")""").message should include regex ".*Cannot.*"
// which is similar to how you can inspect an exception for an error message
// Just had a thought that we could also potentially add an implicit on String that supports this
// syntax:
assert("val s: String == 33".typeErrorMessageIncludes("type mismatch"))
// On third thought, since I don't like implicits, maybe:
assert(typeErrorMessageIncludes("val s: String == 33", "type mismatch"))
// Or even:
assertTypeErrorMessageIncludes("val s: String == 33", "type mismatch")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment