undefined_variable_detection.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. // SPDX-License-Identifier: Apache-2.0
  2. use solang::codegen::{codegen, OptimizationLevel, Options};
  3. use solang::file_resolver::FileResolver;
  4. use solang::sema::ast::Diagnostic;
  5. use solang::sema::ast::Namespace;
  6. use solang::{parse_and_resolve, Target};
  7. use std::ffi::OsStr;
  8. fn parse_and_codegen(src: &'static str) -> Namespace {
  9. let mut cache = FileResolver::new();
  10. cache.set_file_contents("test.sol", src.to_string());
  11. let mut ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::EVM);
  12. let opt = Options {
  13. dead_storage: false,
  14. constant_folding: false,
  15. strength_reduce: false,
  16. vector_to_slice: false,
  17. common_subexpression_elimination: false,
  18. opt_level: OptimizationLevel::Default,
  19. generate_debug_information: false,
  20. log_api_return_codes: false,
  21. log_runtime_errors: false,
  22. log_prints: true,
  23. #[cfg(feature = "wasm_opt")]
  24. wasm_opt: None,
  25. };
  26. codegen(&mut ns, &opt);
  27. ns
  28. }
  29. fn contains_error_message_and_notes(
  30. errors: &[&Diagnostic],
  31. message: &str,
  32. notes_no: usize,
  33. ) -> bool {
  34. for error in errors {
  35. if error.message == message {
  36. return error.notes.len() == notes_no;
  37. }
  38. }
  39. false
  40. }
  41. #[test]
  42. fn used_before_being_defined() {
  43. let file = r#"
  44. contract Test {
  45. bytes byteArr;
  46. bytes32 baRR;
  47. function get() public {
  48. string memory s = "Test";
  49. byteArr = bytes(s);
  50. uint16 a = 1;
  51. uint8 b;
  52. b = uint8(a);
  53. uint256 c;
  54. c = b;
  55. bytes32 b32;
  56. bytes memory char = bytes(bytes32(uint(a) * 2 ** (8 * b)));
  57. baRR = bytes32(c);
  58. bytes32 cdr = bytes32(char);
  59. assert(b32 == baRR);
  60. if(b32 != cdr) {
  61. }
  62. }
  63. }
  64. "#;
  65. let ns = parse_and_codegen(file);
  66. let errors = ns.diagnostics.errors();
  67. assert_eq!(errors.len(), 1);
  68. assert_eq!(errors[0].message, "Variable 'b32' is undefined");
  69. assert_eq!(errors[0].notes.len(), 2);
  70. assert_eq!(
  71. errors[0].notes[0].message,
  72. "Variable read before being defined"
  73. );
  74. assert_eq!(
  75. errors[0].notes[1].message,
  76. "Variable read before being defined"
  77. );
  78. }
  79. #[test]
  80. fn struct_as_ref() {
  81. let file = r#"
  82. contract test_struct_parsing {
  83. struct foo {
  84. bool x;
  85. uint32 y;
  86. }
  87. function func(foo f) private {
  88. // assigning to f members dereferences f
  89. f.x = true;
  90. f.y = 64;
  91. // assigning to f changes the reference
  92. f = foo({ x: false, y: 256 });
  93. // f no longer point to f in caller function
  94. f.x = false;
  95. f.y = 98123;
  96. }
  97. function test() public {
  98. foo f;
  99. func(f);
  100. assert(f.x == true);
  101. assert(f.y == 64);
  102. }
  103. }
  104. "#;
  105. let ns = parse_and_codegen(file);
  106. let errors = ns.diagnostics.errors();
  107. assert_eq!(errors.len(), 1);
  108. assert_eq!(errors[0].message, "Variable 'f' is undefined");
  109. assert_eq!(errors[0].notes.len(), 3);
  110. assert_eq!(
  111. errors[0].notes[0].message,
  112. "Variable read before being defined"
  113. );
  114. assert_eq!(
  115. errors[0].notes[1].message,
  116. "Variable read before being defined"
  117. );
  118. assert_eq!(
  119. errors[0].notes[2].message,
  120. "Variable read before being defined"
  121. );
  122. let file = r#"
  123. contract test_struct_parsing {
  124. struct foo {
  125. bool x;
  126. uint32 y;
  127. }
  128. function func(foo f) private {
  129. // assigning to f members dereferences f
  130. f.x = true;
  131. f.y = 64;
  132. // assigning to f changes the reference
  133. f = foo({ x: false, y: 256 });
  134. // f no longer point to f in caller function
  135. f.x = false;
  136. f.y = 98123;
  137. }
  138. function test() public {
  139. foo f = foo(false, 2);
  140. func(f);
  141. assert(f.x == true);
  142. assert(f.y == 64);
  143. }
  144. }
  145. "#;
  146. let ns = parse_and_codegen(file);
  147. let errors = ns.diagnostics.errors();
  148. assert_eq!(errors.len(), 0);
  149. }
  150. #[test]
  151. fn while_loop() {
  152. let file = r#"
  153. contract testing {
  154. function test(int x) public pure returns (string) {
  155. string s;
  156. while(x > 0){
  157. s = "testing_string";
  158. x--;
  159. }
  160. return s;
  161. }
  162. }
  163. "#;
  164. let ns = parse_and_codegen(file);
  165. let errors = ns.diagnostics.errors();
  166. assert_eq!(errors.len(), 1);
  167. assert_eq!(errors[0].message, "Variable 's' is undefined");
  168. assert_eq!(errors[0].notes.len(), 1);
  169. assert_eq!(
  170. errors[0].notes[0].message,
  171. "Variable read before being defined"
  172. );
  173. let file = r#"
  174. contract testing {
  175. function test(int x) public pure returns (string) {
  176. string s;
  177. while(x > 0){
  178. s = "testing_string";
  179. x--;
  180. }
  181. if(x < 0) {
  182. s = "another_test";
  183. }
  184. return s;
  185. }
  186. }
  187. "#;
  188. let ns = parse_and_codegen(file);
  189. let errors = ns.diagnostics.errors();
  190. assert_eq!(errors.len(), 1);
  191. assert_eq!(errors[0].message, "Variable 's' is undefined");
  192. assert_eq!(errors[0].notes.len(), 1);
  193. assert_eq!(
  194. errors[0].notes[0].message,
  195. "Variable read before being defined"
  196. );
  197. let file = r#"
  198. contract testing {
  199. function test(int x) public pure returns (string) {
  200. string s;
  201. while(x > 0){
  202. s = "testing_string";
  203. x--;
  204. }
  205. if(x < 0) {
  206. s = "another_test";
  207. } else {
  208. s = "should_work";
  209. }
  210. return s;
  211. }
  212. }
  213. "#;
  214. let ns = parse_and_codegen(file);
  215. let errors = ns.diagnostics.errors();
  216. assert_eq!(errors.len(), 0);
  217. }
  218. #[test]
  219. fn for_loop() {
  220. let file = r#"
  221. contract testing {
  222. function test(int x) public pure returns (int) {
  223. int s;
  224. for(int i=0; i<x; i++) {
  225. s = 5;
  226. }
  227. int p;
  228. if(x < 0) {
  229. p = s + 2;
  230. }
  231. return p;
  232. }
  233. }
  234. "#;
  235. let ns = parse_and_codegen(file);
  236. let errors = ns.diagnostics.errors();
  237. assert_eq!(errors.len(), 2);
  238. assert!(contains_error_message_and_notes(
  239. &errors,
  240. "Variable 'p' is undefined",
  241. 1
  242. ));
  243. assert!(contains_error_message_and_notes(
  244. &errors,
  245. "Variable 's' is undefined",
  246. 1
  247. ));
  248. let file = r#"
  249. contract testing {
  250. function test(int x) public pure returns (int) {
  251. int s;
  252. for(int i=0; i<x; i++) {
  253. s = 5;
  254. }
  255. s=5;
  256. int p;
  257. if(x < 0) {
  258. p = s + 2;
  259. } else {
  260. p = 2;
  261. s = 2;
  262. }
  263. return p;
  264. }
  265. }
  266. "#;
  267. let ns = parse_and_codegen(file);
  268. let errors = ns.diagnostics.errors();
  269. assert_eq!(errors.len(), 0);
  270. }
  271. #[test]
  272. fn do_while_loop() {
  273. let file = r#"
  274. contract testing {
  275. struct other {
  276. int a;
  277. }
  278. function test(int x) public pure returns (int) {
  279. other o;
  280. do {
  281. x--;
  282. o = other(1);
  283. }while(x > 0);
  284. return o.a;
  285. }
  286. }
  287. "#;
  288. let ns = parse_and_codegen(file);
  289. let errors = ns.diagnostics.errors();
  290. assert_eq!(errors.len(), 0);
  291. }
  292. #[test]
  293. fn if_else_condition() {
  294. let file = r#"
  295. contract testing {
  296. struct other {
  297. int a;
  298. }
  299. function test(int x) public pure returns (int) {
  300. other o;
  301. if(x > 0) {
  302. o = other(2);
  303. }
  304. return o.a;
  305. }
  306. }
  307. "#;
  308. let ns = parse_and_codegen(file);
  309. let errors = ns.diagnostics.errors();
  310. assert_eq!(errors.len(), 1);
  311. assert_eq!(errors[0].message, "Variable 'o' is undefined");
  312. assert_eq!(errors[0].notes.len(), 1);
  313. assert_eq!(
  314. errors[0].notes[0].message,
  315. "Variable read before being defined"
  316. );
  317. let file = r#"
  318. contract testing {
  319. struct other {
  320. int a;
  321. }
  322. function test(int x) public pure returns (int) {
  323. other o;
  324. if(x > 0) {
  325. x += 2;
  326. } else if(x < 0) {
  327. o = other(2);
  328. } else {
  329. x++;
  330. }
  331. return o.a;
  332. }
  333. }
  334. "#;
  335. let ns = parse_and_codegen(file);
  336. let errors = ns.diagnostics.errors();
  337. assert_eq!(errors.len(), 1);
  338. assert_eq!(errors[0].message, "Variable 'o' is undefined");
  339. assert_eq!(errors[0].notes.len(), 1);
  340. assert_eq!(
  341. errors[0].notes[0].message,
  342. "Variable read before being defined"
  343. );
  344. let file = r#"
  345. contract testing {
  346. struct other {
  347. int a;
  348. }
  349. function test(int x) public pure returns (int) {
  350. other o;
  351. if(x > 0) {
  352. o = other(2);
  353. } else {
  354. o = other(2);
  355. }
  356. return o.a;
  357. }
  358. }
  359. "#;
  360. let ns = parse_and_codegen(file);
  361. let errors = ns.diagnostics.errors();
  362. assert_eq!(errors.len(), 0);
  363. }
  364. #[test]
  365. fn array() {
  366. let file = r#"
  367. contract test {
  368. function testing(int x) public pure returns (int) {
  369. int[] vec;
  370. return vec[0];
  371. }
  372. }
  373. "#;
  374. let ns = parse_and_codegen(file);
  375. let errors = ns.diagnostics.errors();
  376. assert_eq!(errors.len(), 0);
  377. let file = r#"
  378. contract test {
  379. function testing(int x) public pure returns (int) {
  380. int[] vec;
  381. if(x > 0) {
  382. vec.push(2);
  383. }
  384. return vec[0];
  385. }
  386. }
  387. "#;
  388. let ns = parse_and_codegen(file);
  389. let errors = ns.diagnostics.errors();
  390. assert_eq!(errors.len(), 0);
  391. }
  392. #[test]
  393. fn contract_and_enum() {
  394. let file = r#"
  395. contract other {
  396. int public a;
  397. function testing() public returns (int) {
  398. return 2;
  399. }
  400. }
  401. contract test {
  402. enum FreshJuiceSize{ SMALL, MEDIUM, LARGE }
  403. function testing(int x) public returns (int) {
  404. other o;
  405. FreshJuiceSize choice;
  406. if(x > 0 && o.testing() < 5) {
  407. o = new other();
  408. }
  409. assert(choice == FreshJuiceSize.LARGE);
  410. return o.a();
  411. }
  412. }
  413. "#;
  414. let ns = parse_and_codegen(file);
  415. let errors = ns.diagnostics.errors();
  416. assert!(contains_error_message_and_notes(
  417. &errors,
  418. "Variable 'o' is undefined",
  419. 2
  420. ));
  421. assert!(contains_error_message_and_notes(
  422. &errors,
  423. "Variable 'choice' is undefined",
  424. 1
  425. ));
  426. }
  427. #[test]
  428. fn basic_types() {
  429. let file = r#"
  430. contract test {
  431. function testing(int x) public returns (address, int, uint, bool, bytes, int) {
  432. address a;
  433. int i;
  434. uint u;
  435. bool b;
  436. bytes bt;
  437. int[5] vec;
  438. while(x > 0) {
  439. x--;
  440. a = address(this);
  441. i = -2;
  442. u = 2;
  443. b = true;
  444. bt = hex"1234";
  445. vec[0] = 2;
  446. }
  447. return (a, i, u, b, bt, vec[1]);
  448. }
  449. }
  450. "#;
  451. let ns = parse_and_codegen(file);
  452. let errors = ns.diagnostics.errors();
  453. assert!(contains_error_message_and_notes(
  454. &errors,
  455. "Variable 'bt' is undefined",
  456. 1
  457. ));
  458. assert!(contains_error_message_and_notes(
  459. &errors,
  460. "Variable 'b' is undefined",
  461. 1
  462. ));
  463. assert!(contains_error_message_and_notes(
  464. &errors,
  465. "Variable 'a' is undefined",
  466. 1
  467. ));
  468. assert!(contains_error_message_and_notes(
  469. &errors,
  470. "Variable 'i' is undefined",
  471. 1
  472. ));
  473. assert!(contains_error_message_and_notes(
  474. &errors,
  475. "Variable 'u' is undefined",
  476. 1
  477. ));
  478. }
  479. #[test]
  480. fn nested_branches() {
  481. let file = r#"
  482. contract test {
  483. function testing(int x) public returns (int) {
  484. int i;
  485. while(x > 0) {
  486. int b;
  487. if(x > 5) {
  488. b = 2;
  489. }
  490. i = b;
  491. }
  492. int a;
  493. if(x < 5) {
  494. if(x < 2) {
  495. a = 2;
  496. } else {
  497. a = 1;
  498. }
  499. } else if(x < 4) {
  500. a = 5;
  501. }
  502. return i + a;
  503. }
  504. }
  505. "#;
  506. let ns = parse_and_codegen(file);
  507. let errors = ns.diagnostics.errors();
  508. assert!(contains_error_message_and_notes(
  509. &errors,
  510. "Variable 'b' is undefined",
  511. 1
  512. ));
  513. assert!(contains_error_message_and_notes(
  514. &errors,
  515. "Variable 'a' is undefined",
  516. 1
  517. ));
  518. assert!(contains_error_message_and_notes(
  519. &errors,
  520. "Variable 'i' is undefined",
  521. 2
  522. ));
  523. }
  524. #[test]
  525. fn try_catch() {
  526. //TODO: Fix this test case
  527. // let file = r#"
  528. // contract AddNumbers { function add(uint256 a, uint256 b) external pure returns (uint256 c) {c = b;} }
  529. // contract Example {
  530. // AddNumbers addContract;
  531. // event StringFailure(string stringFailure);
  532. // event BytesFailure(bytes bytesFailure);
  533. //
  534. // function exampleFunction(uint256 _a, uint256 _b) public returns (bytes c) {
  535. // bytes r;
  536. // try addContract.add(_a, _b) returns (uint256 _value) {
  537. // r = hex"ABCD";
  538. // return r;
  539. // } catch Error(string memory _err) {
  540. // r = hex"ABCD";
  541. // emit StringFailure(_err);
  542. // } catch (bytes memory _err) {
  543. // emit BytesFailure(_err);
  544. // }
  545. //
  546. // return r;
  547. // }
  548. //
  549. // }
  550. // "#;
  551. //
  552. // let ns = parse_and_codegen(file);
  553. // let errors = ns.diagnostics.errors();
  554. // assert_eq!(errors.len(), 1);
  555. // assert_eq!(errors[0].message, "Variable 'r' is undefined");
  556. // assert_eq!(errors[0].notes.len(), 1);
  557. // assert_eq!(errors[0].notes[0].message, "Variable read before being defined");
  558. let file = r#"
  559. contract AddNumbers { function add(uint256 a, uint256 b) external pure returns (uint256 c) {c = b;} }
  560. contract Example {
  561. AddNumbers addContract;
  562. event StringFailure(string stringFailure);
  563. event BytesFailure(bytes bytesFailure);
  564. function exampleFunction(uint256 _a, uint256 _b) public returns (bytes c) {
  565. bytes r;
  566. try addContract.add(_a, _b) returns (uint256 _value) {
  567. r = hex"ABCD";
  568. return r;
  569. } catch Error(string memory _err) {
  570. r = hex"ABCD";
  571. emit StringFailure(_err);
  572. } catch (bytes memory _err) {
  573. r = hex"ABCD";
  574. emit BytesFailure(_err);
  575. }
  576. return r;
  577. }
  578. }
  579. "#;
  580. let ns = parse_and_codegen(file);
  581. let errors = ns.diagnostics.errors();
  582. assert_eq!(errors.len(), 0);
  583. let file = r#"
  584. contract AddNumbers { function add(uint256 a, uint256 b) external pure returns (uint256 c) {c = b;} }
  585. contract Example {
  586. AddNumbers addContract;
  587. event StringFailure(string stringFailure);
  588. event BytesFailure(bytes bytesFailure);
  589. function exampleFunction(uint256 _a, uint256 _b) public returns (bytes c) {
  590. bytes r;
  591. try addContract.add(_a, _b) returns (uint256 _value) {
  592. return r;
  593. } catch Error(string memory _err) {
  594. r = hex"ABCD";
  595. emit StringFailure(_err);
  596. } catch (bytes memory _err) {
  597. emit BytesFailure(_err);
  598. }
  599. return r;
  600. }
  601. }
  602. "#;
  603. let ns = parse_and_codegen(file);
  604. let errors = ns.diagnostics.errors();
  605. assert_eq!(errors.len(), 1);
  606. assert_eq!(errors[0].message, "Variable 'r' is undefined");
  607. assert_eq!(errors[0].notes.len(), 1);
  608. assert_eq!(
  609. errors[0].notes[0].message,
  610. "Variable read before being defined"
  611. );
  612. let file = r#"
  613. contract AddNumbers { function add(uint256 a, uint256 b) external pure returns (uint256 c) {c = b;} }
  614. contract Example {
  615. AddNumbers addContract;
  616. event StringFailure(string stringFailure);
  617. event BytesFailure(bytes bytesFailure);
  618. function exampleFunction(uint256 _a, uint256 _b) public returns (bytes c) {
  619. bytes r;
  620. try addContract.add(_a, _b) returns (uint256 _value) {
  621. r = hex"ABCD";
  622. return r;
  623. } catch Error(string memory _err) {
  624. emit StringFailure(_err);
  625. } catch (bytes memory _err) {
  626. emit BytesFailure(_err);
  627. }
  628. return r;
  629. }
  630. }
  631. "#;
  632. let ns = parse_and_codegen(file);
  633. let errors = ns.diagnostics.errors();
  634. assert_eq!(errors.len(), 1);
  635. assert_eq!(errors[0].message, "Variable 'r' is undefined");
  636. assert_eq!(errors[0].notes.len(), 1);
  637. assert_eq!(
  638. errors[0].notes[0].message,
  639. "Variable read before being defined"
  640. );
  641. }