Skip to content

Reporting multiple errors #735

@fpoli

Description

@fpoli

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_1b and pos_2b are NoPosition.

@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 assert statements).

Source code
// 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)
        }
    }
  }
}

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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions