1use crate::prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper};
6use alloy_dyn_abi::{DynSolType, DynSolValue};
7use alloy_json_abi::EventParam;
8use alloy_primitives::{Address, B256, U256, hex};
9use eyre::{Result, WrapErr};
10use foundry_compilers::Artifact;
11use foundry_evm::{
12 backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder,
13 inspectors::CheatsConfig, traces::TraceMode,
14};
15use solar::{
16 ast::{BinOpKind, ElementaryType, FunctionKind, LitKind, StateMutability, StrKind, UnOpKind},
17 interface::Symbol,
18 sema::{
19 hir::{
20 ContractId, Event, Expr, ExprKind, Function, ItemId, Res, StmtKind, Type as HirType,
21 TypeKind, Visibility,
22 },
23 ty::{Gcx, Ty, TyKind},
24 },
25};
26use std::ops::ControlFlow;
27use yansi::Paint;
28
29impl SessionSource {
31 pub async fn execute(&mut self) -> Result<ChiselResult> {
33 let output = self.build()?;
35
36 let (bytecode, final_pc) = output.enter(|output| -> Result<_> {
37 let contract = output
38 .repl_contract()
39 .ok_or_else(|| eyre::eyre!("failed to find REPL contract"))?;
40 trace!(?contract, "REPL contract");
41 let bytecode = contract
42 .get_bytecode_bytes()
43 .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
44 Ok((bytecode.into_owned(), output.final_pc(contract)?))
45 })?;
46 let final_pc = final_pc.unwrap_or_default();
47 let mut runner = self.build_runner(final_pc).await?;
48 runner.run(bytecode)
49 }
50
51 pub async fn inspect(&self, input: &str) -> Result<(ControlFlow<()>, Option<String>)> {
63 let line = format!("bytes memory inspectoor = abi.encode({input});");
64 let mut source = match self.clone_with_new_line(line) {
65 Ok((source, _)) => source,
66 Err(err) => {
67 debug!(%err, "failed to build new source for inspection");
68 return Ok((ControlFlow::Continue(()), None));
69 }
70 };
71
72 let mut source_without_inspector = self.clone();
73
74 let (mut res, err) = match source.execute().await {
77 Ok(res) => (res, None),
78 Err(err) => {
79 debug!(?err, %input, "execution failed");
80 match source_without_inspector.execute().await {
81 Ok(res) => (res, Some(err)),
82 Err(_) => {
83 if self.config.foundry_config.verbosity >= 3 {
84 sh_err!("Could not inspect: {err}")?;
85 }
86 return Ok((ControlFlow::Continue(()), None));
87 }
88 }
89 }
90 };
91
92 if let Some(err) = err {
94 let output = source_without_inspector.build()?;
95
96 let formatted_event = output.enter(|output| {
97 let gcx = output.gcx();
98 output.get_event(input).map(|eid| format_event_definition(gcx, gcx.hir.event(eid)))
99 });
100 if let Some(formatted_event) = formatted_event {
101 return Ok((ControlFlow::Break(()), Some(formatted_event?)));
102 }
103
104 if self.config.foundry_config.verbosity >= 3 {
106 sh_err!("Failed eval: {err}")?;
107 }
108
109 debug!(%err, %input, "failed abi encode input");
110 return Ok((ControlFlow::Break(()), None));
111 }
112 drop(source_without_inspector);
113
114 let Some((stack, memory)) = &res.state else {
115 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
117 ChiselDispatcher::show_traces(&decoder, &mut res).await?;
118 }
119 let decoded_logs = decode_console_logs(&res.logs);
120 if !decoded_logs.is_empty() {
121 sh_println!("{}", "Logs:".green())?;
122 for log in decoded_logs {
123 sh_println!(" {log}")?;
124 }
125 }
126
127 return Err(eyre::eyre!("Failed to inspect expression"));
128 };
129
130 let generated_output = source.build()?;
133
134 let res_ty = generated_output.enter(|out| -> Option<(bool, DynSolType)> {
137 let gcx = out.gcx();
138
139 if let Some(direct_ty) = lookup_named_variable_type(gcx, input) {
141 return Some((false, direct_ty));
142 }
143
144 let block = out.run_func_body();
147 let last = block.last()?;
148 let StmtKind::DeclSingle(vid) = last.kind else { return None };
149 let var = gcx.hir.variable(vid);
150 let init = var.initializer?;
151 let ExprKind::Call(_callee, args, _) = &init.kind else { return None };
152 let inner_expr = args.exprs().next()?;
153
154 if let Some(ty) = get_function_return_type(gcx, inner_expr) {
156 return Some((should_continue(inner_expr), ty));
157 }
158
159 let ty = expr_to_dyn(gcx, inner_expr, true)?;
160 Some((should_continue(inner_expr), ty))
161 });
162
163 let Some((cont, ty)) = res_ty else {
164 return Ok((ControlFlow::Continue(()), None));
165 };
166
167 let data = (|| -> Option<_> {
170 let mut offset: usize = stack.last()?.try_into().ok()?;
171 debug!("inspect memory @ {offset}: {}", hex::encode(memory));
172 let mem_offset = memory.get(offset..offset + 32)?;
173 let len: usize = U256::try_from_be_slice(mem_offset)?.try_into().ok()?;
174 offset += 32;
175 memory.get(offset..offset + len)
176 })();
177 let Some(data) = data else {
178 eyre::bail!("Failed to inspect last expression: could not retrieve data from memory")
179 };
180 let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?;
181 let c = if cont { ControlFlow::Continue(()) } else { ControlFlow::Break(()) };
182 Ok((c, Some(format_token(token))))
183 }
184
185 async fn build_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
186 let (evm_env, tx_env, fork_block) = self.config.evm_opts.env().await?;
187
188 let backend = match self.config.backend.clone() {
189 Some(backend) => backend,
190 None => {
191 let fork = self.config.evm_opts.get_fork(
192 &self.config.foundry_config,
193 evm_env.cfg_env.chain_id,
194 fork_block,
195 );
196 let backend = Backend::spawn(fork)?;
197 self.config.backend = Some(backend.clone());
198 backend
199 }
200 };
201
202 let executor = ExecutorBuilder::default()
203 .inspectors(|stack| {
204 stack
205 .logs(self.config.foundry_config.live_logs)
206 .chisel_state(final_pc)
207 .trace_mode(TraceMode::Call)
208 .cheatcodes(
209 CheatsConfig::new(
210 &self.config.foundry_config,
211 self.config.evm_opts.clone(),
212 None,
213 None,
214 None,
215 )
216 .into(),
217 )
218 })
219 .gas_limit(self.config.evm_opts.gas_limit())
220 .spec_id(self.config.foundry_config.evm_spec_id())
221 .legacy_assertions(self.config.foundry_config.legacy_assertions)
222 .build(evm_env, tx_env, backend);
223
224 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
225 }
226}
227
228fn lookup_named_variable_type(gcx: Gcx<'_>, name: &str) -> Option<DynSolType> {
235 let hir = &gcx.hir;
236 let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?;
237
238 for vid in repl.variables() {
240 let var = hir.variable(vid);
241 if var.name.map(|n| n.as_str() == name).unwrap_or(false) {
242 return solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into()));
243 }
244 }
245
246 let run_fid = repl
248 .functions()
249 .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?;
250 let body = hir.function(run_fid).body?;
251 for stmt in body.stmts {
252 match stmt.kind {
253 StmtKind::DeclSingle(vid) => {
254 let var = hir.variable(vid);
255 if var.name.map(|n| n.as_str() == name).unwrap_or(false) {
256 return solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into()));
257 }
258 }
259 StmtKind::DeclMulti(vids, _) => {
260 for vid in vids.iter().flatten() {
261 let var = hir.variable(*vid);
262 if var.name.map(|n| n.as_str() == name).unwrap_or(false) {
263 return solar_ty_to_dyn(gcx, gcx.type_of_item((*vid).into()));
264 }
265 }
266 }
267 _ => {}
268 }
269 }
270 None
271}
272
273fn format_token(token: DynSolValue) -> String {
276 match token {
277 DynSolValue::Address(a) => {
278 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
279 }
280 DynSolValue::FixedBytes(b, byte_len) => {
281 format!(
282 "Type: {}\n└ Data: {}",
283 format!("bytes{byte_len}").red(),
284 hex::encode_prefixed(b).cyan()
285 )
286 }
287 DynSolValue::Int(i, bit_len) => {
288 format!(
289 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
290 format!("int{bit_len}").red(),
291 format!(
292 "0x{}",
293 format!("{i:x}")
294 .chars()
295 .skip(if i.is_negative() { 64 - bit_len / 4 } else { 0 })
296 .collect::<String>()
297 )
298 .cyan(),
299 hex::encode_prefixed(B256::from(i)).cyan(),
300 i.cyan()
301 )
302 }
303 DynSolValue::Uint(i, bit_len) => {
304 format!(
305 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
306 format!("uint{bit_len}").red(),
307 format!("0x{i:x}").cyan(),
308 hex::encode_prefixed(B256::from(i)).cyan(),
309 i.cyan()
310 )
311 }
312 DynSolValue::Bool(b) => {
313 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
314 }
315 DynSolValue::String(_) | DynSolValue::Bytes(_) => {
316 let hex = hex::encode(token.abi_encode());
317 let s = token.as_str();
318 format!(
319 "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
320 if s.is_some() { "string" } else { "dynamic bytes" }.red(),
321 if let Some(s) = s {
322 format!("├ UTF-8: {}\n", s.cyan())
323 } else {
324 String::default()
325 },
326 "[0x00:0x20]".yellow(),
327 format!("0x{}", &hex[64..128]).cyan(),
328 "[0x20:..]".yellow(),
329 format!("0x{}", &hex[128..]).cyan(),
330 "[0x00:0x20]".yellow(),
331 format!("0x{}", &hex[..64]).cyan(),
332 "[0x20:0x40]".yellow(),
333 format!("0x{}", &hex[64..128]).cyan(),
334 "[0x40:..]".yellow(),
335 format!("0x{}", &hex[128..]).cyan(),
336 )
337 }
338 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
339 let mut out = format!(
340 "{}({}) = {}",
341 "array".red(),
342 format!("{}", tokens.len()).yellow(),
343 '['.red()
344 );
345 for token in tokens {
346 out.push_str("\n ├ ");
347 out.push_str(&format_token(token).replace('\n', "\n "));
348 out.push('\n');
349 }
350 out.push_str(&']'.red().to_string());
351 out
352 }
353 DynSolValue::Tuple(tokens) => {
354 let displayed_types = tokens
355 .iter()
356 .map(|t| t.sol_type_name().unwrap_or_default())
357 .collect::<Vec<_>>()
358 .join(", ");
359 let mut out =
360 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
361 for token in tokens {
362 out.push_str("\n ├ ");
363 out.push_str(&format_token(token).replace('\n', "\n "));
364 out.push('\n');
365 }
366 out.push_str(&')'.red().to_string());
367 out
368 }
369 _ => {
370 unimplemented!()
371 }
372 }
373}
374
375fn format_event_definition(gcx: Gcx<'_>, event: &Event<'_>) -> Result<String> {
378 let event_name = event.name.as_str().to_string();
379 let inputs = event
380 .parameters
381 .iter()
382 .map(|&pid| {
383 let var = gcx.hir.variable(pid);
384 let name =
385 var.name.map(|n| n.as_str().to_string()).unwrap_or_else(|| "<anonymous>".into());
386 let kind = solar_ty_to_dyn(gcx, gcx.type_of_item(pid.into()))
387 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
388 Ok(EventParam {
389 name,
390 ty: kind.to_string(),
391 components: vec![],
392 indexed: var.indexed,
393 internal_type: None,
394 })
395 })
396 .collect::<Result<Vec<_>>>()?;
397 let event = alloy_json_abi::Event { name: event_name, inputs, anonymous: event.anonymous };
398
399 Ok(format!(
400 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
401 "event".red(),
402 SolidityHelper::new().highlight(&format!(
403 "{}({})",
404 event.name,
405 event
406 .inputs
407 .iter()
408 .map(|param| format!(
409 "{}{}{}",
410 param.ty,
411 if param.indexed { " indexed" } else { "" },
412 if param.name.is_empty() {
413 String::default()
414 } else {
415 format!(" {}", param.name)
416 },
417 ))
418 .collect::<Vec<_>>()
419 .join(", ")
420 )),
421 event.signature().cyan(),
422 event.selector().cyan(),
423 ))
424}
425
426fn expr_to_dyn(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option<DynSolType> {
435 match &expr.kind {
436 ExprKind::Type(ty) => hir_ty_to_dyn(gcx, ty),
438
439 ExprKind::TypeCall(_) => None,
441
442 ExprKind::Lit(lit) => match &lit.kind {
444 LitKind::Address(_) => Some(DynSolType::Address),
445 LitKind::Bool(_) => Some(DynSolType::Bool),
446 LitKind::Str(kind, _, _) => match kind {
447 StrKind::Hex => Some(DynSolType::Bytes),
448 StrKind::Str | StrKind::Unicode => Some(DynSolType::String),
449 },
450 LitKind::Number(_) | LitKind::Rational(_) => Some(DynSolType::Uint(256)),
451 LitKind::Err(_) => None,
452 },
453
454 ExprKind::Ident(reses) => {
456 let res = reses.first()?;
457 match *res {
458 Res::Item(ItemId::Variable(vid)) => {
459 solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into()))
460 }
461 Res::Item(ItemId::Struct(sid)) => {
462 Some(DynSolType::Tuple(
464 gcx.struct_field_types(sid)
465 .iter()
466 .filter_map(|&t| solar_ty_to_dyn(gcx, t))
467 .collect(),
468 ))
469 }
470 _ => None,
472 }
473 }
474
475 ExprKind::Index(base, idx) => {
477 let base_ty = expr_to_dyn(gcx, base, lookup)?;
478 let num =
479 idx.and_then(|e| parse_number_literal(e)).and_then(|n| usize::try_from(n).ok());
480 match &base.kind {
481 ExprKind::Type(_) | ExprKind::TypeCall(_) => {
483 if let Some(n) = num {
484 Some(DynSolType::FixedArray(Box::new(base_ty), n))
485 } else {
486 Some(DynSolType::Array(Box::new(base_ty)))
487 }
488 }
489 _ => match base_ty {
491 DynSolType::Array(inner) | DynSolType::FixedArray(inner, _) => Some(*inner),
492 DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_) => {
493 Some(DynSolType::FixedBytes(1))
494 }
495 other => Some(other),
496 },
497 }
498 }
499
500 ExprKind::Slice(base, _, _) => expr_to_dyn(gcx, base, lookup),
502
503 ExprKind::Array(values) => values
505 .first()
506 .and_then(|e| expr_to_dyn(gcx, e, lookup))
507 .map(|ty| DynSolType::FixedArray(Box::new(ty), values.len())),
508
509 ExprKind::Tuple(items) => Some(DynSolType::Tuple(
511 items.iter().filter_map(|opt| opt.and_then(|e| expr_to_dyn(gcx, e, lookup))).collect(),
512 )),
513
514 ExprKind::Member(_, _) => resolve_member(gcx, expr, lookup),
516
517 ExprKind::Call(_, _, _) => resolve_call(gcx, expr, lookup),
519
520 ExprKind::New(ty) => hir_ty_to_dyn(gcx, ty),
522
523 ExprKind::Payable(_) => Some(DynSolType::Address),
525
526 ExprKind::Ternary(_, t, e) => {
528 expr_to_dyn(gcx, t, lookup).or_else(|| expr_to_dyn(gcx, e, lookup))
529 }
530
531 ExprKind::Delete(_) => None,
533
534 ExprKind::Unary(op, inner) => match op.kind {
536 UnOpKind::Neg => expr_to_dyn(gcx, inner, lookup).map(|ty| match ty {
537 DynSolType::Uint(n) => DynSolType::Int(n),
538 DynSolType::Int(n) => DynSolType::Uint(n),
539 x => x,
540 }),
541 UnOpKind::Not => Some(DynSolType::Bool),
542 UnOpKind::BitNot
543 | UnOpKind::PreInc
544 | UnOpKind::PreDec
545 | UnOpKind::PostInc
546 | UnOpKind::PostDec => expr_to_dyn(gcx, inner, lookup),
547 },
548
549 ExprKind::Binary(lhs, op, rhs) => match op.kind {
551 BinOpKind::Lt
552 | BinOpKind::Le
553 | BinOpKind::Gt
554 | BinOpKind::Ge
555 | BinOpKind::Eq
556 | BinOpKind::Ne
557 | BinOpKind::And
558 | BinOpKind::Or => Some(DynSolType::Bool),
559 BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul | BinOpKind::Div => {
560 match (expr_to_dyn(gcx, lhs, false), expr_to_dyn(gcx, rhs, false)) {
561 (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_)))
562 | (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => {
563 Some(DynSolType::Int(256))
564 }
565 _ => Some(DynSolType::Uint(256)),
566 }
567 }
568 BinOpKind::Rem
569 | BinOpKind::Pow
570 | BinOpKind::BitAnd
571 | BinOpKind::BitOr
572 | BinOpKind::BitXor
573 | BinOpKind::Shl
574 | BinOpKind::Shr
575 | BinOpKind::Sar => Some(DynSolType::Uint(256)),
576 },
577
578 ExprKind::Assign(lhs, _, _) => expr_to_dyn(gcx, lhs, lookup),
580
581 ExprKind::Err(_) => None,
582 }
583}
584
585fn hir_ty_to_dyn(gcx: Gcx<'_>, ty: &HirType<'_>) -> Option<DynSolType> {
587 match &ty.kind {
588 TypeKind::Elementary(et) => elementary_to_dyn(*et),
589 TypeKind::Array(arr) => {
590 let elem = hir_ty_to_dyn(gcx, &arr.element)?;
591 if let Some(size) = arr.size {
592 let n = parse_number_literal(size).and_then(|n| usize::try_from(n).ok());
593 if let Some(n) = n {
594 Some(DynSolType::FixedArray(Box::new(elem), n))
595 } else {
596 Some(DynSolType::Array(Box::new(elem)))
597 }
598 } else {
599 Some(DynSolType::Array(Box::new(elem)))
600 }
601 }
602 TypeKind::Function(f) => match f.returns.len() {
603 0 => None,
604 1 => {
605 let var = gcx.hir.variable(f.returns[0]);
606 hir_ty_to_dyn(gcx, &var.ty)
607 }
608 _ => Some(DynSolType::Tuple(
609 f.returns
610 .iter()
611 .filter_map(|&pid| hir_ty_to_dyn(gcx, &gcx.hir.variable(pid).ty))
612 .collect(),
613 )),
614 },
615 TypeKind::Mapping(m) => hir_ty_to_dyn(gcx, &m.value),
616 TypeKind::Custom(item) => solar_ty_to_dyn(gcx, gcx.type_of_item(*item)),
617 TypeKind::Err(_) => None,
618 }
619}
620
621fn resolve_member(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option<DynSolType> {
625 let ExprKind::Member(lhs, ident) = &expr.kind else { return None };
626 let member = ident.name;
627
628 if let ExprKind::TypeCall(ty) = &lhs.kind {
630 return match member.as_str() {
631 "name" => Some(DynSolType::String),
632 "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
633 "interfaceId" => Some(DynSolType::FixedBytes(4)),
634 "min" | "max" => match &ty.kind {
636 TypeKind::Elementary(et) => elementary_to_dyn(*et),
637 _ => Some(DynSolType::Uint(256)),
638 },
639 _ => None,
640 };
641 }
642
643 if let ExprKind::Ident(reses) = &lhs.kind
645 && let Some(Res::Builtin(b)) = reses.first()
646 && let Some(ty) = builtin_member(b.name().as_str(), member.as_str())
647 {
648 return Some(ty);
649 }
650
651 if let ExprKind::Type(ty) = &lhs.kind
653 && let TypeKind::Elementary(et) = &ty.kind
654 {
655 return match et {
656 ElementaryType::Address(_) => match member.as_str() {
657 "balance" => Some(DynSolType::Uint(256)),
658 "code" => Some(DynSolType::Bytes),
659 "codehash" => Some(DynSolType::FixedBytes(32)),
660 "send" => Some(DynSolType::Bool),
661 _ => None,
662 },
663 ElementaryType::Bytes => match member.as_str() {
664 "concat" => Some(DynSolType::Bytes),
665 _ => None,
666 },
667 ElementaryType::String => match member.as_str() {
668 "concat" => Some(DynSolType::String),
669 _ => None,
670 },
671 _ => None,
672 };
673 }
674
675 if let Some(lhs_ty) = expr_to_dyn(gcx, lhs, lookup)
677 && let Some(ty) = dyn_member(&lhs_ty, member.as_str())
678 {
679 return Some(ty);
680 }
681
682 if lookup && let Some(mut chain) = expr_name_chain(gcx, lhs) {
684 chain.insert(0, member);
685 return infer_custom_type(gcx, &mut chain, None).ok().flatten();
686 }
687
688 None
689}
690
691fn builtin_member(builtin: &str, member: &str) -> Option<DynSolType> {
693 match builtin {
694 "block" => match member {
695 "coinbase" => Some(DynSolType::Address),
696 "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" | "chainid"
697 | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
698 _ => None,
699 },
700 "msg" => match member {
701 "sender" => Some(DynSolType::Address),
702 "gas" | "value" => Some(DynSolType::Uint(256)),
703 "data" => Some(DynSolType::Bytes),
704 "sig" => Some(DynSolType::FixedBytes(4)),
705 _ => None,
706 },
707 "tx" => match member {
708 "origin" => Some(DynSolType::Address),
709 "gasprice" => Some(DynSolType::Uint(256)),
710 _ => None,
711 },
712 "address" => match member {
713 "balance" => Some(DynSolType::Uint(256)),
714 "code" => Some(DynSolType::Bytes),
715 "codehash" => Some(DynSolType::FixedBytes(32)),
716 "send" => Some(DynSolType::Bool),
717 _ => None,
718 },
719 _ => None,
720 }
721}
722
723fn dyn_member(ty: &DynSolType, member: &str) -> Option<DynSolType> {
725 match member {
726 "length" => match ty {
727 DynSolType::Array(_)
728 | DynSolType::FixedArray(_, _)
729 | DynSolType::Bytes
730 | DynSolType::String
731 | DynSolType::FixedBytes(_) => Some(DynSolType::Uint(256)),
732 _ => None,
733 },
734 "pop" => match ty {
735 DynSolType::Array(inner) => Some(*inner.clone()),
736 _ => None,
737 },
738 "balance" => match ty {
740 DynSolType::Address => Some(DynSolType::Uint(256)),
741 _ => None,
742 },
743 "code" => match ty {
744 DynSolType::Address => Some(DynSolType::Bytes),
745 _ => None,
746 },
747 "codehash" => match ty {
748 DynSolType::Address => Some(DynSolType::FixedBytes(32)),
749 _ => None,
750 },
751 "send" => match ty {
752 DynSolType::Address => Some(DynSolType::Bool),
753 _ => None,
754 },
755 "selector" => Some(DynSolType::FixedBytes(4)),
757 "address" => Some(DynSolType::Address),
758 _ => None,
759 }
760}
761
762fn resolve_call(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option<DynSolType> {
766 let ExprKind::Call(callee, args, _named) = &expr.kind else { return None };
767
768 if let ExprKind::Type(ty) = &callee.kind {
770 return hir_ty_to_dyn(gcx, ty);
771 }
772
773 if let ExprKind::Member(lhs, method) = &callee.kind
775 && let ExprKind::Ident(reses) = &lhs.kind
776 && let Some(Res::Builtin(b)) = reses.first()
777 {
778 match b.name().as_str() {
779 "abi" => {
780 return match method.as_str() {
781 "decode" => {
782 let last = args.exprs().last()?;
783 match expr_to_dyn(gcx, last, false)? {
784 DynSolType::Tuple(tys) => Some(DynSolType::Tuple(tys)),
785 ty => Some(DynSolType::Tuple(vec![ty])),
786 }
787 }
788 s if s.starts_with("encode") => Some(DynSolType::Bytes),
789 _ => None,
790 };
791 }
792 "string" if method.as_str() == "concat" => return Some(DynSolType::String),
793 "bytes" if method.as_str() == "concat" => return Some(DynSolType::Bytes),
794 _ => {}
795 }
796 }
797
798 if let ExprKind::Ident(reses) = &callee.kind {
800 match reses.first() {
801 Some(Res::Builtin(b)) => {
802 return match b.name().as_str() {
803 "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
804 "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
805 "ripemd160" => Some(DynSolType::FixedBytes(20)),
806 "ecrecover" => Some(DynSolType::Address),
807 _ => None,
808 };
809 }
810 Some(Res::Item(ItemId::Function(fid))) if lookup => {
811 let func = gcx.hir.function(*fid);
812 if !matches!(func.state_mutability, StateMutability::View | StateMutability::Pure) {
813 return None;
814 }
815 let ret_id = *func.returns.first()?;
816 return solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into()));
817 }
818 _ => {}
819 }
820 }
821
822 expr_to_dyn(gcx, callee, lookup)
824}
825
826fn expr_name_chain(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option<Vec<Symbol>> {
831 match &expr.kind {
832 ExprKind::Ident(reses) => {
833 let res = reses.first()?;
834 let name = match *res {
835 Res::Item(ItemId::Variable(vid)) => gcx.hir.variable(vid).name?.name,
836 Res::Item(ItemId::Function(fid)) => gcx.hir.function(fid).name?.name,
837 Res::Item(ItemId::Contract(cid)) => gcx.hir.contract(cid).name.name,
838 Res::Builtin(b) => b.name(),
839 _ => return None,
840 };
841 Some(vec![name])
842 }
843 ExprKind::Member(lhs, ident) => {
844 let mut chain = expr_name_chain(gcx, lhs)?;
845 chain.insert(0, ident.name);
846 Some(chain)
847 }
848 _ => None,
849 }
850}
851
852fn infer_custom_type(
857 gcx: Gcx<'_>,
858 custom_type: &mut Vec<Symbol>,
859 contract_id: Option<ContractId>,
860) -> Result<Option<DynSolType>> {
861 if let Some(last) = custom_type.last()
862 && (last.as_str() == "this" || last.as_str() == "super")
863 {
864 custom_type.pop();
865 }
866 if custom_type.is_empty() {
867 return Ok(None);
868 }
869
870 if let Some(cid) = contract_id {
871 let hir = &gcx.hir;
872 let contract = hir.contract(cid);
873
874 let cur_name = *custom_type.last().unwrap();
875 let cur = cur_name.as_str();
876
877 if let Some(fid) = contract
879 .functions()
880 .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str() == cur).unwrap_or(false))
881 {
882 let func = hir.function(fid);
883 if let res @ Some(_) = func_members(func, custom_type) {
884 return Ok(res);
885 }
886
887 if func.returns.is_empty() {
888 eyre::bail!(
889 "This call expression does not return any values to inspect. Insert as statement."
890 )
891 }
892
893 let sm = func.state_mutability;
894 if !matches!(sm, StateMutability::View | StateMutability::Pure) {
895 eyre::bail!("This function mutates state. Insert as a statement.")
896 }
897
898 let ret_id = func.returns[0];
899 let ret_var = hir.variable(ret_id);
900 return Ok(solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into()))
901 .or_else(|| hir_ty_to_dyn(gcx, &ret_var.ty)));
902 }
903
904 if let Some(vid) = contract
906 .variables()
907 .find(|&v| hir.variable(v).name.as_ref().map(|n| n.as_str() == cur).unwrap_or(false))
908 {
909 if let Some(ty) = solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())) {
910 custom_type.pop();
911 if custom_type.is_empty() {
912 return Ok(Some(ty));
913 }
914 let next_member = custom_type.drain(..).next().unwrap_or(Symbol::DUMMY);
915 return Ok(dyn_member(&ty, next_member.as_str()).or(Some(ty)));
916 }
917 let var = hir.variable(vid);
918 return infer_var_ty(gcx, &var.ty, custom_type);
919 }
920
921 if let Some(sid) = contract.items.iter().find_map(|i| {
923 if let ItemId::Struct(sid) = i
924 && hir.strukt(*sid).name.as_str() == cur
925 {
926 Some(*sid)
927 } else {
928 None
929 }
930 }) {
931 let inner = gcx
932 .struct_field_types(sid)
933 .iter()
934 .map(|&t| {
935 solar_ty_to_dyn(gcx, t)
936 .ok_or_else(|| eyre::eyre!("Struct `{cur}` has invalid fields"))
937 })
938 .collect::<Result<Vec<_>>>()?;
939 return Ok(Some(DynSolType::Tuple(inner)));
940 }
941
942 eyre::bail!(
943 "Could not find any definition in contract \"{}\" for type: {custom_type:?}",
944 contract.name.as_str()
945 )
946 }
947
948 let repl_id = gcx
949 .hir
950 .contracts_enumerated()
951 .find_map(|(cid, c)| (c.name.as_str() == "REPL").then_some(cid));
952 if let Some(repl_id) = repl_id
953 && let Ok(res) = infer_custom_type(gcx, custom_type, Some(repl_id))
954 {
955 return Ok(res);
956 }
957
958 let last_name = *custom_type.last().unwrap();
959 let last = last_name.as_str();
960 let contract_match = gcx
961 .hir
962 .contracts_enumerated()
963 .find_map(|(cid, c)| (c.name.as_str() == last).then_some(cid));
964 if let Some(cid) = contract_match {
965 custom_type.pop();
966 return infer_custom_type(gcx, custom_type, Some(cid));
967 }
968
969 Ok(None)
970}
971
972fn infer_var_ty(
974 gcx: Gcx<'_>,
975 ty: &HirType<'_>,
976 custom_type: &mut Vec<Symbol>,
977) -> Result<Option<DynSolType>> {
978 let Some(ty) = hir_ty_to_dyn(gcx, ty) else { return Ok(None) };
979 let next_member = custom_type.drain(..).next();
980 if let Some(m) = next_member {
981 Ok(dyn_member(&ty, m.as_str()).or(Some(ty)))
982 } else {
983 Ok(Some(ty))
984 }
985}
986
987fn get_function_return_type(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option<DynSolType> {
989 let ExprKind::Call(callee, _, _) = &expr.kind else { return None };
990 let ExprKind::Member(obj, fn_ident) = &callee.kind else { return None };
991 let ExprKind::Ident(reses) = &obj.kind else { return None };
992 let res = reses.first()?;
993 let var_id = match res {
994 Res::Item(ItemId::Variable(vid)) => *vid,
995 _ => return None,
996 };
997 let var_ty = gcx.type_of_item(var_id.into()).peel_refs();
998 let cid = match var_ty.kind {
999 TyKind::Contract(cid) => cid,
1000 _ => return None,
1001 };
1002
1003 let hir = &gcx.hir;
1004 let contract = hir.contract(cid);
1005 let fid = contract
1006 .functions()
1007 .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some(fn_ident.as_str()))?;
1008 let func = hir.function(fid);
1009 let ret_id = *func.returns.first()?;
1010 solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into()))
1011}
1012
1013#[inline]
1017fn func_members(func: &Function<'_>, custom_type: &[Symbol]) -> Option<DynSolType> {
1018 if !matches!(func.kind, FunctionKind::Function) {
1019 return None;
1020 }
1021 if !matches!(func.visibility, Visibility::External | Visibility::Public) {
1022 return None;
1023 }
1024 match custom_type.first().unwrap().as_str() {
1025 "address" => Some(DynSolType::Address),
1026 "selector" => Some(DynSolType::FixedBytes(4)),
1027 _ => None,
1028 }
1029}
1030
1031#[inline]
1033fn should_continue(expr: &Expr<'_>) -> bool {
1034 match &expr.kind {
1035 ExprKind::Assign(_, _, _) => true,
1037 ExprKind::Unary(op, _) => matches!(
1039 op.kind,
1040 UnOpKind::PreInc | UnOpKind::PreDec | UnOpKind::PostInc | UnOpKind::PostDec
1041 ),
1042 ExprKind::Call(callee, _, _) => match &callee.kind {
1044 ExprKind::Member(_, ident) => ident.as_str() == "pop",
1045 _ => false,
1046 },
1047 _ => false,
1048 }
1049}
1050
1051const fn parse_number_literal(expr: &Expr<'_>) -> Option<U256> {
1056 match &expr.kind {
1057 ExprKind::Lit(lit) => match &lit.kind {
1058 LitKind::Number(n) => Some(*n),
1059 _ => None,
1060 },
1061 _ => None,
1062 }
1063}
1064
1065const fn elementary_to_dyn(et: ElementaryType) -> Option<DynSolType> {
1067 Some(match et {
1068 ElementaryType::Address(_) => DynSolType::Address,
1069 ElementaryType::Bool => DynSolType::Bool,
1070 ElementaryType::String => DynSolType::String,
1071 ElementaryType::Bytes => DynSolType::Bytes,
1072 ElementaryType::Int(size) => DynSolType::Int(size.bits() as usize),
1073 ElementaryType::UInt(size) => DynSolType::Uint(size.bits() as usize),
1074 ElementaryType::FixedBytes(size) => DynSolType::FixedBytes(size.bytes() as usize),
1075 ElementaryType::Fixed(_, _) | ElementaryType::UFixed(_, _) => return None,
1077 })
1078}
1079
1080fn solar_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> Option<DynSolType> {
1082 match ty.kind {
1083 TyKind::Elementary(et) => elementary_to_dyn(et),
1084 TyKind::Ref(inner, _) => solar_ty_to_dyn(gcx, inner),
1085 TyKind::Array(elem, n) => {
1086 let inner = solar_ty_to_dyn(gcx, elem)?;
1087 let size: usize = n.try_into().ok()?;
1088 Some(DynSolType::FixedArray(Box::new(inner), size))
1089 }
1090 TyKind::DynArray(elem) | TyKind::Slice(elem) => {
1091 let inner = solar_ty_to_dyn(gcx, elem)?;
1092 Some(DynSolType::Array(Box::new(inner)))
1093 }
1094 TyKind::Tuple(tys) => {
1095 Some(DynSolType::Tuple(tys.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect()))
1096 }
1097 TyKind::Mapping(_, _) => None,
1098 TyKind::Struct(sid) => Some(DynSolType::Tuple(
1099 gcx.struct_field_types(sid).iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(),
1100 )),
1101 TyKind::Enum(_) => Some(DynSolType::Uint(8)),
1102 TyKind::Udvt(inner, _) => solar_ty_to_dyn(gcx, inner),
1103 TyKind::Contract(_) => Some(DynSolType::Address),
1104 TyKind::FnPtr(f) => match f.returns.len() {
1109 0 => None,
1110 1 => solar_ty_to_dyn(gcx, f.returns[0]),
1111 _ => Some(DynSolType::Tuple(
1112 f.returns.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(),
1113 )),
1114 },
1115 TyKind::Type(inner) => solar_ty_to_dyn(gcx, inner),
1116 TyKind::Meta(inner) => solar_ty_to_dyn(gcx, inner),
1117 TyKind::IntLiteral(neg, size) => {
1118 let bits = (size.bits() as usize).max(8);
1119 let bits = bits.div_ceil(8) * 8;
1121 let bits = bits.min(256);
1122 if neg {
1123 Some(DynSolType::Int(bits.max(8)))
1124 } else {
1125 Some(DynSolType::Uint(bits.max(8)))
1126 }
1127 }
1128 TyKind::StringLiteral(valid_utf8, _) => {
1129 if valid_utf8 {
1130 Some(DynSolType::String)
1131 } else {
1132 Some(DynSolType::Bytes)
1133 }
1134 }
1135 TyKind::Module(_)
1136 | TyKind::BuiltinModule(_)
1137 | TyKind::Error(_, _)
1138 | TyKind::Event(_, _)
1139 | TyKind::Err(_) => None,
1140 _ => None,
1141 }
1142}
1143
1144#[cfg(test)]
1145mod tests {
1146 use super::*;
1147 use foundry_compilers::{error::SolcError, solc::Solc};
1148 use solar::sema::Compiler;
1149 use std::sync::Mutex;
1150
1151 #[test]
1152 fn test_expressions() {
1153 static EXPRESSIONS: &[(&str, DynSolType)] = {
1154 use DynSolType::*;
1155 &[
1156 ("1 seconds", Uint(256)),
1159 ("1 minutes", Uint(256)),
1160 ("1 hours", Uint(256)),
1161 ("1 days", Uint(256)),
1162 ("1 weeks", Uint(256)),
1163 ("1 wei", Uint(256)),
1164 ("1 gwei", Uint(256)),
1165 ("1 ether", Uint(256)),
1166 ("-1 seconds", Int(256)),
1168 ("-1 minutes", Int(256)),
1169 ("-1 hours", Int(256)),
1170 ("-1 days", Int(256)),
1171 ("-1 weeks", Int(256)),
1172 ("-1 wei", Int(256)),
1173 ("-1 gwei", Int(256)),
1174 ("-1 ether", Int(256)),
1175 ("true ? 1 : 0", Uint(256)),
1177 ("true ? -1 : 0", Int(256)),
1178 ("1 + 1", Uint(256)),
1184 ("1 - 1", Uint(256)),
1185 ("1 * 1", Uint(256)),
1186 ("1 / 1", Uint(256)),
1187 ("1 % 1", Uint(256)),
1188 ("1 ** 1", Uint(256)),
1189 ("1 | 1", Uint(256)),
1190 ("1 & 1", Uint(256)),
1191 ("1 ^ 1", Uint(256)),
1192 ("1 >> 1", Uint(256)),
1193 ("1 << 1", Uint(256)),
1194 ("int(1) + 1", Int(256)),
1196 ("int(1) - 1", Int(256)),
1197 ("int(1) * 1", Int(256)),
1198 ("int(1) / 1", Int(256)),
1199 ("1 + int(1)", Int(256)),
1200 ("1 - int(1)", Int(256)),
1201 ("1 * int(1)", Int(256)),
1202 ("1 / int(1)", Int(256)),
1203 ("uint256 a; a--", Uint(256)),
1207 ("uint256 a; --a", Uint(256)),
1208 ("uint256 a; a++", Uint(256)),
1209 ("uint256 a; ++a", Uint(256)),
1210 ("uint256 a; a = 1", Uint(256)),
1211 ("uint256 a; a += 1", Uint(256)),
1212 ("uint256 a; a -= 1", Uint(256)),
1213 ("uint256 a; a *= 1", Uint(256)),
1214 ("uint256 a; a /= 1", Uint(256)),
1215 ("uint256 a; a %= 1", Uint(256)),
1216 ("uint256 a; a &= 1", Uint(256)),
1217 ("uint256 a; a |= 1", Uint(256)),
1218 ("uint256 a; a ^= 1", Uint(256)),
1219 ("uint256 a; a <<= 1", Uint(256)),
1220 ("uint256 a; a >>= 1", Uint(256)),
1221 ("true && true", Bool),
1225 ("true || true", Bool),
1226 ("true == true", Bool),
1227 ("true != true", Bool),
1228 ("true < true", Bool),
1229 ("true <= true", Bool),
1230 ("true > true", Bool),
1231 ("true >= true", Bool),
1232 ("!true", Bool),
1233 ]
1235 };
1236
1237 let source = &mut source();
1238
1239 let array_expressions: &[(&str, DynSolType)] = &[
1240 ("[1, 2, 3]", fixed_array(DynSolType::Uint(256), 3)),
1241 ("[uint8(1), 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
1242 ("[int8(1), 2, 3]", fixed_array(DynSolType::Int(8), 3)),
1243 ("new uint256[](3)", array(DynSolType::Uint(256))),
1244 ("uint256[] memory a = new uint256[](3);\na[0]", DynSolType::Uint(256)),
1245 ("uint256[] memory a = new uint256[](3);\na[0:3]", array(DynSolType::Uint(256))),
1246 ];
1247 generic_type_test(source, array_expressions);
1248 generic_type_test(source, EXPRESSIONS);
1249 }
1250
1251 #[test]
1252 fn test_types() {
1253 static TYPES: &[(&str, DynSolType)] = {
1254 use DynSolType::*;
1255 &[
1256 ("bool", Bool),
1258 ("true", Bool),
1259 ("false", Bool),
1260 ("uint", Uint(256)),
1264 ("uint(1)", Uint(256)),
1265 ("1", Uint(256)),
1266 ("0x01", Uint(256)),
1267 ("int", Int(256)),
1268 ("int(1)", Int(256)),
1269 ("int(-1)", Int(256)),
1270 ("-1", Int(256)),
1271 ("-0x01", Int(256)),
1272 ("address", Address),
1276 ("address(0)", Address),
1277 ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
1278 ("payable(0)", Address),
1279 ("payable(address(0))", Address),
1280 ("string", String),
1284 ("string(\"hello world\")", String),
1285 ("\"hello world\"", String),
1286 ("unicode\"hello world 😀\"", String),
1287 ("bytes", Bytes),
1291 ("bytes(\"hello world\")", Bytes),
1292 ("bytes(unicode\"hello world 😀\")", Bytes),
1293 ("hex\"68656c6c6f20776f726c64\"", Bytes),
1294 ]
1296 };
1297
1298 let mut types: Vec<(String, DynSolType)> = Vec::with_capacity(96 + 32 + 100);
1299 for (n, b) in (8..=256).step_by(8).zip(1..=32) {
1300 types.push((format!("uint{n}(0)"), DynSolType::Uint(n)));
1301 types.push((format!("int{n}(0)"), DynSolType::Int(n)));
1302 types.push((format!("bytes{b}(0x00)"), DynSolType::FixedBytes(b)));
1303 }
1304
1305 for n in 0..=32 {
1306 types.push((
1307 format!("uint256[{n}]"),
1308 DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), n),
1309 ));
1310 }
1311
1312 generic_type_test(&mut source(), TYPES);
1313 generic_type_test(&mut source(), &types);
1314 }
1315
1316 #[test]
1317 fn test_global_vars() {
1318 init_tracing();
1319
1320 let global_variables = {
1322 use DynSolType::*;
1323 &[
1324 ("abi.decode(bytes, (uint8[13]))", Tuple(vec![FixedArray(Box::new(Uint(8)), 13)])),
1326 ("abi.decode(bytes, (address, bytes))", Tuple(vec![Address, Bytes])),
1327 ("abi.decode(bytes, (uint112, uint48))", Tuple(vec![Uint(112), Uint(48)])),
1328 ("abi.encode(_, _)", Bytes),
1329 ("abi.encodePacked(_, _)", Bytes),
1330 ("abi.encodeWithSelector(bytes4, _, _)", Bytes),
1331 ("abi.encodeCall(func(), (_, _))", Bytes),
1332 ("abi.encodeWithSignature(string, _, _)", Bytes),
1333 ("bytes.concat()", Bytes),
1337 ("bytes.concat(_)", Bytes),
1338 ("bytes.concat(_, _)", Bytes),
1339 ("string.concat()", String),
1340 ("string.concat(_)", String),
1341 ("string.concat(_, _)", String),
1342 ("block.basefee", Uint(256)),
1346 ("block.chainid", Uint(256)),
1347 ("block.coinbase", Address),
1348 ("block.difficulty", Uint(256)),
1349 ("block.gaslimit", Uint(256)),
1350 ("block.number", Uint(256)),
1351 ("block.timestamp", Uint(256)),
1352 ("gasleft()", Uint(256)),
1356 ("msg.data", Bytes),
1357 ("msg.sender", Address),
1358 ("msg.sig", FixedBytes(4)),
1359 ("msg.value", Uint(256)),
1360 ("tx.gasprice", Uint(256)),
1361 ("tx.origin", Address),
1362 ("blockhash(uint)", FixedBytes(32)),
1373 ("keccak256(bytes)", FixedBytes(32)),
1374 ("sha256(bytes)", FixedBytes(32)),
1375 ("ripemd160(bytes)", FixedBytes(20)),
1376 ("ecrecover(bytes32, uint8, bytes32, bytes32)", Address),
1377 ("addmod(uint, uint, uint)", Uint(256)),
1378 ("mulmod(uint, uint, uint)", Uint(256)),
1379 ("address(_)", Address),
1383 ("address(this)", Address),
1384 ("address.balance", Uint(256)),
1387 ("address.code", Bytes),
1388 ("address.codehash", FixedBytes(32)),
1389 ("address.send(uint256)", Bool),
1390 ("type(C).name", String),
1395 ("type(C).creationCode", Bytes),
1396 ("type(C).runtimeCode", Bytes),
1397 ("type(I).interfaceId", FixedBytes(4)),
1398 ("type(uint256).min", Uint(256)),
1399 ("type(int128).min", Int(128)),
1400 ("type(int256).min", Int(256)),
1401 ("type(uint256).max", Uint(256)),
1402 ("type(int128).max", Int(128)),
1403 ("type(int256).max", Int(256)),
1404 ("type(Enum1).min", Uint(256)),
1405 ("type(Enum1).max", Uint(256)),
1406 ("this.run.address", Address),
1408 ("this.run.selector", FixedBytes(4)),
1409 ]
1410 };
1411
1412 generic_type_test(&mut source(), global_variables);
1413 }
1414
1415 #[track_caller]
1416 fn source() -> SessionSource {
1417 static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
1419
1420 let version = "0.8.20";
1423 for _ in 0..3 {
1424 let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap();
1425 if !*is_preinstalled {
1426 let solc = Solc::find_or_install(&version.parse().unwrap())
1427 .map(|solc| (solc.version.clone(), solc));
1428 match solc {
1429 Ok((v, solc)) => {
1430 let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
1432 break;
1433 }
1434 Err(e) => {
1435 let _ = sh_err!("error while trying to re-install Solc v{version}: {e}");
1437 let solc = Solc::blocking_install(&version.parse().unwrap());
1438 if solc.map_err(SolcError::from).is_ok() {
1439 *is_preinstalled = true;
1440 break;
1441 }
1442 }
1443 }
1444 }
1445 }
1446
1447 SessionSource::new(Default::default()).unwrap()
1448 }
1449
1450 fn array(ty: DynSolType) -> DynSolType {
1451 DynSolType::Array(Box::new(ty))
1452 }
1453
1454 fn fixed_array(ty: DynSolType, len: usize) -> DynSolType {
1455 DynSolType::FixedArray(Box::new(ty), len)
1456 }
1457
1458 fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option<DynSolType> {
1467 if clear {
1468 s.clear();
1469 }
1470
1471 *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0;
1473
1474 let input = format!("{};", input.trim_end().trim_end_matches(';'));
1475 let (new_source, _) = s.clone_with_new_line(input).unwrap();
1476 *s = new_source.clone();
1477
1478 let src = new_source.to_repl_source();
1479 let sess =
1480 solar::interface::Session::builder().with_buffer_emitter(Default::default()).build();
1481 let mut compiler = Compiler::new(sess);
1482
1483 compiler.enter_mut(|c| -> Option<DynSolType> {
1484 let lowered = {
1486 let mut pcx = c.parse();
1487 let file = c
1488 .sess()
1489 .source_map()
1490 .new_source_file(
1491 std::path::PathBuf::from(new_source.file_name.clone()),
1492 src.clone(),
1493 )
1494 .ok()?;
1495 pcx.add_file(file);
1496 pcx.parse();
1497 matches!(c.lower_asts(), Ok(ControlFlow::Continue(())))
1498 };
1499 if !lowered {
1500 return None;
1501 }
1502
1503 let gcx = c.gcx();
1505 let hir = &gcx.hir;
1506 let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?;
1507 let run_fid = repl
1508 .functions()
1509 .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?;
1510 let body = hir.function(run_fid).body?;
1511 let last = body.last()?;
1512 let expr = match last.kind {
1513 StmtKind::Expr(e) => e,
1514 _ => return None,
1515 };
1516 expr_to_dyn(gcx, expr, true)
1517 })
1518 }
1519
1520 fn generic_type_test<'a, T, I>(s: &mut SessionSource, input: I)
1521 where
1522 T: AsRef<str> + std::fmt::Display + 'a,
1523 I: IntoIterator<Item = &'a (T, DynSolType)> + 'a,
1524 {
1525 for (input, expected) in input {
1526 let input = input.as_ref();
1527 let ty = get_type_ethabi(s, input, true);
1528 assert_eq!(ty.as_ref(), Some(expected), "\n{input}");
1529 }
1530 }
1531
1532 fn init_tracing() {
1533 let _ = tracing_subscriber::FmtSubscriber::builder()
1534 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
1535 .try_init();
1536 }
1537}