// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2011-2023 ETH Zurich.
import org.scalatest.funsuite.AnyFunSuite
import viper.silicon.Silicon
import viper.silver.ast.{Assert, EqCmp, HasLineColumn, Int, IntLit, LineColumnPosition, LocalVar, LocalVarAssign, LocalVarDecl, Method, Program, Seqn}
import viper.silver.reporter.NoopReporter
import viper.silver.verifier.errors.AssertFailed
import viper.silver.verifier.{Failure, Success}
class SiliconMultipleErrorsTests extends AnyFunSuite {
val silicon: Silicon = {
val reporter = NoopReporter
val debugInfo = ("startedBy" -> "SiliconMultipleErrorsTests") :: Nil
new Silicon(reporter, debugInfo)
}
silicon.parseCommandLine(Seq("--numberOfErrorsToReport=0", "dummy.vpr"))
silicon.start()
test("Multiple error messages are reported") {
val xDecl = LocalVarDecl("x", Int)()
val x = LocalVar("x", Int)()
val vDecl = LocalVarDecl("v", Int)()
val v = LocalVar("v", Int)()
val pos_1 = LineColumnPosition(100, 0)
val pos_1b = LineColumnPosition(101, 0) // or, NoPosition
val pos_2 = LineColumnPosition(200, 0)
val pos_2b = LineColumnPosition(202, 0) // or, NoPosition
val assert_1 = Assert(EqCmp(x, IntLit(0)())(pos_1b))(pos_1)
val havoc = LocalVarAssign(x, v)()
val assert_2 = Assert(EqCmp(x, IntLit(0)())(pos_2b))(pos_2)
val body = Seqn(Seq(assert_1, havoc, assert_2), Seq(xDecl))()
val method = Method("foo", Seq(vDecl), Seq(), Seq(), Seq(), Some(body))()
val program = Program(Seq(), Seq(), Seq(), Seq(), Seq(method), Seq())()
val verificationResult = silicon.verify(program)
verificationResult match {
case Success => assert(false)
case Failure(errors) =>
assert(errors.length == 2)
// Check the position of the offending node of the first error
errors(0) match {
case AssertFailed(offending, _reason, _cached) =>
val pos = offending.pos
pos match {
case posWithId: HasLineColumn =>
assert(posWithId.line == 100)
assert(posWithId.column == 0)
case _ => assert(false)
}
case _ => assert(false)
}
// Check the position of the offending node of the second error
errors(1) match {
case AssertFailed(offending, _reason, _cached) =>
val pos = offending.pos
pos match {
case posWithId: HasLineColumn =>
assert(posWithId.line == 200)
assert(posWithId.column == 0)
case _ => assert(false)
}
case _ => assert(false)
}
}
}
}
I determined that to report multiple verification errors in the same method Silicon does not only require the position of the offending node of the error to be set, but also the position of the reason node. The following test, for example, fails if
pos_1bandpos_2bareNoPosition.@Aurel300 This is why we don't get multiple errors in Prusti: we do not always generate positions for sub-expressions, even though we always generate positions for the offending nodes (e.g. the
assertstatements).Source code
Is this intentional or is this a bug? Would it be hard to lift this requirement? As far I can observe, Carbon does not have a similar requirement, nor Silicon when configured to report one error per function.
(This is a follow-up of #732.)