Skip to content

Commit

Permalink
Fix DCA warnings for unused struct fields in contracts. (FuelLabs#3562)
Browse files Browse the repository at this point in the history
If a struct type is used as a return type in the interface surface of
the contract, then assume that any fields inside the struct can be used
outside of the contract.

Closes FuelLabs#1305.
  • Loading branch information
tritao authored Dec 12, 2022
1 parent 5fc1750 commit 2b6189f
Show file tree
Hide file tree
Showing 17 changed files with 193 additions and 4 deletions.
85 changes: 81 additions & 4 deletions sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use crate::{
declaration_engine::{declaration_engine::*, DeclarationId},
language::{parsed::TreeType, ty, CallPath, Visibility},
type_system::TypeInfo,
TypeEngine,
TypeEngine, TypeId,
};
use petgraph::{prelude::NodeIndex, visit::Dfs};
use std::collections::BTreeSet;
use sway_error::error::CompileError;
use sway_error::warning::{CompileWarning, Warning};
use sway_error::{error::CompileError, type_error::TypeError};
use sway_types::{span::Span, Ident, Spanned};

impl ControlFlowGraph {
Expand Down Expand Up @@ -252,6 +252,7 @@ fn connect_node(
expr.span.clone(),
options,
)?;

for leaf in return_contents.clone() {
graph.add_edge(this_index, leaf, "".into());
}
Expand Down Expand Up @@ -393,7 +394,7 @@ fn connect_declaration(
}
AbiDeclaration(decl_id) => {
let abi_decl = de_get_abi(decl_id.clone(), &span)?;
connect_abi_declaration(&abi_decl, graph, entry_node);
connect_abi_declaration(type_engine, &abi_decl, graph, entry_node)?;
Ok(leaves.to_vec())
}
StructDeclaration(decl_id) => {
Expand Down Expand Up @@ -562,10 +563,11 @@ fn connect_trait_declaration(

/// See [connect_trait_declaration] for implementation details.
fn connect_abi_declaration(
type_engine: &TypeEngine,
decl: &ty::TyAbiDeclaration,
graph: &mut ControlFlowGraph,
entry_node: NodeIndex,
) {
) -> Result<(), CompileError> {
graph.namespace.add_trait(
CallPath {
prefixes: vec![],
Expand All @@ -574,6 +576,81 @@ fn connect_abi_declaration(
},
entry_node,
);

// If a struct type is used as a return type in the interface surface
// of the contract, then assume that any fields inside the struct can
// be used outside of the contract.
for fn_decl_id in decl.interface_surface.iter() {
let fn_decl = de_get_trait_fn(fn_decl_id.clone(), &decl.span)?;
if let Some(TypeInfo::Struct { name, .. }) =
get_struct_type_info_from_type_id(type_engine, fn_decl.return_type)?
{
if let Some(ns) = graph.namespace.get_struct(&name).cloned() {
for (_, field_ix) in ns.fields.iter() {
graph.add_edge(ns.struct_decl_ix, *field_ix, "".into());
}
}
}
}

Ok(())
}

fn get_struct_type_info_from_type_id(
type_engine: &TypeEngine,
type_id: TypeId,
) -> Result<Option<TypeInfo>, TypeError> {
let type_info = type_engine.to_typeinfo(type_id, &Span::dummy())?;
match type_info {
TypeInfo::Enum {
type_parameters,
variant_types,
..
} => {
for param in type_parameters.iter() {
if let Ok(Some(type_info)) =
get_struct_type_info_from_type_id(type_engine, param.type_id)
{
return Ok(Some(type_info));
}
}
for var in variant_types.iter() {
if let Ok(Some(type_info)) =
get_struct_type_info_from_type_id(type_engine, var.type_id)
{
return Ok(Some(type_info));
}
}
Ok(None)
}
TypeInfo::Tuple(type_args) => {
for arg in type_args.iter() {
if let Ok(Some(type_info)) =
get_struct_type_info_from_type_id(type_engine, arg.type_id)
{
return Ok(Some(type_info));
}
}
Ok(None)
}
TypeInfo::Custom { type_arguments, .. } => {
if let Some(type_arguments) = type_arguments {
for arg in type_arguments.iter() {
if let Ok(Some(type_info)) =
get_struct_type_info_from_type_id(type_engine, arg.type_id)
{
return Ok(Some(type_info));
}
}
}
Ok(None)
}
TypeInfo::Struct { .. } => Ok(Some(type_info)),
TypeInfo::Array(type_arg, _) => {
get_struct_type_info_from_type_id(type_engine, type_arg.type_id)
}
_ => Ok(None),
}
}

/// For an enum declaration, we want to make a declaration node for every individual enum
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'unused_struct_field'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
implicit-std = false
license = "Apache-2.0"
name = "unused_struct_field"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
contract;

struct S {
x: u64,
}

abi MyContract {
fn foo(s: S) -> S;
}

impl MyContract for Contract {
fn foo(s: S) -> S {
s
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "compile"

# not: $()This struct field is never accessed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'unused_struct_field_array'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
implicit-std = false
license = "Apache-2.0"
name = "unused_struct_field_array"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
contract;

struct S {
x: u64,
}

abi MyContract {
fn foo(s: S) -> [S; 1];
}

impl MyContract for Contract {
fn foo(s: S) -> [S; 1] {
[s]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "compile"

# not: $()This struct field is never accessed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'unused_struct_field_enum'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
implicit-std = false
license = "Apache-2.0"
name = "unused_struct_field_enum"
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
contract;

struct S {
x: u64,
}

enum E {
A: S,
}

abi MyContract {
fn foo(s: S) -> E;
}

impl MyContract for Contract {
fn foo(s: S) -> E {
E::A(s)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "compile"

# not: $()This struct field is never accessed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'unused_struct_field_tuple'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
implicit-std = false
license = "Apache-2.0"
name = "unused_struct_field_tuple"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
contract;

struct S {
x: u64,
}

abi MyContract {
fn foo(s: S) -> (S);
}

impl MyContract for Contract {
fn foo(s: S) -> (S) {
(s)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "compile"

# not: $()This struct field is never accessed.

0 comments on commit 2b6189f

Please sign in to comment.