Bundle all unproved targets in a single message and only show all if setting chooses that

This commit is contained in:
Leo Alt 2021-07-02 14:42:53 +02:00
parent ae519c1278
commit 685d7a8c99
7 changed files with 79 additions and 19 deletions

View File

@ -60,7 +60,7 @@ BMC::BMC(
#endif #endif
} }
void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<VerificationTargetType>> _solvedTargets) void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<VerificationTargetType>, smt::EncodingContext::IdCompare> _solvedTargets)
{ {
if (m_interface->solvers() == 0) if (m_interface->solvers() == 0)
{ {
@ -84,8 +84,21 @@ void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<Verificatio
m_context.setAssertionAccumulation(true); m_context.setAssertionAccumulation(true);
m_variableUsage.setFunctionInlining(shouldInlineFunctionCall); m_variableUsage.setFunctionInlining(shouldInlineFunctionCall);
createFreeConstants(sourceDependencies(_source)); createFreeConstants(sourceDependencies(_source));
m_unprovedAmt = 0;
_source.accept(*this); _source.accept(*this);
if (m_unprovedAmt > 0 && !m_settings.showUnproved)
m_errorReporter.warning(
2788_error,
{},
"BMC: " +
to_string(m_unprovedAmt) +
" verification condition(s) could not be proved." +
" Enable the model checker option \"show unproved\" to see all of them." +
" Consider choosing a specific contract to be verified in order to reduce the solving problems." +
" Consider increasing the timeout per query."
);
} }
// If this check is true, Z3 and CVC4 are not available // If this check is true, Z3 and CVC4 are not available
@ -961,8 +974,12 @@ void BMC::checkCondition(
case smtutil::CheckResult::UNSATISFIABLE: case smtutil::CheckResult::UNSATISFIABLE:
break; break;
case smtutil::CheckResult::UNKNOWN: case smtutil::CheckResult::UNKNOWN:
m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation); {
++m_unprovedAmt;
if (m_settings.showUnproved)
m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation);
break; break;
}
case smtutil::CheckResult::CONFLICTING: case smtutil::CheckResult::CONFLICTING:
m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound.");
break; break;

View File

@ -66,7 +66,7 @@ public:
langutil::CharStreamProvider const& _charStreamProvider langutil::CharStreamProvider const& _charStreamProvider
); );
void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTargetType>> _solvedTargets); void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> _solvedTargets);
/// This is used if the SMT solver is not directly linked into this binary. /// This is used if the SMT solver is not directly linked into this binary.
/// @returns a list of inputs to the SMT solver that were not part of the argument to /// @returns a list of inputs to the SMT solver that were not part of the argument to
@ -192,7 +192,10 @@ private:
std::vector<BMCVerificationTarget> m_verificationTargets; std::vector<BMCVerificationTarget> m_verificationTargets;
/// Targets that were already proven. /// Targets that were already proven.
std::map<ASTNode const*, std::set<VerificationTargetType>> m_solvedTargets; std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> m_solvedTargets;
/// Number of verification conditions that could not be proved.
size_t m_unprovedAmt = 0;
}; };
} }

View File

