undefined_variable_detection.rs 17 KB

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