undefined_variable_detection.rs 17 KB

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