Skip to content

Commit e349f30

Browse files
asteriteTomAFrench
andauthored
feat: use visibility (#5856)
# Description ## Problem Part of #4515 ## Summary We recently added a warning for unused imports... but only for bin and contract packages. We didn't enable it for lib packages because a `use` could also be used as `pub use`, something we don't have yet. I thought it would be really nice if we had `pub use`, and warned on unused imports in libs too. I checked the code and we already track visibility for any item, in general, it's just that for things that don't allow a visibility modifier we just consider it's public. So I tried to see how difficult it would be to implement it, and it turned out it wasn't that hard or time-consuming. That said, visibility for `use` involves some more logic, particularly for autocompletion, because now `pub use` should be suggested, but the "parent" module of that item isn't the actual parent (it's the module where the `pub use` is defined) but that was relatively striaght-forward to implement too. ## Additional Context If we decide to go forward with this, any existing `use` that was used as `pub use` will likely start producing a warning for libs (a lot of them in Aztec-Packages), but now that can be silenced by changing them to `pub use`. Where should this new feature be documented? I'm not sure if it should go in `dependencies.md` or `modules.md`. ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
1 parent 4012429 commit e349f30

File tree

52 files changed

+735
-361
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+735
-361
lines changed

aztec_macros/src/utils/parse_utils.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn empty_item(item: &mut Item) {
5050
empty_parsed_submodule(parsed_submodule);
5151
}
5252
ItemKind::ModuleDecl(module_declaration) => empty_module_declaration(module_declaration),
53-
ItemKind::Import(use_tree) => empty_use_tree(use_tree),
53+
ItemKind::Import(use_tree, _) => empty_use_tree(use_tree),
5454
ItemKind::Struct(noir_struct) => empty_noir_struct(noir_struct),
5555
ItemKind::TypeAlias(noir_type_alias) => empty_noir_type_alias(noir_type_alias),
5656
}
@@ -404,9 +404,9 @@ fn empty_pattern(pattern: &mut Pattern) {
404404
}
405405