@ -933,6 +933,7 @@ void CHC::resetSourceAnalysis()
{ {
m_safeTargets.clear(); m_safeTargets.clear();
m_unsafeTargets.clear(); m_unsafeTargets.clear();
m_unprovedTargets.clear();
m_functionTargetIds.clear(); m_functionTargetIds.clear();
m_verificationTargets.clear(); m_verificationTargets.clear();
m_queryPlaceholders.clear(); m_queryPlaceholders.clear();
@ -1594,6 +1595,32 @@ void CHC::checkVerificationTargets()
checkedErrorIds.insert(target.errorId); checkedErrorIds.insert(target.errorId);
} }
auto toReport = m_unsafeTargets;
if (m_settings.showUnproved)
for (auto const& [node, targets]: m_unprovedTargets)
for (auto const& [target, info]: targets)
toReport[node].emplace(target, info);
for (auto const& [node, targets]: toReport)
for (auto const& [target, info]: targets)
m_errorReporter.warning(
info.error,
info.location,
info.message
);
if (!m_settings.showUnproved && !m_unprovedTargets.empty())
m_errorReporter.warning(
5840_error,
{},
"CHC: " +
to_string(m_unprovedTargets.size()) +
" verification condition(s) could not be proved." +
" Enable the model checker option \"show unproved\" to see all of them." +
" Consider choosing a specific contract to be verified in order to reduce the solving problems." +
" Consider increasing the timeout per query."
);
// There can be targets in internal functions that are not reachable from the external interface. // There can be targets in internal functions that are not reachable from the external interface.
// These are safe by definition and are not even checked by the CHC engine, but this information // These are safe by definition and are not even checked by the CHC engine, but this information
// must still be reported safe by the BMC engine. // must still be reported safe by the BMC engine.
@ -1633,27 +1660,26 @@ void CHC::checkAndReportTarget(
else if (result == CheckResult::SATISFIABLE) else if (result == CheckResult::SATISFIABLE)
{ {
solAssert(!_satMsg.empty(), ""); solAssert(!_satMsg.empty(), "");
m_unsafeTargets[_target.errorNode].insert(_target.type);
auto cex = generateCounterexample(model, error().name); auto cex = generateCounterexample(model, error().name);
if (cex) if (cex)
m_errorReporter.warning( m_unsafeTargets[_target.errorNode][_target.type] = {
_errorReporterId, _errorReporterId,
location, location,
"CHC: " + _satMsg + "\nCounterexample:\n" + *cex "CHC: " + _satMsg + "\nCounterexample:\n" + *cex
); };
else else
m_errorReporter.warning( m_unsafeTargets[_target.errorNode][_target.type] = {
_errorReporterId, _errorReporterId,
location, location,
"CHC: " + _satMsg "CHC: " + _satMsg
); };
} }
else if (!_unknownMsg.empty()) else if (!_unknownMsg.empty())
m_errorReporter.warning( m_unprovedTargets[_target.errorNode][_target.type] = {
_errorReporterId, _errorReporterId,
location, location,
"CHC: " + _unknownMsg "CHC: " + _unknownMsg
); };
} }
/** /**

View File

@ -39,6 +39,8 @@
#include <libsmtutil/CHCSolverInterface.h> #include <libsmtutil/CHCSolverInterface.h>
#include <liblangutil/SourceLocation.h>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <map> #include <map>
@ -62,8 +64,14 @@ public:
void analyze(SourceUnit const& _sources); void analyze(SourceUnit const& _sources);
std::map<ASTNode const*, std::set<VerificationTargetType>> const& safeTargets() const { return m_safeTargets; } struct ReportTargetInfo
std::map<ASTNode const*, std::set<VerificationTargetType>> const& unsafeTargets() const { return m_unsafeTargets; } {
langutil::ErrorId error;
langutil::SourceLocation location;
std::string message;
};
std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> const& safeTargets() const { return m_safeTargets; }
std::map<ASTNode const*, std::map<VerificationTargetType, ReportTargetInfo>, smt::EncodingContext::IdCompare> const& unsafeTargets() const { return m_unsafeTargets; }
/// This is used if the Horn solver is not directly linked into this binary. /// This is used if the Horn solver is not directly linked into this binary.
/// @returns a list of inputs to the Horn solver that were not part of the argument to /// @returns a list of inputs to the Horn solver that were not part of the argument to
@ -347,10 +355,12 @@ private:
/// Helper mapping unique IDs to actual verification targets. /// Helper mapping unique IDs to actual verification targets.
std::map<unsigned, CHCVerificationTarget> m_verificationTargets; std::map<unsigned, CHCVerificationTarget> m_verificationTargets;
/// Targets proven safe. /// Targets proved safe.
std::map<ASTNode const*, std::set<VerificationTargetType>> m_safeTargets; std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> m_safeTargets;
/// Targets proven unsafe. /// Targets proved unsafe.
std::map<ASTNode const*, std::set<VerificationTargetType>> m_unsafeTargets; std::map<ASTNode const*, std::map<VerificationTargetType, ReportTargetInfo>, smt::EncodingContext::IdCompare> m_unsafeTargets;
/// Targets not proved.
std::map<ASTNode const*, std::map<VerificationTargetType, ReportTargetInfo>, smt::EncodingContext::IdCompare> m_unprovedTargets;
//@} //@}
/// Control-flow. /// Control-flow.

View File

@ -120,8 +120,8 @@ void ModelChecker::analyze(SourceUnit const& _source)
m_chc.analyze(_source); m_chc.analyze(_source);
auto solvedTargets = m_chc.safeTargets(); auto solvedTargets = m_chc.safeTargets();
for (auto const& target: m_chc.unsafeTargets()) for (auto const& [node, targets]: m_chc.unsafeTargets())
solvedTargets[target.first] += target.second; solvedTargets[node] += targets | ranges::views::keys;
if (m_settings.engine.bmc) if (m_settings.engine.bmc)
m_bmc.analyze(_source, solvedTargets); m_bmc.analyze(_source, solvedTargets);

View File

@ -113,6 +113,7 @@ struct ModelCheckerSettings
{ {
ModelCheckerContracts contracts = ModelCheckerContracts::Default(); ModelCheckerContracts contracts = ModelCheckerContracts::Default();
ModelCheckerEngine engine = ModelCheckerEngine::None(); ModelCheckerEngine engine = ModelCheckerEngine::None();
bool showUnproved = false;
smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::All(); smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::All();
ModelCheckerTargets targets = ModelCheckerTargets::Default(); ModelCheckerTargets targets = ModelCheckerTargets::Default();
std::optional<unsigned> timeout; std::optional<unsigned> timeout;
@ -123,6 +124,7 @@ struct ModelCheckerSettings
return return
contracts == _other.contracts && contracts == _other.contracts &&
engine == _other.engine && engine == _other.engine &&
showUnproved == _other.showUnproved &&
solvers == _other.solvers && solvers == _other.solvers &&
targets == _other.targets && targets == _other.targets &&
timeout == _other.timeout; timeout == _other.timeout;

View File

@ -20,6 +20,7 @@
#include <libsolidity/interface/OptimiserSettings.h> #include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolutil/JSON.h> #include <libsolutil/JSON.h>
@ -104,6 +105,7 @@ void FuzzerUtil::testCompiler(
compiler.setModelCheckerSettings({ compiler.setModelCheckerSettings({
frontend::ModelCheckerContracts::Default(), frontend::ModelCheckerContracts::Default(),
frontend::ModelCheckerEngine::All(), frontend::ModelCheckerEngine::All(),
/*showUnproved=*/false,
smtutil::SMTSolverChoice::All(), smtutil::SMTSolverChoice::All(),
frontend::ModelCheckerTargets::Default(), frontend::ModelCheckerTargets::Default(),
/*timeout=*/1 /*timeout=*/1