diff --git a/third_party/move/move-model/src/builder/module_builder.rs b/third_party/move/move-model/src/builder/module_builder.rs index 3109adcfc9d..08013d99184 100644 --- a/third_party/move/move-model/src/builder/module_builder.rs +++ b/third_party/move/move-model/src/builder/module_builder.rs @@ -4844,6 +4844,7 @@ impl ModuleBuilder<'_, '_> { using_funs: RefCell::default(), transitive_closure_of_used_funs: RefCell::default(), used_functions_with_transitive_inline: RefCell::default(), + using_functions_with_transitive_inline: RefCell::default(), used_structs: RefCell::default(), }; function_data.insert(fun_id, data); @@ -4890,6 +4891,7 @@ impl ModuleBuilder<'_, '_> { using_funs: RefCell::default(), transitive_closure_of_used_funs: RefCell::default(), used_functions_with_transitive_inline: RefCell::default(), + using_functions_with_transitive_inline: RefCell::default(), used_structs: RefCell::default(), }; function_data.insert(fun_id, data); diff --git a/third_party/move/move-model/src/model.rs b/third_party/move/move-model/src/model.rs index c1eeb212c32..00842ff3a52 100644 --- a/third_party/move/move-model/src/model.rs +++ b/third_party/move/move-model/src/model.rs @@ -2229,6 +2229,7 @@ impl GlobalEnv { *data.using_funs.borrow_mut() = None; *data.transitive_closure_of_used_funs.borrow_mut() = None; *data.used_functions_with_transitive_inline.borrow_mut() = None; + *data.using_functions_with_transitive_inline.borrow_mut() = None; // Set the new function definition. data.def = Some(def); } @@ -2303,6 +2304,7 @@ impl GlobalEnv { using_funs: RefCell::new(None), transitive_closure_of_used_funs: RefCell::new(None), used_functions_with_transitive_inline: RefCell::new(None), + using_functions_with_transitive_inline: RefCell::new(None), used_structs: RefCell::new(None), }; assert!(self @@ -2375,6 +2377,7 @@ impl GlobalEnv { using_funs: RefCell::new(None), transitive_closure_of_used_funs: RefCell::new(None), used_functions_with_transitive_inline: RefCell::new(None), + using_functions_with_transitive_inline: RefCell::new(None), used_structs: RefCell::new(None), } } @@ -4923,6 +4926,11 @@ pub struct FunctionData { /// A cache for used functions including ones obtained by transitively traversing used inline functions. pub(crate) used_functions_with_transitive_inline: RefCell>>>, + /// A cache for using functions with direct inline callers replaced by their (transitive) callers, + /// reflecting the post-inlining call graph. + pub(crate) using_functions_with_transitive_inline: + RefCell>>>, + /// A cache for used structs. pub(crate) used_structs: RefCell>>>, } @@ -4958,6 +4966,7 @@ impl FunctionData { using_funs: RefCell::new(None), transitive_closure_of_used_funs: RefCell::new(None), used_functions_with_transitive_inline: RefCell::new(None), + using_functions_with_transitive_inline: RefCell::new(None), used_structs: RefCell::new(None), } } @@ -5813,6 +5822,39 @@ impl<'env> FunctionEnv<'env> { set } + /// Get the functions that effectively use (call) this one after inline expansion. + /// Direct callers that are themselves inline functions are replaced by their + /// (transitive) callers, because after inlining those inline callers no longer + /// contain a call site — their callers do. + pub fn get_using_functions_with_transitive_inline(&self) -> BTreeSet> { + if let Some(using) = &*self.data.using_functions_with_transitive_inline.borrow() { + return using.clone(); + } + let mut result = BTreeSet::new(); + let mut visited = BTreeSet::new(); + visited.insert(self.get_qualified_id()); + let mut reachable_funcs = VecDeque::new(); + reachable_funcs.push_back(self.clone()); + while let Some(fnc) = reachable_funcs.pop_front() { + for user in fnc.get_using_functions().expect("call info available") { + if !visited.insert(user) { + continue; + } + let user_fun = self.module_env.env.get_function(user); + if user_fun.is_inline() { + reachable_funcs.push_back(user_fun); + } else { + result.insert(user); + } + } + } + *self + .data + .using_functions_with_transitive_inline + .borrow_mut() = Some(result.clone()); + result + } + /// Get used structs/enums including ones obtained by transitively traversing used inline functions pub fn get_used_structs_with_transitive_inline(&self) -> BTreeSet> { if let Some(used_structs) = &*self.data.used_structs.borrow() { diff --git a/third_party/move/tools/move-linter/src/model_ast_lints/unused_common.rs b/third_party/move/tools/move-linter/src/model_ast_lints/unused_common.rs index 9afe910a05e..268c480f427 100644 --- a/third_party/move/tools/move-linter/src/model_ast_lints/unused_common.rs +++ b/third_party/move/tools/move-linter/src/model_ast_lints/unused_common.rs @@ -46,10 +46,13 @@ pub fn has_users(func: &FunctionEnv) -> bool { /// Returns true if the function has at least one non-self caller, and all /// callers are within the same module. +/// +/// Direct callers that are inline functions are replaced by their (transitive) +/// callers, because inline bodies are expanded at call sites during compilation: +/// after inlining, this function is effectively called from wherever the inline +/// caller is called. pub fn has_same_module_users_only(func: &FunctionEnv) -> bool { - let Some(using_funs) = func.get_using_functions() else { - return false; - }; + let using_funs = func.get_using_functions_with_transitive_inline(); let func_qfid = func.get_qualified_id(); let func_module_id = func.module_env.get_id(); diff --git a/third_party/move/tools/move-linter/tests/model_ast_lints/needless_visibility_lint_fp_fix.exp b/third_party/move/tools/move-linter/tests/model_ast_lints/needless_visibility_lint_fp_fix.exp new file mode 100644 index 00000000000..144ada2dd20 --- /dev/null +++ b/third_party/move/tools/move-linter/tests/model_ast_lints/needless_visibility_lint_fp_fix.exp @@ -0,0 +1,2 @@ + +No errors or warnings! diff --git a/third_party/move/tools/move-linter/tests/model_ast_lints/needless_visibility_lint_fp_fix.move b/third_party/move/tools/move-linter/tests/model_ast_lints/needless_visibility_lint_fp_fix.move new file mode 100644 index 00000000000..257db1280f4 --- /dev/null +++ b/third_party/move/tools/move-linter/tests/model_ast_lints/needless_visibility_lint_fp_fix.move @@ -0,0 +1,17 @@ +module 0xc0ffee::m { + package fun package_inner(): u64 { + 42 + } + + package inline fun outer(): u64 { + package_inner() + } +} + +module 0xc0ffee::n { + use 0xc0ffee::m; + + public fun call_outer(): u64 { + m::outer() + } +}