Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix nargo not showing compiler errors or warnings #1694

Merged
merged 5 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions crates/nargo_cli/src/cli/check_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use clap::Args;
use iter_extended::btree_map;
use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME};
use noirc_driver::CompileOptions;
use noirc_errors::reporter;
use noirc_errors::reporter::ReportedErrors;
use std::path::{Path, PathBuf};

use super::NargoConfig;
Expand Down Expand Up @@ -34,16 +34,7 @@ fn check_from_path<B: Backend, P: AsRef<Path>>(
compile_options: &CompileOptions,
) -> Result<(), CliError<B>> {
let mut driver = setup_driver(backend, program_dir.as_ref())?;

let result = driver.check_crate();
if let Err(errs) = result {
let file_manager = driver.file_manager();
let error_count = reporter::report_all(file_manager, &errs, compile_options.deny_warnings);
if error_count != 0 {
reporter::finish_report(error_count);
return Err(CliError::CompilationError);
}
}
check_crate_and_report_errors(&mut driver, compile_options.deny_warnings)?;

// XXX: We can have a --overwrite flag to determine if you want to overwrite the Prover/Verifier.toml files
if let Some((parameters, return_type)) = driver.compute_function_signature() {
Expand Down Expand Up @@ -214,3 +205,13 @@ d2 = ["", "", ""]
}
}
}

/// Run the lexing, parsing, name resolution, and type checking passes and report any warnings
/// and errors found.
pub(crate) fn check_crate_and_report_errors(
driver: &mut noirc_driver::Driver,
deny_warnings: bool,
) -> Result<(), ReportedErrors> {
let result = driver.check_crate(deny_warnings).map(|warnings| ((), warnings));
super::compile_cmd::report_errors(result, driver, deny_warnings)
}
29 changes: 23 additions & 6 deletions crates/nargo_cli/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use acvm::Backend;
use iter_extended::try_vecmap;
use nargo::artifacts::contract::PreprocessedContract;
use noirc_driver::{CompileOptions, CompiledProgram, Driver};
use noirc_driver::{CompileOptions, CompiledProgram, Driver, ErrorsAndWarnings, Warnings};
use noirc_errors::reporter::ReportedErrors;
use std::path::Path;

