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

feat: warn on unused functions #5892

Merged
merged 18 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 16 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
40 changes: 19 additions & 21 deletions compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::hir::resolution::errors::ResolverError;
use crate::hir::resolution::path_resolver;
use crate::hir::type_check::TypeCheckError;
use crate::token::SecondaryAttribute;
use crate::usage_tracker::UnusedItem;
use crate::{Generics, Type};

use crate::hir::resolution::import::{resolve_import, ImportDirective, PathResolution};
Expand Down Expand Up @@ -271,7 +272,7 @@ impl DefCollector {
root_file_id: FileId,
debug_comptime_in_file: Option<&str>,
enable_arithmetic_generics: bool,
error_on_usage_tracker: bool,
error_on_unused_items: bool,
macro_processors: &[&dyn MacroProcessor],
) -> Vec<(CompilationError, FileId)> {
let mut errors: Vec<(CompilationError, FileId)> = vec![];
Expand Down Expand Up @@ -406,20 +407,14 @@ impl DefCollector {
let result = current_def_map.modules[resolved_import.module_scope.0]
.import(name.clone(), visibility, module_def_id, is_prelude);

// Empty spans could come from implicitly injected imports, and we don't want to track those
if visibility != ItemVisibility::Public
&& name.span().start() < name.span().end()
{
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let module_id = ModuleId {
krate: crate_id,
local_id: resolved_import.module_scope,
};

context
.def_interner
.usage_tracker
.add_unused_import(module_id, name.clone());
}
let module_id =
ModuleId { krate: crate_id, local_id: resolved_import.module_scope };
context.def_interner.usage_tracker.add_unused_item(
module_id,
name.clone(),
UnusedItem::Import,
visibility,
);

if visibility != ItemVisibility::Private {
let local_id = resolved_import.module_scope;
Expand Down Expand Up @@ -494,26 +489,29 @@ impl DefCollector {
);
}

if error_on_usage_tracker {
Self::check_usage_tracker(context, crate_id, &mut errors);
if error_on_unused_items {
Self::check_unused_items(context, crate_id, &mut errors);
}

errors
}

fn check_usage_tracker(
fn check_unused_items(
context: &Context,
crate_id: CrateId,
errors: &mut Vec<(CompilationError, FileId)>,
) {
let unused_imports = context.def_interner.usage_tracker.unused_imports().iter();
let unused_imports = context.def_interner.usage_tracker.unused_items().iter();
let unused_imports = unused_imports.filter(|(module_id, _)| module_id.krate == crate_id);

errors.extend(unused_imports.flat_map(|(module_id, usage_tracker)| {
let module = &context.def_maps[&crate_id].modules()[module_id.local_id.0];
usage_tracker.iter().map(|ident| {
usage_tracker.iter().map(|(ident, unused_item)| {
let ident = ident.clone();
let error = CompilationError::ResolverError(ResolverError::UnusedImport { ident });
let error = CompilationError::ResolverError(ResolverError::UnusedItem {
ident,
item_type: unused_item.item_type(),
});
(error, module.location.file)
})
}));
Expand Down
24 changes: 21 additions & 3 deletions compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::hir::resolution::errors::ResolverError;
use crate::macros_api::{Expression, NodeInterner, StructId, UnresolvedType, UnresolvedTypeData};
use crate::node_interner::ModuleAttributes;
use crate::token::SecondaryAttribute;
use crate::usage_tracker::UnusedItem;
use crate::{
graph::CrateId,
hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait},
Expand All @@ -36,7 +37,7 @@ use super::{
},
errors::{DefCollectorErrorKind, DuplicateType},
};
use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleId};
use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION};
use crate::hir::resolution::import::ImportDirective;
use crate::hir::Context;

Expand Down Expand Up @@ -268,6 +269,15 @@ impl<'a> ModCollector<'a> {
context.def_interner.register_function(func_id, &function.def);
}

let module_data = &mut self.def_collector.def_map.modules[self.module_id.0];

let is_test = function.def.attributes.is_test_function();
let is_entry_point_function = if module_data.is_contract {
function.attributes().is_contract_entry_point()
} else {
function.name() == MAIN_FUNCTION
};

// Now link this func_id to a crate level map with the noir function and the module id
// Encountering a NoirFunction, we retrieve it's module_data to get the namespace
// Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner
Expand All @@ -277,8 +287,16 @@ impl<'a> ModCollector<'a> {
unresolved_functions.push_fn(self.module_id, func_id, function);

// Add function to scope/ns of the module
let result = self.def_collector.def_map.modules[self.module_id.0]
.declare_function(name, visibility, func_id);
let result = module_data.declare_function(name.clone(), visibility, func_id);

if !is_test && !is_entry_point_function {
let module_id = ModuleId { krate, local_id: self.module_id };
let item = UnusedItem::Function(func_id);
context
.def_interner
.usage_tracker
.add_unused_item(module_id, name, item, visibility);
}

if let Err((first_def, second_def)) = result {
let error = DefCollectorErrorKind::Duplicate {
Expand Down
10 changes: 5 additions & 5 deletions compiler/noirc_frontend/src/hir/resolution/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub enum ResolverError {
DuplicateDefinition { name: String, first_span: Span, second_span: Span },
#[error("Unused variable")]
UnusedVariable { ident: Ident },
#[error("Unused import")]
UnusedImport { ident: Ident },
#[error("Unused {item_type}")]
UnusedItem { ident: Ident, item_type: &'static str },
#[error("Could not find variable in this scope")]
VariableNotDeclared { name: String, span: Span },
#[error("path is not an identifier")]
Expand Down Expand Up @@ -158,12 +158,12 @@ impl<'a> From<&'a ResolverError> for Diagnostic {
diagnostic.unnecessary = true;
diagnostic
}
ResolverError::UnusedImport { ident } => {
ResolverError::UnusedItem { ident, item_type } => {
let name = &ident.0.contents;

let mut diagnostic = Diagnostic::simple_warning(
format!("unused import {name}"),
"unused import ".to_string(),
format!("unused {item_type} {name}"),
format!("unused {item_type}"),
ident.span(),
);
diagnostic.unnecessary = true;
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ impl Default for NodeInterner {
auto_import_names: HashMap::default(),
comptime_scopes: vec![HashMap::default()],
trait_impl_associated_types: HashMap::default(),
usage_tracker: UsageTracker::default(),
usage_tracker: UsageTracker::new(),
}
}
}
Expand Down
Loading
Loading