Skip to content

Commit 5d748d4

Browse files
authored
Generate cleaner output (#927)
* drop unnecessary info output * clean-up even more * simplify error, info, and debug messages * drop unnecessary package ids from error messages when verifying a single package * improve error messages further * address Linard's feedback * address Linard's feedback
1 parent 9db248c commit 5d748d4

File tree

3 files changed

+40
-25
lines changed

3 files changed

+40
-25
lines changed

src/main/scala/viper/gobra/Gobra.scala

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,27 @@ trait GoVerifier extends StrictLogging {
6161
* Additionally statistics are collected with the StatsCollector reporter class
6262
*/
6363
def verifyAllPackages(config: Config)(executor: GobraExecutionContext): VerifierResult = {
64+
def addPlural(n: Int): String = if (n != 1) "s" else ""
6465
val statsCollector = StatsCollector(config.reporter)
6566
var warningCount: Int = 0
6667
var allVerifierErrors: Vector[VerifierError] = Vector()
6768
var allTimeoutErrors: Vector[TimeoutError] = Vector()
69+
val isVerifyingMultiplePackages = config.packageInfoInputMap.size != 1
6870

6971
// write report to file on shutdown, this makes sure a report is produced even if a run is shutdown
7072
// by some signal.
7173
Runtime.getRuntime.addShutdownHook(new Thread() {
7274
override def run(): Unit = {
73-
val statsFile = config.gobraDirectory.resolve("stats.json").toFile
74-
logger.info("Writing report to " + statsFile.getPath)
75-
val wroteFile = statsCollector.writeJsonReportToFile(statsFile)
76-
if (!wroteFile) {
77-
logger.error(s"Could not write to the file $statsFile. Check whether the permissions to the file allow writing to it.")
75+
config.gobraDirectory match {
76+
case Some(path) =>
77+
val statsFile = path.resolve("stats.json").toFile
78+
logger.info("Writing report to " + statsFile.getPath)
79+
val wroteFile = statsCollector.writeJsonReportToFile(statsFile)
80+
if (!wroteFile) {
81+
logger.error(s"Could not write to the file $statsFile. Check whether the permissions to the file allow writing to it.")
82+
}
83+
case _ =>
7884
}
79-
8085
// Report timeouts that were not previously reported
8186
statsCollector.getTimeoutErrorsForNonFinishedTasks.foreach(err => logger.error(err.formattedMessage))
8287
}
@@ -95,10 +100,12 @@ trait GoVerifier extends StrictLogging {
95100
warningCount += warnings.size
96101
warnings.foreach(w => logger.debug(w))
97102

103+
val suffix = if (isVerifyingMultiplePackages) s" in package $pkgId" else ""
98104
result match {
99-
case VerifierResult.Success => logger.info(s"$name found no errors")
105+
case VerifierResult.Success =>
106+
logger.info(s"$name found 0 errors$suffix.")
100107
case VerifierResult.Failure(errors) =>
101-
logger.error(s"$name has found ${errors.length} error(s) in package $pkgId")
108+
logger.error(s"$name found ${errors.length} error${addPlural(errors.length)}$suffix.")
102109
if (config.noStreamErrors) {
103110
errors.foreach(err => logger.error(s"\t${err.formattedMessage}"))
104111
}
@@ -109,7 +116,7 @@ trait GoVerifier extends StrictLogging {
109116
Await.result(future, config.packageTimeout)
110117
} catch {
111118
case _: TimeoutException =>
112-
logger.error(s"The verification of package $pkgId got terminated after " + config.packageTimeout.toString)
119+
logger.error(s"The verification of package $pkgId timed out after ${config.packageTimeout}.")
113120
statsCollector.report(VerificationTaskFinishedMessage(pkgId))
114121
val errors = statsCollector.getTimeoutErrors(pkgId)
115122
errors.foreach(err => logger.error(err.formattedMessage))
@@ -119,24 +126,33 @@ trait GoVerifier extends StrictLogging {
119126

120127
// Print statistics for caching
121128
if(config.cacheFile.isDefined) {
122-
logger.debug(s"Number of cacheable Viper member(s): ${statsCollector.getNumberOfCacheableViperMembers}")
123-
logger.debug(s"Number of cached Viper member(s): ${statsCollector.getNumberOfCachedViperMembers}")
129+
logger.debug(s"Number of cacheable Viper members: ${statsCollector.getNumberOfCacheableViperMembers}")
130+
logger.debug(s"Number of cached Viper members: ${statsCollector.getNumberOfCachedViperMembers}")
124131
}
125132

126133
// Print general statistics
127-
logger.debug(s"Gobra has found ${statsCollector.getNumberOfVerifiableMembers} methods and functions" )
128-
logger.debug(s"${statsCollector.getNumberOfSpecifiedMembers} have specification")
129-
logger.debug(s"${statsCollector.getNumberOfSpecifiedMembersWithAssumptions} are assumed to be satisfied")
134+
logger.debug(s"Gobra found ${statsCollector.getNumberOfVerifiableMembers} methods and functions." )
135+
logger.debug{
136+
val specMem = statsCollector.getNumberOfSpecifiedMembers
137+
s"$specMem member${addPlural(specMem)} are explicitly specified."
138+
}
139+
logger.debug{
140+
val memAssum = statsCollector.getNumberOfSpecifiedMembersWithAssumptions
141+
val isOrAre = if (memAssum != 1) "are" else "is"
142+
s"$memAssum specified member${addPlural(memAssum)} of the package under verification $isOrAre trusted or abstract."
143+
}
130144

131145
// Print warnings
132146
if(warningCount > 0) {
133-
logger.info(s"$name has found $warningCount warning(s)")
147+
logger.debug(s"$name reported $warningCount warning${addPlural(warningCount)}.")
134148
}
135149

136150
// Print errors
137-
logger.info(s"$name has found ${allVerifierErrors.size} error(s)")
151+
if (isVerifyingMultiplePackages) {
152+
logger.info(s"$name found ${allVerifierErrors.size} error${addPlural(allVerifierErrors.size)} across all verified packages.")
153+
}
138154
if(allTimeoutErrors.nonEmpty) {
139-
logger.info(s"The verification of ${allTimeoutErrors.size} members timed out")
155+
logger.info(s"The verification of ${allTimeoutErrors.size} member${addPlural(allTimeoutErrors.size)} timed out.")
140156
}
141157

142158
val allErrors = allVerifierErrors ++ allTimeoutErrors
@@ -362,7 +378,7 @@ object GobraRunner extends GobraFrontend with StrictLogging {
362378
}
363379
} catch {
364380
case e: UglyErrorMessage =>
365-
logger.error(s"${verifier.name} has found 1 error(s): ")
381+
logger.error(s"${verifier.name} has found 1 error: ")
366382
logger.error(s"\t${e.error.formattedMessage}")
367383
exitCode = 1
368384
case e: LogicException =>

src/main/scala/viper/gobra/frontend/Config.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ object ConfigDefaults {
6060
// or when the goal is to gradually verify part of a package without having to provide an explicit list of the files
6161
// to verify.
6262
val DefaultOnlyFilesWithHeader: Boolean = false
63-
lazy val DefaultGobraDirectory: Path = Path.of(".gobra")
63+
// In the past, the default Gobra directory used to be Path.of(".gobra")
64+
lazy val DefaultGobraDirectory: Option[Path] = None
6465
val DefaultTaskName: String = "gobra-task"
6566
val DefaultAssumeInjectivityOnInhale: Boolean = true
6667
val DefaultParallelizeBranches: Boolean = false
@@ -117,7 +118,7 @@ object MoreJoins {
117118
}
118119

119120
case class Config(
120-
gobraDirectory: Path = ConfigDefaults.DefaultGobraDirectory,
121+
gobraDirectory: Option[Path] = ConfigDefaults.DefaultGobraDirectory,
121122
// Used as an identifier of a verification task, ideally it shouldn't change between verifications
122123
// because it is used as a caching key. Additionally it should be unique when using the StatsCollector
123124
taskName: String = ConfigDefaults.DefaultTaskName,
@@ -261,7 +262,7 @@ object Config {
261262
}
262263

263264
// have a look at `Config` to see an inline description of some of these parameters
264-
case class BaseConfig(gobraDirectory: Path = ConfigDefaults.DefaultGobraDirectory,
265+
case class BaseConfig(gobraDirectory: Option[Path] = ConfigDefaults.DefaultGobraDirectory,
265266
moduleName: String = ConfigDefaults.DefaultModuleName,
266267
includeDirs: Vector[Path] = ConfigDefaults.DefaultIncludeDirs.map(_.toPath).toVector,
267268
reporter: GobraReporter = ConfigDefaults.DefaultReporter,
@@ -561,7 +562,7 @@ class ScallopGobraConfig(arguments: Seq[String], isInputOptional: Boolean = fals
561562
val gobraDirectory: ScallopOption[Path] = opt[Path](
562563
name = "gobraDirectory",
563564
descr = "Output directory for Gobra",
564-
default = Some(ConfigDefaults.DefaultGobraDirectory),
565+
default = ConfigDefaults.DefaultGobraDirectory,
565566
short = 'g'
566567
)(singleArgConverter(arg => Path.of(arg)))
567568

@@ -1038,7 +1039,7 @@ class ScallopGobraConfig(arguments: Seq[String], isInputOptional: Boolean = fals
10381039
)
10391040

10401041
private def baseConfig(isolate: List[(Path, List[Int])]): BaseConfig = BaseConfig(
1041-
gobraDirectory = gobraDirectory(),
1042+
gobraDirectory = gobraDirectory.toOption,
10421043
moduleName = module(),
10431044
includeDirs = includeDirs,
10441045
reporter = FileWriterReporter(

src/main/scala/viper/gobra/translator/encodings/closures/ClosureDomainEncoder.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ class ClosureDomainEncoder(specs: ClosureSpecsEncoder) {
2020

2121
private var domainNeeded: Boolean = false
2222
lazy val vprType: vpr.DomainType = {
23-
println("Use of closures detected: Closures are an experimental feature and may exhibit bugs.")
2423
domainNeeded = true
2524
vpr.DomainType(Names.closureDomain, Map.empty)(Vector.empty)
2625
}
2726

28-
2927
/** Encoding of the Closure domain. This is used to represent variables with function types. It looks as follows:
3028
*
3129
* domain Closure {

0 commit comments

Comments
 (0)