|
| 1 | +// This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | +// License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 4 | +// |
| 5 | +// Copyright (c) 2011-2022 ETH Zurich. |
| 6 | + |
| 7 | +package viper.silver.plugin.standard.refute |
| 8 | + |
| 9 | +import fastparse.P |
| 10 | +import viper.silver.ast.utility.ViperStrategy |
| 11 | +import viper.silver.ast._ |
| 12 | +import viper.silver.parser.FastParser.{FP, exp, keyword, whitespace} |
| 13 | +import viper.silver.parser.ParserExtension |
| 14 | +import viper.silver.plugin.{ParserPluginTemplate, SilverPlugin} |
| 15 | +import viper.silver.verifier._ |
| 16 | +import viper.silver.verifier.errors.AssertFailed |
| 17 | + |
| 18 | +class RefutePlugin extends SilverPlugin with ParserPluginTemplate { |
| 19 | + |
| 20 | + /** Keyword used to define refute statements. */ |
| 21 | + private val refuteKeyword: String = "refute" |
| 22 | + |
| 23 | + private var refuteAsserts: Map[Position, Refute] = Map() |
| 24 | + |
| 25 | + /** Parser for refute statements. */ |
| 26 | + def refute[_: P]: P[PRefute] = |
| 27 | + FP(keyword(refuteKeyword) ~/ exp).map{ case (pos, e) => PRefute(e)(pos) } |
| 28 | + |
| 29 | + /** Add refute to the parser. */ |
| 30 | + override def beforeParse(input: String, isImported: Boolean): String = { |
| 31 | + // Add new keyword |
| 32 | + ParserExtension.addNewKeywords(Set[String](refuteKeyword)) |
| 33 | + // Add new parser to the precondition |
| 34 | + ParserExtension.addNewStmtAtEnd(refute(_)) |
| 35 | + input |
| 36 | + } |
| 37 | + |
| 38 | + /** |
| 39 | + * Remove refute statements from the AST and add them as non-det asserts. |
| 40 | + * The ⭐ is nice since such a variable name cannot be parsed, but will it cause issues? |
| 41 | + */ |
| 42 | + override def beforeVerify(input: Program): Program = |
| 43 | + ViperStrategy.Slim({ |
| 44 | + case r@Refute(exp) => { |
| 45 | + this.refuteAsserts += (r.pos -> r) |
| 46 | + Seqn(Seq( |
| 47 | + If(LocalVar(s"__plugin_refute_nondet${this.refuteAsserts.size}", Bool)(r.pos), |
| 48 | + Seqn(Seq( |
| 49 | + Assert(exp)(r.pos, RefuteInfo), |
| 50 | + Inhale(BoolLit(false)(r.pos))(r.pos) |
| 51 | + ), Seq())(r.pos), |
| 52 | + Seqn(Seq(), Seq())(r.pos))(r.pos) |
| 53 | + ), |
| 54 | + Seq(LocalVarDecl(s"__plugin_refute_nondet${this.refuteAsserts.size}", Bool)(r.pos)) |
| 55 | + )(r.pos) |
| 56 | + } |
| 57 | + }).recurseFunc({ |
| 58 | + case Method(_, _, _, _, _, body) => Seq(body) |
| 59 | + }).execute(input) |
| 60 | + |
| 61 | + /** Remove refutation related errors and add refuteAsserts that didn't report an error. */ |
| 62 | + override def mapVerificationResult(input: VerificationResult): VerificationResult = { |
| 63 | + val errors: Seq[AbstractError] = (input match { |
| 64 | + case Success => Seq() |
| 65 | + case Failure(errors) => { |
| 66 | + errors.filter { |
| 67 | + case AssertFailed(a, _, _) if a.info == RefuteInfo => { |
| 68 | + this.refuteAsserts -= a.pos |
| 69 | + false |
| 70 | + } |
| 71 | + case _ => true |
| 72 | + } |
| 73 | + } |
| 74 | + }) ++ this.refuteAsserts.map(r => RefuteFailed(r._2, RefutationTrue(r._2.exp))) |
| 75 | + if (errors.length == 0) Success |
| 76 | + else Failure(errors) |
| 77 | + } |
| 78 | +} |
0 commit comments