use clap::Args;
Expand Down Expand Up @@ -49,16 +50,16 @@ pub(crate) fn run<B: Backend>(
// If contracts is set we're compiling every function in a 'contract' rather than just 'main'.
if args.contracts {
let mut driver = setup_driver(backend, &config.program_dir)?;
let compiled_contracts = driver
.compile_contracts(&args.compile_options)
.map_err(|_| CliError::CompilationError)?;

let result = driver.compile_contracts(&args.compile_options);
let contracts = report_errors(result, &driver, args.compile_options.deny_warnings)?;

// TODO(#1389): I wonder if it is incorrect for nargo-core to know anything about contracts.
// As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms)
// are compiled via nargo-core and then the PreprocessedContract is constructed here.
// This is due to EACH function needing it's own CRS, PKey, and VKey from the backend.
let preprocessed_contracts: Result<Vec<PreprocessedContract>, CliError<B>> =
try_vecmap(compiled_contracts, |contract| {
try_vecmap(contracts, |contract| {
let preprocessed_contract_functions = try_vecmap(contract.functions, |func| {
common_reference_string = update_common_reference_string(
backend,
Expand Down Expand Up @@ -118,5 +119,21 @@ pub(crate) fn compile_circuit<B: Backend>(
compile_options: &CompileOptions,
) -> Result<CompiledProgram, CliError<B>> {
let mut driver = setup_driver(backend, program_dir)?;
driver.compile_main(compile_options).map_err(|_| CliError::CompilationError)
let result = driver.compile_main(compile_options);
report_errors(result, &driver, compile_options.deny_warnings).map_err(Into::into)
}

/// Helper function for reporting any errors in a Result<(T, Warnings), ErrorsAndWarnings>
/// structure that is commonly used as a return result in this file.
pub(crate) fn report_errors<T>(
result: Result<(T, Warnings), ErrorsAndWarnings>,
driver: &Driver,
deny_warnings: bool,
) -> Result<T, ReportedErrors> {
let (t, warnings) = result.map_err(|errors| {
noirc_errors::reporter::report_all(driver.file_manager(), &errors, deny_warnings)
})?;

noirc_errors::reporter::report_all(driver.file_manager(), &warnings, deny_warnings);
Ok(t)
}
13 changes: 12 additions & 1 deletion crates/nargo_cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ pub fn prove_and_verify(program_dir: &Path, experimental_ssa: bool) -> bool {
#[cfg(test)]
mod tests {
use noirc_driver::Driver;
use noirc_errors::reporter;
use noirc_frontend::graph::CrateType;

use std::path::{Path, PathBuf};
Expand All @@ -184,7 +185,17 @@ mod tests {
);
driver.create_local_crate(&root_file, CrateType::Binary);
crate::resolver::add_std_lib(&mut driver);
driver.file_compiles()

let result = driver.check_crate(false);
let success = result.is_ok();

let errors = match result {
Ok(warnings) => warnings,
Err(errors) => errors,
};

reporter::report_all(driver.file_manager(), &errors, false);
success
}

#[test]
Expand Down
17 changes: 5 additions & 12 deletions crates/nargo_cli/src/cli/test_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ use acvm::{acir::native_types::WitnessMap, Backend};
use clap::Args;
use nargo::ops::execute_circuit;
use noirc_driver::{CompileOptions, Driver};
use noirc_errors::reporter;
use noirc_frontend::node_interner::FuncId;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::{cli::compile_cmd::setup_driver, errors::CliError};
use crate::{
cli::{check_cmd::check_crate_and_report_errors, compile_cmd::setup_driver},
errors::CliError,
};

use super::NargoConfig;

Expand Down Expand Up @@ -39,16 +41,7 @@ fn run_tests<B: Backend>(
compile_options: &CompileOptions,
) -> Result<(), CliError<B>> {
let mut driver = setup_driver(backend, program_dir)?;

let result = driver.check_crate();
if let Err(errs) = result {
let file_manager = driver.file_manager();
let error_count = reporter::report_all(file_manager, &errs, compile_options.deny_warnings);
if error_count != 0 {
reporter::finish_report(error_count);
return Err(CliError::CompilationError);
}
}
check_crate_and_report_errors(&mut driver, compile_options.deny_warnings)?;

let test_functions = driver.get_all_test_functions_in_crate_matching(test_name);
println!("Running {} test functions...", test_functions.len());
Expand Down
14 changes: 11 additions & 3 deletions crates/nargo_cli/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use acvm::{
use hex::FromHexError;
use nargo::NargoError;
use noirc_abi::errors::{AbiError, InputParserError};
use noirc_errors::reporter::ReportedErrors;
use std::path::PathBuf;
use thiserror::Error;

Expand Down Expand Up @@ -43,9 +44,10 @@ pub(crate) enum CliError<B: Backend> {
#[error(transparent)]
ResolutionError(#[from] DependencyResolutionError),

/// Error while compiling Noir into ACIR.
#[error("Failed to compile circuit")]
CompilationError,
/// Errors encountered while compiling the noir program.
/// These errors are already written to stderr.
#[error("Aborting due to {} previous error{}", .0.error_count, if .0.error_count == 1 { "" } else { "s" })]
ReportedErrors(ReportedErrors),

/// ABI encoding/decoding error
#[error(transparent)]
Expand Down Expand Up @@ -74,3 +76,9 @@ pub(crate) enum CliError<B: Backend> {
#[error(transparent)]
CommonReferenceStringError(<B as CommonReferenceString>::Error), // Unfortunately, Rust won't let us `impl From` over an Associated Type on a generic
}

impl<B: Backend> From<ReportedErrors> for CliError<B> {
fn from(errors: ReportedErrors) -> Self {
Self::ReportedErrors(errors)
}
}
84 changes: 53 additions & 31 deletions crates/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use acvm::Language;
use clap::Args;
use fm::{FileId, FileManager, FileType};
use noirc_abi::FunctionSignature;
use noirc_errors::{reporter, CustomDiagnostic, FileDiagnostic};
use noirc_errors::{CustomDiagnostic, FileDiagnostic};
use noirc_evaluator::{create_circuit, ssa_refactor::experimental_create_circuit};
use noirc_frontend::graph::{CrateId, CrateName, CrateType, LOCAL_CRATE};
use noirc_frontend::hir::def_map::{Contract, CrateDefMap};
Expand Down Expand Up @@ -65,6 +65,12 @@ impl Default for CompileOptions {
}
}

/// Helper type used to signify where only warnings are expected in file diagnostics
pub type Warnings = Vec<FileDiagnostic>;

/// Helper type used to signify where errors or warnings are expected in file diagnostics
pub type ErrorsAndWarnings = Vec<FileDiagnostic>;

impl Driver {
pub fn new(language: &Language, is_opcode_supported: Box<dyn Fn(&Opcode) -> bool>) -> Self {
Driver { context: Context::default(), language: language.clone(), is_opcode_supported }
Expand All @@ -81,22 +87,12 @@ impl Driver {
root_file: PathBuf,
language: &Language,
is_opcode_supported: Box<dyn Fn(&Opcode) -> bool>,
) -> Result<CompiledProgram, Vec<FileDiagnostic>> {
) -> Result<(CompiledProgram, Warnings), ErrorsAndWarnings> {
let mut driver = Driver::new(language, is_opcode_supported);
driver.create_local_crate(root_file, CrateType::Binary);
driver.compile_main(&CompileOptions::default())
}

/// Compiles a file and returns true if compilation was successful
///
/// This is used for tests.
pub fn file_compiles(&mut self) -> bool {
let mut errs = vec![];
CrateDefMap::collect_defs(LOCAL_CRATE, &mut self.context, &mut errs);
reporter::report_all(&self.context.file_manager, &errs, false);
errs.is_empty()
}

/// Adds the File with the local crate root to the file system
/// and adds the local crate to the graph
/// XXX: This may pose a problem with workspaces, where you can change the local crate and where
Expand Down Expand Up @@ -169,15 +165,18 @@ impl Driver {
}
}

/// Run the lexing, parsing, name resolution, and type checking passes,
/// returning Err(FrontendError) and printing any errors that were found.
pub fn check_crate(&mut self) -> Result<(), Vec<FileDiagnostic>> {
let mut errs = vec![];
CrateDefMap::collect_defs(LOCAL_CRATE, &mut self.context, &mut errs);
if errs.is_empty() {
Ok(())
/// Run the lexing, parsing, name resolution, and type checking passes.
///
/// This returns a (possibly empty) vector of any warnings found on success.
/// On error, this returns a non-empty vector of warnings and error messages, with at least one error.
pub fn check_crate(&mut self, deny_warnings: bool) -> Result<Warnings, ErrorsAndWarnings> {
let mut errors = vec![];
CrateDefMap::collect_defs(LOCAL_CRATE, &mut self.context, &mut errors);

if Self::has_errors(&errors, deny_warnings) {
Err(errors)
} else {
Err(errs)
Ok(errors)
}
}

Expand All @@ -192,45 +191,57 @@ impl Driver {
}

/// Run the frontend to check the crate for errors then compile the main function if there were none
///
/// On success this returns the compiled program alongside any warnings that were found.
/// On error this returns the non-empty list of warnings and errors.
pub fn compile_main(
&mut self,
options: &CompileOptions,
) -> Result<CompiledProgram, Vec<FileDiagnostic>> {
self.check_crate()?;
) -> Result<(CompiledProgram, Warnings), ErrorsAndWarnings> {
let warnings = self.check_crate(options.deny_warnings)?;

let main = match self.main_function() {
Some(m) => m,
None => {
let err = FileDiagnostic {
file_id: FileId::default(),
diagnostic: CustomDiagnostic::from_message("cannot compile crate into a program as the local crate is not a binary. For libraries, please use the check command")
};
return Err(err.into());
return Err(vec![err]);
}
};

let compiled_program = self.compile_no_check(options, main)?;

if options.print_acir {
println!("Compiled ACIR for main:");
println!("{}", compiled_program.circuit);
}
Ok(compiled_program)

Ok((compiled_program, warnings))
}

/// Run the frontend to check the crate for errors then compile all contracts if there were none
pub fn compile_contracts(
&mut self,
options: &CompileOptions,
) -> Result<Vec<CompiledContract>, Vec<FileDiagnostic>> {
self.check_crate()?;
) -> Result<(Vec<CompiledContract>, Warnings), ErrorsAndWarnings> {
let warnings = self.check_crate(options.deny_warnings)?;

let contracts = self.get_all_contracts();
let mut compiled_contracts = vec![];
let mut errs = vec![];
let mut errors = warnings;

for contract in contracts {
match self.compile_contract(contract, options) {
Ok(contract) => compiled_contracts.push(contract),
Err(err) => errs.push(err),
Err(mut more_errors) => errors.append(&mut more_errors),
}
}
if errs.is_empty() {

if Self::has_errors(&errors, options.deny_warnings) {
Err(errors)
} else {
if options.print_acir {
for compiled_contract in &compiled_contracts {
for contract_function in &compiled_contract.functions {
Expand All @@ -242,9 +253,17 @@ impl Driver {
}
}
}
Ok(compiled_contracts)
// errors here is either empty or contains only warnings
Ok((compiled_contracts, errors))
}
}

/// True if there are (non-warning) errors present and we should halt compilation
fn has_errors(errors: &[FileDiagnostic], deny_warnings: bool) -> bool {
if deny_warnings {
!errors.is_empty()
} else {
Err(errs.concat())
errors.iter().filter(|error| error.diagnostic.is_error()).count() != 0
}
}

Expand Down Expand Up @@ -305,6 +324,9 @@ impl Driver {
}

/// Compile the current crate. Assumes self.check_crate is called beforehand!
///
/// This function also assumes all errors in experimental_create_circuit and create_circuit
/// are not warnings.
#[allow(deprecated)]
pub fn compile_no_check(
&self,
Expand Down
Loading