class Identifier(ExpressionAbc):
"""
Represents a single identifier referencing a declaration (or multiple overloaded declarations).
"""
_ast_node: SolcIdentifier
_parent: weakref.ReferenceType[SolidityAbc] # TODO: make this more specific
_name: str
_overloaded_declarations: List[AstNodeId]
_referenced_declaration_ids: Set[AstNodeId]
def __init__(
self, init: IrInitTuple, identifier: SolcIdentifier, parent: SolidityAbc
):
from ..meta.import_directive import ImportDirective
super().__init__(init, identifier, parent)
self._name = identifier.name
self._overloaded_declarations = list(identifier.overloaded_declarations)
if identifier.referenced_declaration is None:
assert isinstance(self.parent, ImportDirective)
self._referenced_declaration_ids = set()
else:
self._referenced_declaration_ids = {identifier.referenced_declaration}
init.reference_resolver.register_post_process_callback(
self._post_process, priority=-1
)
def _post_process(self, callback_params: CallbackParams):
from ..meta.import_directive import ImportDirective
new_referenced_declaration_ids = set()
for referenced_declaration_id in self._referenced_declaration_ids:
if referenced_declaration_id < 0:
global_symbol = GlobalSymbol(referenced_declaration_id)
self._reference_resolver.register_global_symbol_reference(
global_symbol, self
)
self._reference_resolver.register_destroy_callback(
self.source_unit.file, partial(self._destroy, global_symbol)
)
new_referenced_declaration_ids.add(referenced_declaration_id)
else:
node = self._reference_resolver.resolve_node(
referenced_declaration_id, self.source_unit.cu_hash
)
if isinstance(node, DeclarationAbc):
node.register_reference(self)
self._reference_resolver.register_destroy_callback(
self.source_unit.file, partial(self._destroy, node)
)
new_referenced_declaration_ids.add(referenced_declaration_id)
elif isinstance(node, ImportDirective):
# make this node to reference the source unit directly
assert node.unit_alias is not None
source_unit = callback_params.source_units[node.imported_file]
node_path_order = self._reference_resolver.get_node_path_order(
AstNodeId(source_unit.ast_node_id),
source_unit.cu_hash,
)
new_referenced_declaration_ids.add(
self._reference_resolver.get_ast_id_from_cu_node_path_order(
node_path_order, self.source_unit.cu_hash
)
)
else:
raise TypeError(f"Unexpected type: {type(node)}")
self._referenced_declaration_ids = new_referenced_declaration_ids
def _destroy(
self, referenced_declaration: Union[GlobalSymbol, DeclarationAbc]
) -> None:
if isinstance(referenced_declaration, GlobalSymbol):
self._reference_resolver.unregister_global_symbol_reference(
referenced_declaration, self
)
elif isinstance(referenced_declaration, DeclarationAbc):
referenced_declaration.unregister_reference(self)
else:
raise TypeError(f"Unexpected type: {type(referenced_declaration)}")
@property
def parent(self) -> SolidityAbc:
return super().parent
@property
def name(self) -> str:
"""
Returns:
Name of the referenced declaration.
"""
return self._name
@property
def overloaded_declarations(self) -> FrozenSet[FunctionDefinition]:
"""
Returns:
Empty set if [referenced_declaration][wake.ir.expressions.identifier.Identifier.referenced_declaration] is not overloaded.
Otherwise, set of all overloaded declarations (including [referenced_declaration][wake.ir.expressions.identifier.Identifier.referenced_declaration]) with the same name as the identifier.
"""
from ..declarations.function_definition import FunctionDefinition
overloaded_declarations = set()
for overloaded_declaration_id in self._overloaded_declarations:
assert overloaded_declaration_id >= 0
overloaded_declaration = self._reference_resolver.resolve_node(
overloaded_declaration_id, self.source_unit.cu_hash
)
assert isinstance(overloaded_declaration, FunctionDefinition)
overloaded_declarations.add(overloaded_declaration)
# fix overloaded declarations are not set for identifiers in ImportDirective symbol alias
if len(overloaded_declarations) == 0:
ref_decl = self.referenced_declaration
if isinstance(ref_decl, set):
return frozenset(ref_decl)
return frozenset(overloaded_declarations)
@property
def referenced_declaration(
self,
) -> Union[DeclarationAbc, GlobalSymbol, SourceUnit, FrozenSet[FunctionDefinition]]:
"""
If the referenced function name is overloaded and a single function cannot be inferred from the context, returns a set of all overloaded functions.
This is the case of identifiers in [ImportDirectives][wake.ir.meta.import_directive.ImportDirective].
In the following example, the identifier `max` in the example import directive references both `max` functions from `Math.sol`:
```solidity title="Math.sol"
function max(uint256 a, uint256 b) pure returns (uint256) {
return a >= b ? a : b;
}
function max(int256 a, int256 b) pure returns (int256) {
return a >= b ? a : b;
}
```
```solidity title="A.sol"
import { max } from "./Math.sol";
```
If the referenced function name is overloaded and a single function can be inferred from the context, returns the inferred function.
A [SourceUnit][wake.ir.meta.source_unit.SourceUnit] is returned if the identifier references a source unit alias defined in an [ImportDirective][wake.ir.meta.import_directive.ImportDirective].
For example, `Math` in `Math.max(-1, 2)` references the source unit `Math.sol`:
```solidity
import "./Math.sol" as Math;
contract C {
function test() public {
Math.max(-1, 2);
}
}
```
Returns:
Referenced declaration(s).
"""
def resolve(referenced_declaration_id: AstNodeId):
if referenced_declaration_id < 0:
return GlobalSymbol(referenced_declaration_id)
node = self._reference_resolver.resolve_node(
referenced_declaration_id, self.source_unit.cu_hash
)
assert isinstance(
node, (DeclarationAbc, SourceUnit)
), f"Unexpected type: {type(node)}\n{node.source}\n{self.source}\n{self.source_unit.file}"
return node
from ..declarations.function_definition import FunctionDefinition
from ..meta.source_unit import SourceUnit
assert len(self._referenced_declaration_ids) != 0
if len(self._referenced_declaration_ids) == 1:
return resolve(next(iter(self._referenced_declaration_ids)))
else:
# Identifier in ImportDirective symbol alias referencing multiple overloaded functions
ret = set(map(resolve, self._referenced_declaration_ids))
assert all(isinstance(x, FunctionDefinition) for x in ret)
return ret # pyright: ignore reportGeneralTypeIssues
@property
@weak_self_lru_cache(maxsize=2048)
def is_ref_to_state_variable(self) -> bool:
referenced_declaration = self.referenced_declaration
return (
isinstance(referenced_declaration, VariableDeclaration)
and referenced_declaration.is_state_variable
)
@property
def modifies_state(
self,
) -> Set[Tuple[Union[ExpressionAbc, StatementAbc, YulAbc], ModifiesStateFlag]]:
return set()