406406
fn empty_unresolved_trait_constraints(
407-
unresolved_trait_constriants: &mut [UnresolvedTraitConstraint],
407+
unresolved_trait_constraints: &mut [UnresolvedTraitConstraint],
408408
) {
409-
for trait_constraint in unresolved_trait_constriants.iter_mut() {
409+
for trait_constraint in unresolved_trait_constraints.iter_mut() {
410410
empty_unresolved_trait_constraint(trait_constraint);
411411
}
412412
}

compiler/noirc_driver/src/lib.rs

+5-23
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,6 @@ pub struct CompileOptions {
131131
pub skip_underconstrained_check: bool,
132132
}
133133

134-
#[derive(Clone, Debug, Default)]
135-
pub struct CheckOptions {
136-
pub compile_options: CompileOptions,
137-
pub error_on_unused_imports: bool,
138-
}
139-
140-
impl CheckOptions {
141-
pub fn new(compile_options: &CompileOptions, error_on_unused_imports: bool) -> Self {
142-
Self { compile_options: compile_options.clone(), error_on_unused_imports }
143-
}
144-
}
145-
146134
pub fn parse_expression_width(input: &str) -> Result<ExpressionWidth, std::io::Error> {
147135
use std::io::{Error, ErrorKind};
148136
let width = input
@@ -290,20 +278,19 @@ pub fn add_dep(
290278
pub fn check_crate(
291279
context: &mut Context,
292280
crate_id: CrateId,
293-
check_options: &CheckOptions,
281+
options: &CompileOptions,
294282
) -> CompilationResult<()> {
295-
let options = &check_options.compile_options;
296-
297283
let macros: &[&dyn MacroProcessor] =
298284
if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] };
299285

300286
let mut errors = vec![];
287+
let error_on_unused_imports = true;
301288
let diagnostics = CrateDefMap::collect_defs(
302289
crate_id,
303290
context,
304291
options.debug_comptime_in_file.as_deref(),
305292
options.arithmetic_generics,
306-
check_options.error_on_unused_imports,
293+
error_on_unused_imports,
307294
macros,
308295
);
309296
errors.extend(diagnostics.into_iter().map(|(error, file_id)| {
@@ -337,10 +324,7 @@ pub fn compile_main(
337324
options: &CompileOptions,
338325
cached_program: Option<CompiledProgram>,
339326
) -> CompilationResult<CompiledProgram> {
340-
let error_on_unused_imports = true;
341-
let check_options = CheckOptions::new(options, error_on_unused_imports);
342-
343-
let (_, mut warnings) = check_crate(context, crate_id, &check_options)?;
327+
let (_, mut warnings) = check_crate(context, crate_id, options)?;
344328

345329
let main = context.get_main_function(&crate_id).ok_or_else(|| {
346330
// TODO(#2155): This error might be a better to exist in Nargo
@@ -375,9 +359,7 @@ pub fn compile_contract(
375359
crate_id: CrateId,
376360
options: &CompileOptions,
377361
) -> CompilationResult<CompiledContract> {
378-
let error_on_unused_imports = true;
379-
let check_options = CheckOptions::new(options, error_on_unused_imports);
380-
let (_, warnings) = check_crate(context, crate_id, &check_options)?;
362+
let (_, warnings) = check_crate(context, crate_id, options)?;
381363

382364
// TODO: We probably want to error if contracts is empty
383365
let contracts = context.get_all_contracts(&crate_id);

compiler/noirc_frontend/src/ast/mod.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -460,12 +460,22 @@ impl UnresolvedTypeExpression {
460460
}
461461
}
462462

463-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
463+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
464464
/// Represents whether the definition can be referenced outside its module/crate
465465
pub enum ItemVisibility {
466-
Public,
467466
Private,
468467
PublicCrate,
468+
Public,
469+
}
470+
471+
impl std::fmt::Display for ItemVisibility {
472+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473+
match self {
474+
ItemVisibility::Public => write!(f, "pub"),
475+
ItemVisibility::Private => Ok(()),
476+
ItemVisibility::PublicCrate => write!(f, "pub(crate)"),
477+
}
478+
}
469479
}
470480

471481
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]

compiler/noirc_frontend/src/ast/statement.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use iter_extended::vecmap;
77
use noirc_errors::{Span, Spanned};
88

99
use super::{
10-
BlockExpression, Expression, ExpressionKind, GenericTypeArgs, IndexExpression,
10+
BlockExpression, Expression, ExpressionKind, GenericTypeArgs, IndexExpression, ItemVisibility,
1111
MemberAccessExpression, MethodCallExpression, UnresolvedType,
1212
};
1313
use crate::elaborator::types::SELF_TYPE_NAME;
@@ -302,6 +302,7 @@ impl std::fmt::Display for ModuleDeclaration {
302302

303303
#[derive(Debug, PartialEq, Eq, Clone)]
304304
pub struct ImportStatement {
305+
pub visibility: ItemVisibility,
305306
pub path: Path,
306307
pub alias: Option<Ident>,
307308
}
@@ -350,7 +351,7 @@ pub enum UseTreeKind {
350351
}
351352

352353
impl UseTree {
353-
pub fn desugar(self, root: Option<Path>) -> Vec<ImportStatement> {
354+
pub fn desugar(self, root: Option<Path>, visibility: ItemVisibility) -> Vec<ImportStatement> {
354355
let prefix = if let Some(mut root) = root {
355356
root.segments.extend(self.prefix.segments);
356357
root
@@ -360,10 +361,11 @@ impl UseTree {
360361

361362
match self.kind {
362363
UseTreeKind::Path(name, alias) => {
363-
vec![ImportStatement { path: prefix.join(name), alias }]
364+
vec![ImportStatement { visibility, path: prefix.join(name), alias }]
364365
}
365366
UseTreeKind::List(trees) => {
366-
trees.into_iter().flat_map(|tree| tree.desugar(Some(prefix.clone()))).collect()
367+
let trees = trees.into_iter();
368+
trees.flat_map(|tree| tree.desugar(Some(prefix.clone()), visibility)).collect()
367369
}
368370
}
369371
}

compiler/noirc_frontend/src/ast/visitor.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ use crate::{
2121
};
2222

2323
use super::{
24-
FunctionReturnType, GenericTypeArgs, IntegerBitSize, Pattern, Signedness, UnresolvedGenerics,
25-
UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression,
24+
FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, Pattern, Signedness,
25+
UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData,
26+
UnresolvedTypeExpression,
2627
};
2728

2829
/// Implements the [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for Noir's AST.
@@ -252,7 +253,7 @@ pub trait Visitor {
252253
true
253254
}
254255

255-
fn visit_import(&mut self, _: &UseTree) -> bool {
256+
fn visit_import(&mut self, _: &UseTree, _visibility: ItemVisibility) -> bool {
256257
true
257258
}
258259

@@ -470,8 +471,8 @@ impl Item {
470471
}
471472
}
472473
ItemKind::Trait(noir_trait) => noir_trait.accept(self.span, visitor),
473-
ItemKind::Import(use_tree) => {
474-
if visitor.visit_import(use_tree) {
474+
ItemKind::Import(use_tree, visibility) => {
475+
if visitor.visit_import(use_tree, *visibility) {
475476
use_tree.accept(visitor);
476477
}
477478
}

compiler/noirc_frontend/src/elaborator/comptime.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ impl<'context> Elaborator<'context> {
333333
TopLevelStatement::Error => (),
334334

335335
TopLevelStatement::Module(_)
336-
| TopLevelStatement::Import(_)
336+
| TopLevelStatement::Import(..)
337337
| TopLevelStatement::Struct(_)
338338
| TopLevelStatement::Trait(_)
339339
| TopLevelStatement::Impl(_)

compiler/noirc_frontend/src/elaborator/scope.rs

+37-30
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@ impl<'context> Elaborator<'context> {
6060
let mut module_id = self.module_id();
6161
let mut path = path;
6262

63-
if path.kind == PathKind::Plain {
64-
let def_map = self.def_maps.get_mut(&self.crate_id).unwrap();
65-
let module_data = &mut def_map.modules[module_id.local_id.0];
66-
module_data.use_import(&path.segments[0].ident);
67-
}
68-
6963
if path.kind == PathKind::Plain && path.first_name() == SELF_TYPE_NAME {
7064
if let Some(Type::Struct(struct_type, _)) = &self.self_type {
7165
let struct_type = struct_type.borrow();
@@ -90,34 +84,47 @@ impl<'context> Elaborator<'context> {
9084

9185
fn resolve_path_in_module(&mut self, path: Path, module_id: ModuleId) -> PathResolutionResult {
9286
let resolver = StandardPathResolver::new(module_id);
93-
let path_resolution;
94-
95-
if self.interner.lsp_mode {
96-
let last_segment = path.last_ident();
97-
let location = Location::new(last_segment.span(), self.file);
98-
let is_self_type_name = last_segment.is_self_type_name();
99-
100-
let mut references: Vec<_> = Vec::new();
101-
path_resolution =
102-
resolver.resolve(self.def_maps, path.clone(), &mut Some(&mut references))?;
103-
104-
for (referenced, segment) in references.iter().zip(path.segments) {
105-
self.interner.add_reference(
106-
*referenced,
107-
Location::new(segment.ident.span(), self.file),
108-
segment.ident.is_self_type_name(),
109-
);
110-
}
11187

112-
self.interner.add_module_def_id_reference(
113-
path_resolution.module_def_id,
114-
location,
115-
is_self_type_name,
88+
if !self.interner.lsp_mode {
89+
return resolver.resolve(
90+
self.def_maps,
91+
path,
92+
&mut self.interner.usage_tracker,
93+
&mut None,
94+
);
95+
}
96+
97+
let last_segment = path.last_ident();
98+
let location = Location::new(last_segment.span(), self.file);
99+
let is_self_type_name = last_segment.is_self_type_name();
100+
101+
let mut references: Vec<_> = Vec::new();
102+
let path_resolution = resolver.resolve(
103+
self.def_maps,
104+
path.clone(),
105+
&mut self.interner.usage_tracker,
106+
&mut Some(&mut references),
107+
);
108+
109+
for (referenced, segment) in references.iter().zip(path.segments) {
110+
self.interner.add_reference(
111+
*referenced,
112+
Location::new(segment.ident.span(), self.file),
113+
segment.ident.is_self_type_name(),
116114
);
117-
} else {
118-
path_resolution = resolver.resolve(self.def_maps, path, &mut None)?;
119115
}
120116

117+
let path_resolution = match path_resolution {
118+
Ok(path_resolution) => path_resolution,
119+
Err(err) => return Err(err),
120+
};
121+
122+
self.interner.add_module_def_id_reference(
123+
path_resolution.module_def_id,
124+
location,
125+
is_self_type_name,
126+
);
127+
121128
Ok(path_resolution)
122129
}
123130

0 commit comments

Comments
 (0)