diff --git a/Cargo.lock b/Cargo.lock index 931f265d..8230a60d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -90,9 +90,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arrayvec" @@ -117,9 +117,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bit-set" @@ -362,7 +362,7 @@ source = "git+https://github.com/starkware-libs/cairo?tag=v2.8.2#14b1d8c1566b334 dependencies = [ "cairo-lang-debug", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -630,7 +630,7 @@ version = "2.8.2" source = "git+https://github.com/starkware-libs/cairo?tag=v2.8.2#14b1d8c1566b3346545eb7e65724e3d0cbb80a81" dependencies = [ "hashbrown 0.14.5", - "indexmap 2.4.0", + "indexmap 2.5.0", "itertools 0.12.1", "num-bigint", "num-traits 0.2.19", @@ -696,9 +696,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.16" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -718,14 +718,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -771,18 +771,18 @@ checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" dependencies = [ "proc-macro2", "quote", @@ -800,9 +800,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -855,7 +855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -985,7 +985,7 @@ checksum = "553630feadf7b76442b0849fd25fdf89b860d933623aec9693fed19af0400c78" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1011,9 +1011,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", @@ -1069,9 +1069,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", @@ -1113,9 +1113,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -1259,9 +1259,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" @@ -1399,9 +1399,12 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "oorandom" @@ -1483,7 +1486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.4.0", + "indexmap 2.5.0", ] [[package]] @@ -1509,9 +1512,9 @@ checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "precomputed-hash" @@ -1521,9 +1524,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -1531,11 +1534,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", + "toml_edit", ] [[package]] @@ -1549,9 +1552,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1590,9 +1593,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] @@ -1610,9 +1613,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -1622,9 +1625,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1633,9 +1636,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" @@ -1649,7 +1652,7 @@ version = "0.17.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719825638c59fd26a55412a24561c7c5bcf54364c88b9a7a04ba08a6eafaba8d" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.5.0", "lock_api", "oorandom", "parking_lot", @@ -1669,7 +1672,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1774,7 +1777,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1794,22 +1797,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1820,7 +1823,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1837,9 +1840,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1905,9 +1908,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "starknet-types-core" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6bacf0ba19bc721e518bc4bf389ff13daa8a7c5db5fd320600473b8aa9fcbd" +checksum = "9b889ee5734db8b3c8a6551135c16764bf4ce1ab4955fffbb2ac5b6706542b64" dependencies = [ "lambdaworks-crypto", "lambdaworks-math", @@ -1950,9 +1953,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1994,7 +1997,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2005,28 +2008,28 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2047,7 +2050,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.20", + "toml_edit", ] [[package]] @@ -2061,26 +2064,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.4.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -2102,7 +2094,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2142,27 +2134,27 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "utf8parse" @@ -2379,18 +2371,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -2421,9 +2404,9 @@ checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" @@ -2442,5 +2425,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] diff --git a/FETCH_HEAD b/FETCH_HEAD new file mode 100644 index 00000000..e69de29b diff --git a/crates/cairo-lint-core/src/lints/comparison_to_empty.rs b/crates/cairo-lint-core/src/lints/comparison_to_empty.rs new file mode 100644 index 00000000..1cfb5580 --- /dev/null +++ b/crates/cairo-lint-core/src/lints/comparison_to_empty.rs @@ -0,0 +1,33 @@ +use cairo_lang_defs::plugin::PluginDiagnostic; +use cairo_lang_diagnostics::Severity; +use cairo_lang_syntax::node::ast::{BinaryOperator, Expr}; +use cairo_lang_syntax::node::db::SyntaxGroup; +use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode}; + +pub const COMPARISON_TO_EMPTY: &str = "Consider using `.is_empty()` instead of comparing to an empty array."; + +pub fn check_comparison_to_empty(db: &dyn SyntaxGroup, expr: &Expr, diagnostics: &mut Vec) { + if let Expr::Binary(binary_expr) = expr { + if let BinaryOperator::Eq(_) = binary_expr.op(db) { + let lhs = binary_expr.lhs(db); + let rhs = binary_expr.rhs(db); + + if is_empty_array(db, &lhs) || is_empty_array(db, &rhs) { + diagnostics.push(PluginDiagnostic { + stable_ptr: expr.stable_ptr().untyped(), + message: COMPARISON_TO_EMPTY.to_string(), + severity: Severity::Warning, + }); + } + } + } +} + +fn is_empty_array(db: &dyn SyntaxGroup, expr: &Expr) -> bool { + if let Expr::FunctionCall(func_call) = expr { + let func_path = func_call.path(db); + func_path.as_syntax_node().get_text_without_trivia(db) == "ArrayTrait::new" + } else { + false + } +} diff --git a/crates/cairo-lint-core/src/lints/mod.rs b/crates/cairo-lint-core/src/lints/mod.rs index 95a6b3cd..945cfb08 100644 --- a/crates/cairo-lint-core/src/lints/mod.rs +++ b/crates/cairo-lint-core/src/lints/mod.rs @@ -1,6 +1,7 @@ pub mod bitwise_for_parity_check; pub mod bool_comparison; pub mod breaks; +pub mod comparison_to_empty; pub mod double_comparison; pub mod double_parens; pub mod duplicate_underscore_args; diff --git a/crates/cairo-lint-core/src/plugin.rs b/crates/cairo-lint-core/src/plugin.rs index 3c48dba3..2853df16 100644 --- a/crates/cairo-lint-core/src/plugin.rs +++ b/crates/cairo-lint-core/src/plugin.rs @@ -10,8 +10,8 @@ use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode}; use crate::lints::ifs::*; use crate::lints::manual::*; use crate::lints::{ - bitwise_for_parity_check, bool_comparison, breaks, double_comparison, double_parens, duplicate_underscore_args, - eq_op, erasing_op, loop_for_while, loops, panic, single_match, + bitwise_for_parity_check, bool_comparison, breaks, comparison_to_empty, double_comparison, double_parens, + duplicate_underscore_args, eq_op, erasing_op, loop_for_while, loops, panic, single_match, }; pub fn cairo_lint_plugin_suite() -> PluginSuite { @@ -43,6 +43,7 @@ pub enum CairoLintKind { ManualIsSome, ManualIsNone, ManualExpect, + ComparisonToEmpty, } pub fn diagnostic_kind_from_message(message: &str) -> CairoLintKind { @@ -67,6 +68,7 @@ pub fn diagnostic_kind_from_message(message: &str) -> CairoLintKind { manual_is_some::MANUAL_IS_SOME => CairoLintKind::ManualIsSome, manual_is_none::MANUAL_IS_NONE => CairoLintKind::ManualIsNone, manual_expect::MANUAL_EXPECT => CairoLintKind::ManualExpect, + comparison_to_empty::COMPARISON_TO_EMPTY => CairoLintKind::ComparisonToEmpty, _ => CairoLintKind::Unknown, } } diff --git a/crates/cairo-lint-core/src/plugin.txt b/crates/cairo-lint-core/src/plugin.txt new file mode 100644 index 00000000..a77c36a4 --- /dev/null +++ b/crates/cairo-lint-core/src/plugin.txt @@ -0,0 +1,216 @@ +use cairo_lang_defs::ids::{FunctionWithBodyId, ModuleId, ModuleItemId}; +use cairo_lang_defs::plugin::PluginDiagnostic; +use cairo_lang_semantic::db::SemanticGroup; +use cairo_lang_semantic::plugin::{AnalyzerPlugin, PluginSuite}; +use cairo_lang_semantic::Expr; +use cairo_lang_syntax::node::ast::{ElseClause, Expr as AstExpr, ExprBinary, ExprIf, ExprLoop, ExprMatch}; +use cairo_lang_syntax::node::kind::SyntaxKind; +use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode}; + +use crate::lints::ifs::*; +use crate::lints::manual::*; +use crate::lints::{ + bitwise_for_parity_check, bool_comparison, bool_comparison, breaks, breaks, comparison_to_empty, + comparison_to_empty, double_comparison, double_comparison, double_parens, double_parens, duplicate_underscore_args, + duplicate_underscore_args, eq_op, eq_op, erasing_op, erasing_op, loop_for_while, loop_for_while, loops, loops, + panic, panic, single_match, single_match, +}; + +pub fn cairo_lint_plugin_suite() -> PluginSuite { + let mut suite = PluginSuite::default(); + suite.add_analyzer_plugin::(); + suite +} +#[derive(Debug, Default)] +pub struct CairoLint; + +#[derive(Debug, PartialEq)] +pub enum CairoLintKind { + DestructMatch, + MatchForEquality, + DoubleComparison, + DoubleParens, + EquatableIfLet, + BreakUnit, + BoolComparison, + CollapsibleIfElse, + DuplicateUnderscoreArgs, + LoopMatchPopFront, + BitwiseForParityCheck, + LoopForWhile, + Unknown, + Panic, + ErasingOperation, + ManualOkOr, + ManualIsSome, + ManualIsNone, + ManualExpect, + ComparisonToEmpty, +} + +pub fn diagnostic_kind_from_message(message: &str) -> CairoLintKind { + match message { + single_match::DESTRUCT_MATCH => CairoLintKind::DestructMatch, + single_match::MATCH_FOR_EQUALITY => CairoLintKind::MatchForEquality, + double_parens::DOUBLE_PARENS => CairoLintKind::DoubleParens, + double_comparison::SIMPLIFIABLE_COMPARISON => CairoLintKind::DoubleComparison, + double_comparison::REDUNDANT_COMPARISON => CairoLintKind::DoubleComparison, + double_comparison::CONTRADICTORY_COMPARISON => CairoLintKind::DoubleComparison, + breaks::BREAK_UNIT => CairoLintKind::BreakUnit, + equatable_if_let::EQUATABLE_IF_LET => CairoLintKind::EquatableIfLet, + bool_comparison::BOOL_COMPARISON => CairoLintKind::BoolComparison, + collapsible_if_else::COLLAPSIBLE_IF_ELSE => CairoLintKind::CollapsibleIfElse, + duplicate_underscore_args::DUPLICATE_UNDERSCORE_ARGS => CairoLintKind::DuplicateUnderscoreArgs, + loops::LOOP_MATCH_POP_FRONT => CairoLintKind::LoopMatchPopFront, + panic::PANIC_IN_CODE => CairoLintKind::Panic, + loop_for_while::LOOP_FOR_WHILE => CairoLintKind::LoopForWhile, + erasing_op::ERASING_OPERATION => CairoLintKind::ErasingOperation, + manual_ok_or::MANUAL_OK_OR => CairoLintKind::ManualOkOr, + bitwise_for_parity_check::BITWISE_FOR_PARITY => CairoLintKind::BitwiseForParityCheck, + manual_is_some::MANUAL_IS_SOME => CairoLintKind::ManualIsSome, + manual_is_none::MANUAL_IS_NONE => CairoLintKind::ManualIsNone, + manual_expect::MANUAL_EXPECT => CairoLintKind::ManualExpect, + comparison_to_empty::COMPARISON_TO_EMPTY => CairoLintKind::ComparisonToEmpty, + _ => CairoLintKind::Unknown, + } +} + +impl AnalyzerPlugin for CairoLint { + fn diagnostics(&self, db: &dyn SemanticGroup, module_id: ModuleId) -> Vec { + let mut diags = Vec::new(); + let syntax_db = db.upcast(); + let Ok(items) = db.module_items(module_id) else { + return diags; + }; + for item in &*items { + let function_nodes = match item { + ModuleItemId::Constant(constant_id) => { + constant_id.stable_ptr(db.upcast()).lookup(syntax_db).as_syntax_node() + } + ModuleItemId::FreeFunction(free_function_id) => { + let func_id = FunctionWithBodyId::Free(*free_function_id); + check_function(db, func_id, &mut diags); + free_function_id.stable_ptr(db.upcast()).lookup(syntax_db).as_syntax_node() + } + ModuleItemId::Impl(impl_id) => { + let impl_functions = db.impl_functions(*impl_id); + let Ok(functions) = impl_functions else { + continue; + }; + for (_fn_name, fn_id) in functions.iter() { + let func_id = FunctionWithBodyId::Impl(*fn_id); + check_function(db, func_id, &mut diags); + } + impl_id.stable_ptr(db.upcast()).lookup(syntax_db).as_syntax_node() + } + _ => continue, + } + .descendants(syntax_db); + + for node in function_nodes { + match node.kind(syntax_db) { + SyntaxKind::ExprParenthesized => double_parens::check_double_parens( + db.upcast(), + &AstExpr::from_syntax_node(db.upcast(), node), + &mut diags, + ), + SyntaxKind::StatementBreak => breaks::check_break(db.upcast(), node, &mut diags), + SyntaxKind::ExprIf => { + equatable_if_let::check_equatable_if_let( + db.upcast(), + &ExprIf::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_is_some::check_manual_if_is_some( + db.upcast(), + &ExprIf::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_is_none::check_manual_if_is_none( + db.upcast(), + &ExprIf::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_expect::check_manual_if_expect( + db.upcast(), + &ExprIf::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_ok_or::check_manual_if_ok_or( + db.upcast(), + &ExprIf::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + } + SyntaxKind::ExprBinary => { + let expr_binary = ExprBinary::from_syntax_node(db.upcast(), node); + bool_comparison::check_bool_comparison(db.upcast(), &expr_binary, &mut diags); + double_comparison::check_double_comparison(db.upcast(), &expr_binary, &mut diags); + eq_op::check_eq_op(db.upcast(), &expr_binary, &mut diags); + bitwise_for_parity_check::check_bitwise_for_parity(db.upcast(), &expr_binary, &mut diags); + erasing_op::check_erasing_operation(db.upcast(), expr_binary, &mut diags); + } + SyntaxKind::ElseClause => { + collapsible_if_else::check_collapsible_if_else( + db.upcast(), + &ElseClause::from_syntax_node(db.upcast(), node), + &mut diags, + ); + } + SyntaxKind::ExprLoop => { + loop_for_while::check_loop_for_while( + db.upcast(), + &ExprLoop::from_syntax_node(db.upcast(), node), + &mut diags, + ); + } + SyntaxKind::ExprMatch => { + manual_ok_or::check_manual_ok_or( + db.upcast(), + &ExprMatch::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_is_some::check_manual_is_some( + db.upcast(), + &ExprMatch::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_is_none::check_manual_is_none( + db.upcast(), + &ExprMatch::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + manual_expect::check_manual_expect( + db.upcast(), + &ExprMatch::from_syntax_node(db.upcast(), node.clone()), + &mut diags, + ); + } + _ => continue, + } + } + } + diags + } +} +fn check_function(db: &dyn SemanticGroup, func_id: FunctionWithBodyId, diagnostics: &mut Vec) { + duplicate_underscore_args::check_duplicate_underscore_args( + db.function_with_body_signature(func_id).unwrap().params, + diagnostics, + ); + let Ok(function_body) = db.function_body(func_id) else { + return; + }; + for (_expression_id, expression) in &function_body.arenas.exprs { + match &expression { + Expr::Match(expr_match) => { + single_match::check_single_match(db, expr_match, diagnostics, &function_body.arenas) + } + Expr::Loop(expr_loop) => { + loops::check_loop_match_pop_front(db, expr_loop, diagnostics, &function_body.arenas) + } + Expr::FunctionCall(expr_func) => panic::check_panic_usage(db, expr_func, diagnostics), + _ => (), + }; + } +} diff --git a/crates/cairo-lint-core/tests/test_files/comparison_to_empty/comparison_to_empty b/crates/cairo-lint-core/tests/test_files/comparison_to_empty/comparison_to_empty new file mode 100644 index 00000000..8ea20da5 --- /dev/null +++ b/crates/cairo-lint-core/tests/test_files/comparison_to_empty/comparison_to_empty @@ -0,0 +1,146 @@ +//! > Comparison with ArrayTrait::new() + +//! > cairo_code +fn main() { + let arr = ArrayTrait::new(); + if arr == ArrayTrait::new() { + println!("arr is empty"); + } +} + +//! > diagnostics +warning: Plugin diagnostic: Consider using `.is_empty()` instead of comparing to an empty array. + --> lib.cairo:4:8 + | +4 | if arr == ArrayTrait::new() { + | ------------------------- + | + +//! > fixed +fn main() { + let arr = ArrayTrait::new(); + if arr.is_empty() { + println!("arr is empty"); + } +} + +//! > ========================================================================== + +//! > Comparison with ArrayTrait::new() on LHS + +//! > cairo_code +fn main() { + let arr = ArrayTrait::new(); + if ArrayTrait::new() == arr { + println!("arr is empty"); + } +} + +//! > diagnostics +warning: Plugin diagnostic: Consider using `.is_empty()` instead of comparing to an empty array. + --> lib.cairo:4:8 + | +4 | if ArrayTrait::new() == arr { + | ------------------------- + | + +//! > fixed +fn main() { + let arr = ArrayTrait::new(); + if arr.is_empty() { + println!("arr is empty"); + } +} + +//! > ========================================================================== + +//! > Negated comparison with ArrayTrait::new() + +//! > cairo_code +fn main() { + let arr = ArrayTrait::new(); + if arr != ArrayTrait::new() { + println!("arr is not empty"); + } +} + +//! > diagnostics +warning: Plugin diagnostic: Consider using `.is_empty()` instead of comparing to an empty array. + --> lib.cairo:4:8 + | +4 | if arr != ArrayTrait::new() { + | ------------------------- + | + +//! > fixed +fn main() { + let arr = ArrayTrait::new(); + if !arr.is_empty() { + println!("arr is not empty"); + } +} + +//! > ========================================================================== + +//! > Negated comparison with ArrayTrait::new() on LHS + +//! > cairo_code +fn main() { + let arr = ArrayTrait::new(); + if ArrayTrait::new() != arr { + println!("arr is not empty"); + } +} + +//! > diagnostics +warning: Plugin diagnostic: Consider using `.is_empty()` instead of comparing to an empty array. + --> lib.cairo:4:8 + | +4 | if ArrayTrait::new() != arr { + | ------------------------- + | + +//! > fixed +fn main() { + let arr = ArrayTrait::new(); + if !arr.is_empty() { + println!("arr is not empty"); + } +} + +//! > ========================================================================== + +//! > Comparison with ArrayTrait::new() in complex expression + +//! > cairo_code +fn main() { + let arr1 = ArrayTrait::new(); + let arr2 = ArrayTrait::new(); + if arr1 == ArrayTrait::new() && arr2 != ArrayTrait::new() { + println!("arr1 is empty and arr2 is not"); + } +} + +//! > diagnostics +warning: Plugin diagnostic: Consider using `.is_empty()` instead of comparing to an empty array. + --> lib.cairo:4:8 + | +4 | if arr1 == ArrayTrait::new() && arr2 != ArrayTrait::new() { + | ------------------------- + | + +warning: Plugin diagnostic: Consider using `.is_empty()` instead of comparing to an empty array. + --> lib.cairo:4:37 + | +4 | if arr1 == ArrayTrait::new() && arr2 != ArrayTrait::new() { + | ------------------------- + | + +//! > fixed +fn main() { + let arr1 = ArrayTrait::new(); + let arr2 = ArrayTrait::new(); + if arr1.is_empty() && !arr2.is_empty() { + println!("arr1 is empty and arr2 is not"); + } +} \ No newline at end of file diff --git a/crates/cairo-lint-core/tests/tests.rs b/crates/cairo-lint-core/tests/tests.rs index 77da583d..24d93ce6 100644 --- a/crates/cairo-lint-core/tests/tests.rs +++ b/crates/cairo-lint-core/tests/tests.rs @@ -261,3 +261,13 @@ test_file!( "test manual match result", "test manual match result with unwrapped error" ); + +test_file!( + comparison_to_empty, + comparison_to_empty, + "test comparison with ArrayTrait::new()", + "test comparison with ArrayTrait::new() on LHS", + "test negated comparison with ArrayTrait::new()", + "test negated comparison with ArrayTrait::new() on LHS", + "test comparison with ArrayTrait::new() in complex expression" +);