undefined_variable_detection.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. use solang::codegen::{codegen, OptimizationLevel, Options};
  2. use solang::file_resolver::FileResolver;
  3. use solang::sema::ast::Diagnostic;
  4. use solang::sema::ast::Namespace;
  5. use solang::{parse_and_resolve, Target};
  6. use std::ffi::OsStr;
  7. fn parse_and_codegen(src: &'static str) -> Namespace {
  8. let mut cache = FileResolver::new();
  9. cache.set_file_contents("test.sol", src.to_string());
  10. let mut ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Ewasm);
  11. let opt = Options {
  12. dead_storage: false,
  13. constant_folding: false,
  14. strength_reduce: false,
  15. vector_to_slice: false,
  16. common_subexpression_elimination: false,
  17. opt_level: OptimizationLevel::Default,
  18. math_overflow_check: false,
  19. };
  20. codegen(&mut ns, &opt);
  21. ns
  22. }
  23. fn contains_error_message_and_notes(
  24. errors: &[&Diagnostic],
  25. message: &str,
  26. notes_no: usize,
  27. ) -> bool {
  28. for error in errors {
  29. if error.message == message {
  30. return error.notes.len() == notes_no;
  31. }
  32. }
  33. false
  34. }
  35. #[test]
  36. fn used_before_being_defined() {
  37. let file = r#"
  38. contract Test {
  39. bytes byteArr;
  40. bytes32 baRR;
  41. function get() public {
  42. string memory s = "Test";
  43. byteArr = bytes(s);
  44. uint16 a = 1;
  45. uint8 b;
  46. b = uint8(a);
  47. uint256 c;
  48. c = b;
  49. bytes32 b32;
  50. bytes memory char = bytes(bytes32(uint(a) * 2 ** (8 * b)));
  51. baRR = bytes32(c);
  52. bytes32 cdr = bytes32(char);
  53. assert(b32 == baRR);
  54. if(b32 != cdr) {
  55. }
  56. }
  57. }
  58. "#;
  59. let ns = parse_and_codegen(file);
  60. let errors = ns.diagnostics.errors();
  61. assert_eq!(errors.len(), 1);
  62. assert_eq!(errors[0].message, "Variable 'b32' is undefined");
  63. assert_eq!(errors[0].notes.len(), 2);
  64. assert_eq!(
  65. errors[0].notes[0].message,
  66. "Variable read before being defined"
  67. );
  68. assert_eq!(
  69. errors[0].notes[1].message,
  70. "Variable read before being defined"
  71. );
  72. }
  73. #[test]
  74. fn struct_as_ref() {
  75. let file = r#"
  76. contract test_struct_parsing {
  77. struct foo {
  78. bool x;
  79. uint32 y;
  80. }
  81. function func(foo f) private {
  82. // assigning to f members dereferences f
  83. f.x = true;
  84. f.y = 64;
  85. // assigning to f changes the reference
  86. f = foo({ x: false, y: 256 });
  87. // f no longer point to f in caller function
  88. f.x = false;
  89. f.y = 98123;
  90. }
  91. function test() public {
  92. foo f;
  93. func(f);
  94. assert(f.x == true);
  95. assert(f.y == 64);
  96. }
  97. }
  98. "#;
  99. let ns = parse_and_codegen(file);
  100. let errors = ns.diagnostics.errors();
  101. assert_eq!(errors.len(), 1);
  102. assert_eq!(errors[0].message, "Variable 'f' is undefined");
  103. assert_eq!(errors[0].notes.len(), 3);
  104. assert_eq!(
  105. errors[0].notes[0].message,
  106. "Variable read before being defined"
  107. );
  108. assert_eq!(
  109. errors[0].notes[1].message,
  110. "Variable read before being defined"
  111. );
  112. assert_eq!(
  113. errors[0].notes[2].message,
  114. "Variable read before being defined"
  115. );
  116. let file = r#"
  117. contract test_struct_parsing {
  118. struct foo {
  119. bool x;
  120. uint32 y;
  121. }
  122. function func(foo f) private {
  123. // assigning to f members dereferences f
  124. f.x = true;
  125. f.y = 64;
  126. // assigning to f changes the reference
  127. f = foo({ x: false, y: 256 });
  128. // f no longer point to f in caller function
  129. f.x = false;
  130. f.y = 98123;
  131. }
  132. function test() public {
  133. foo f = foo(false, 2);
  134. func(f);
  135. assert(f.x == true);
  136. assert(f.y == 64);
  137. }
  138. }
  139. "#;
  140. let ns = parse_and_codegen(file);
  141. let errors = ns.diagnostics.errors();
  142. assert_eq!(errors.len(), 0);
  143. }
  144. #[test]
  145. fn while_loop() {
  146. let file = r#"
  147. contract testing {
  148. function test(int x) public pure returns (string) {
  149. string s;
  150. while(x > 0){
  151. s = "testing_string";
  152. x--;
  153. }
  154. return s;
  155. }
  156. }
  157. "#;
  158. let ns = parse_and_codegen(file);
  159. let errors = ns.diagnostics.errors();
  160. assert_eq!(errors.len(), 1);
  161. assert_eq!(errors[0].message, "Variable 's' is undefined");
  162. assert_eq!(errors[0].notes.len(), 1);
  163. assert_eq!(
  164. errors[0].notes[0].message,
  165. "Variable read before being defined"
  166. );
  167. let file = r#"
  168. contract testing {
  169. function test(int x) public pure returns (string) {
  170. string s;
  171. while(x > 0){
  172. s = "testing_string";
  173. x--;
  174. }
  175. if(x < 0) {
  176. s = "another_test";
  177. }
  178. return s;
  179. }
  180. }
  181. "#;
  182. let ns = parse_and_codegen(file);
  183. let errors = ns.diagnostics.errors();
  184. assert_eq!(errors.len(), 1);
  185. assert_eq!(errors[0].message, "Variable 's' is undefined");
  186. assert_eq!(errors[0].notes.len(), 1);
  187. assert_eq!(
  188. errors[0].notes[0].message,
  189. "Variable read before being defined"
  190. );
  191. let file = r#"
  192. contract testing {
  193. function test(int x) public pure returns (string) {
  194. string s;
  195. while(x > 0){
  196. s = "testing_string";
  197. x--;
  198. }
  199. if(x < 0) {
  200. s = "another_test";
  201. } else {
  202. s = "should_work";
  203. }
  204. return s;
  205. }
  206. }
  207. "#;
  208. let ns = parse_and_codegen(file);
  209. let errors = ns.diagnostics.errors();
  210. assert_eq!(errors.len(), 0);
  211. }
  212. #[test]
  213. fn for_loop() {
  214. let file = r#"
  215. contract testing {
  216. function test(int x) public pure returns (int) {
  217. int s;
  218. for(int i=0; i<x; i++) {
  219. s = 5;
  220. }
  221. int p;
  222. if(x < 0) {
  223. p = s + 2;
  224. }
  225. return p;
  226. }
  227. }
  228. "#;
  229. let ns = parse_and_codegen(file);
  230. let errors = ns.diagnostics.errors();
  231. assert_eq!(errors.len(), 2);
  232. assert!(contains_error_message_and_notes(
  233. &errors,
  234. "Variable 'p' is undefined",
  235. 1
  236. ));
  237. assert!(contains_error_message_and_notes(
  238. &errors,
  239. "Variable 's' is undefined",
  240. 1
  241. ));
  242. let file = r#"
  243. contract testing {
  244. function test(int x) public pure returns (int) {
  245. int s;
  246. for(int i=0; i<x; i++) {
  247. s = 5;
  248. }
  249. s=5;
  250. int p;
  251. if(x < 0) {
  252. p = s + 2;
  253. } else {
  254. p = 2;
  255. s = 2;
  256. }
  257. return p;
  258. }
  259. }
  260. "#;
  261. let ns = parse_and_codegen(file);
  262. let errors = ns.diagnostics.errors();
  263. assert_eq!(errors.len(), 0);
  264. }
  265. #[test]
  266. fn do_while_loop() {
  267. let file = r#"
  268. contract testing {
  269. struct other {
  270. int a;
  271. }
  272. function test(int x) public pure returns (int) {
  273. other o;
  274. do {
  275. x--;
  276. o = other(1);
  277. }while(x > 0);
  278. return o.a;
  279. }
  280. }
  281. "#;
  282. let ns = parse_and_codegen(file);
  283. let errors = ns.diagnostics.errors();
  284. assert_eq!(errors.len(), 0);
  285. }
  286. #[test]
  287. fn if_else_condition() {
  288. let file = r#"
  289. contract testing {
  290. struct other {
  291. int a;
  292. }
  293. function test(int x) public pure returns (int) {
  294. other o;
  295. if(x > 0) {
  296. o = other(2);
  297. }
  298. return o.a;
  299. }
  300. }
  301. "#;
  302. let ns = parse_and_codegen(file);
  303. let errors = ns.diagnostics.errors();
  304. assert_eq!(errors.len(), 1);
  305. assert_eq!(errors[0].message, "Variable 'o' is undefined");
  306. assert_eq!(errors[0].notes.len(), 1);
  307. assert_eq!(
  308. errors[0].notes[0].message,
  309. "Variable read before being defined"
  310. );
  311. let file = r#"
  312. contract testing {
  313. struct other {
  314. int a;
  315. }
  316. function test(int x) public pure returns (int) {
  317. other o;
  318. if(x > 0) {
  319. x += 2;
  320. } else if(x < 0) {
  321. o = other(2);
  322. } else {
  323. x++;
  324. }
  325. return o.a;
  326. }
  327. }
  328. "#;
  329. let ns = parse_and_codegen(file);
  330. let errors = ns.diagnostics.errors();
  331. assert_eq!(errors.len(), 1);
  332. assert_eq!(errors[0].message, "Variable 'o' is undefined");
  333. assert_eq!(errors[0].notes.len(), 1);
  334. assert_eq!(
  335. errors[0].notes[0].message,
  336. "Variable read before being defined"
  337. );
  338. let file = r#"
  339. contract testing {
  340. struct other {
  341. int a;
  342. }
  343. function test(int x) public pure returns (int) {
  344. other o;
  345. if(x > 0) {
  346. o = other(2);
  347. } else {
  348. o = other(2);
  349. }
  350. return o.a;
  351. }
  352. }
  353. "#;
  354. let ns = parse_and_codegen(file);
  355. let errors = ns.diagnostics.errors();
  356. assert_eq!(errors.len(), 0);
  357. }
  358. #[test]
  359. fn array() {
  360. let file = r#"
  361. contract test {
  362. function testing(int x) public pure returns (int) {
  363. int[] vec;
  364. return vec[0];
  365. }
  366. }
  367. "#;
  368. let ns = parse_and_codegen(file);
  369. let errors = ns.diagnostics.errors();
  370. assert_eq!(errors.len(), 1);
  371. assert_eq!(errors[0].message, "Variable 'vec' is undefined");
  372. assert_eq!(errors[0].notes.len(), 1);
  373. assert_eq!(
  374. errors[0].notes[0].message,
  375. "Variable read before being defined"
  376. );
  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. }